mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-03 21:43:32 +00:00 
			
		
		
		
	Compare commits
	
		
			272 Commits
		
	
	
		
			1.10.1
			...
			neels/sdp2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					2c42f44d7e | ||
| 
						 | 
					53877bfb8e | ||
| 
						 | 
					aca4ad4cce | ||
| 
						 | 
					46c3882f75 | ||
| 
						 | 
					5f8bad4b21 | ||
| 
						 | 
					35357e80ee | ||
| 
						 | 
					6c09836faa | ||
| 
						 | 
					c43d76dc4d | ||
| 
						 | 
					0051a29df9 | ||
| 
						 | 
					ef5f1d3d7b | ||
| 
						 | 
					c74c3d02c8 | ||
| 
						 | 
					7338fd833b | ||
| 
						 | 
					fcf1864db3 | ||
| 
						 | 
					9221b67f3b | ||
| 
						 | 
					732d595a26 | ||
| 
						 | 
					3bc9d53628 | ||
| 
						 | 
					b5a4f45dbf | ||
| 
						 | 
					e6bafbf3e3 | ||
| 
						 | 
					5c7b128166 | ||
| 
						 | 
					5469edc278 | ||
| 
						 | 
					66952183a9 | ||
| 
						 | 
					cc97f58d96 | ||
| 
						 | 
					5d6931881a | ||
| 
						 | 
					0f4be7699b | ||
| 
						 | 
					546983cdbc | ||
| 
						 | 
					c04de4e7bb | ||
| 
						 | 
					d46cdd80de | ||
| 
						 | 
					6676e63c89 | ||
| 
						 | 
					1f99c3aaae | ||
| 
						 | 
					e6ef4e7484 | ||
| 
						 | 
					b0fa041a28 | ||
| 
						 | 
					e17bf77bdd | ||
| 
						 | 
					802b1fad61 | ||
| 
						 | 
					8a8973eec2 | ||
| 
						 | 
					17e7ea66e4 | ||
| 
						 | 
					9c3fad2c42 | ||
| 
						 | 
					227a800d26 | ||
| 
						 | 
					24cae67f3e | ||
| 
						 | 
					0d47c408ac | ||
| 
						 | 
					f1b557988d | ||
| 
						 | 
					95e504aa97 | ||
| 
						 | 
					a5ae091fdd | ||
| 
						 | 
					249f799a88 | ||
| 
						 | 
					dc395843ff | ||
| 
						 | 
					c5d003e09e | ||
| 
						 | 
					e6398d886b | ||
| 
						 | 
					23140d6f94 | ||
| 
						 | 
					36c823eef1 | ||
| 
						 | 
					fa393fa1e9 | ||
| 
						 | 
					e0fc37a437 | ||
| 
						 | 
					4aa2b0e35b | ||
| 
						 | 
					e01f6e7f33 | ||
| 
						 | 
					388b48d14e | ||
| 
						 | 
					352b967d1b | ||
| 
						 | 
					ed4da25f2f | ||
| 
						 | 
					5abda312ed | ||
| 
						 | 
					75862d3131 | ||
| 
						 | 
					8733542b13 | ||
| 
						 | 
					d7aac20cc1 | ||
| 
						 | 
					8b5361412c | ||
| 
						 | 
					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 | ||
| 
						 | 
					342a9a9418 | ||
| 
						 | 
					22f9cf2687 | ||
| 
						 | 
					26d6b2b5ce | ||
| 
						 | 
					169d50ed4a | ||
| 
						 | 
					82dfb505db | ||
| 
						 | 
					9c0d105d09 | ||
| 
						 | 
					ded02cfc29 | ||
| 
						 | 
					0cbed362cd | ||
| 
						 | 
					310e41cdf3 | ||
| 
						 | 
					a55bfdc436 | ||
| 
						 | 
					6ea8d7dac6 | ||
| 
						 | 
					9befdeb673 | ||
| 
						 | 
					5fb814b6b9 | ||
| 
						 | 
					9ac9cb963d | ||
| 
						 | 
					377d39122d | ||
| 
						 | 
					d1e94c7ac3 | ||
| 
						 | 
					6db3f7f1cb | ||
| 
						 | 
					58c0b0a6f7 | ||
| 
						 | 
					cca55247ed | ||
| 
						 | 
					d266c374ee | ||
| 
						 | 
					d358d71206 | ||
| 
						 | 
					5e6661eece | ||
| 
						 | 
					c1468ef7a5 | ||
| 
						 | 
					24763eaa73 | ||
| 
						 | 
					d8c2f31a87 | ||
| 
						 | 
					01b812ea76 | ||
| 
						 | 
					0807771b9e | ||
| 
						 | 
					103e50a561 | ||
| 
						 | 
					3a914e33e9 | ||
| 
						 | 
					2172f402a5 | ||
| 
						 | 
					8c138fe89c | ||
| 
						 | 
					7881f7da0b | ||
| 
						 | 
					d17a9de79e | ||
| 
						 | 
					8e74c63e15 | ||
| 
						 | 
					80dd576ad2 | ||
| 
						 | 
					cb922b646f | ||
| 
						 | 
					5d8b5b0935 | ||
| 
						 | 
					35bd2523e2 | ||
| 
						 | 
					f45a9b9760 | ||
| 
						 | 
					f48cd95bbc | ||
| 
						 | 
					7558c20973 | ||
| 
						 | 
					6ddc79b6ed | ||
| 
						 | 
					ff5921573a | ||
| 
						 | 
					d4ad77d326 | ||
| 
						 | 
					a6917fecb1 | ||
| 
						 | 
					58877f04e3 | ||
| 
						 | 
					d4707e2185 | ||
| 
						 | 
					f633302c73 | ||
| 
						 | 
					36413c011f | ||
| 
						 | 
					833281dcb9 | ||
| 
						 | 
					07bbd766c5 | ||
| 
						 | 
					c7c8e649f1 | ||
| 
						 | 
					c0e1b1aec5 | ||
| 
						 | 
					9735d21b9d | ||
| 
						 | 
					705565d745 | ||
| 
						 | 
					63088e0e61 | ||
| 
						 | 
					887c93e198 | ||
| 
						 | 
					2ca48823b1 | ||
| 
						 | 
					e7de137b29 | ||
| 
						 | 
					c1ad7fd92b | ||
| 
						 | 
					3779919551 | ||
| 
						 | 
					8e0d101997 | ||
| 
						 | 
					654b68d602 | ||
| 
						 | 
					70c03f5e44 | ||
| 
						 | 
					e56ec4a146 | ||
| 
						 | 
					5ffd127384 | ||
| 
						 | 
					051eb4d563 | ||
| 
						 | 
					e740665cab | ||
| 
						 | 
					abb9d47c90 | ||
| 
						 | 
					2dd2b33030 | ||
| 
						 | 
					69f15e4e95 | ||
| 
						 | 
					b0ea7976bf | ||
| 
						 | 
					9d939b6d5d | ||
| 
						 | 
					de805b6772 | ||
| 
						 | 
					cb25a0aa48 | ||
| 
						 | 
					72eff2cf6a | ||
| 
						 | 
					1c5fcb0791 | ||
| 
						 | 
					48fb176fbc | ||
| 
						 | 
					4d090d85f4 | ||
| 
						 | 
					81c5847434 | ||
| 
						 | 
					d48a811f6c | ||
| 
						 | 
					b7f52b414e | ||
| 
						 | 
					ea7aaf2eca | ||
| 
						 | 
					432ee9d150 | ||
| 
						 | 
					b4a59475d2 | ||
| 
						 | 
					2177919edb | ||
| 
						 | 
					87f114a1d0 | ||
| 
						 | 
					928a20b540 | ||
| 
						 | 
					b9022497d8 | ||
| 
						 | 
					582c2bf7b0 | ||
| 
						 | 
					663ba5c41f | ||
| 
						 | 
					daf5bcea99 | ||
| 
						 | 
					a29f155301 | ||
| 
						 | 
					941e317c97 | ||
| 
						 | 
					3fbf035923 | ||
| 
						 | 
					21bcf6abd8 | ||
| 
						 | 
					26e810d8a9 | ||
| 
						 | 
					4d1a52d463 | ||
| 
						 | 
					83df47cd7a | ||
| 
						 | 
					c5bbb78ffe | ||
| 
						 | 
					e0e5a772f4 | ||
| 
						 | 
					e39ae87ea3 | ||
| 
						 | 
					8b0a614e43 | ||
| 
						 | 
					c61664c2b2 | ||
| 
						 | 
					15e7e4f37d | ||
| 
						 | 
					ab8371c6d7 | ||
| 
						 | 
					8c777990a5 | ||
| 
						 | 
					ec5e85b84e | ||
| 
						 | 
					ab4e8663e1 | ||
| 
						 | 
					a4f783b51b | ||
| 
						 | 
					479cf76e55 | ||
| 
						 | 
					33639166a2 | ||
| 
						 | 
					709aad28ed | ||
| 
						 | 
					5892b2e4db | ||
| 
						 | 
					f1f73b8e1d | ||
| 
						 | 
					0fdc08c551 | ||
| 
						 | 
					5be57da075 | ||
| 
						 | 
					147184eb06 | 
@@ -18,13 +18,14 @@ SUBDIRS = \
 | 
			
		||||
 | 
			
		||||
pkgconfigdir = $(libdir)/pkgconfig
 | 
			
		||||
pkgconfig_DATA = \
 | 
			
		||||
	libosmo-sdp.pc \
 | 
			
		||||
	libosmo-mgcp-client.pc \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
BUILT_SOURCES = $(top_srcdir)/.version
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	     .version \
 | 
			
		||||
	     contrib/osmo-mgw.spec.in \
 | 
			
		||||
	     README.md \
 | 
			
		||||
	     debian \
 | 
			
		||||
	     git-version-gen \
 | 
			
		||||
	     osmoappdesc.py \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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.
 | 
			
		||||
							
								
								
									
										35
									
								
								TODO-RELEASE
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								TODO-RELEASE
									
									
									
									
									
								
							@@ -1,26 +1,11 @@
 | 
			
		||||
# When cleaning up this file upon a release:
 | 
			
		||||
#
 | 
			
		||||
# - Note that the release version number is entirely unrelated to the API
 | 
			
		||||
#   versions. A release version 5.2.3 may happily have an API version of 42:7:5.
 | 
			
		||||
#
 | 
			
		||||
# - Bump API version in src/lib*/Makefile.am files according to chapter
 | 
			
		||||
#   "Library interface versions" of the libtool documentation.
 | 
			
		||||
#   https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
 | 
			
		||||
#
 | 
			
		||||
# - Iff the 'current' API version has changed, rename debian/lib*.install
 | 
			
		||||
#
 | 
			
		||||
# API version bumping for the impatient:
 | 
			
		||||
# LIBVERSION=c:r:a  (current:revision_of_current:backwards_compat_age)
 | 
			
		||||
# 5:2:4 means that
 | 
			
		||||
# - this implements version 5 of the API;
 | 
			
		||||
# - this is the 2nd (compatible) revision of API version 5;
 | 
			
		||||
# - this is backwards compatible to all APIs since 4 versions ago,
 | 
			
		||||
#   i.e. callers that need API versions from 1 to 5 can use this.
 | 
			
		||||
#
 | 
			
		||||
# Bumping API versions recipe:
 | 
			
		||||
# If the library source code has changed at all since the last update, r++;
 | 
			
		||||
# If any interfaces have been added, removed, or changed since the last update, c++, r=0;
 | 
			
		||||
# If any interfaces have been added since the last public release, a++;
 | 
			
		||||
# If any interfaces have been removed or changed since the last public release, a=0.
 | 
			
		||||
#
 | 
			
		||||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
 | 
			
		||||
# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
 | 
			
		||||
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
 | 
			
		||||
# LIBVERSION=c:r:a
 | 
			
		||||
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
 | 
			
		||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a.
 | 
			
		||||
# If any interfaces have been added since the last public release: c:r:a + 1.
 | 
			
		||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
 | 
			
		||||
#library	what			description / commit summary line
 | 
			
		||||
libosmocore		bump_dep; workaround	Bump libosmocore version dependency after I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5
 | 
			
		||||
						has been merged to libosmocore.git; then remove my_msgb_copy_c wrapper function.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								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.7.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.7.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.7.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.7.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.2.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.3.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.3.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.10.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.6.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.6.0)
 | 
			
		||||
 | 
			
		||||
CFLAGS="$CFLAGS -pthread"
 | 
			
		||||
CFLAGS="$CFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
 | 
			
		||||
CPPFLAGS="$CPPFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
 | 
			
		||||
LDFLAGS="$LDFLAGS -pthread"
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(sanitize,
 | 
			
		||||
@@ -79,6 +80,7 @@ AC_ARG_ENABLE(werror,
 | 
			
		||||
if test x"$werror" = x"yes"
 | 
			
		||||
then
 | 
			
		||||
	WERROR_FLAGS="-Werror"
 | 
			
		||||
	WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
 | 
			
		||||
	WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
 | 
			
		||||
	WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
 | 
			
		||||
	CFLAGS="$CFLAGS $WERROR_FLAGS"
 | 
			
		||||
@@ -185,11 +187,14 @@ AM_CONFIG_HEADER(bscconfig.h)
 | 
			
		||||
 | 
			
		||||
AC_OUTPUT(
 | 
			
		||||
    libosmo-mgcp-client.pc
 | 
			
		||||
    libosmo-sdp.pc
 | 
			
		||||
    include/Makefile
 | 
			
		||||
    include/osmocom/Makefile
 | 
			
		||||
    include/osmocom/sdp/Makefile
 | 
			
		||||
    include/osmocom/mgcp_client/Makefile
 | 
			
		||||
    include/osmocom/mgcp/Makefile
 | 
			
		||||
    src/Makefile
 | 
			
		||||
    src/libosmo-sdp/Makefile
 | 
			
		||||
    src/libosmo-mgcp-client/Makefile
 | 
			
		||||
    src/libosmo-mgcp/Makefile
 | 
			
		||||
    src/osmo-mgw/Makefile
 | 
			
		||||
@@ -197,10 +202,10 @@ AC_OUTPUT(
 | 
			
		||||
    tests/atlocal
 | 
			
		||||
    tests/mgcp_client/Makefile
 | 
			
		||||
    tests/mgcp/Makefile
 | 
			
		||||
    tests/sdp/Makefile
 | 
			
		||||
    doc/Makefile
 | 
			
		||||
    doc/examples/Makefile
 | 
			
		||||
    doc/manuals/Makefile
 | 
			
		||||
    contrib/Makefile
 | 
			
		||||
    contrib/systemd/Makefile
 | 
			
		||||
    contrib/osmo-mgw.spec
 | 
			
		||||
    Makefile)
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ export deps inst
 | 
			
		||||
osmo-clean-workspace.sh
 | 
			
		||||
 | 
			
		||||
mkdir "$deps" || true
 | 
			
		||||
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
 | 
			
		||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
 | 
			
		||||
 | 
			
		||||
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
 | 
			
		||||
 | 
			
		||||
@@ -30,8 +30,8 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
 | 
			
		||||
export LD_LIBRARY_PATH="$inst/lib"
 | 
			
		||||
export PATH="$inst/bin:$PATH"
 | 
			
		||||
 | 
			
		||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
 | 
			
		||||
osmo-build-dep.sh libosmo-abis
 | 
			
		||||
osmo-build-dep.sh libosmo-netif
 | 
			
		||||
 | 
			
		||||
# Additional configure options and depends
 | 
			
		||||
CONFIG=""
 | 
			
		||||
 
 | 
			
		||||
@@ -1,137 +0,0 @@
 | 
			
		||||
#
 | 
			
		||||
# spec file for package osmo-mgw
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
 | 
			
		||||
#
 | 
			
		||||
# All modifications and additions to the file contributed by third parties
 | 
			
		||||
# remain the property of their copyright owners, unless otherwise agreed
 | 
			
		||||
# upon. The license for this file, and modifications and additions to the
 | 
			
		||||
# file, is the same license as for the pristine package itself (unless the
 | 
			
		||||
# license for the pristine package is not an Open Source License, in which
 | 
			
		||||
# case the license is the MIT License). An "Open Source License" is a
 | 
			
		||||
# license that conforms to the Open Source Definition (Version 1.9)
 | 
			
		||||
# published by the Open Source Initiative.
 | 
			
		||||
 | 
			
		||||
# Please submit bugfixes or comments via http://bugs.opensuse.org/
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
Name:           osmo-mgw
 | 
			
		||||
Version:        @VERSION@
 | 
			
		||||
Release:        0
 | 
			
		||||
Summary:        Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
 | 
			
		||||
License:        AGPL-3.0-or-later AND GPL-2.0-or-later
 | 
			
		||||
Group:          Hardware/Mobile
 | 
			
		||||
URL:            https://osmocom.org/projects/osmo-mgw
 | 
			
		||||
Source:         %{name}-%{version}.tar.xz
 | 
			
		||||
BuildRequires:  automake >= 1.9
 | 
			
		||||
BuildRequires:  libtool >= 2
 | 
			
		||||
BuildRequires:  pkgconfig >= 0.20
 | 
			
		||||
%if 0%{?suse_version}
 | 
			
		||||
BuildRequires:  systemd-rpm-macros
 | 
			
		||||
%endif
 | 
			
		||||
BuildRequires:  pkgconfig(libosmo-netif) >= 1.2.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocore) >= 1.7.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoctrl) >= 1.7.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmogsm) >= 1.7.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmovty) >= 1.7.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocoding) >= 1.7.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoabis) >= 1.3.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmotrau) >= 1.3.0
 | 
			
		||||
%{?systemd_requires}
 | 
			
		||||
 | 
			
		||||
%description
 | 
			
		||||
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
 | 
			
		||||
 | 
			
		||||
%package -n libosmo-mgcp-client9
 | 
			
		||||
Summary:        Osmocom's Media Gateway Control Protocol client library
 | 
			
		||||
Group:          System/Libraries
 | 
			
		||||
 | 
			
		||||
%description -n libosmo-mgcp-client9
 | 
			
		||||
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-client9 = %{version}
 | 
			
		||||
 | 
			
		||||
%description -n libosmo-mgcp-client-devel
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
%build
 | 
			
		||||
echo "%{version}" >.tarball-version
 | 
			
		||||
autoreconf -fi
 | 
			
		||||
%configure \
 | 
			
		||||
  --disable-static \
 | 
			
		||||
  --docdir=%{_docdir}/%{name} \
 | 
			
		||||
  --with-systemdsystemunitdir=%{_unitdir}
 | 
			
		||||
 | 
			
		||||
make %{?_smp_mflags}
 | 
			
		||||
 | 
			
		||||
%install
 | 
			
		||||
%make_install
 | 
			
		||||
find %{buildroot} -type f -name "*.la" -delete -print
 | 
			
		||||
 | 
			
		||||
%check
 | 
			
		||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
 | 
			
		||||
 | 
			
		||||
%post   -n libosmo-mgcp-client9 -p /sbin/ldconfig
 | 
			
		||||
%postun -n libosmo-mgcp-client9 -p /sbin/ldconfig
 | 
			
		||||
 | 
			
		||||
%if 0%{?suse_version}
 | 
			
		||||
%preun
 | 
			
		||||
%service_del_preun osmo-mgw.service
 | 
			
		||||
 | 
			
		||||
%postun
 | 
			
		||||
%service_del_postun osmo-mgw.service
 | 
			
		||||
 | 
			
		||||
%pre
 | 
			
		||||
%service_add_pre osmo-mgw.service
 | 
			
		||||
 | 
			
		||||
%post
 | 
			
		||||
%service_add_post osmo-mgw.service
 | 
			
		||||
%endif
 | 
			
		||||
 | 
			
		||||
%files
 | 
			
		||||
%license COPYING
 | 
			
		||||
%doc AUTHORS README
 | 
			
		||||
%dir %{_docdir}/%{name}/examples
 | 
			
		||||
%dir %{_docdir}/%{name}/examples/osmo-mgw
 | 
			
		||||
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw.cfg
 | 
			
		||||
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
 | 
			
		||||
%{_bindir}/osmo-mgw
 | 
			
		||||
%{_unitdir}/osmo-mgw.service
 | 
			
		||||
%dir %{_sysconfdir}/osmocom
 | 
			
		||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg
 | 
			
		||||
 | 
			
		||||
%files -n libosmo-mgcp-client9
 | 
			
		||||
%{_libdir}/libosmo-mgcp-client.so.9*
 | 
			
		||||
 | 
			
		||||
%files -n libosmo-mgcp-client-devel
 | 
			
		||||
%{_libdir}/libosmo-mgcp-client.so
 | 
			
		||||
%{_libdir}/pkgconfig/libosmo-mgcp-client.pc
 | 
			
		||||
%dir %{_includedir}/osmocom
 | 
			
		||||
%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,11 +1,24 @@
 | 
			
		||||
[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
 | 
			
		||||
User=osmocom
 | 
			
		||||
Group=osmocom
 | 
			
		||||
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
 | 
			
		||||
RestartSec=2
 | 
			
		||||
AmbientCapabilities=CAP_SYS_NICE
 | 
			
		||||
# CPU scheduling policy:
 | 
			
		||||
CPUSchedulingPolicy=rr
 | 
			
		||||
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
 | 
			
		||||
CPUSchedulingPriority=1
 | 
			
		||||
# See sched(7) for further details on real-time policies and priorities
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										304
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										304
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,8 +1,308 @@
 | 
			
		||||
osmo-mgw (1.10.1) unstable; urgency=medium
 | 
			
		||||
osmo-mgw (1.13.1) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * mgcp_network: use an uint16_t to store the port number
 | 
			
		||||
  * mgcp_network: add missing ntohs
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * tests/mgcp/mgcp_test: Add some extra asserts in code
 | 
			
		||||
 | 
			
		||||
 -- Oliver Smith <osmith@sysmocom.de>  Thu, 12 Sep 2024 13:53:19 +0200
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.13.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * add mgcp_conn_rtp_type_names[]
 | 
			
		||||
  * mgcp_parse_audio_port_pt(): fix buffer overflow
 | 
			
		||||
  * client: replace two assertions with graceful error handling
 | 
			
		||||
  * systemd,manual: set LimitNOFILE=65536
 | 
			
		||||
  * IuUP: allow Initialization from any address if not yet set
 | 
			
		||||
  * check_rtp_origin: drop special case for legacy IuUP hack
 | 
			
		||||
  * mgcp_client_test: fix function name
 | 
			
		||||
  * fix possible NULL deref on early media
 | 
			
		||||
  * client: move some items to internal header
 | 
			
		||||
  * client: safely handle dealloc on event dispatch
 | 
			
		||||
  * build: move mgcp/*.h to noinst_HEADERS, drop RPM libosmo-mgcp-devel
 | 
			
		||||
  * client: deprecate legacy API
 | 
			
		||||
  * client: collapse codecs[] and ptmap[]; allow codec variants
 | 
			
		||||
  * client: allow MGCP_MAX_CODECS entries
 | 
			
		||||
  * client SDP: more verbose error logging
 | 
			
		||||
  * mgcp_client_test: add test_parse_response()
 | 
			
		||||
  * drop (now) unused code
 | 
			
		||||
  * tests/mgcp: add update_exp target
 | 
			
		||||
  * drop get_net_downlink_format_cb
 | 
			
		||||
  * drop cfg 'sdp audio fmtp-extra'
 | 
			
		||||
  * mgcp_codec_decide: remove redundant lookup
 | 
			
		||||
  * tweak DEBUG log
 | 
			
		||||
  * mgcp_test: fix false negatives in test output
 | 
			
		||||
  * mgw: do not fail MGCP on codec mismatch
 | 
			
		||||
  * mgcp-client: always send 'm=audio' line
 | 
			
		||||
  * mgcp_test.c: verify osmo-mgw accepts m=audio 0
 | 
			
		||||
  * mgcp_test.c: fix various missing '\r' and '\n'
 | 
			
		||||
  * mgcp_test: test a=ptime:20, not 40
 | 
			
		||||
  * mgcp_test: add CRCX for IUFP in sendrecv
 | 
			
		||||
  * do not FAIL on CRCX in sendrecv mode
 | 
			
		||||
 | 
			
		||||
  [ Keith Whyte ]
 | 
			
		||||
  * vty and log: also show local port for RTP conns
 | 
			
		||||
 | 
			
		||||
  [ Andreas Eversberg ]
 | 
			
		||||
  * Use uniform log format for default config files
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * mgcp_network: Improve err logging when rtp pkt from unexpected origin comes in
 | 
			
		||||
  * cosmetic: Fix line indentation
 | 
			
		||||
  * IuUP: Allow Initialization with set rem IP address and unset rem port
 | 
			
		||||
  * mgcp-client: Transmit remote IP addr in CRCX if known and port=0
 | 
			
		||||
  * Fix IuUP RTP hdr seqnr field not incremented
 | 
			
		||||
  * iuup: Increment RTP hdr seqnr even if Tx over UDP fails
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * mgcp: simplify getting msgb tail in mgcp_msg_terminate_nul()
 | 
			
		||||
  * mgcp: reserve once byte for '\0' in mgcp_do_read()
 | 
			
		||||
  * mgcp: correctly put NUL character in mgcp_msg_terminate_nul()
 | 
			
		||||
  * build: include README into the release tarball
 | 
			
		||||
 | 
			
		||||
  [ neels ]
 | 
			
		||||
  * Revert "drop (now) unused code"
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * Convert README to README.md and expand like in other projects
 | 
			
		||||
  * migrate mgcp_client from osmo_wqueue to osmo_io
 | 
			
		||||
  * mgw: Add our usual SIGABRT, SIGUSR1 signal handlers
 | 
			
		||||
  * don't log useless "transcoding disabled" message
 | 
			
		||||
  * remove strange loop for non-existant transcoding support
 | 
			
		||||
  * simplify unused transcoding/processing call-back
 | 
			
		||||
  * remove osmo_fd from mgcp_create_bind()
 | 
			
		||||
  * Change msgb ownership in processing of received msgb
 | 
			
		||||
  * cosmetic: make linter happy
 | 
			
		||||
  * Convert RTP/RTCP/OSMUX I/O from osmo_fd to osmo_io
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * contrib: remove rpm spec file
 | 
			
		||||
  * contrib/systemd: run as osmocom user
 | 
			
		||||
  * doc: example configs: fix deprecation warnings
 | 
			
		||||
 | 
			
		||||
  [ Mychaela N. Falconia ]
 | 
			
		||||
  * E1: support HRv1 codec on both 16k and 8k subslots
 | 
			
		||||
  * fix E1 TS output when used with osmo-e1d
 | 
			
		||||
 | 
			
		||||
 -- Oliver Smith <osmith@sysmocom.de>  Wed, 24 Jul 2024 15:44:47 +0200
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Fri, 10 Mar 2023 16:04:04 +0100
 | 
			
		||||
  [ 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 ]
 | 
			
		||||
  * mgcp-client: Remove impossible code path
 | 
			
		||||
  * mgcp_osmux: Drop duplicated conn_osmux_release_cid() in code path
 | 
			
		||||
  * Use Osmux default port define from libosmo-netif
 | 
			
		||||
  * osmux: Use new osmux APIs to let libosmo-netif alloc struct osmux_out_handle
 | 
			
		||||
  * mgw: Fix osmux conn local IP selection
 | 
			
		||||
  * cosmetic: osmux: Fix wrong indentation
 | 
			
		||||
  * mgw: Use X-Osmux string define
 | 
			
		||||
  * Add Osmux log category
 | 
			
		||||
  * cosmetic: main: Properly format log_info_cat
 | 
			
		||||
  * osmux: Use better name for function which may allocate a new struct
 | 
			
		||||
  * osmux: Use osmo_sockaddr wherever possible
 | 
			
		||||
  * osmux: Log refcounting of osmux_handle_list
 | 
			
		||||
  * osmux: Fix memleak on error code path
 | 
			
		||||
  * osmux: don't store conn ptr inside shared osmux_handle
 | 
			
		||||
  * osmux: set log level of expected code path to INFO
 | 
			
		||||
  * cosmetic: vty: Fix indentation whitespace
 | 
			
		||||
  * osmux: Log sendto() error
 | 
			
		||||
  * cosmetic: mgcp_conn.h: fix indentation whitespace
 | 
			
		||||
  * osmux: Attach osmux to virtual trunk
 | 
			
		||||
  * osmux: Clean up helper macro osmux_chunk_length()
 | 
			
		||||
  * osmux: Get rid of static NULL talloc context
 | 
			
		||||
  * Fix typo in ratectr description
 | 
			
		||||
  * mgcp_conn: rename field s/rate_ctr_group/ctrg/g
 | 
			
		||||
  * Use 'static const' instead of 'const static' everywhere
 | 
			
		||||
  * osmux: Add connection and global rate counters
 | 
			
		||||
  * cosmetic: osmux: Fix formatting of if-else brackets
 | 
			
		||||
  * osmux: Rename field osmux usage policy and define it with proper type
 | 
			
		||||
  * Use bool type instead of int in config field
 | 
			
		||||
  * osmux: Support local CID != remote CID
 | 
			
		||||
  * osmux: Fix incorrect rate_ctr_group used in mgcp_osmux.c
 | 
			
		||||
  * osmux: Improve per-conn tx rate counters
 | 
			
		||||
  * vty: show per-connection Osmux VTY stats
 | 
			
		||||
  * osmux: Rename field s/init/initialized
 | 
			
		||||
  * osmux: Allocate rate counters during initialization of osmux conn
 | 
			
		||||
  * cosmetic: osmux: Drop extra empty line
 | 
			
		||||
  * osmux: Drop unneeded comment block
 | 
			
		||||
  * osmux: Lower log level when osmux batch received for unknown CID
 | 
			
		||||
  * osmux: Keep decoding osmux pkt if a batch contains an unknown CID
 | 
			
		||||
  * Allocate struct osmux_in_handle through new libosmo-netif APIs
 | 
			
		||||
  * osmux: Drop logging of osmux internal counters
 | 
			
		||||
  * osmux: cleanup misleading code calling rtp_bridge_cb
 | 
			
		||||
  * osmux: Unify rtp_conn osmux type into a single type
 | 
			
		||||
  * osmux: Match remote address in osmux_conn_lookup()
 | 
			
		||||
  * osmux: Fill in from_addr in struct osmo_rtp_msg_ctx
 | 
			
		||||
  * osmux: Log remote address upon rx of osmux pkt
 | 
			
		||||
  * send_dummy: Use proper condition to test if conn is osmux
 | 
			
		||||
  * Fix regression in detection of legacy dummy packets
 | 
			
		||||
  * Use new libosmocore API osmo_sockaddr_is_any()
 | 
			
		||||
  * Get rid of separate rtp_port field
 | 
			
		||||
  * osmux: Use available API to check if remote end is known
 | 
			
		||||
  * Add Osmux IPv6 support
 | 
			
		||||
  * Clean up local var pointers in mgcp_get_local_addr()
 | 
			
		||||
  * osmux: Change couple log lines to OSMUX category
 | 
			
		||||
  * osmux: Add square brackets around IPv6 address to distinguish port in log line
 | 
			
		||||
  * osmux: Drop unused role parameter
 | 
			
		||||
  * osmux: cosmetic: Fix indentation
 | 
			
		||||
  * Check once if remote addr is available when sending dummy packet
 | 
			
		||||
  * osmux: Simplify and constify param passing
 | 
			
		||||
  * osmux: Set conn->type during osmux_init_conn()
 | 
			
		||||
  * osmux: Make conn_osmux_{allocate,release}_local_cid() APIs static
 | 
			
		||||
  * osmux: Define osmux_dummy cfg as boolean
 | 
			
		||||
  * osmux: Move setting OSMUX_STATE_DISABLED to initializer function
 | 
			
		||||
  * osmux: Erase references to bsc-nat
 | 
			
		||||
  * mgcp_conn_dump(): Separate dump for osmux and iuup connections
 | 
			
		||||
  * osmux: Introduce osmux peer-behind-nat (on|off) and rework conn activation
 | 
			
		||||
  * osmux: Clean up mgcp_config osmux fields
 | 
			
		||||
  * cosmetic: Fix typo in comment
 | 
			
		||||
  * mgcp-client: pool: Improve documentation of some internal fields
 | 
			
		||||
  * mgcp-client: Avoid double iteration picking client from pool
 | 
			
		||||
  * mgcp-client: Fix typo in internal function name
 | 
			
		||||
  * mgcp-client: Rename internal field in mgcp_client_pool
 | 
			
		||||
  * mgcp-client: Move & rename helper function outside of vty code
 | 
			
		||||
  * mgcp-client: Create alloc() and free() internal APIs for mgcp_client_pool_member
 | 
			
		||||
  * mgcp-client: Move internal API acting on mgcp_client_pool to the correct file section
 | 
			
		||||
  * mgcp-client: Refactor reinit of mgcp clients
 | 
			
		||||
  * mgcp-client: Rearrange order of structs and APIs in header
 | 
			
		||||
  * mgcp-client: Rearrange internal backpointers
 | 
			
		||||
  * mgcp-client: Introduce APIs to manually select mgcp_client from pool
 | 
			
		||||
  * mgcp-client: vty: Write deprecation warning using non-mgw nodes
 | 
			
		||||
  * mgcp-client: Use random free local port by default
 | 
			
		||||
  * mgcp-client: Convert users supporting new MGW Pool VTY node during write-config
 | 
			
		||||
  * mgcp-client: Introduce API mgcp_client_pool_config_write
 | 
			
		||||
  * mgcp-client: Add new VTY commands under mgw node without mgw prefix
 | 
			
		||||
  * mgcp-client: Fix 'mgw endpoint-range' command dropped from old VTY node
 | 
			
		||||
  * mgcp-client: Refactor system keeping old users not calling mgcp_client_pool_config_write() working
 | 
			
		||||
  * mgcp-client: Fix no 'mgw ' prefix written in old VTY node
 | 
			
		||||
  * Fix misleading error log
 | 
			
		||||
  * mgcp_send: Use mgcp_conn_rtp_is_iuup() helper
 | 
			
		||||
  * cosmetic: Clarify and fix typos in comment
 | 
			
		||||
  * mgw: Log unexpected RTP AMR OA-vs-BE payload
 | 
			
		||||
  * Rename and move func checking if amr mode is explicitly configured
 | 
			
		||||
  * mgw: rx_rtp(): reorder checks and handlings
 | 
			
		||||
  * mgw: Rename s/mgcp_send_rtp/mgcp_conn_rtp_dispatch_rtp/
 | 
			
		||||
  * osmux: Rename function and pass msgb directly to it
 | 
			
		||||
  * osmux: Make sure RTP AMR feed to osmux is in octet-aligned mode
 | 
			
		||||
  * Improve logging on AMR OA<->BWE conversion failure
 | 
			
		||||
  * osmux: Rotate over available Osmux CID when allocating a new one
 | 
			
		||||
  * osmux: Use new osmux_xfrm_input API to set name on each link
 | 
			
		||||
  * iuup: Use osmo_amr_ft_valid() API
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * libosmo-mgcp-client: add -no-undefined to *_la_LDFLAGS
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * mgcp_e1: fix apidoc
 | 
			
		||||
  * mgcp_network: improve coment
 | 
			
		||||
 | 
			
		||||
  [ Max ]
 | 
			
		||||
  * Set working directory in systemd service file
 | 
			
		||||
  * Add realtime scheduling and set priority in service file
 | 
			
		||||
  * ctrl: take both address and port from vty config
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * Make osmo_mgcpc_ep_fsm_pre_term() static
 | 
			
		||||
  * Support building with -Werror=strict-prototypes / -Werror=old-style-definition
 | 
			
		||||
  * Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
 | 
			
		||||
  * update outdated vty copyright statement
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * mgcp_client_pool.h: add missing stdbool include
 | 
			
		||||
  * Fix various typos
 | 
			
		||||
  * mgcp_client: add new clearmode codec
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * AMR->IuUP: do not crash on AMR data before IuUP Init
 | 
			
		||||
  * AMR->IuUP: properly translate Q -> FQC
 | 
			
		||||
  * IuUP->AMR: do not patch payload type a second time
 | 
			
		||||
  * IuUP->AMR: log whether converting to AMR OA or BE
 | 
			
		||||
  * AMR->IuUP: log conversion, like for the flipside
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 07 Feb 2023 16:58:41 +0100
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.10.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							@@ -1 +1 @@
 | 
			
		||||
9
 | 
			
		||||
10
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								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.7.0),
 | 
			
		||||
               libosmo-netif-dev (>= 1.2.0),
 | 
			
		||||
               libosmo-abis-dev (>= 1.3.0),
 | 
			
		||||
               osmo-gsm-manuals-dev (>= 1.3.0)
 | 
			
		||||
               libosmocore-dev (>= 1.10.0),
 | 
			
		||||
               libosmo-netif-dev (>= 1.5.0),
 | 
			
		||||
               libosmo-abis-dev (>= 1.6.0),
 | 
			
		||||
               osmo-gsm-manuals-dev (>= 1.6.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-client9
 | 
			
		||||
Package: libosmo-mgcp-client14
 | 
			
		||||
Section: libs
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
@@ -33,9 +33,24 @@ Package: libosmo-mgcp-client-dev
 | 
			
		||||
Section: libdevel
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
Depends: libosmo-mgcp-client9 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Depends: libosmo-mgcp-client14 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
 | 
			
		||||
 | 
			
		||||
Package: libosmo-sdp1
 | 
			
		||||
Section: libs
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
Pre-Depends: ${misc:Pre-Depends}
 | 
			
		||||
Depends: ${misc:Depends}, ${shlibs:Depends}
 | 
			
		||||
Description: libosmo-sdp: Osmocom's Session Description Protocol encoder and decoder
 | 
			
		||||
 | 
			
		||||
Package: libosmo-sdp-dev
 | 
			
		||||
Section: libdevel
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
Depends: libosmo-sdp1 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Description: libosmo-sdp: Osmocom's Session Description Protocol encoder and decoder
 | 
			
		||||
 | 
			
		||||
Package: osmo-mgw-doc
 | 
			
		||||
Architecture: all
 | 
			
		||||
Section: doc
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								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
 | 
			
		||||
@@ -63,3 +63,22 @@ License:   GPL-3.0+
 | 
			
		||||
Files:     osmoappdesc.py
 | 
			
		||||
Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
 | 
			
		||||
License:   GPL-3.0+
 | 
			
		||||
 | 
			
		||||
Files:     src/libosmo-sdp/* include/osmocom/sdp/*
 | 
			
		||||
Copyright: 2024 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
License:   GPL-3.0+
 | 
			
		||||
 This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 it under the terms of the GNU General Public License as published by
 | 
			
		||||
 the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 (at your option) any later version.
 | 
			
		||||
 .
 | 
			
		||||
 This program is distributed in the hope that it will be useful,
 | 
			
		||||
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 GNU General Public License for more details.
 | 
			
		||||
 .
 | 
			
		||||
 You should have received a copy of the GNU General Public License
 | 
			
		||||
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 .
 | 
			
		||||
 On Debian systems, the complete text of the GNU General Public License
 | 
			
		||||
 Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								debian/libosmo-sdp-dev.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								debian/libosmo-sdp-dev.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
usr/include/osmocom/sdp
 | 
			
		||||
usr/lib/*/libosmo-sdp.so
 | 
			
		||||
usr/lib/*/libosmo-sdp.a
 | 
			
		||||
usr/lib/*/pkgconfig/libosmo-sdp.pc
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/libosmo-sdp1.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/libosmo-sdp1.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
usr/lib/*/libosmo-sdp.so.*
 | 
			
		||||
							
								
								
									
										38
									
								
								debian/postinst
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										38
									
								
								debian/postinst
									
									
									
									
										vendored
									
									
										Executable file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
#!/bin/sh -e
 | 
			
		||||
case "$1" in
 | 
			
		||||
	configure)
 | 
			
		||||
		# Create the osmocom group and user (if it doesn't exist yet)
 | 
			
		||||
		if ! getent group osmocom >/dev/null; then
 | 
			
		||||
			groupadd --system osmocom
 | 
			
		||||
		fi
 | 
			
		||||
		if ! getent passwd osmocom >/dev/null; then
 | 
			
		||||
			useradd \
 | 
			
		||||
				--system \
 | 
			
		||||
				--gid osmocom \
 | 
			
		||||
				--home-dir /var/lib/osmocom \
 | 
			
		||||
				--shell /sbin/nologin \
 | 
			
		||||
				--comment "Open Source Mobile Communications" \
 | 
			
		||||
				osmocom
 | 
			
		||||
		fi
 | 
			
		||||
 | 
			
		||||
		# Fix permissions of previous (root-owned) install (OS#4107)
 | 
			
		||||
		if dpkg --compare-versions "$2" le "1.13.0"; then
 | 
			
		||||
			if [ -e /etc/osmocom/osmo-mgw.cfg ]; then
 | 
			
		||||
				chown -v osmocom:osmocom /etc/osmocom/osmo-mgw.cfg
 | 
			
		||||
				chmod -v 0660 /etc/osmocom/osmo-mgw.cfg
 | 
			
		||||
			fi
 | 
			
		||||
 | 
			
		||||
			if [ -d /etc/osmocom ]; then
 | 
			
		||||
				chown -v root:osmocom /etc/osmocom
 | 
			
		||||
				chmod -v 2775 /etc/osmocom
 | 
			
		||||
			fi
 | 
			
		||||
 | 
			
		||||
			mkdir -p /var/lib/osmocom
 | 
			
		||||
			chown -R -v osmocom:osmocom /var/lib/osmocom
 | 
			
		||||
		fi
 | 
			
		||||
		;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
# dh_installdeb(1) will replace this with shell code automatically
 | 
			
		||||
# generated by other debhelper scripts.
 | 
			
		||||
#DEBHELPER#
 | 
			
		||||
@@ -1,20 +1,25 @@
 | 
			
		||||
!
 | 
			
		||||
! 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
 | 
			
		||||
mgcp
 | 
			
		||||
  bind ip 127.0.0.1
 | 
			
		||||
  rtp port-range 4002 16000
 | 
			
		||||
  rtp port-range 4002 16001
 | 
			
		||||
  rtp bind-ip 127.0.0.1
 | 
			
		||||
  rtp ip-probing
 | 
			
		||||
  rtp ip-dscp 46
 | 
			
		||||
  bind port 2427
 | 
			
		||||
  sdp audio payload number 98
 | 
			
		||||
  sdp audio payload name GSM
 | 
			
		||||
  number endpoints 512
 | 
			
		||||
  loop 0
 | 
			
		||||
  force-realloc 1
 | 
			
		||||
  rtcp-omit
 | 
			
		||||
  rtp-patch ssrc
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,22 @@
 | 
			
		||||
!
 | 
			
		||||
! 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
 | 
			
		||||
  rtp port-range 4002 16001
 | 
			
		||||
  rtp bind-ip 127.0.0.1
 | 
			
		||||
  rtp ip-probing
 | 
			
		||||
  rtp ip-dscp 46
 | 
			
		||||
  bind port 2427
 | 
			
		||||
  sdp audio payload number 98
 | 
			
		||||
  sdp audio payload name GSM
 | 
			
		||||
  number endpoints 512
 | 
			
		||||
  loop 0
 | 
			
		||||
  force-realloc 1
 | 
			
		||||
  rtcp-omit
 | 
			
		||||
  rtp-patch ssrc
 | 
			
		||||
 
 | 
			
		||||
@@ -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,19 @@ 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 \
 | 
			
		||||
	osmocom/sdp/fmtp.h \
 | 
			
		||||
	osmocom/sdp/sdp_codec.h \
 | 
			
		||||
	osmocom/sdp/sdp_codec_list.h \
 | 
			
		||||
	osmocom/sdp/sdp_msg.h \
 | 
			
		||||
	osmocom/sdp/sdp_strings.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	osmocom/mgcp/mgcp.h \
 | 
			
		||||
	osmocom/mgcp/mgcp_common.h \
 | 
			
		||||
	osmocom/mgcp/osmux.h \
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	sdp \
 | 
			
		||||
	mgcp_client \
 | 
			
		||||
	mgcp \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ noinst_HEADERS = \
 | 
			
		||||
	mgcp_endp.h \
 | 
			
		||||
	mgcp_sdp.h \
 | 
			
		||||
	mgcp_codec.h \
 | 
			
		||||
	mgcp_ctrl.h \
 | 
			
		||||
	mgcp_trunk.h \
 | 
			
		||||
	debug.h \
 | 
			
		||||
	mgcp_ratectr.h \
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@
 | 
			
		||||
enum {
 | 
			
		||||
	DRTP,
 | 
			
		||||
	DE1,
 | 
			
		||||
	DOSMUX,
 | 
			
		||||
	Debug_LastEntry,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,11 +24,13 @@
 | 
			
		||||
 | 
			
		||||
#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>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
@@ -69,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;
 | 
			
		||||
 | 
			
		||||
@@ -137,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;
 | 
			
		||||
@@ -156,24 +154,26 @@ struct mgcp_config {
 | 
			
		||||
 | 
			
		||||
	enum mgcp_role role;
 | 
			
		||||
 | 
			
		||||
	/* osmux translator: 0 means disabled, 1 means enabled */
 | 
			
		||||
	int osmux;
 | 
			
		||||
	struct {
 | 
			
		||||
		/* Osmux usage policy: */
 | 
			
		||||
		enum osmux_usage usage;
 | 
			
		||||
		/* addr to bind the server to */
 | 
			
		||||
	char osmux_addr[INET6_ADDRSTRLEN];
 | 
			
		||||
	/* The BSC-NAT may ask for enabling osmux on demand. This tells us if
 | 
			
		||||
	 * the osmux socket is already initialized.
 | 
			
		||||
	 */
 | 
			
		||||
	int osmux_init;
 | 
			
		||||
	/* osmux batch factor: from 1 to 4 maximum */
 | 
			
		||||
	int osmux_batch;
 | 
			
		||||
	/* osmux batch size (in bytes) */
 | 
			
		||||
	int osmux_batch_size;
 | 
			
		||||
		char *local_addr_v4;
 | 
			
		||||
		char *local_addr_v6;
 | 
			
		||||
		/* osmux port */
 | 
			
		||||
	uint16_t osmux_port;
 | 
			
		||||
	/* Pad circuit with dummy messages until we see the first voice
 | 
			
		||||
	 * message.
 | 
			
		||||
	 */
 | 
			
		||||
	uint16_t osmux_dummy;
 | 
			
		||||
		uint16_t local_port;
 | 
			
		||||
		/* The osmux socket is allocated on demand (1st time used).
 | 
			
		||||
		* This tells us if the osmux socket is already initialized.  */
 | 
			
		||||
		bool initialized;
 | 
			
		||||
		/* osmux batch factor: from 1 to 4 maximum */
 | 
			
		||||
		int batch_factor;
 | 
			
		||||
		/* osmux batch size (in bytes) */
 | 
			
		||||
		int batch_size;
 | 
			
		||||
		/* Pad circuit with dummy AMR frames if no payload to transmit is available */
 | 
			
		||||
		bool dummy_padding;
 | 
			
		||||
		/* Whether peer is behind NAT (Retrieve remote addr from 1st received Osmux packet) */
 | 
			
		||||
		bool peer_behind_nat;
 | 
			
		||||
	} osmux;
 | 
			
		||||
	/* domain name of the media gateway */
 | 
			
		||||
	char domain[255+1];
 | 
			
		||||
 | 
			
		||||
@@ -205,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, struct osmo_sockaddr *addr, int port, 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,8 +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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -24,10 +24,12 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_network.h>
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/gsm/iuup.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
@@ -46,10 +48,14 @@ LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
 | 
			
		||||
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
 | 
			
		||||
enum mgcp_conn_rtp_type {
 | 
			
		||||
	MGCP_RTP_DEFAULT	= 0,
 | 
			
		||||
	MGCP_OSMUX_BSC,
 | 
			
		||||
	MGCP_OSMUX_BSC_NAT,
 | 
			
		||||
	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) */
 | 
			
		||||
@@ -80,19 +86,20 @@ struct mgcp_conn_rtp {
 | 
			
		||||
	struct {
 | 
			
		||||
		/* Osmux state: disabled, activating, active */
 | 
			
		||||
		enum osmux_state state;
 | 
			
		||||
		/* Is cid holding valid data? is it allocated from pool? */
 | 
			
		||||
		bool cid_allocated;
 | 
			
		||||
		/* Allocated Osmux circuit ID for this conn */
 | 
			
		||||
		uint8_t cid;
 | 
			
		||||
		/* handle to batch messages */
 | 
			
		||||
		/* Is local_cid holding valid data? is it allocated from pool? */
 | 
			
		||||
		bool local_cid_allocated;
 | 
			
		||||
		/* Allocated local Osmux circuit ID for this conn */
 | 
			
		||||
		uint8_t local_cid;
 | 
			
		||||
		/* Is remote_cid holding valid data? was it already received from client? */
 | 
			
		||||
		bool remote_cid_present;
 | 
			
		||||
		/* Received remote Osmux circuit ID for this conn */
 | 
			
		||||
		uint8_t remote_cid;
 | 
			
		||||
		/* handle to batch messages, shared (refcounted) among several conns */
 | 
			
		||||
		struct osmux_in_handle *in;
 | 
			
		||||
		/* handle to unbatch messages */
 | 
			
		||||
		struct osmux_out_handle out;
 | 
			
		||||
		/* statistics */
 | 
			
		||||
		struct {
 | 
			
		||||
			uint32_t chunks;
 | 
			
		||||
			uint32_t octets;
 | 
			
		||||
		} stats;
 | 
			
		||||
		/* handle to unbatch messages, one allocated and owned per conn */
 | 
			
		||||
		struct osmux_out_handle *out;
 | 
			
		||||
		/* statistics: */
 | 
			
		||||
		struct rate_ctr_group *ctrg;
 | 
			
		||||
	} osmux;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
@@ -103,7 +110,7 @@ struct mgcp_conn_rtp {
 | 
			
		||||
		struct osmo_iuup_rnl_prim *init_ind;
 | 
			
		||||
	} iuup;
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr_group *rate_ctr_group;
 | 
			
		||||
	struct rate_ctr_group *ctrg;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! MGCP connection (untyped) */
 | 
			
		||||
@@ -163,7 +170,7 @@ static const struct rate_ctr_desc mgcp_conn_rate_ctr_desc[] = {
 | 
			
		||||
	[RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."},
 | 
			
		||||
	[RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."},
 | 
			
		||||
	[RTP_OCTETS_TX_CTR] = {"rtp:octets_tx", "Outbound rtp octets."},
 | 
			
		||||
	[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."}
 | 
			
		||||
	[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "Dropped rtp packets."}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Aggregated RTP connection stats. These are updated when an RTP connection is freed.
 | 
			
		||||
@@ -181,9 +188,49 @@ static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = {
 | 
			
		||||
	[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Osmux connection related counters */
 | 
			
		||||
enum {
 | 
			
		||||
	OSMUX_CHUNKS_RX_CTR,
 | 
			
		||||
	OSMUX_OCTETS_RX_CTR,
 | 
			
		||||
	OSMUX_RTP_PACKETS_TX_CTR,
 | 
			
		||||
	OSMUX_RTP_PACKETS_TX_DROPPED_CTR,
 | 
			
		||||
	OSMUX_AMR_OCTETS_TX_CTR,
 | 
			
		||||
	/* Only available in global stats: */
 | 
			
		||||
	OSMUX_NUM_CONNECTIONS,
 | 
			
		||||
	OSMUX_PACKETS_RX_CTR,
 | 
			
		||||
	OSMUX_PACKETS_TX_CTR,
 | 
			
		||||
	OSMUX_DROPPED_PACKETS_CTR,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* RTP per-connection statistics. Instances of the corresponding rate counter group
 | 
			
		||||
 * exist for the lifetime of an RTP connection.
 | 
			
		||||
 * Must be kept in sync with all_rtp_conn_rate_ctr_desc below */
 | 
			
		||||
static const struct rate_ctr_desc mgcp_conn_osmux_rate_ctr_desc[] = {
 | 
			
		||||
	[OSMUX_CHUNKS_RX_CTR] = {"osmux:chunks_rx", "Inbound Osmux chunks."},
 | 
			
		||||
	[OSMUX_OCTETS_RX_CTR] = {"osmux:octets_rx", "Inbound Osmux octets."},
 | 
			
		||||
	[OSMUX_RTP_PACKETS_TX_CTR] = {"osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
 | 
			
		||||
	[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
 | 
			
		||||
	[OSMUX_AMR_OCTETS_TX_CTR] = {"osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Aggregated Osmux connection stats. These are updated when an Osmux connection is freed.
 | 
			
		||||
 * Must be kept in sync with mgcp_conn_osmux_rate_ctr_desc above */
 | 
			
		||||
static const struct rate_ctr_desc all_osmux_conn_rate_ctr_desc[] = {
 | 
			
		||||
	[OSMUX_CHUNKS_RX_CTR] = {"all_osmux:chunks_rx", "Inbound Osmux chunks."},
 | 
			
		||||
	[OSMUX_OCTETS_RX_CTR] = {"all_osmux:octets_rx", "Inbound Osmux octets."},
 | 
			
		||||
	[OSMUX_RTP_PACKETS_TX_CTR] = {"all_osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
 | 
			
		||||
	[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"all_osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
 | 
			
		||||
	[OSMUX_AMR_OCTETS_TX_CTR] = {"all_osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
 | 
			
		||||
	/* These last counters below do not exist in per-connection stats, only here: */
 | 
			
		||||
	[OSMUX_NUM_CONNECTIONS] = {"all_osmux:num_closed_conns", "Total number of osmux connections closed."},
 | 
			
		||||
	[OSMUX_PACKETS_RX_CTR] = {"all_osmux:packets_rx", "Total inbound UDP/Osmux packets."},
 | 
			
		||||
	[OSMUX_PACKETS_TX_CTR] = {"all_osmux:packets_tx", "Total outbound UDP/Osmux packets."},
 | 
			
		||||
	[OSMUX_DROPPED_PACKETS_CTR] = {"all_osmux:dropped_packets", "Dropped outbound UDP/Osmux packets."}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Was conn configured to handle Osmux? */
 | 
			
		||||
static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
 | 
			
		||||
	return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
 | 
			
		||||
	return conn->type == MGCP_RTP_OSMUX;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Was conn configured to handle Osmux? */
 | 
			
		||||
@@ -192,15 +239,16 @@ static inline bool mgcp_conn_rtp_is_iuup(const struct mgcp_conn_rtp *conn)
 | 
			
		||||
	return conn->type == MGCP_RTP_IUUP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct mgcp_conn_rtp *mgcp_conn_get_conn_rtp(struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(conn->type == MGCP_CONN_TYPE_RTP);
 | 
			
		||||
	return &conn->u.rtp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
				  enum mgcp_conn_type type, char *name);
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
 | 
			
		||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
					const char *id);
 | 
			
		||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id);
 | 
			
		||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_conn_free(struct mgcp_conn *conn);
 | 
			
		||||
int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode);
 | 
			
		||||
char *mgcp_conn_dump(struct mgcp_conn *conn);
 | 
			
		||||
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2020 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
 | 
			
		||||
                                             const char *bind_addr, uint16_t port);
 | 
			
		||||
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -40,8 +40,13 @@ LOGP(cat, level, "endpoint:%s " fmt, \
 | 
			
		||||
     endp ? endp->name : "none", \
 | 
			
		||||
     ## args)
 | 
			
		||||
 | 
			
		||||
enum rtp_proto {
 | 
			
		||||
	MGCP_PROTO_RTP,
 | 
			
		||||
	MGCP_PROTO_RTCP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_rtp_msg_ctx {
 | 
			
		||||
	int proto;
 | 
			
		||||
	enum rtp_proto proto;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_src;
 | 
			
		||||
	struct osmo_sockaddr *from_addr;
 | 
			
		||||
};
 | 
			
		||||
@@ -125,18 +130,28 @@ 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,
 | 
			
		||||
					struct mgcp_config *cfg);
 | 
			
		||||
bool mgcp_endp_avail(struct mgcp_endpoint *endp);
 | 
			
		||||
bool mgcp_endp_avail(const struct mgcp_endpoint *endp);
 | 
			
		||||
unsigned int mgcp_endp_num_conns(const struct mgcp_endpoint *endp);
 | 
			
		||||
bool mgcp_endp_is_full(const struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
 | 
			
		||||
void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
 | 
			
		||||
void mgcp_endp_free_conn_oldest(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_endp_free_conn_all(struct mgcp_endpoint *endp);
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id);
 | 
			
		||||
struct mgcp_conn *mgcp_endp_get_conn_oldest(struct mgcp_endpoint *endp);
 | 
			
		||||
struct mgcp_conn_rtp *mgcp_endp_get_conn_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
					const char *id);
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,8 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn;
 | 
			
		||||
struct mgcp_parse_data;
 | 
			
		||||
struct mgcp_endpoint;
 | 
			
		||||
@@ -34,8 +36,7 @@ struct mgcp_trunk;
 | 
			
		||||
 | 
			
		||||
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble);
 | 
			
		||||
 | 
			
		||||
int mgcp_parse_conn_mode(const char *msg, struct mgcp_endpoint *endp,
 | 
			
		||||
			 struct mgcp_conn *conn);
 | 
			
		||||
enum mgcp_connection_mode mgcp_parse_conn_mode(const char *msg);
 | 
			
		||||
 | 
			
		||||
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,14 @@
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/osmo_io.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
 | 
			
		||||
/* The following constant defines an RTP dummy payload that is used for
 | 
			
		||||
 * "UDP Hole Punching" (NAT) */
 | 
			
		||||
static const char rtp_dummy_payload[] = { 0x23 };
 | 
			
		||||
#define MGCP_DUMMY_LOAD 0x23
 | 
			
		||||
static const char rtp_dummy_payload[] = { MGCP_DUMMY_LOAD };
 | 
			
		||||
 | 
			
		||||
/* Check if the data in a given message buffer matches the rtp dummy payload
 | 
			
		||||
 * defined above */
 | 
			
		||||
@@ -92,7 +94,7 @@ struct mgcp_rtp_end {
 | 
			
		||||
	struct osmo_sockaddr addr;
 | 
			
		||||
 | 
			
		||||
	/* in network byte order */
 | 
			
		||||
	int rtp_port, rtcp_port;
 | 
			
		||||
	uint16_t rtcp_port;
 | 
			
		||||
 | 
			
		||||
	/* currently selected audio codec */
 | 
			
		||||
	struct mgcp_rtp_codec *codec;
 | 
			
		||||
@@ -107,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! */
 | 
			
		||||
@@ -120,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;
 | 
			
		||||
@@ -131,6 +132,7 @@ struct mgcp_rtp_end {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end);
 | 
			
		||||
void mgcp_rtp_end_free_port(struct mgcp_rtp_end *end);
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_tap {
 | 
			
		||||
	/* is this tap active (1) or not (0) */
 | 
			
		||||
@@ -151,16 +153,14 @@ int mgcp_dispatch_e1_bridge_cb(struct msgb *msg);
 | 
			
		||||
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
 | 
			
		||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
 | 
			
		||||
			   struct mgcp_conn_rtp *conn);
 | 
			
		||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
 | 
			
		||||
void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
 | 
			
		||||
			  struct mgcp_rtp_state *state,
 | 
			
		||||
			  struct mgcp_rtp_end *rtp_end,
 | 
			
		||||
			  struct osmo_sockaddr *addr, struct msgb *msg);
 | 
			
		||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
 | 
			
		||||
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,
 | 
			
		||||
@@ -178,5 +178,9 @@ void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_stat
 | 
			
		||||
 | 
			
		||||
void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
 | 
			
		||||
				 int id, int inc);
 | 
			
		||||
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
 | 
			
		||||
void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
 | 
			
		||||
				 int id);
 | 
			
		||||
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);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,8 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
 | 
			
		||||
/* Internal structure while parsing a request */
 | 
			
		||||
struct mgcp_parse_data {
 | 
			
		||||
	struct mgcp_config *cfg;
 | 
			
		||||
@@ -28,3 +31,9 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
 | 
			
		||||
 | 
			
		||||
uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
 | 
			
		||||
				  const struct mgcp_rtp_end *rtp);
 | 
			
		||||
 | 
			
		||||
extern const struct value_string mgcp_connection_mode_strs[];
 | 
			
		||||
static inline const char *mgcp_cmode_name(enum mgcp_connection_mode mode)
 | 
			
		||||
{
 | 
			
		||||
	return get_value_string(mgcp_connection_mode_strs, mode);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,8 @@ struct mgcp_ratectr_trunk {
 | 
			
		||||
	struct rate_ctr_group *mgcp_dlcx_ctr_group;
 | 
			
		||||
	/* Rate counter group which aggregates stats of individual RTP connections. */
 | 
			
		||||
	struct rate_ctr_group *all_rtp_conn_stats;
 | 
			
		||||
	/* Rate counter group which aggregates stats of individual Osmux connections. */
 | 
			
		||||
	struct rate_ctr_group *all_osmux_conn_stats;
 | 
			
		||||
	/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
 | 
			
		||||
	struct rate_ctr_group *e1_stats;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,25 +1,22 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/netif/osmux.h>
 | 
			
		||||
struct mgcp_conn_rtp;
 | 
			
		||||
struct mgcp_trunk;
 | 
			
		||||
struct mgcp_endpoint;
 | 
			
		||||
struct mgcp_conn_rtp;
 | 
			
		||||
 | 
			
		||||
#define OSMUX_PORT	1984
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	OSMUX_ROLE_BSC = 0,
 | 
			
		||||
	OSMUX_ROLE_BSC_NAT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int osmux_init(int role, struct mgcp_config *cfg);
 | 
			
		||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
 | 
			
		||||
		      struct osmo_sockaddr *addr, uint16_t port);
 | 
			
		||||
int osmux_init(struct mgcp_trunk *trunk);
 | 
			
		||||
int osmux_init_conn(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int conn_osmux_enable(struct mgcp_conn_rtp *conn);
 | 
			
		||||
void conn_osmux_disable(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid);
 | 
			
		||||
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
 | 
			
		||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
 | 
			
		||||
int conn_osmux_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg);
 | 
			
		||||
int osmux_send_dummy(struct mgcp_conn_rtp *conn);
 | 
			
		||||
 | 
			
		||||
void osmux_cid_pool_get(uint8_t osmux_cid);
 | 
			
		||||
int osmux_cid_pool_get_next(void);
 | 
			
		||||
@@ -29,10 +26,14 @@ int osmux_cid_pool_count_used(void);
 | 
			
		||||
 | 
			
		||||
enum osmux_state {
 | 
			
		||||
	OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
 | 
			
		||||
	OSMUX_STATE_ACTIVATING,   /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */
 | 
			
		||||
	OSMUX_STATE_ENABLED,	  /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */
 | 
			
		||||
	OSMUX_STATE_ACTIVATING,   /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref conn_osmux_enable. */
 | 
			
		||||
	OSMUX_STATE_ENABLED,	  /* Osmux was initialized by \ref conn_osmux_enable and can process frames */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct value_string osmux_state_strs[];
 | 
			
		||||
static inline const char *osmux_state_str(enum osmux_state val)
 | 
			
		||||
{ return get_value_string(osmux_state_strs, val); }
 | 
			
		||||
 | 
			
		||||
enum osmux_usage {
 | 
			
		||||
	OSMUX_USAGE_OFF = 0,
 | 
			
		||||
	OSMUX_USAGE_ON = 1,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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,14 +3,15 @@
 | 
			
		||||
#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 */
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT NULL /* INADDR(6)_ANY */
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
 | 
			
		||||
#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,11 +52,12 @@ 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 */
 | 
			
		||||
};
 | 
			
		||||
/* Note: when new codec types are added, the corresponding value strings
 | 
			
		||||
 * in mgcp_client.c (codec_table) must be updated as well. Enumerations
 | 
			
		||||
@@ -71,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,
 | 
			
		||||
@@ -101,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);
 | 
			
		||||
@@ -140,7 +98,7 @@ struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp);
 | 
			
		||||
struct mgcp_client *mgcp_client_init(void *ctx,
 | 
			
		||||
				     struct mgcp_client_conf *conf);
 | 
			
		||||
int mgcp_client_connect(struct mgcp_client *mgcp);
 | 
			
		||||
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports);
 | 
			
		||||
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports) OSMO_DEPRECATED("Use mgcp_client_connect() instead");
 | 
			
		||||
void mgcp_client_disconnect(struct mgcp_client *mgcp);
 | 
			
		||||
 | 
			
		||||
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp);
 | 
			
		||||
@@ -152,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,9 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
#include <osmocom/core/osmo_io.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
 | 
			
		||||
#define MSGB_CB_MGCP_TRANS_ID 0
 | 
			
		||||
 | 
			
		||||
@@ -12,18 +15,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 *pool;
 | 
			
		||||
	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 +63,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,24 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/vty.h>
 | 
			
		||||
 | 
			
		||||
struct mgcp_client;
 | 
			
		||||
struct mgcp_client_pool;
 | 
			
		||||
struct mgcp_client_pool_member;
 | 
			
		||||
 | 
			
		||||
struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx);
 | 
			
		||||
 | 
			
		||||
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool);
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr);
 | 
			
		||||
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member);
 | 
			
		||||
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,31 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
/* Struct to handle a pool of MGWs. (Use _pool functions) */
 | 
			
		||||
struct mgcp_client_pool {
 | 
			
		||||
 | 
			
		||||
	/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using
 | 
			
		||||
	 * mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */
 | 
			
		||||
	struct mgcp_client *mgcp_client_single;
 | 
			
		||||
 | 
			
		||||
	/* A list that manages the pool members (see mgcp_client_pool_member->list above) */
 | 
			
		||||
	struct llist_head member_list;
 | 
			
		||||
 | 
			
		||||
	/* String to use for indentation when writing the configuration file to the VTY. This field is populated by
 | 
			
		||||
	 * mgcp_client_pool_vty_init() */
 | 
			
		||||
	char *vty_indent;
 | 
			
		||||
 | 
			
		||||
	/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */
 | 
			
		||||
	struct cmd_node *vty_node;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Struct to handle a member of a pool of MGWs. */
 | 
			
		||||
struct mgcp_client_pool_member {
 | 
			
		||||
	/* Entry in llist mgcp_client_pool->pool. */
 | 
			
		||||
	struct llist_head list;
 | 
			
		||||
 | 
			
		||||
	/* The pool managing this object: */
 | 
			
		||||
	struct mgcp_client_pool *pool;
 | 
			
		||||
 | 
			
		||||
	/* Reference number assinged by VTY. This number is used to manage the pool from the VTY and to identify it in
 | 
			
		||||
	 * the log. */
 | 
			
		||||
	unsigned int nr;
 | 
			
		||||
@@ -24,22 +46,7 @@ struct mgcp_client_pool_member {
 | 
			
		||||
	unsigned int refcount;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Struct to handle a pool of MGWs. (Use _pool functions) */
 | 
			
		||||
struct mgcp_client_pool {
 | 
			
		||||
 | 
			
		||||
	/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using
 | 
			
		||||
	 * mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */
 | 
			
		||||
	struct mgcp_client *mgcp_client_single;
 | 
			
		||||
 | 
			
		||||
	/* A list that manages the pool members (see above) */
 | 
			
		||||
	struct llist_head pool;
 | 
			
		||||
 | 
			
		||||
	/* String to use for indentation when writing the configuration file to the VTY. This field is populated by
 | 
			
		||||
	 * mgcp_client_pool_vty_init() */
 | 
			
		||||
	char *vty_indent;
 | 
			
		||||
 | 
			
		||||
	/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */
 | 
			
		||||
	struct cmd_node *vty_node;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr);
 | 
			
		||||
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member);
 | 
			
		||||
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member);
 | 
			
		||||
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								include/osmocom/sdp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								include/osmocom/sdp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	sdp_internal.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
							
								
								
									
										34
									
								
								include/osmocom/sdp/fmtp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								include/osmocom/sdp/fmtp.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
/* Public API for codec management in SDP messages: managing SDP fmtp strings. */
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name);
 | 
			
		||||
int64_t osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int64_t default_value);
 | 
			
		||||
 | 
			
		||||
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp);
 | 
			
		||||
bool osmo_sdp_fmtp_amr_match(const char *a, const char *b);
 | 
			
		||||
							
								
								
									
										126
									
								
								include/osmocom/sdp/sdp_codec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								include/osmocom/sdp/sdp_codec.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
/* Public API for codec management in SDP messages. */
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
 | 
			
		||||
/* RFC-8866 5.14 and 6.6.
 | 
			
		||||
 *
 | 
			
		||||
 * Represent the items describing an SDP codec entry, as in:
 | 
			
		||||
 *
 | 
			
		||||
 *   m=audio 1234 RTP/AVP <payload_type>
 | 
			
		||||
 *   a=rtpmap:<payload_type> <encoding-name>/<clock-rate>
 | 
			
		||||
 *   a=fmtp:<payload_type> <fmtp>
 | 
			
		||||
 *
 | 
			
		||||
 * For example:
 | 
			
		||||
 *
 | 
			
		||||
 *   m=audio 1234 RTP/AVP 98
 | 
			
		||||
 *   a=rtpmap:98 AMR/8000
 | 
			
		||||
 *   a=fmtp:98 octet-align=1;mode-set=0,2,4,7
 | 
			
		||||
 */
 | 
			
		||||
struct osmo_sdp_codec {
 | 
			
		||||
	/* Payload type number ("payload-type"), like 3 for GSM-FR. Limited to 0..127. */
 | 
			
		||||
	int8_t payload_type;
 | 
			
		||||
 | 
			
		||||
	/* Encoding name like "GSM", "AMR", "GSM-EFR".
 | 
			
		||||
	 *
 | 
			
		||||
	 * RFC-8866 defines no length limit on the encoding name. This API leaves it up to the caller to provide
 | 
			
		||||
	 * sufficient space, via the SDP_SIZES_* definitions.
 | 
			
		||||
	 *
 | 
			
		||||
	 * encoding-name = token
 | 
			
		||||
	 * token = 1*(token-char)
 | 
			
		||||
	 * token-char = ALPHA / DIGIT
 | 
			
		||||
	 *                    / "!" / "#" / "$" / "%" / "&"
 | 
			
		||||
	 *                    / "'" ; (single quote)
 | 
			
		||||
	 *                    / "*" / "+" / "-" / "." / "^" / "_"
 | 
			
		||||
	 *                    / "`" ; (Grave accent)
 | 
			
		||||
	 */
 | 
			
		||||
	char *encoding_name;
 | 
			
		||||
 | 
			
		||||
	/* Samplerate ("clock-rate"), usually 8000 for GSM. */
 | 
			
		||||
	unsigned int rate;
 | 
			
		||||
 | 
			
		||||
	/* Codec parameters as supplied in SDP line 'a=fmtp:<payload-type> <format-specific-params>'. This holds only
 | 
			
		||||
	 * the 'format-specific-params' bytestring. For example, for SDP line 'a=fmtp:123 param1=val1;param2=val2', this
 | 
			
		||||
	 * holds only the , "param1=val1;param2=val2" part. For the buffer size, see fmtp_size. */
 | 
			
		||||
	char *fmtp;
 | 
			
		||||
 | 
			
		||||
	/* Entry used by osmo_sdp_codec_list. */
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
	/* For future extension, always set to false. */
 | 
			
		||||
	bool v2;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_codec *osmo_sdp_codec_alloc(void *ctx);
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_codec_set(struct osmo_sdp_codec *c,
 | 
			
		||||
		       int8_t payload_type, const char *encoding_name, unsigned int rate, const char *fmtp);
 | 
			
		||||
int osmo_sdp_codec_set_encoding_name(struct osmo_sdp_codec *c, const char *encoding_name);
 | 
			
		||||
int osmo_sdp_codec_set_fmtp(struct osmo_sdp_codec *c, const char *fmtp);
 | 
			
		||||
 | 
			
		||||
bool osmo_sdp_codec_is_set(const struct osmo_sdp_codec *a);
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_codec_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec *codec);
 | 
			
		||||
char *osmo_sdp_codec_to_str_c(void *ctx, const struct osmo_sdp_codec *codec);
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_codec_from_str(struct osmo_sdp_codec *dst, const char *str, int str_len);
 | 
			
		||||
 | 
			
		||||
enum osmo_sdp_cmp {
 | 
			
		||||
	OSMO_SDP_CMP_IGNORE = 0,
 | 
			
		||||
	OSMO_SDP_CMP_EQUIVALENT,
 | 
			
		||||
	OSMO_SDP_CMP_EXACT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! Indicate how to match SDP codecs to various osmo_sdp_*() functions.
 | 
			
		||||
 * Callers may define own flags, or use predefined instances:
 | 
			
		||||
 * osmo_sdp_codec_cmp_exact, osmo_sdp_codec_cmp_equivalent, ...
 | 
			
		||||
 *
 | 
			
		||||
 * For example, to trigger some action if any item has changed, set all items to true / OSMO_SDP_CMP_EXACT (see
 | 
			
		||||
 * osmo_sdp_codec_cmp_exact).
 | 
			
		||||
 * To find codecs that are the same between two SDP sessions, set payload_type=false and fmtp=OSMO_SDP_CMP_EQUIVALENT
 | 
			
		||||
 * (see osmo_sdp_codec_cmp_equivalent).
 | 
			
		||||
 * To just list all contained "AMR" codecs, set only encoding_name=true (see osmo_sdp_codec_cmp_name).
 | 
			
		||||
 */
 | 
			
		||||
struct osmo_sdp_codec_cmp_flags {
 | 
			
		||||
	/*! true = compare payload type numbers 1:1; false = ignore. */
 | 
			
		||||
	bool payload_type;
 | 
			
		||||
	/*! true = compare encoding_name 1:1; false = ignore. */
 | 
			
		||||
	bool encoding_name;
 | 
			
		||||
	/*! true = compare rate 1:1; false = ignore. */
 | 
			
		||||
	bool rate;
 | 
			
		||||
	/*! OSMO_SDP_CMP_IGNORE = ignore fmtp;
 | 
			
		||||
	 * OSMO_SDP_CMP_EQUIVALENT = use osmo_sdp_fmtp_amr_match() for AMR, otherwise compare 1:1;
 | 
			
		||||
	 * OSMO_SDP_CMP_EXACT = compare 1:1.
 | 
			
		||||
	 */
 | 
			
		||||
	enum osmo_sdp_cmp fmtp;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_exact;
 | 
			
		||||
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent;
 | 
			
		||||
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_name;
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_codec_cmp(const struct osmo_sdp_codec *a, const struct osmo_sdp_codec *b,
 | 
			
		||||
		       const struct osmo_sdp_codec_cmp_flags *cmp);
 | 
			
		||||
							
								
								
									
										69
									
								
								include/osmocom/sdp/sdp_codec_list.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								include/osmocom/sdp/sdp_codec_list.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
/* Public API for codec management in SDP messages: list of struct osmo_sdp_codec. */
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/sdp_codec.h>
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_codec_list {
 | 
			
		||||
	struct llist_head list;
 | 
			
		||||
 | 
			
		||||
	/* For future extension, always set to false. */
 | 
			
		||||
	bool v2;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_codec_list *osmo_sdp_codec_list_alloc(void *ctx);
 | 
			
		||||
void osmo_sdp_codec_list_free_items(struct osmo_sdp_codec_list *codec_list);
 | 
			
		||||
 | 
			
		||||
int8_t osmo_sdp_codec_list_get_unused_dyn_pt_nr(const struct osmo_sdp_codec_list *codec_list, int8_t suggest_pt_nr);
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_codec *osmo_sdp_codec_list_add_empty(struct osmo_sdp_codec_list *codec_list);
 | 
			
		||||
struct osmo_sdp_codec *osmo_sdp_codec_list_add(struct osmo_sdp_codec_list *codec_list,
 | 
			
		||||
					       const struct osmo_sdp_codec *codec,
 | 
			
		||||
					       const struct osmo_sdp_codec_cmp_flags *once, bool pick_unused_pt_nr);
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_codec_list_remove(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
 | 
			
		||||
			       const struct osmo_sdp_codec_cmp_flags *cmpf);
 | 
			
		||||
void osmo_sdp_codec_list_remove_entry(struct osmo_sdp_codec *codec);
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_codec_list_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec_list *codec_list, bool summarize);
 | 
			
		||||
char *osmo_sdp_codec_list_to_str_c(void *ctx, const struct osmo_sdp_codec_list *codec_list, bool summarize);
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_codec *osmo_sdp_codec_list_first(const struct osmo_sdp_codec_list *list);
 | 
			
		||||
int osmo_sdp_codec_list_move_to_first(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
 | 
			
		||||
				      const struct osmo_sdp_codec_cmp_flags *cmpf);
 | 
			
		||||
 | 
			
		||||
#define osmo_sdp_codec_list_foreach(STRUCT_SDP_CODEC_P, SDP_CODEC_LIST) \
 | 
			
		||||
	llist_for_each_entry(STRUCT_SDP_CODEC_P, &(SDP_CODEC_LIST)->list, entry)
 | 
			
		||||
#define osmo_sdp_codec_list_foreach_safe(STRUCT_SDP_CODEC_P, SAFE_P, SDP_CODEC_LIST) \
 | 
			
		||||
	llist_for_each_entry_safe(STRUCT_SDP_CODEC_P, SAFE_P, &(SDP_CODEC_LIST)->list, entry)
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_codec_list_cmp(const struct osmo_sdp_codec_list *a, const struct osmo_sdp_codec_list *b,
 | 
			
		||||
			    const struct osmo_sdp_codec_cmp_flags *cmpf);
 | 
			
		||||
 | 
			
		||||
void osmo_sdp_codec_list_intersection(struct osmo_sdp_codec_list *dst, const struct osmo_sdp_codec_list *other,
 | 
			
		||||
				      const struct osmo_sdp_codec_cmp_flags *cmpf,
 | 
			
		||||
				      bool translate_payload_type_numbers);
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_codec *osmo_sdp_codec_list_by_payload_type(struct osmo_sdp_codec_list *codec_list, int8_t payload_type);
 | 
			
		||||
bool osmo_sdp_codec_list_is_empty(const struct osmo_sdp_codec_list *codec_list);
 | 
			
		||||
							
								
								
									
										18
									
								
								include/osmocom/sdp/sdp_internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								include/osmocom/sdp/sdp_internal.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
/* Internal header for non-public API shared across .c files */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
struct token {
 | 
			
		||||
	const char *start;
 | 
			
		||||
	const char *end;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Copy a string from [start,end[, return as talloc allocated under ctx in *dst.
 | 
			
		||||
 * If *dst is non-NULL, talloc_free(*dst) first. */
 | 
			
		||||
void token_copy(void *ctx, char **dst, const struct token *t);
 | 
			
		||||
 | 
			
		||||
const char *token_chr(const struct token *src, char c);
 | 
			
		||||
const char *token_chrs(const struct token *src, const char *chrs);
 | 
			
		||||
void token_next(struct token *t, const char *str, const char *end, const char *separators);
 | 
			
		||||
 | 
			
		||||
const char *token_to_int64(int64_t *result, const struct token *t, int base, int min_val, int max_val);
 | 
			
		||||
const char *token_to_int(int *result, const struct token *t, int base, int min_val, int max_val);
 | 
			
		||||
							
								
								
									
										105
									
								
								include/osmocom/sdp/sdp_msg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								include/osmocom/sdp/sdp_msg.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
/* Public API for SDP message encoding and decoding */
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/sockaddr_str.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/sdp_codec.h>
 | 
			
		||||
#include <osmocom/sdp/sdp_codec_list.h>
 | 
			
		||||
 | 
			
		||||
/* Media Direction Attributes "a=recvonly", "a=sendrecv", "a=sendonly", "a=inactive" RFC-8866 6.7. */
 | 
			
		||||
enum osmo_sdp_media_direcion_e {
 | 
			
		||||
	OSMO_SDP_MDIR_UNSET = 0,
 | 
			
		||||
	OSMO_SDP_MDIR_RECVONLY = 1,
 | 
			
		||||
	OSMO_SDP_MDIR_SENDRECV = 2,
 | 
			
		||||
	OSMO_SDP_MDIR_SENDONLY = 3,
 | 
			
		||||
	OSMO_SDP_MDIR_INACTIVE = 4,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Session Description Protocol (SDP) message, RFC-8866. */
 | 
			
		||||
struct osmo_sdp_msg {
 | 
			
		||||
	/* 5.2 Origin ("o="). */
 | 
			
		||||
	struct {
 | 
			
		||||
		struct osmo_sockaddr_str addr;
 | 
			
		||||
		char *username;
 | 
			
		||||
		char *sess_id;
 | 
			
		||||
		char *sess_version;
 | 
			
		||||
	} origin;
 | 
			
		||||
 | 
			
		||||
	/* 5.3 Session Name ("s="). */
 | 
			
		||||
	char *session_name;
 | 
			
		||||
 | 
			
		||||
	/* 5.7 Connection Information ("c=") and port from 5.14 Media Descriptions ("m="). */
 | 
			
		||||
	struct osmo_sockaddr_str rtp;
 | 
			
		||||
 | 
			
		||||
	/* 5.9. Time Active ("t="). */
 | 
			
		||||
	struct {
 | 
			
		||||
		int64_t start;
 | 
			
		||||
		int64_t stop;
 | 
			
		||||
	} time_active;
 | 
			
		||||
 | 
			
		||||
	/* 6.4 "a=ptime:<val>". */
 | 
			
		||||
	unsigned int ptime;
 | 
			
		||||
 | 
			
		||||
	/* 6.7 "a=sendrecv"... */
 | 
			
		||||
	enum osmo_sdp_media_direcion_e media_direction;
 | 
			
		||||
 | 
			
		||||
	/* List of codecs defined in the SDP message.
 | 
			
		||||
	 * This should not be NULL -- osmo_sdp_msg_alloc() returns an empty osmo_sdp_codec_list instance, ready for
 | 
			
		||||
	 * adding codecs.
 | 
			
		||||
	 * Combination of:
 | 
			
		||||
	 * - payload_type numbers from 5.14 Media Descriptions ("m="),
 | 
			
		||||
	 * - 6.6 "a=rtpmap",
 | 
			
		||||
	 * - 6.15 Format Parameters "a=fmtp".
 | 
			
		||||
	 */
 | 
			
		||||
	struct osmo_sdp_codec_list *codecs;
 | 
			
		||||
 | 
			
		||||
	/* For future extension, always set to false. */
 | 
			
		||||
	bool v2;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_msg_decode_ret {
 | 
			
		||||
	int rc;
 | 
			
		||||
	/* If rc != 0 */
 | 
			
		||||
	struct {
 | 
			
		||||
		/* Point at the position that caused the error, in the src string. */
 | 
			
		||||
		const char *at_input_str;
 | 
			
		||||
		/* Nr of characters at *src_str that are relevant to the error. */
 | 
			
		||||
		size_t at_input_str_len;
 | 
			
		||||
	} error;
 | 
			
		||||
	/* Pointer to the remaining part of src after parsing one SDP message.
 | 
			
		||||
	 * For example, in MGCP, there may be multiple SDP messages concatenated. */
 | 
			
		||||
	const char *src_remain;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx);
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src_str, int src_str_len,
 | 
			
		||||
					 struct osmo_sdp_msg_decode_ret *ret);
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_msg_encode_buf(char *dst, size_t dst_size, const struct osmo_sdp_msg *sdp);
 | 
			
		||||
char *osmo_sdp_msg_encode_c(void *ctx, const struct osmo_sdp_msg *sdp);
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_msg_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_msg *sdp, bool summarize);
 | 
			
		||||
char *osmo_sdp_msg_to_str_c(void *ctx, const struct osmo_sdp_msg *sdp, bool summarize);
 | 
			
		||||
							
								
								
									
										39
									
								
								include/osmocom/sdp/sdp_strings.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								include/osmocom/sdp/sdp_strings.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
/* Central definition of string tokens used for parsing and composing SDP messages */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#define OSMO_SDP_STR_MEDIA "m"
 | 
			
		||||
#define OSMO_SDP_STR_ATTRIB "a"
 | 
			
		||||
#define OSMO_SDP_STR_TIME_ACTIVE "t"
 | 
			
		||||
 | 
			
		||||
#define OSMO_SDP_STR_RTPMAP "rtpmap"
 | 
			
		||||
#define OSMO_SDP_STR_FMTP "fmtp"
 | 
			
		||||
#define OSMO_SDP_STR_PTIME "ptime"
 | 
			
		||||
 | 
			
		||||
/*! "a=foo:" */
 | 
			
		||||
#define OSMO_SDP_A_PREFIX(STR) OSMO_SDP_STR_ATTRIB "=" STR ":"
 | 
			
		||||
 | 
			
		||||
/*! "a=fmtp:" */
 | 
			
		||||
#define OSMO_SDP_STR_A_FMTP OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP)
 | 
			
		||||
 | 
			
		||||
/* Media Direction Attributes "a=recvonly", "a=sendrecv", "a=sendonly", "a=inactive" RFC-8866 6.7. */
 | 
			
		||||
#define OSMO_SDP_STR_RECVONLY "recvonly"
 | 
			
		||||
#define OSMO_SDP_STR_SENDRECV "sendrecv"
 | 
			
		||||
#define OSMO_SDP_STR_SENDONLY "sendonly"
 | 
			
		||||
#define OSMO_SDP_STR_INACTIVE "inactive"
 | 
			
		||||
 | 
			
		||||
/* AMR related tokens */
 | 
			
		||||
 | 
			
		||||
#define OSMO_SDP_STR_AMR_OCTET_ALIGN "octet-align"
 | 
			
		||||
 | 
			
		||||
/*! "octet-align=1" */
 | 
			
		||||
#define OSMO_SDP_STR_AMR_OCTET_ALIGN_1  OSMO_SDP_STR_AMR_OCTET_ALIGN "=1"
 | 
			
		||||
 | 
			
		||||
/*! "octet-align=0".
 | 
			
		||||
 * According to spec [1], "octet-align=0" is identical to omitting 'octet-align' entirely. In Osmocom practice, whether
 | 
			
		||||
 * or not "octet-align=0" is present can make a big difference for osmo-mgw versions 1.12 and older, which do not heed
 | 
			
		||||
 * [1].
 | 
			
		||||
 *
 | 
			
		||||
 * spec [1]: RFC4867, see details in description of osmo_sdp_fmtp_amr_is_octet_aligned().
 | 
			
		||||
 */
 | 
			
		||||
#define OSMO_SDP_STR_AMR_OCTET_ALIGN_0  OSMO_SDP_STR_AMR_OCTET_ALIGN "=0"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								libosmo-sdp.pc.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								libosmo-sdp.pc.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
prefix=@prefix@
 | 
			
		||||
exec_prefix=@exec_prefix@
 | 
			
		||||
libdir=@libdir@
 | 
			
		||||
includedir=@includedir@
 | 
			
		||||
 | 
			
		||||
Name: Osmocom Session Description Protocol coding library
 | 
			
		||||
Description: C Utility Library
 | 
			
		||||
Version: @VERSION@
 | 
			
		||||
Libs: -L${libdir} -losmo-sdp
 | 
			
		||||
Cflags: -I${includedir}/
 | 
			
		||||
@@ -13,14 +13,9 @@ AM_CFLAGS = \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# Libraries
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	libosmo-sdp \
 | 
			
		||||
	libosmo-mgcp-client \
 | 
			
		||||
	libosmo-mgcp \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -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=10:0:1
 | 
			
		||||
MGCP_CLIENT_LIBVERSION=14:0:0
 | 
			
		||||
 | 
			
		||||
lib_LTLIBRARIES = \
 | 
			
		||||
	libosmo-mgcp-client.la \
 | 
			
		||||
@@ -35,4 +33,13 @@ libosmo_mgcp_client_la_SOURCES = \
 | 
			
		||||
	mgcp_client_pool.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)
 | 
			
		||||
libosmo_mgcp_client_la_LDFLAGS = \
 | 
			
		||||
	$(AM_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
 | 
			
		||||
@@ -227,7 +251,7 @@ static struct value_string osmo_mgcpc_ep_fsm_event_names[33] = {};
 | 
			
		||||
 | 
			
		||||
static char osmo_mgcpc_ep_fsm_event_name_bufs[32][32] = {};
 | 
			
		||||
 | 
			
		||||
static void fill_event_names()
 | 
			
		||||
static void fill_event_names(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) {
 | 
			
		||||
@@ -243,7 +267,7 @@ static void fill_event_names()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init()
 | 
			
		||||
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init(void)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0);
 | 
			
		||||
	fill_event_names();
 | 
			
		||||
@@ -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,
 | 
			
		||||
@@ -997,7 +1053,7 @@ static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
 | 
			
		||||
static void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -149,6 +157,7 @@ static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer
 | 
			
		||||
		mgcp_msg->conn_mode = conn_mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* returns message buffer containing MGXP MDCX on success, NULL on error. */
 | 
			
		||||
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_msg mgcp_msg;
 | 
			
		||||
@@ -163,12 +172,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));
 | 
			
		||||
 | 
			
		||||
@@ -186,6 +193,7 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
	return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* returns message buffer containing MGXP DLCX on success, NULL on error. */
 | 
			
		||||
struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_msg mgcp_msg;
 | 
			
		||||
@@ -221,13 +229,14 @@ 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);
 | 
			
		||||
 | 
			
		||||
		msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
 | 
			
		||||
		OSMO_ASSERT(msg);
 | 
			
		||||
 | 
			
		||||
		if (!msg) {
 | 
			
		||||
			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
 | 
			
		||||
		mgcp_ctx->mgw_trans_pending = true;
 | 
			
		||||
		rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi);
 | 
			
		||||
@@ -364,13 +373,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 +628,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 +708,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 +748,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);
 | 
			
		||||
 | 
			
		||||
@@ -760,7 +846,7 @@ const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info)
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init()
 | 
			
		||||
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init(void)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <asm-generic/errno.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
 | 
			
		||||
@@ -27,32 +28,6 @@
 | 
			
		||||
#define LOGPPMGW(pool_member, level, fmt, args...) \
 | 
			
		||||
LOGP(DLMGCP, level, "MGW-pool(%s) " fmt, mgcp_client_pool_member_name(pool_member), ## args)
 | 
			
		||||
 | 
			
		||||
/* Get a human readable name for a given pool member. */
 | 
			
		||||
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member)
 | 
			
		||||
{
 | 
			
		||||
	const struct mgcp_client *mpcp_client;
 | 
			
		||||
	struct mgcp_client mpcp_client_dummy;
 | 
			
		||||
	static char name[512];
 | 
			
		||||
	const char *description;
 | 
			
		||||
 | 
			
		||||
	if (!pool_member)
 | 
			
		||||
		return "(null)";
 | 
			
		||||
 | 
			
		||||
	/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the
 | 
			
		||||
	 * initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */
 | 
			
		||||
	if (!pool_member->client) {
 | 
			
		||||
		memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual));
 | 
			
		||||
		mpcp_client = &mpcp_client_dummy;
 | 
			
		||||
	} else {
 | 
			
		||||
		mpcp_client = pool_member->client;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	description = mgcp_client_name(mpcp_client);
 | 
			
		||||
	snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description);
 | 
			
		||||
 | 
			
		||||
	return name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Allocate MGCP client pool. This is called once on startup and before the pool is used with
 | 
			
		||||
 *  mgcp_client_pool_vty_init(). Since the pool is linked with the VTY it must exist througout the entire runtime.
 | 
			
		||||
 *  \param[in] talloc_ctx talloc context. */
 | 
			
		||||
@@ -64,7 +39,7 @@ struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx)
 | 
			
		||||
	if (!pool)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&pool->pool);
 | 
			
		||||
	INIT_LLIST_HEAD(&pool->member_list);
 | 
			
		||||
 | 
			
		||||
	return pool;
 | 
			
		||||
}
 | 
			
		||||
@@ -77,28 +52,10 @@ unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool)
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
	unsigned int pool_members_initialized = 0;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &pool->pool, list) {
 | 
			
		||||
	llist_for_each_entry(pool_member, &pool->member_list, list) {
 | 
			
		||||
 | 
			
		||||
		/* Initialize client */
 | 
			
		||||
		pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
 | 
			
		||||
		if (!pool_member->client) {
 | 
			
		||||
			LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n");
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Set backpointer so that we can detect later that this MGCP client is managed
 | 
			
		||||
		 * by this pool. */
 | 
			
		||||
		pool_member->client->pool = pool;
 | 
			
		||||
 | 
			
		||||
		/* Connect client */
 | 
			
		||||
		if (mgcp_client_connect2(pool_member->client, 0)) {
 | 
			
		||||
			LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n",
 | 
			
		||||
				 pool_member->conf.remote_addr, pool_member->conf.remote_port);
 | 
			
		||||
			talloc_free(pool_member->client);
 | 
			
		||||
			pool_member->client = NULL;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (mgcp_client_pool_member_reinit_client(pool_member) == 0)
 | 
			
		||||
			pool_members_initialized++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -118,23 +75,43 @@ 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)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &pool->member_list, list) {
 | 
			
		||||
		if (pool_member->nr == nr)
 | 
			
		||||
			return pool_member;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member_picked = NULL;
 | 
			
		||||
	unsigned int n_pool_members = llist_count(&pool->pool);
 | 
			
		||||
	unsigned int n_pool_members = 0;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &pool->pool, list) {
 | 
			
		||||
		if (pool_member->blocked == false && pool_member->client) {
 | 
			
		||||
	llist_for_each_entry(pool_member, &pool->member_list, list) {
 | 
			
		||||
		n_pool_members++;
 | 
			
		||||
		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, "MGW pool has %u members -- MGW %u is unusable\n", n_pool_members,
 | 
			
		||||
				 pool_member->nr);
 | 
			
		||||
			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);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -163,26 +140,24 @@ struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool)
 | 
			
		||||
	 *  by the application code. */
 | 
			
		||||
 | 
			
		||||
	/* When the pool is empty, return a single MGCP client if it is registered. */
 | 
			
		||||
	if (llist_empty(&pool->pool) && pool->mgcp_client_single) {
 | 
			
		||||
	if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "MGW pool is empty -- using (single) MGW %s\n",
 | 
			
		||||
		     mgcp_client_name(pool->mgcp_client_single));
 | 
			
		||||
		return pool->mgcp_client_single;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Abort when the pool is empty */
 | 
			
		||||
	if (llist_empty(&pool->pool)) {
 | 
			
		||||
	if (llist_empty(&pool->member_list)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "MGW pool is empty -- no MGW available!\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Pick a suitable pool member */
 | 
			
		||||
	pool_member = mgcp_client_pool_pick(pool);
 | 
			
		||||
	if (pool_member) {
 | 
			
		||||
		pool_member->refcount++;
 | 
			
		||||
		return pool_member->client;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!pool_member)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	return mgcp_client_pool_member_get(pool_member);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! put an MGCP client back into the pool (decrement reference counter).
 | 
			
		||||
@@ -194,23 +169,130 @@ struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool)
 | 
			
		||||
void mgcp_client_pool_put(struct mgcp_client *mgcp_client)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
	struct mgcp_client_pool *pool;
 | 
			
		||||
 | 
			
		||||
	if (!mgcp_client)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (mgcp_client->pool)
 | 
			
		||||
		pool = mgcp_client->pool;
 | 
			
		||||
	else
 | 
			
		||||
	if (!mgcp_client->pool_member)
 | 
			
		||||
		return;
 | 
			
		||||
	pool_member = mgcp_client->pool_member;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &pool->pool, list) {
 | 
			
		||||
		if (pool_member->client == mgcp_client) {
 | 
			
		||||
	if (pool_member->refcount == 0) {
 | 
			
		||||
		LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	pool_member->refcount--;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************
 | 
			
		||||
 * mgcp_client_pool_member:
 | 
			
		||||
 ***************************/
 | 
			
		||||
 | 
			
		||||
/*! Allocate an mgcp_client_pool_member.
 | 
			
		||||
 *  \param[in] pool MGCP client pool descriptor.
 | 
			
		||||
 *  \param[in] nr Reference number of the pool member.
 | 
			
		||||
 */
 | 
			
		||||
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
 | 
			
		||||
	pool_member = talloc_zero(pool, struct mgcp_client_pool_member);
 | 
			
		||||
	OSMO_ASSERT(pool_member);
 | 
			
		||||
	mgcp_client_conf_init(&pool_member->conf);
 | 
			
		||||
	pool_member->pool = pool;
 | 
			
		||||
	pool_member->nr = nr;
 | 
			
		||||
	llist_add_tail(&pool_member->list, &pool->member_list);
 | 
			
		||||
	return pool_member;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Free an mgcp_client_pool_member allocated through mgcp_client_pool_member_alloc().
 | 
			
		||||
 *  \param[in] pool_member MGCP client pool descriptor.
 | 
			
		||||
 *
 | 
			
		||||
 * It also frees the associated MGCP client if present.
 | 
			
		||||
 */
 | 
			
		||||
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member)
 | 
			
		||||
{
 | 
			
		||||
	llist_del(&pool_member->list);
 | 
			
		||||
	if (pool_member->client) {
 | 
			
		||||
		mgcp_client_disconnect(pool_member->client);
 | 
			
		||||
		talloc_free(pool_member->client);
 | 
			
		||||
	}
 | 
			
		||||
	talloc_free(pool_member);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Recreate and reconnect the MGCP client associated to the pool descriptor.
 | 
			
		||||
 *  \param[in] pool_member MGCP client pool descriptor.
 | 
			
		||||
 */
 | 
			
		||||
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member)
 | 
			
		||||
{
 | 
			
		||||
	/* Get rid of a possibly existing old MGCP client instance first */
 | 
			
		||||
	if (pool_member->client) {
 | 
			
		||||
		mgcp_client_disconnect(pool_member->client);
 | 
			
		||||
		talloc_free(pool_member->client);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Initialize client */
 | 
			
		||||
	pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
 | 
			
		||||
	if (!pool_member->client) {
 | 
			
		||||
		LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */
 | 
			
		||||
	pool_member->client->pool_member = pool_member;
 | 
			
		||||
 | 
			
		||||
	/* Connect client */
 | 
			
		||||
	if (mgcp_client_connect(pool_member->client)) {
 | 
			
		||||
		LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n",
 | 
			
		||||
				pool_member->conf.remote_addr, pool_member->conf.remote_port);
 | 
			
		||||
		talloc_free(pool_member->client);
 | 
			
		||||
		pool_member->client = NULL;
 | 
			
		||||
		return -ECONNABORTED;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get a human readable name for a given pool member. */
 | 
			
		||||
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member)
 | 
			
		||||
{
 | 
			
		||||
	const struct mgcp_client *mpcp_client;
 | 
			
		||||
	struct mgcp_client mpcp_client_dummy;
 | 
			
		||||
	static char name[512];
 | 
			
		||||
	const char *description;
 | 
			
		||||
 | 
			
		||||
	if (!pool_member)
 | 
			
		||||
		return "(null)";
 | 
			
		||||
 | 
			
		||||
	/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the
 | 
			
		||||
	 * initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */
 | 
			
		||||
	if (!pool_member->client) {
 | 
			
		||||
		memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual));
 | 
			
		||||
		mpcp_client = &mpcp_client_dummy;
 | 
			
		||||
	} else {
 | 
			
		||||
		mpcp_client = pool_member->client;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	description = mgcp_client_name(mpcp_client);
 | 
			
		||||
	snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description);
 | 
			
		||||
 | 
			
		||||
	return name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Get the MGCP client associated with the pool reference from the pool (increment reference counter).
 | 
			
		||||
 *  \param[in] pool_member MGCP client pool descriptor.
 | 
			
		||||
 *  \returns MGCP client descriptor, NULL if no member was not ready.
 | 
			
		||||
 */
 | 
			
		||||
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member)
 | 
			
		||||
{
 | 
			
		||||
	pool_member->refcount++;
 | 
			
		||||
	return pool_member->client;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Get whether the MGCP client associated with the pool reference is blocked by policy.
 | 
			
		||||
 *  \param[in] pool_member MGCP client pool descriptor.
 | 
			
		||||
 *  \returns true if blocked, false otherwise
 | 
			
		||||
 */
 | 
			
		||||
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member)
 | 
			
		||||
{
 | 
			
		||||
	return pool_member->blocked;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,14 +28,16 @@
 | 
			
		||||
#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>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_pool.h>
 | 
			
		||||
 | 
			
		||||
#define MGW_STR MGCP_CLIENT_MGW_STR
 | 
			
		||||
 | 
			
		||||
/* Only common (non-pooled) VTY connands will use this talloc context. All
 | 
			
		||||
/* Only common (non-pooled) VTY commands will use this talloc context. All
 | 
			
		||||
 * pooled VTY commands will use the pool (global_mgcp_client_pool) as
 | 
			
		||||
 * talloc context. */
 | 
			
		||||
static void *global_mgcp_client_ctx = NULL;
 | 
			
		||||
@@ -47,17 +49,46 @@ 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;
 | 
			
		||||
	else
 | 
			
		||||
 | 
			
		||||
	/* 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);
 | 
			
		||||
 | 
			
		||||
	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,
 | 
			
		||||
      "mgw local-ip " VTY_IPV46_CMD,
 | 
			
		||||
      MGW_STR "local bind to connect to MGW from\n"
 | 
			
		||||
      "local-ip " VTY_IPV46_CMD,
 | 
			
		||||
      "local bind to connect to MGW from\n"
 | 
			
		||||
      "local bind IPv4 address\n"
 | 
			
		||||
      "local bind IPv6 address\n")
 | 
			
		||||
{
 | 
			
		||||
@@ -72,10 +103,16 @@ ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
 | 
			
		||||
		 "mgcpgw local-ip A.B.C.D",
 | 
			
		||||
		 MGW_STR "local bind to connect to MGCP gateway with\n"
 | 
			
		||||
		 "local bind IP address\n")
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_local_ip,
 | 
			
		||||
		 cfg_mgw_mgw_local_ip_cmd,
 | 
			
		||||
		 "mgw local-ip " VTY_IPV46_CMD,
 | 
			
		||||
		 MGW_STR "local bind to connect to MGW from\n"
 | 
			
		||||
		 "local bind IPv4 address\n"
 | 
			
		||||
		 "local bind IPv6 address\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd,
 | 
			
		||||
      "mgw local-port <0-65535>",
 | 
			
		||||
      MGW_STR "local port to connect to MGW from\n"
 | 
			
		||||
      "local-port <0-65535>",
 | 
			
		||||
      "local port to connect to MGW from\n"
 | 
			
		||||
      "local bind port\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
@@ -87,10 +124,15 @@ ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
 | 
			
		||||
		 "mgcpgw local-port <0-65535>",
 | 
			
		||||
		 MGW_STR "local bind to connect to MGCP gateway with\n"
 | 
			
		||||
		 "local bind port\n")
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_local_port,
 | 
			
		||||
		 cfg_mgw_mgw_local_port_cmd,
 | 
			
		||||
		 "mgw local-port <0-65535>",
 | 
			
		||||
		 MGW_STR "local port to connect to MGW from\n"
 | 
			
		||||
		 "local bind port\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
 | 
			
		||||
      "mgw remote-ip " VTY_IPV46_CMD,
 | 
			
		||||
      MGW_STR "remote IP address to reach the MGW at\n"
 | 
			
		||||
      "remote-ip " VTY_IPV46_CMD,
 | 
			
		||||
      "remote IP address to reach the MGW at\n"
 | 
			
		||||
      "remote IPv4 address\n"
 | 
			
		||||
      "remote IPv6 address\n")
 | 
			
		||||
{
 | 
			
		||||
@@ -104,10 +146,16 @@ ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
 | 
			
		||||
		 "mgcpgw remote-ip A.B.C.D",
 | 
			
		||||
		 MGW_STR "remote bind to connect to MGCP gateway with\n"
 | 
			
		||||
		 "remote bind IP address\n")
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_remote_ip,
 | 
			
		||||
		 cfg_mgw_mgw_remote_ip_cmd,
 | 
			
		||||
		 "mgw remote-ip " VTY_IPV46_CMD,
 | 
			
		||||
		 MGW_STR "remote IP address to reach the MGW at\n"
 | 
			
		||||
		 "remote IPv4 address\n"
 | 
			
		||||
		 "remote IPv6 address\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd,
 | 
			
		||||
      "mgw remote-port <0-65535>",
 | 
			
		||||
      MGW_STR "remote port to reach the MGW at\n"
 | 
			
		||||
      "remote-port <0-65535>",
 | 
			
		||||
      "remote port to reach the MGW at\n"
 | 
			
		||||
      "remote port\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
@@ -119,8 +167,13 @@ ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
 | 
			
		||||
		 "mgcpgw remote-port <0-65535>",
 | 
			
		||||
		 MGW_STR "remote bind to connect to MGCP gateway with\n"
 | 
			
		||||
		 "remote bind port\n")
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_remote_port,
 | 
			
		||||
		 cfg_mgw_mgw_remote_port_cmd,
 | 
			
		||||
		 "mgw remote-port <0-65535>",
 | 
			
		||||
		 MGW_STR "remote port to reach the MGW at\n"
 | 
			
		||||
		 "remote port\n")
 | 
			
		||||
 | 
			
		||||
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
 | 
			
		||||
DEFUN_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgw_mgw_endpoint_range_cmd,
 | 
			
		||||
      "mgw endpoint-range <1-65534> <1-65534>",
 | 
			
		||||
      MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
 | 
			
		||||
      "-\n" "-\n")
 | 
			
		||||
@@ -130,7 +183,7 @@ DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
 | 
			
		||||
      "mgcpgw endpoint-range <1-65534> <1-65534>",
 | 
			
		||||
      MGW_STR "usable range of endpoint identifiers\n"
 | 
			
		||||
      "set first useable endpoint identifier\n"
 | 
			
		||||
@@ -158,8 +211,8 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_endpoint_domain_name,
 | 
			
		||||
      cfg_mgw_endpoint_domain_name_cmd,
 | 
			
		||||
      "mgw endpoint-domain NAME",
 | 
			
		||||
      MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
 | 
			
		||||
      "endpoint-domain NAME",
 | 
			
		||||
      "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
 | 
			
		||||
      "Domain name, should be alphanumeric.\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
@@ -172,11 +225,16 @@ DEFUN(cfg_mgw_endpoint_domain_name,
 | 
			
		||||
	}
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_domain_name,
 | 
			
		||||
      cfg_mgw_mgw_endpoint_domain_name_cmd,
 | 
			
		||||
      "mgw endpoint-domain NAME",
 | 
			
		||||
      MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
 | 
			
		||||
      "Domain name, should be alphanumeric.\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_reset_ep_name,
 | 
			
		||||
      cfg_mgw_reset_ep_name_cmd,
 | 
			
		||||
      "mgw reset-endpoint NAME",
 | 
			
		||||
      MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
 | 
			
		||||
      "reset-endpoint NAME",
 | 
			
		||||
      "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
 | 
			
		||||
      "e.g. 'rtpbridge/*'\n"
 | 
			
		||||
      "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
 | 
			
		||||
{
 | 
			
		||||
@@ -214,11 +272,17 @@ DEFUN(cfg_mgw_reset_ep_name,
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_reset_ep_name,
 | 
			
		||||
      cfg_mgw_mgw_reset_ep_name_cmd,
 | 
			
		||||
      "mgw reset-endpoint NAME",
 | 
			
		||||
      MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to 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_no_reset_ep_name,
 | 
			
		||||
      cfg_mgw_no_reset_ep_name_cmd,
 | 
			
		||||
      "no mgw reset-endpoint NAME",
 | 
			
		||||
      NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
 | 
			
		||||
      "no reset-endpoint NAME",
 | 
			
		||||
      NO_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")
 | 
			
		||||
{
 | 
			
		||||
	struct reset_ep *reset_ep;
 | 
			
		||||
@@ -235,6 +299,96 @@ DEFUN(cfg_mgw_no_reset_ep_name,
 | 
			
		||||
	vty_out(vty, "%% no such endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE);
 | 
			
		||||
	return CMD_WARNING;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_no_reset_ep_name,
 | 
			
		||||
      cfg_mgw_mgw_no_reset_ep_name_cmd,
 | 
			
		||||
      "no mgw reset-endpoint 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)
 | 
			
		||||
{
 | 
			
		||||
@@ -242,33 +396,47 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_
 | 
			
		||||
	int port;
 | 
			
		||||
	struct reset_ep *reset_ep;
 | 
			
		||||
 | 
			
		||||
	if (conf->description)
 | 
			
		||||
	/* If caller doesn't the MGW pool API (mgcp_client_pool_vty_init was never called),
 | 
			
		||||
	 * then the "mgw" cmd prefix must be added since the old node always contained it.
 | 
			
		||||
	 */
 | 
			
		||||
	const char *mgw_prefix = global_mgcp_client_pool ? "" : "mgw ";
 | 
			
		||||
 | 
			
		||||
	if (conf->description) /* description never had "mgw" prefix even on old node: */
 | 
			
		||||
		vty_out(vty, "%sdescription %s%s", indent, conf->description, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	addr = conf->local_addr;
 | 
			
		||||
	if (addr)
 | 
			
		||||
		vty_out(vty, "%smgw local-ip %s%s", indent, addr,
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "%s%slocal-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
 | 
			
		||||
	port = conf->local_port;
 | 
			
		||||
	if (port >= 0)
 | 
			
		||||
		vty_out(vty, "%smgw local-port %u%s", indent,
 | 
			
		||||
		vty_out(vty, "%s%slocal-port %u%s", indent, mgw_prefix,
 | 
			
		||||
			(uint16_t)port, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	addr = conf->remote_addr;
 | 
			
		||||
	if (addr)
 | 
			
		||||
		vty_out(vty, "%smgw remote-ip %s%s", indent, addr,
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "%s%sremote-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
 | 
			
		||||
	port = conf->remote_port;
 | 
			
		||||
	if (port >= 0)
 | 
			
		||||
		vty_out(vty, "%smgw remote-port %u%s", indent,
 | 
			
		||||
		vty_out(vty, "%s%sremote-port %u%s", indent, mgw_prefix,
 | 
			
		||||
			(uint16_t)port, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (conf->endpoint_domain_name[0])
 | 
			
		||||
		vty_out(vty, "%smgw endpoint-domain %s%s", indent,
 | 
			
		||||
		vty_out(vty, "%s%sendpoint-domain %s%s", indent, mgw_prefix,
 | 
			
		||||
			conf->endpoint_domain_name, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(reset_ep, &conf->reset_epnames, list)
 | 
			
		||||
		vty_out(vty, "%smgw reset-endpoint %s%s", indent, reset_ep->name, VTY_NEWLINE);
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
@@ -279,6 +447,11 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_
 | 
			
		||||
 *  \returns CMD_SUCCESS on success, CMD_WARNING on error */
 | 
			
		||||
int mgcp_client_config_write(struct vty *vty, const char *indent)
 | 
			
		||||
{
 | 
			
		||||
	/* If caller supports MGW pool API (mgcp_client_pool_vty_init was
 | 
			
		||||
	 * called), then skip printing any config in this node and print it when
 | 
			
		||||
	 * the whole 'mgw' node is printed. */
 | 
			
		||||
	if (global_mgcp_client_pool)
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
	return config_write(vty, indent, global_mgcp_client_conf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -286,15 +459,18 @@ static void vty_init_common(void *talloc_ctx, int node)
 | 
			
		||||
{
 | 
			
		||||
	global_mgcp_client_ctx = talloc_ctx;
 | 
			
		||||
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_local_ip_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_local_port_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_remote_ip_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_remote_port_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_endpoint_range_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_endpoint_domain_name_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_reset_ep_name_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_no_reset_ep_name_cmd);
 | 
			
		||||
	/* deprecated 'mgw' commands ('mgw' prepended as first arg) */
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_local_ip_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_local_port_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_remote_ip_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_remote_port_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_endpoint_range_cmd);
 | 
			
		||||
	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();
 | 
			
		||||
}
 | 
			
		||||
@@ -319,38 +495,62 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
 | 
			
		||||
	vty_init_common(talloc_ctx, node);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_write_pool(struct vty *vty)
 | 
			
		||||
/* Mark whether user called mgcp_client_pool_config_write() and hence support new API */
 | 
			
		||||
static bool mgcp_client_pool_config_write_called = false;
 | 
			
		||||
 | 
			
		||||
static int _mgcp_client_pool_config_write(struct vty *vty, const char *indent)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_pool *pool = global_mgcp_client_pool;
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
	unsigned int indent_buf_len = strlen(pool->vty_indent) + 1 + 1;
 | 
			
		||||
	char *indent = talloc_zero_size(vty, indent_buf_len);
 | 
			
		||||
	unsigned int subindent_buf_len;
 | 
			
		||||
	char *subindent;
 | 
			
		||||
 | 
			
		||||
	snprintf(indent, indent_buf_len, "%s ", pool->vty_indent);
 | 
			
		||||
	if (!indent)
 | 
			
		||||
		indent = pool->vty_indent ? : "";
 | 
			
		||||
	subindent_buf_len = strlen(indent) + 1 + 1;
 | 
			
		||||
	subindent = talloc_zero_size(vty, subindent_buf_len);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &pool->pool, list) {
 | 
			
		||||
		vty_out(vty, "%smgw %u%s", pool->vty_indent, pool_member->nr, VTY_NEWLINE);
 | 
			
		||||
		config_write(vty, indent, &pool_member->conf);
 | 
			
		||||
	snprintf(subindent, subindent_buf_len, "%s ", indent);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &pool->member_list, list) {
 | 
			
		||||
		vty_out(vty, "%smgw %u%s", indent, pool_member->nr, VTY_NEWLINE);
 | 
			
		||||
		config_write(vty, subindent, &pool_member->conf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(indent);
 | 
			
		||||
	/* MGW pool API is supported by user (global_mgcp_client_pool is set
 | 
			
		||||
	 * because mgcp_client_pool_vty_init was called). If single MGW was
 | 
			
		||||
	 * configured through old VTY and no mgw in the new MGW pool VTY is
 | 
			
		||||
	 * replacing it, then output the single MGW converted to the new MGW
 | 
			
		||||
	 * pool VTY. */
 | 
			
		||||
	if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
 | 
			
		||||
		vty_out(vty, "%smgw 0%s", indent, VTY_NEWLINE);
 | 
			
		||||
		config_write(vty, subindent, global_mgcp_client_conf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(subindent);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Lookup the selected MGCP client config by its reference number */
 | 
			
		||||
static struct mgcp_client_pool_member *pool_member_by_nr(unsigned int nr)
 | 
			
		||||
/* Deprecated, used for backward compatibility with older users which didn't call
 | 
			
		||||
 * mgcp_client_pool_config_write(): */
 | 
			
		||||
static int config_write_pool(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member = NULL;
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member_tmp;
 | 
			
		||||
	if (mgcp_client_pool_config_write_called)
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member_tmp, &global_mgcp_client_pool->pool, list) {
 | 
			
		||||
		if (pool_member_tmp->nr == nr) {
 | 
			
		||||
			pool_member = pool_member_tmp;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	return _mgcp_client_pool_config_write(vty, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	return pool_member;
 | 
			
		||||
/*! Write out MGCP client config to VTY.
 | 
			
		||||
 *  \param[in] vty VTY to which we should print.
 | 
			
		||||
 *  \param[in] indent string used for indentation (e.g. " ").
 | 
			
		||||
	       If NULL, indentation passed during mgcp_client_pool_vty_init() will be used.
 | 
			
		||||
 *  \returns CMD_SUCCESS on success, CMD_WARNING on error */
 | 
			
		||||
int mgcp_client_pool_config_write(struct vty *vty, const char *indent)
 | 
			
		||||
{
 | 
			
		||||
	/* Tell internal node write function that the user supports calling proper API: */
 | 
			
		||||
	mgcp_client_pool_config_write_called = true;
 | 
			
		||||
	return _mgcp_client_pool_config_write(vty, indent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_ATTR(cfg_mgw,
 | 
			
		||||
@@ -359,13 +559,10 @@ DEFUN_ATTR(cfg_mgw,
 | 
			
		||||
	int nr = atoi(argv[0]);
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
 | 
			
		||||
	pool_member = pool_member_by_nr(nr);
 | 
			
		||||
	pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
 | 
			
		||||
	if (!pool_member) {
 | 
			
		||||
		pool_member = talloc_zero(global_mgcp_client_pool, struct mgcp_client_pool_member);
 | 
			
		||||
		pool_member = mgcp_client_pool_member_alloc(global_mgcp_client_pool, nr);
 | 
			
		||||
		OSMO_ASSERT(pool_member);
 | 
			
		||||
		mgcp_client_conf_init(&pool_member->conf);
 | 
			
		||||
		pool_member->nr = nr;
 | 
			
		||||
		llist_add_tail(&pool_member->list, &global_mgcp_client_pool->pool);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vty->index = &pool_member->conf;
 | 
			
		||||
@@ -382,7 +579,7 @@ DEFUN_ATTR(cfg_no_mgw,
 | 
			
		||||
	int nr = atoi(argv[0]);
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
 | 
			
		||||
	pool_member = pool_member_by_nr(nr);
 | 
			
		||||
	pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
 | 
			
		||||
	if (!pool_member) {
 | 
			
		||||
		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
@@ -395,12 +592,7 @@ DEFUN_ATTR(cfg_no_mgw,
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_del(&pool_member->list);
 | 
			
		||||
	if (pool_member->client) {
 | 
			
		||||
		mgcp_client_disconnect(pool_member->client);
 | 
			
		||||
		talloc_free(pool_member->client);
 | 
			
		||||
	}
 | 
			
		||||
	talloc_free(pool_member);
 | 
			
		||||
	mgcp_client_pool_member_free(pool_member);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
@@ -412,7 +604,7 @@ DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd,
 | 
			
		||||
	int nr = atoi(argv[0]);
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member = NULL;
 | 
			
		||||
 | 
			
		||||
	pool_member = pool_member_by_nr(nr);
 | 
			
		||||
	pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
 | 
			
		||||
	if (!pool_member) {
 | 
			
		||||
		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
@@ -425,32 +617,10 @@ DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd,
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Get rid of a possibly existing old MGCP client instance first */
 | 
			
		||||
	if (pool_member->client) {
 | 
			
		||||
		mgcp_client_disconnect(pool_member->client);
 | 
			
		||||
		talloc_free(pool_member->client);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Create a new MGCP client instance with the current config */
 | 
			
		||||
	pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
 | 
			
		||||
	if (!pool_member->client) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s initalization failed\n",
 | 
			
		||||
		     mgcp_client_pool_member_name(pool_member));
 | 
			
		||||
		vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s",
 | 
			
		||||
			mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */
 | 
			
		||||
	pool_member->client->pool = global_mgcp_client_pool;
 | 
			
		||||
 | 
			
		||||
	/* Connect client */
 | 
			
		||||
	if (mgcp_client_connect(pool_member->client)) {
 | 
			
		||||
	if (mgcp_client_pool_member_reinit_client(pool_member) < 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s connect failed at (%s:%u)\n",
 | 
			
		||||
		     mgcp_client_pool_member_name(pool_member), pool_member->conf.remote_addr,
 | 
			
		||||
		     pool_member->conf.remote_port);
 | 
			
		||||
		talloc_free(pool_member->client);
 | 
			
		||||
		pool_member->client = NULL;
 | 
			
		||||
		vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s",
 | 
			
		||||
			mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
@@ -466,7 +636,7 @@ DEFUN_ATTR(mgw_block, mgw_block_cmd,
 | 
			
		||||
	int nr = atoi(argv[0]);
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member = NULL;
 | 
			
		||||
 | 
			
		||||
	pool_member = pool_member_by_nr(nr);
 | 
			
		||||
	pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
 | 
			
		||||
	if (!pool_member) {
 | 
			
		||||
		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
@@ -483,7 +653,7 @@ DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd,
 | 
			
		||||
	int nr = atoi(argv[0]);
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member = NULL;
 | 
			
		||||
 | 
			
		||||
	pool_member = pool_member_by_nr(nr);
 | 
			
		||||
	pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
 | 
			
		||||
	if (!pool_member) {
 | 
			
		||||
		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
@@ -493,22 +663,27 @@ DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(mgw_show, mgw_snow_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n")
 | 
			
		||||
DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n")
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% MGW-Pool:%s", VTY_NEWLINE);
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
 | 
			
		||||
	if (llist_empty(&global_mgcp_client_pool->pool) && global_mgcp_client_pool->mgcp_client_single) {
 | 
			
		||||
	if (llist_empty(&global_mgcp_client_pool->member_list) && global_mgcp_client_pool->mgcp_client_single) {
 | 
			
		||||
		vty_out(vty, "%%  (pool is empty, single MGCP client will be used)%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
	} else if (llist_empty(&global_mgcp_client_pool->pool)) {
 | 
			
		||||
	} else if (llist_empty(&global_mgcp_client_pool->member_list)) {
 | 
			
		||||
		vty_out(vty, "%%  (pool is empty)%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &global_mgcp_client_pool->pool, list) {
 | 
			
		||||
	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);
 | 
			
		||||
@@ -520,7 +695,8 @@ DEFUN(mgw_show, mgw_snow_cmd, "show mgw-pool", SHOW_STR "Display information abo
 | 
			
		||||
 *  (called once at startup by the application process).
 | 
			
		||||
 *  \param[in] parent_node identifier of the parent node on which the mgw node appears.
 | 
			
		||||
 *  \param[in] mgw_node identifier that should be used with the newly installed MGW node.
 | 
			
		||||
 *  \param[in] indent indentation string to match the indentation in the VTY config
 | 
			
		||||
 *  \param[in] indent indentation string to match the indentation in the VTY config.
 | 
			
		||||
	       If NULL, it must be passed explicitly each time mgcp_client_pool_config_write() is called.
 | 
			
		||||
 *  \param[in] pool user provided memory to store the configured MGCP client (MGW) pool. */
 | 
			
		||||
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool)
 | 
			
		||||
{
 | 
			
		||||
@@ -528,11 +704,12 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
 | 
			
		||||
	OSMO_ASSERT(pool);
 | 
			
		||||
 | 
			
		||||
	/* Never allow this function to be called twice on the same pool */
 | 
			
		||||
	OSMO_ASSERT(!pool->vty_indent);
 | 
			
		||||
	OSMO_ASSERT(!pool->vty_node);
 | 
			
		||||
 | 
			
		||||
	if (indent) {
 | 
			
		||||
		pool->vty_indent = talloc_strdup(pool, indent);
 | 
			
		||||
		OSMO_ASSERT(pool->vty_indent);
 | 
			
		||||
	}
 | 
			
		||||
	pool->vty_node = talloc_zero(pool, struct cmd_node);
 | 
			
		||||
	OSMO_ASSERT(pool->vty_node);
 | 
			
		||||
	pool->vty_node->node = mgw_node;
 | 
			
		||||
@@ -542,8 +719,19 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
 | 
			
		||||
	install_lib_element(parent_node, &cfg_mgw_cmd);
 | 
			
		||||
	install_lib_element(parent_node, &cfg_no_mgw_cmd);
 | 
			
		||||
 | 
			
		||||
	/* Note: config_write_pool is deprecated and user is expected to
 | 
			
		||||
	 * manually call mgcp_client_pool_config_write() when printing the VTY
 | 
			
		||||
	 * config */
 | 
			
		||||
	install_node(pool->vty_node, config_write_pool);
 | 
			
		||||
	vty_init_common(pool, mgw_node);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_local_ip_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_local_port_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_remote_ip_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_remote_port_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_rtp_bts_base_port_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_endpoint_domain_name_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_reset_ep_name_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_no_reset_ep_name_cmd);
 | 
			
		||||
 | 
			
		||||
	install_element(mgw_node, &cfg_description_cmd);
 | 
			
		||||
 | 
			
		||||
@@ -551,7 +739,7 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
 | 
			
		||||
	install_lib_element(ENABLE_NODE, &mgw_block_cmd);
 | 
			
		||||
	install_lib_element(ENABLE_NODE, &mgw_unblock_cmd);
 | 
			
		||||
 | 
			
		||||
	install_lib_element_ve(&mgw_snow_cmd);
 | 
			
		||||
	install_lib_element_ve(&mgw_show_cmd);
 | 
			
		||||
 | 
			
		||||
	global_mgcp_client_pool = pool;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,24 +16,18 @@ AM_CFLAGS = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(LIBOSMOTRAU_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_LIBRARIES = \
 | 
			
		||||
	libosmo-mgcp.a \
 | 
			
		||||
noinst_LTLIBRARIES = \
 | 
			
		||||
	libosmo-mgcp.la \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	g711common.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_mgcp_a_SOURCES = \
 | 
			
		||||
libosmo_mgcp_la_SOURCES = \
 | 
			
		||||
	mgcp_protocol.c \
 | 
			
		||||
	mgcp_network.c \
 | 
			
		||||
	mgcp_vty.c \
 | 
			
		||||
@@ -45,8 +39,16 @@ libosmo_mgcp_a_SOURCES = \
 | 
			
		||||
	mgcp_stat.c \
 | 
			
		||||
	mgcp_endp.c \
 | 
			
		||||
	mgcp_trunk.c \
 | 
			
		||||
	mgcp_ctrl.c \
 | 
			
		||||
	mgcp_ratectr.c \
 | 
			
		||||
	mgcp_e1.c \
 | 
			
		||||
	mgcp_iuup.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_mgcp_la_LIBADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(LIBOSMOTRAU_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -276,75 +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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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":
 | 
			
		||||
@@ -364,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)
 | 
			
		||||
@@ -376,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.
 | 
			
		||||
@@ -440,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)
 | 
			
		||||
{
 | 
			
		||||
@@ -457,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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
 | 
			
		||||
static const struct rate_ctr_group_desc rate_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "conn_rtp",
 | 
			
		||||
	.group_description = "rtp connection statistics",
 | 
			
		||||
	.class_id = 1,
 | 
			
		||||
@@ -74,7 +74,7 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
 | 
			
		||||
 | 
			
		||||
		/* ensure that the generated conn_id is unique
 | 
			
		||||
		 * for this endpoint */
 | 
			
		||||
		if (!mgcp_conn_get_rtp(endp, id_hex)) {
 | 
			
		||||
		if (!mgcp_endp_get_conn_rtp(endp, id_hex)) {
 | 
			
		||||
			osmo_strlcpy(id, id_hex, MGCP_CONN_ID_MAXLEN);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
@@ -95,17 +95,21 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
 | 
			
		||||
	static atomic_uint rate_ctr_index = 0;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->type = MGCP_RTP_DEFAULT;
 | 
			
		||||
	conn_rtp->osmux.cid_allocated = false;
 | 
			
		||||
	conn_rtp->osmux.cid = 0;
 | 
			
		||||
 | 
			
		||||
	/* Osmux specific defaults, only used if conn is later on Osmux-enabled: */
 | 
			
		||||
	conn_rtp->osmux.state = OSMUX_STATE_DISABLED;
 | 
			
		||||
	conn_rtp->osmux.local_cid_allocated = false;
 | 
			
		||||
	conn_rtp->osmux.local_cid = 0;
 | 
			
		||||
	conn_rtp->osmux.remote_cid_present = false;
 | 
			
		||||
	conn_rtp->osmux.remote_cid = 0;
 | 
			
		||||
 | 
			
		||||
	/* backpointer to the generic part of the connection */
 | 
			
		||||
	conn->u.rtp.conn = conn;
 | 
			
		||||
 | 
			
		||||
	end->rtp.fd = -1;
 | 
			
		||||
	end->rtcp.fd = -1;
 | 
			
		||||
	end->rtp_port = end->rtcp_port = 0;
 | 
			
		||||
	talloc_free(end->fmtp_extra);
 | 
			
		||||
	end->fmtp_extra = NULL;
 | 
			
		||||
	end->rtp = NULL;
 | 
			
		||||
	end->rtcp = NULL;
 | 
			
		||||
	memset(&end->addr, 0, sizeof(end->addr));
 | 
			
		||||
	end->rtcp_port = 0;
 | 
			
		||||
 | 
			
		||||
	/* Set default values */
 | 
			
		||||
	end->frames_per_packet = 0;	/* unknown */
 | 
			
		||||
@@ -113,12 +117,12 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
 | 
			
		||||
	end->output_enabled = false;
 | 
			
		||||
	end->maximum_packet_time = -1;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
 | 
			
		||||
	if (!conn_rtp->rate_ctr_group)
 | 
			
		||||
	conn_rtp->ctrg = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
 | 
			
		||||
	if (!conn_rtp->ctrg)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, IN_STREAM_ERR_TSTMP_CTR);
 | 
			
		||||
	conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, OUT_STREAM_ERR_TSTMP_CTR);
 | 
			
		||||
	conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, IN_STREAM_ERR_TSTMP_CTR);
 | 
			
		||||
	conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, OUT_STREAM_ERR_TSTMP_CTR);
 | 
			
		||||
 | 
			
		||||
	/* Make sure codec table is reset */
 | 
			
		||||
	mgcp_codec_reset_all(conn_rtp);
 | 
			
		||||
@@ -133,8 +137,8 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
		conn_osmux_disable(conn_rtp);
 | 
			
		||||
	if (mgcp_conn_rtp_is_iuup(conn_rtp))
 | 
			
		||||
		mgcp_conn_iuup_cleanup(conn_rtp);
 | 
			
		||||
	mgcp_free_rtp_port(&conn_rtp->end);
 | 
			
		||||
	rate_ctr_group_free(conn_rtp->rate_ctr_group);
 | 
			
		||||
	mgcp_rtp_end_free_port(&conn_rtp->end);
 | 
			
		||||
	rate_ctr_group_free(conn_rtp->ctrg);
 | 
			
		||||
	mgcp_codec_reset_all(conn_rtp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -142,7 +146,7 @@ void mgcp_conn_watchdog_cb(void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn = data;
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_ERROR, "connection timed out!\n");
 | 
			
		||||
	mgcp_conn_free(conn->endp, conn->id);
 | 
			
		||||
	mgcp_conn_free(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
 | 
			
		||||
@@ -167,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 */
 | 
			
		||||
@@ -208,62 +212,10 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
	return conn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! find a connection by its ID.
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection
 | 
			
		||||
 *  \returns pointer to allocated connection, NULL if not found */
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	const char *id_upper;
 | 
			
		||||
	const char *conn_id;
 | 
			
		||||
 | 
			
		||||
	if (!id || !*id)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Ignore leading zeros in needle */
 | 
			
		||||
	while (*id == '0')
 | 
			
		||||
		id++;
 | 
			
		||||
 | 
			
		||||
	/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
 | 
			
		||||
	 * Connections" defines the id as a hex string, so clients may return lower case hex even though
 | 
			
		||||
	 * we sent upper case hex in the CRCX response. */
 | 
			
		||||
	id_upper = osmo_str_toupper(id);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(conn, &endp->conns, entry) {
 | 
			
		||||
		/* Ignore leading zeros in haystack */
 | 
			
		||||
		for (conn_id=conn->id; *conn_id == '0'; conn_id++);
 | 
			
		||||
 | 
			
		||||
		if (strcmp(conn_id, id_upper) == 0)
 | 
			
		||||
			return conn;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! find an RTP connection by its ID.
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection
 | 
			
		||||
 *  \returns pointer to allocated connection, NULL if not found */
 | 
			
		||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
					const char *id)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	conn = mgcp_conn_get(endp, id);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP)
 | 
			
		||||
		return &conn->u.rtp;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
{
 | 
			
		||||
	struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
 | 
			
		||||
	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
 | 
			
		||||
	struct rate_ctr_group *conn_stats = conn_rtp->ctrg;
 | 
			
		||||
 | 
			
		||||
	if (all_stats == NULL || conn_stats == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
@@ -282,21 +234,21 @@ static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_con
 | 
			
		||||
	rate_ctr_inc(rate_ctr_group_get_ctr(all_stats, RTP_NUM_CONNECTIONS));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! free a connection by its ID.
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection */
 | 
			
		||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
/*! free a connection
 | 
			
		||||
 *  \param[in] conn the conn to free. May be NULL.
 | 
			
		||||
*/
 | 
			
		||||
void mgcp_conn_free(struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp;
 | 
			
		||||
 | 
			
		||||
	conn = mgcp_conn_get(endp, id);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		aggregate_rtp_conn_stats(endp, &conn->u.rtp);
 | 
			
		||||
		mgcp_rtp_conn_cleanup(&conn->u.rtp);
 | 
			
		||||
		conn_rtp = mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
		aggregate_rtp_conn_stats(conn->endp, conn_rtp);
 | 
			
		||||
		mgcp_rtp_conn_cleanup(conn_rtp);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		/* NOTE: This should never be called with an
 | 
			
		||||
@@ -306,43 +258,40 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osmo_timer_del(&conn->watchdog);
 | 
			
		||||
	mgcp_endp_remove_conn(endp, conn);
 | 
			
		||||
	mgcp_endp_remove_conn(conn->endp, conn);
 | 
			
		||||
	/* WARN: endp may have be freed after call to mgcp_endp_remove_conn */
 | 
			
		||||
	talloc_free(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! free oldest connection in the list.
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
 | 
			
		||||
/*! Parse connection mode.
 | 
			
		||||
 *  \param[in] conn Connection whose mode is being set
 | 
			
		||||
 *  \param[in] mode Mode to set
 | 
			
		||||
 *  \returns 0 on success, -1 on error */
 | 
			
		||||
int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
	if (mode == MGCP_CONN_NONE) {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "missing connection mode\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	conn->mode = mode;
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
 | 
			
		||||
		 mgcp_cmode_name(mode), conn->mode);
 | 
			
		||||
 | 
			
		||||
	if (llist_empty(&endp->conns))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	mgcp_conn_free(endp, conn->id);
 | 
			
		||||
	/* Special handling for RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
		conn_rtp->end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
 | 
			
		||||
			 conn_rtp->end.output_enabled);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/*! free all connections at once.
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
#if defined(__has_attribute)
 | 
			
		||||
#if __has_attribute(no_sanitize)
 | 
			
		||||
__attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	/* The VTY might change the connection mode at any time, so we have
 | 
			
		||||
	 * to hold a copy of the original connection mode */
 | 
			
		||||
	conn->mode_orig = conn->mode;
 | 
			
		||||
 | 
			
		||||
	/* Drop all items in the list, might be consecutive! */
 | 
			
		||||
	while ((conn = llist_first_entry_or_null(&endp->conns, struct mgcp_conn, entry)))
 | 
			
		||||
		mgcp_conn_free(endp, conn->id);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! dump basic connection information to human readable string.
 | 
			
		||||
@@ -352,29 +301,39 @@ 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) };
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp;
 | 
			
		||||
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		snprintf(str, sizeof(str), "(null connection)");
 | 
			
		||||
		return str;
 | 
			
		||||
	}
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return "NULL";
 | 
			
		||||
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		/* Dump RTP connection */
 | 
			
		||||
		snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
 | 
			
		||||
			 "rtp:%u rtcp:%u)",
 | 
			
		||||
		conn_rtp = mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
		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),
 | 
			
		||||
			 ntohs(conn->u.rtp.end.rtp_port),
 | 
			
		||||
			 ntohs(conn->u.rtp.end.rtcp_port));
 | 
			
		||||
				   osmo_sockaddr_ntop(&conn_rtp->end.addr.u.sa, ipbuf) ? : "NULL",
 | 
			
		||||
				   osmo_sockaddr_port(&conn_rtp->end.addr.u.sa),
 | 
			
		||||
				   conn_rtp->end.local_addr ? : "NULL",
 | 
			
		||||
				   conn_rtp->end.local_port);
 | 
			
		||||
 | 
			
		||||
		switch (conn_rtp->type) {
 | 
			
		||||
		case MGCP_RTP_OSMUX:
 | 
			
		||||
			OSMO_STRBUF_PRINTF(sb, " CID=%u", conn_rtp->osmux.local_cid);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			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;
 | 
			
		||||
@@ -402,12 +361,9 @@ struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! get oldest connection in the list.
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	if (llist_empty(&endp->conns))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	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" },
 | 
			
		||||
	{}
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,36 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2020 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/ctrl/control_if.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int mgw_ctrl_node_lookup(void *data, vector vline, int *node_type,
 | 
			
		||||
				void **node_data, int *i)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
 | 
			
		||||
					     const char *bind_addr, uint16_t port)
 | 
			
		||||
{
 | 
			
		||||
	return ctrl_interface_setup_dynip2(cfg, bind_addr, port, mgw_ctrl_node_lookup,
 | 
			
		||||
					   _LAST_CTRL_NODE);
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -325,7 +324,7 @@ static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
 | 
			
		||||
				     msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
 | 
			
		||||
#endif
 | 
			
		||||
	/* Hand data over to the E1 stack */
 | 
			
		||||
	msgb_enqueue(&ts->raw.tx_queue, msg);
 | 
			
		||||
	e1inp_ts_send_raw(ts, msg);
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
@@ -417,11 +465,21 @@ static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name,
 | 
			
		||||
{
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "GSM") == 0)
 | 
			
		||||
		return OSMO_TRAU16_FT_FR;
 | 
			
		||||
	else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
 | 
			
		||||
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
 | 
			
		||||
		return OSMO_TRAU16_FT_EFR;
 | 
			
		||||
	else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
 | 
			
		||||
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0) {
 | 
			
		||||
		if (i460_rate == OSMO_I460_RATE_16k)
 | 
			
		||||
			return OSMO_TRAU16_FT_HR;
 | 
			
		||||
	else if (strcmp(sdp_subtype_name, "AMR") == 0) {
 | 
			
		||||
		if (i460_rate == OSMO_I460_RATE_8k)
 | 
			
		||||
			return OSMO_TRAU8_SPEECH;
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_ERROR,
 | 
			
		||||
			 "E1-TRAU-TX: unsupported or illegal I.460 rate for HR\n");
 | 
			
		||||
		return OSMO_TRAU_FT_NONE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "AMR") == 0) {
 | 
			
		||||
		if (i460_rate == OSMO_I460_RATE_8k) {
 | 
			
		||||
			switch (amr_ft) {
 | 
			
		||||
			case AMR_4_75:
 | 
			
		||||
@@ -439,12 +497,12 @@ static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return OSMO_TRAU16_FT_AMR;
 | 
			
		||||
	} else {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
 | 
			
		||||
		 sdp_subtype_name);
 | 
			
		||||
	return OSMO_TRAU_FT_NONE;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Determine a suitable TRAU frame type for a given codec */
 | 
			
		||||
static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
 | 
			
		||||
@@ -452,11 +510,21 @@ static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name
 | 
			
		||||
{
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "GSM") == 0)
 | 
			
		||||
		return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
	else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
 | 
			
		||||
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
 | 
			
		||||
		return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
	else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
 | 
			
		||||
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0) {
 | 
			
		||||
		if (i460_rate == OSMO_I460_RATE_16k)
 | 
			
		||||
			return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
		if (i460_rate == OSMO_I460_RATE_8k)
 | 
			
		||||
			return OSMO_TRAU_SYNCP_8_HR;
 | 
			
		||||
	else if (strcmp(sdp_subtype_name, "AMR") == 0) {
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_ERROR,
 | 
			
		||||
			 "E1-TRAU-TX: unsupported or illegal I.460 rate for HR\n");
 | 
			
		||||
		return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "AMR") == 0) {
 | 
			
		||||
		if (i460_rate == OSMO_I460_RATE_8k) {
 | 
			
		||||
			switch (amr_ft) {
 | 
			
		||||
			case AMR_4_75:
 | 
			
		||||
@@ -474,12 +542,12 @@ static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
	} else {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
 | 
			
		||||
		 sdp_subtype_name);
 | 
			
		||||
	return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find out if a given TRAU frame type is AMR */
 | 
			
		||||
static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
 | 
			
		||||
@@ -496,7 +564,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 +585,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;
 | 
			
		||||
 | 
			
		||||
@@ -582,15 +650,17 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8
 | 
			
		||||
void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp;
 | 
			
		||||
	struct mgcp_rtp_codec *codec;
 | 
			
		||||
	enum osmo_tray_sync_pat_id sync_pat_id;
 | 
			
		||||
 | 
			
		||||
	/* In order to determine the codec, find the oldest connection on
 | 
			
		||||
	 * the endpoint and use its codec information. Normally on an E1
 | 
			
		||||
	 * endpoint no more than one connection should exist. */
 | 
			
		||||
	conn = mgcp_conn_get_oldest(endp);
 | 
			
		||||
	conn = mgcp_endp_get_conn_oldest(endp);
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
	codec = conn->u.rtp.end.codec;
 | 
			
		||||
	conn_rtp = mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
	codec = conn_rtp->end.codec;
 | 
			
		||||
	OSMO_ASSERT(codec);
 | 
			
		||||
 | 
			
		||||
	/* Update codec information */
 | 
			
		||||
@@ -604,9 +674,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 +691,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,
 | 
			
		||||
	},
 | 
			
		||||
@@ -59,7 +58,7 @@ static char *gen_virtual_epname(void *ctx, const char *domain,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Generate E1 endpoint name from given numeric parameters */
 | 
			
		||||
static char *gen_e1_epname(void *ctx, const char *domain, unsigned int trunk_nr,
 | 
			
		||||
static char *gen_e1_epname(const void *ctx, const char *domain, unsigned int trunk_nr,
 | 
			
		||||
			   uint8_t ts_nr, uint8_t ss_nr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int rate;
 | 
			
		||||
@@ -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.
 | 
			
		||||
@@ -467,7 +446,7 @@ static uint8_t e1_ss_nr_from_epname(const char *epname)
 | 
			
		||||
/* Check if the selected E1 endpoint is avalable, which means that none of
 | 
			
		||||
 * the overlapping endpoints are currently serving a call. (if the system
 | 
			
		||||
 * is properly configured such a situation should never ocurr!) */
 | 
			
		||||
static bool endp_avail_e1(struct mgcp_endpoint *endp)
 | 
			
		||||
static bool endp_avail_e1(const struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	/* The following map shows the overlapping of the subslots and their
 | 
			
		||||
	 * respective rates. The numbers on the right running from top to bottom
 | 
			
		||||
@@ -573,7 +552,7 @@ static bool endp_avail_e1(struct mgcp_endpoint *endp)
 | 
			
		||||
/*! check if an endpoint is available for any kind of operation.
 | 
			
		||||
 *  \param[in] endp endpoint to check.
 | 
			
		||||
 *  \returns true if endpoint is avalable, false it is blocked for any reason. */
 | 
			
		||||
bool mgcp_endp_avail(struct mgcp_endpoint *endp)
 | 
			
		||||
bool mgcp_endp_avail(const struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	switch (endp->trunk->trunk_type) {
 | 
			
		||||
	case MGCP_TRUNK_VIRTUAL:
 | 
			
		||||
@@ -590,6 +569,24 @@ bool mgcp_endp_avail(struct mgcp_endpoint *endp)
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Get number of conns in an endpoint.
 | 
			
		||||
 *  \param[in] endp endpoint to check.
 | 
			
		||||
 *  \returns Number of connections present in the endpoint. */
 | 
			
		||||
unsigned int mgcp_endp_num_conns(const struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	return llist_count(&endp->conns);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! check if an endpoint can in current state allocate new conns.
 | 
			
		||||
 *  \param[in] endp endpoint to check.
 | 
			
		||||
 *  \returns true if more connections can be allowed on endpoint, false if it is already busy. */
 | 
			
		||||
bool mgcp_endp_is_full(const struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	if (endp->type->max_conns == 0)
 | 
			
		||||
		return false;
 | 
			
		||||
	return mgcp_endp_num_conns(endp) >= endp->type->max_conns;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! claim endpoint, sets callid and activates endpoint, should be called at the
 | 
			
		||||
 *  beginning of the CRCX procedure when it is clear that a new call should be
 | 
			
		||||
 *  created.
 | 
			
		||||
@@ -675,3 +672,127 @@ void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
 | 
			
		||||
	if (llist_empty(&endp->conns))
 | 
			
		||||
		mgcp_endp_release(endp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! free oldest connection in the list.
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
void mgcp_endp_free_conn_oldest(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	if (llist_empty(&endp->conns))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
 | 
			
		||||
	mgcp_conn_free(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! free all connections at once.
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
#if defined(__has_attribute)
 | 
			
		||||
#if __has_attribute(no_sanitize)
 | 
			
		||||
__attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
void mgcp_endp_free_conn_all(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	/* Drop all items in the list, might be consecutive! */
 | 
			
		||||
	while ((conn = llist_first_entry_or_null(&endp->conns, struct mgcp_conn, entry)))
 | 
			
		||||
		mgcp_conn_free(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! find a connection by its ID.
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection
 | 
			
		||||
 *  \returns pointer to allocated connection, NULL if not found */
 | 
			
		||||
struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	const char *id_upper;
 | 
			
		||||
	const char *conn_id;
 | 
			
		||||
 | 
			
		||||
	if (!id || !*id)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Ignore leading zeros in needle */
 | 
			
		||||
	while (*id == '0')
 | 
			
		||||
		id++;
 | 
			
		||||
 | 
			
		||||
	/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
 | 
			
		||||
	 * Connections" defines the id as a hex string, so clients may return lower case hex even though
 | 
			
		||||
	 * we sent upper case hex in the CRCX response. */
 | 
			
		||||
	id_upper = osmo_str_toupper(id);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(conn, &endp->conns, entry) {
 | 
			
		||||
		/* Ignore leading zeros in haystack */
 | 
			
		||||
		for (conn_id = conn->id; *conn_id == '0'; conn_id++);
 | 
			
		||||
 | 
			
		||||
		if (strcmp(conn_id, id_upper) == 0)
 | 
			
		||||
			return conn;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! get oldest connection in the list.
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
struct mgcp_conn *mgcp_endp_get_conn_oldest(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	if (llist_empty(&endp->conns))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! find an RTP connection by its ID.
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection
 | 
			
		||||
 *  \returns pointer to allocated connection, NULL if not found */
 | 
			
		||||
struct mgcp_conn_rtp *mgcp_endp_get_conn_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
					const char *id)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	conn = mgcp_endp_get_conn(endp, id);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP)
 | 
			
		||||
		return mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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_endp_free_conn_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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -98,7 +98,11 @@ static int8_t _conn_iuup_amr_ft_2_rfci(struct mgcp_conn_rtp *conn_rtp, uint8_t f
 | 
			
		||||
	uint8_t rfci_cnt = 0;
 | 
			
		||||
	unsigned match_bytes = (unsigned)osmo_amr_bytes(ft);
 | 
			
		||||
	struct osmo_iuup_rnl_prim *irp = conn_rtp->iuup.init_ind;
 | 
			
		||||
	OSMO_ASSERT(irp);
 | 
			
		||||
	if (!irp) {
 | 
			
		||||
		/* No IuUP Initialization has occured on the IuUP side yet. Return error and drop the RTP data, until
 | 
			
		||||
		 * the IuUP Initialization has configured the link. */
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* TODO: cache this somehow */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
 | 
			
		||||
@@ -270,9 +274,9 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
 | 
			
		||||
		return ft;
 | 
			
		||||
	}
 | 
			
		||||
	msgb_pull_to_l3(msg);
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR: ft %d, len %d\n", ft, msgb_l3len(msg));
 | 
			
		||||
 | 
			
		||||
	if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.codec)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR OA: ft %d, len %d\n", ft, msgb_length(msg));
 | 
			
		||||
		amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr));
 | 
			
		||||
		amr_hdr->cmr = 15; /* no change */
 | 
			
		||||
		amr_hdr->f = 0;
 | 
			
		||||
@@ -291,6 +295,7 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
 | 
			
		||||
		amr_data[1] |= ((ft & 0x1) << 7) | (((!fqc) & 0x1) << 6);
 | 
			
		||||
		amr_length = (osmo_amr_bits(ft) + 10 + 7) / 8;
 | 
			
		||||
		msgb_trim(msg, amr_length);
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR BE: ft %d, len %zd\n", ft, amr_length);
 | 
			
		||||
	}
 | 
			
		||||
	rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(*rtp_hdr));
 | 
			
		||||
	*rtp_hdr = (struct rtp_hdr){
 | 
			
		||||
@@ -306,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -342,8 +346,7 @@ static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osm
 | 
			
		||||
		return bridge_iuup_to_iuup_peer(conn_rtp_src, conn_rtp_dst, irp);
 | 
			
		||||
	case MGCP_RTP_DEFAULT:
 | 
			
		||||
		return bridge_iuup_to_rtp_peer(conn_rtp_src, conn_rtp_dst, irp);
 | 
			
		||||
	case MGCP_OSMUX_BSC:
 | 
			
		||||
	case MGCP_OSMUX_BSC_NAT:
 | 
			
		||||
	case MGCP_RTP_OSMUX:
 | 
			
		||||
	default:
 | 
			
		||||
		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
 | 
			
		||||
			 "Forward of IuUP payload to RTP connection type %u not supported!\n",
 | 
			
		||||
@@ -465,7 +468,7 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
 | 
			
		||||
	struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg);
 | 
			
		||||
	int buflen = msgb_length(msg);
 | 
			
		||||
	char *dest_name;
 | 
			
		||||
	int len;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(conn_src);
 | 
			
		||||
	OSMO_ASSERT(conn_dst);
 | 
			
		||||
@@ -490,7 +493,7 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
 | 
			
		||||
			 "rtp_port:%u rtcp_port:%u\n",
 | 
			
		||||
			 dest_name,
 | 
			
		||||
			 osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
 | 
			
		||||
			 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
 | 
			
		||||
			 osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
 | 
			
		||||
			);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
@@ -502,27 +505,25 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
 | 
			
		||||
	hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
 | 
			
		||||
	hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence);
 | 
			
		||||
	hdr->ssrc = rtp_state->alt_rtp_tx_ssrc;
 | 
			
		||||
	rtp_state->alt_rtp_tx_sequence++;
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
			
		||||
		 "process/send IuUP to %s %s rtp_port:%u rtcp_port:%u\n",
 | 
			
		||||
		 dest_name, osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
 | 
			
		||||
		 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port));
 | 
			
		||||
		 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, rtp_end->rtp_port,
 | 
			
		||||
			    (char *)hdr, buflen);
 | 
			
		||||
	rc = mgcp_udp_send(rtp_end->rtp, &rtp_end->addr, (char *)hdr, buflen);
 | 
			
		||||
 | 
			
		||||
	if (len <= 0)
 | 
			
		||||
		return len;
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	rtpconn_rate_ctr_add(conn_dst, endp, RTP_PACKETS_TX_CTR, 1);
 | 
			
		||||
	rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
 | 
			
		||||
	rtp_state->alt_rtp_tx_sequence++;
 | 
			
		||||
	rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, buflen);
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Received TNL primitive from IuUP layer FSM, transmit it further down to the
 | 
			
		||||
@@ -543,7 +544,7 @@ static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
 | 
			
		||||
 | 
			
		||||
	msgb_pull_to_l2(msg);
 | 
			
		||||
	rtph = (struct rtp_hdr *)msgb_push(msg, sizeof(*rtph));
 | 
			
		||||
	/* TODO: fill rtph properly: */
 | 
			
		||||
	/* rtph is further filled in mgcp_send_iuup() below. */
 | 
			
		||||
	*rtph = (struct rtp_hdr){
 | 
			
		||||
		.csrc_count = 0,
 | 
			
		||||
		.extension = 0,
 | 
			
		||||
@@ -618,9 +619,8 @@ int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg)
 | 
			
		||||
		force_output_enabled = true;
 | 
			
		||||
		/* Fill in the peer address so that we can send Init-ACK back: */
 | 
			
		||||
		prev_rem_addr = conn_rtp_src->end.addr;
 | 
			
		||||
		prev_rem_rtp_port = conn_rtp_src->end.rtp_port;
 | 
			
		||||
		prev_rem_rtp_port = osmo_sockaddr_port(&conn_rtp_src->end.addr.u.sa);
 | 
			
		||||
		conn_rtp_src->end.addr = *mc->from_addr;
 | 
			
		||||
		conn_rtp_src->end.rtp_port = htons(osmo_sockaddr_port(&mc->from_addr->u.sa));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = _conn_iuup_rtp_pl_up(conn_rtp_src, msg);
 | 
			
		||||
@@ -628,7 +628,7 @@ int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg)
 | 
			
		||||
	if (force_output_enabled) {
 | 
			
		||||
		conn_rtp_src->end.output_enabled = prev_output_enabled;
 | 
			
		||||
		conn_rtp_src->end.addr = prev_rem_addr;
 | 
			
		||||
		conn_rtp_src->end.rtp_port = prev_rem_rtp_port;
 | 
			
		||||
		osmo_sockaddr_set_port(&conn_rtp_src->end.addr.u.sa, prev_rem_rtp_port);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
@@ -638,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;
 | 
			
		||||
@@ -670,7 +670,7 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
 | 
			
		||||
				     "Bridge RTP=>IuUP: too short for AMR OA hdr (%u)\n", msgb_length(msg));
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		if (amr_hdr->ft >= AMR_FT_MAX) {
 | 
			
		||||
		if (!osmo_amr_ft_valid(amr_hdr->ft)) {
 | 
			
		||||
			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR OA ft=%u\n", amr_hdr->ft);
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
@@ -678,19 +678,22 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
 | 
			
		||||
			LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR OA ft=%u\n", amr_hdr->ft);
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		irp->u.data.fqc = amr_hdr->q;
 | 
			
		||||
		irp->u.data.fqc = amr_hdr->q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
 | 
			
		||||
		irp->u.data.rfci = rfci;
 | 
			
		||||
		msgb_pull(msg, 2);
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR OA -> IuUP: ft %d -> rfci %d len %d\n",
 | 
			
		||||
		     amr_hdr->ft, rfci, msgb_length(msg));
 | 
			
		||||
	} else {
 | 
			
		||||
		uint8_t *amr_bwe_hdr = (uint8_t *) msgb_data(msg);
 | 
			
		||||
		int8_t ft;
 | 
			
		||||
		uint8_t q;
 | 
			
		||||
		if (msgb_length(msg) < 2) {
 | 
			
		||||
			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
 | 
			
		||||
				     "Bridge RTP=>IuUP: too short for AMR BE hdr (%u)\n", msgb_length(msg));
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		ft = ((amr_bwe_hdr[0] & 0x07) << 1) | ((amr_bwe_hdr[1] & 0x80) >> 7);
 | 
			
		||||
		if (ft >= AMR_FT_MAX) {
 | 
			
		||||
		if (!osmo_amr_ft_valid(ft)) {
 | 
			
		||||
			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR BE ft=%u\n", ft);
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
@@ -698,7 +701,8 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
 | 
			
		||||
			LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR BE ft=%u\n", ft);
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		irp->u.data.fqc = ((amr_bwe_hdr[1] & 0x40) >> 6);
 | 
			
		||||
		q = amr_bwe_hdr[1] & 0x40;
 | 
			
		||||
		irp->u.data.fqc = q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
 | 
			
		||||
		irp->u.data.rfci = rfci;
 | 
			
		||||
		rc = iuup_length = osmo_amr_bwe_to_iuup(msgb_data(msg), msgb_length(msg));
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
@@ -706,6 +710,8 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
 | 
			
		||||
			return rc;
 | 
			
		||||
		}
 | 
			
		||||
		msgb_trim(msg, iuup_length);
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR BE -> IuUP: ft %d -> rfci %d len %d\n",
 | 
			
		||||
		     ft, rfci, msgb_length(msg));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	irp->oph.msg->l3h = msgb_put(irp->oph.msg, msgb_length(msg));
 | 
			
		||||
 
 | 
			
		||||
@@ -75,60 +75,25 @@ 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] endp pointer to endpoint (only used for log output)
 | 
			
		||||
 *  \param[out] associated connection to be modified accordingly
 | 
			
		||||
 *  \returns 0 on success, -1 on error */
 | 
			
		||||
int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
 | 
			
		||||
			 struct mgcp_conn *conn)
 | 
			
		||||
 *  \param[in] mode as string (recvonly, sendrecv, sendonly confecho or loopback)
 | 
			
		||||
 *  \returns MGCP_CONN_* on success, MGCP_CONN_NONE on error */
 | 
			
		||||
enum mgcp_connection_mode mgcp_parse_conn_mode(const char *mode)
 | 
			
		||||
{
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	if (!mode) {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "missing connection mode\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return -1;
 | 
			
		||||
	if (!endp)
 | 
			
		||||
		return -1;
 | 
			
		||||
	if (!mode)
 | 
			
		||||
		return MGCP_CONN_NONE;
 | 
			
		||||
 | 
			
		||||
	if (strcasecmp(mode, "recvonly") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_RECV_ONLY;
 | 
			
		||||
	else if (strcasecmp(mode, "sendrecv") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_RECV_SEND;
 | 
			
		||||
	else if (strcasecmp(mode, "sendonly") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_SEND_ONLY;
 | 
			
		||||
	else if (strcasecmp(mode, "loopback") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_LOOPBACK;
 | 
			
		||||
	else {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "unknown connection mode: '%s'\n", mode);
 | 
			
		||||
		ret = -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Special handling for RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		conn->u.rtp.end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
 | 
			
		||||
		 mode, conn->mode);
 | 
			
		||||
 | 
			
		||||
	/* Special handling für RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
 | 
			
		||||
			 conn->u.rtp.end.output_enabled);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* The VTY might change the connection mode at any time, so we have
 | 
			
		||||
	 * to hold a copy of the original connection mode */
 | 
			
		||||
	conn->mode_orig = conn->mode;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
		return MGCP_CONN_RECV_ONLY;
 | 
			
		||||
	if (strcasecmp(mode, "sendrecv") == 0)
 | 
			
		||||
		return MGCP_CONN_RECV_SEND;
 | 
			
		||||
	if (strcasecmp(mode, "sendonly") == 0)
 | 
			
		||||
		return MGCP_CONN_SEND_ONLY;
 | 
			
		||||
	if (strcasecmp(mode, "confecho") == 0)
 | 
			
		||||
		return MGCP_CONN_CONFECHO;
 | 
			
		||||
	if (strcasecmp(mode, "loopback") == 0)
 | 
			
		||||
		return MGCP_CONN_LOOPBACK;
 | 
			
		||||
	return MGCP_CONN_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Analyze and parse the the hader of an MGCP messeage string.
 | 
			
		||||
@@ -202,7 +167,7 @@ int mgcp_parse_osmux_cid(const char *line)
 | 
			
		||||
		     osmux_cid, OSMUX_CID_MAX);
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "MGCP client offered Osmux CID %u\n", osmux_cid);
 | 
			
		||||
 | 
			
		||||
	return osmux_cid;
 | 
			
		||||
}
 | 
			
		||||
@@ -292,7 +257,7 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if connection exists */
 | 
			
		||||
	if (mgcp_conn_get(endp, conn_id))
 | 
			
		||||
	if (mgcp_endp_get_conn(endp, conn_id))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -47,6 +47,7 @@
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_iuup.h>
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
 | 
			
		||||
/* Contains the last successfully resolved endpoint name. This variable is used
 | 
			
		||||
 * for the unit-tests to verify that the endpoint was correctly resolved. */
 | 
			
		||||
@@ -58,6 +59,16 @@ char *mgcp_debug_get_last_endpoint_name(void)
 | 
			
		||||
	return debug_last_endpoint_name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct value_string mgcp_connection_mode_strs[] = {
 | 
			
		||||
	{ MGCP_CONN_NONE, "none" },
 | 
			
		||||
	{ MGCP_CONN_RECV_SEND, "sendrecv" },
 | 
			
		||||
	{ MGCP_CONN_SEND_ONLY, "sendonly" },
 | 
			
		||||
	{ MGCP_CONN_RECV_ONLY, "recvonly" },
 | 
			
		||||
	{ MGCP_CONN_CONFECHO, "confecho" },
 | 
			
		||||
	{ MGCP_CONN_LOOPBACK, "loopback" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* A combination of LOGPENDP and LOGPTRUNK that automatically falls back to
 | 
			
		||||
 * LOGPTRUNK when the endp parameter is NULL */
 | 
			
		||||
#define LOGPEPTR(endp, trunk, cat, level, fmt, args...) \
 | 
			
		||||
@@ -85,6 +96,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;
 | 
			
		||||
};
 | 
			
		||||
@@ -150,8 +164,7 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_RTP_DEFAULT:
 | 
			
		||||
	case MGCP_OSMUX_BSC:
 | 
			
		||||
	case MGCP_OSMUX_BSC_NAT:
 | 
			
		||||
	case MGCP_RTP_OSMUX:
 | 
			
		||||
	case MGCP_RTP_IUUP:
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
@@ -169,7 +182,7 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
 | 
			
		||||
	/* Find the "sister" connection */
 | 
			
		||||
	llist_for_each_entry(_conn, &endp->conns, entry) {
 | 
			
		||||
		if (_conn->id != conn->conn->id) {
 | 
			
		||||
			conn_src = &_conn->u.rtp;
 | 
			
		||||
			conn_src = mgcp_conn_get_conn_rtp(_conn);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -301,7 +314,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
	/* Attach optional OSMUX parameters */
 | 
			
		||||
	if (mgcp_conn_rtp_is_osmux(conn)) {
 | 
			
		||||
		rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
 | 
			
		||||
		rc = msgb_printf(sdp, MGCP_X_OSMO_OSMUX_HEADER " %u\r\n", conn->osmux.local_cid);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto error;
 | 
			
		||||
	}
 | 
			
		||||
@@ -324,8 +337,13 @@ error:
 | 
			
		||||
 * osmux connection, send the dummy packet via OSMUX */
 | 
			
		||||
static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	if (conn->osmux.state != OSMUX_STATE_DISABLED)
 | 
			
		||||
		osmux_send_dummy(endp, conn);
 | 
			
		||||
	/* Avoid sending dummy packet if the remote address was not yet
 | 
			
		||||
	 * configured through CRCX/MDCX: */
 | 
			
		||||
	if (!mgcp_rtp_end_remote_addr_available(&conn->end))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (mgcp_conn_rtp_is_osmux(conn))
 | 
			
		||||
		osmux_send_dummy(conn);
 | 
			
		||||
	else
 | 
			
		||||
		mgcp_send_dummy(endp, conn);
 | 
			
		||||
}
 | 
			
		||||
@@ -385,6 +403,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) {
 | 
			
		||||
@@ -402,14 +423,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;
 | 
			
		||||
@@ -455,6 +476,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);
 | 
			
		||||
@@ -656,8 +682,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: */
 | 
			
		||||
@@ -701,7 +727,7 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk *trunk = endp->trunk;
 | 
			
		||||
 | 
			
		||||
	int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
 | 
			
		||||
	bool patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
 | 
			
		||||
 | 
			
		||||
	rtp->force_aligned_timing = trunk->force_aligned_timing;
 | 
			
		||||
	rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
 | 
			
		||||
@@ -709,7 +735,7 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
 | 
			
		||||
		 "Configuring RTP endpoint: local port %d%s%s\n",
 | 
			
		||||
		 ntohs(rtp->rtp_port),
 | 
			
		||||
		 osmo_sockaddr_port(&rtp->addr.u.sa),
 | 
			
		||||
		 rtp->force_aligned_timing ? ", force constant timing" : "",
 | 
			
		||||
		 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
 | 
			
		||||
}
 | 
			
		||||
@@ -740,12 +766,12 @@ uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
 | 
			
		||||
 */
 | 
			
		||||
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
 | 
			
		||||
{
 | 
			
		||||
	if (!endp->trunk->cfg->osmux_init) {
 | 
			
		||||
		if (osmux_init(OSMUX_ROLE_BSC, endp->trunk->cfg) < 0) {
 | 
			
		||||
			LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
 | 
			
		||||
	if (!endp->trunk->cfg->osmux.initialized) {
 | 
			
		||||
		if (osmux_init(endp->trunk) < 0) {
 | 
			
		||||
			LOGPENDP(endp, DOSMUX, LOGL_ERROR, "Cannot init OSMUX\n");
 | 
			
		||||
			return -3;
 | 
			
		||||
		}
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
 | 
			
		||||
		LOGPENDP(endp, DOSMUX, LOGL_NOTICE, "OSMUX socket has been set up\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return mgcp_parse_osmux_cid(line);
 | 
			
		||||
@@ -756,6 +782,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;
 | 
			
		||||
 | 
			
		||||
@@ -797,8 +826,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 = mgcp_conn_get_conn_rtp(conn_dst);
 | 
			
		||||
	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;
 | 
			
		||||
@@ -840,20 +876,28 @@ 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;
 | 
			
		||||
	const char *mode = NULL;
 | 
			
		||||
	enum mgcp_connection_mode mode = MGCP_CONN_NONE;
 | 
			
		||||
	char *line;
 | 
			
		||||
	int have_sdp = 0, osmux_cid = -2;
 | 
			
		||||
	struct mgcp_conn_rtp *conn = NULL;
 | 
			
		||||
	struct mgcp_conn *_conn = NULL;
 | 
			
		||||
	int have_sdp = 0, remote_osmux_cid = -2;
 | 
			
		||||
	struct mgcp_conn *conn = NULL;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp = NULL;
 | 
			
		||||
	char conn_name[512];
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	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));
 | 
			
		||||
@@ -888,14 +932,14 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
 | 
			
		||||
			return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'M':
 | 
			
		||||
			mode = (const char *)line + 3;
 | 
			
		||||
			mode = mgcp_parse_conn_mode((const char *)line + 3);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'X':
 | 
			
		||||
			if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
 | 
			
		||||
				/* If osmux is disabled, just skip setting it up */
 | 
			
		||||
				if (!rq->endp->trunk->cfg->osmux)
 | 
			
		||||
				if (rq->endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
 | 
			
		||||
					break;
 | 
			
		||||
				osmux_cid = mgcp_osmux_setup(endp, line);
 | 
			
		||||
				remote_osmux_cid = mgcp_osmux_setup(endp, line);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -925,15 +969,15 @@ mgcp_header_done:
 | 
			
		||||
		return create_err_response(endp, endp, 516, "CRCX", pdata->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!mode) {
 | 
			
		||||
	if (mode == MGCP_CONN_NONE) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "CRCX: insufficient parameters, missing mode\n");
 | 
			
		||||
			 "CRCX: insufficient parameters, invalid mode\n");
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
 | 
			
		||||
		return create_err_response(endp, endp, 517, "CRCX", pdata->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if we are able to accept the creation of another connection */
 | 
			
		||||
	if (llist_count(&endp->conns) >= endp->type->max_conns) {
 | 
			
		||||
	if (mgcp_endp_is_full(endp)) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			"CRCX: endpoint full, max. %i connections allowed!\n",
 | 
			
		||||
			endp->type->max_conns);
 | 
			
		||||
@@ -941,7 +985,8 @@ mgcp_header_done:
 | 
			
		||||
			/* There is no more room for a connection, make some
 | 
			
		||||
			 * room by blindly tossing the oldest of the two two
 | 
			
		||||
			 * connections */
 | 
			
		||||
			mgcp_conn_free_oldest(endp);
 | 
			
		||||
			mgcp_endp_free_conn_oldest(endp);
 | 
			
		||||
			OSMO_ASSERT(!mgcp_endp_is_full(endp));
 | 
			
		||||
		} else {
 | 
			
		||||
			/* There is no more room for a connection, leave
 | 
			
		||||
			 * everything as it is and return with an error */
 | 
			
		||||
@@ -980,35 +1025,35 @@ mgcp_header_done:
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snprintf(conn_name, sizeof(conn_name), "%s", callid);
 | 
			
		||||
	_conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
 | 
			
		||||
	if (!_conn) {
 | 
			
		||||
	conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "CRCX: unable to allocate RTP connection\n");
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
 | 
			
		||||
		goto error2;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn = mgcp_conn_get_rtp(endp, _conn->id);
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
 | 
			
		||||
	if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
 | 
			
		||||
	if (mgcp_conn_set_mode(conn, mode) < 0) {
 | 
			
		||||
		error_code = 517;
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
 | 
			
		||||
		goto error2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Annotate Osmux circuit ID and set it to negotiating state until this
 | 
			
		||||
	 * is fully set up from the dummy load. */
 | 
			
		||||
	conn->osmux.state = OSMUX_STATE_DISABLED;
 | 
			
		||||
	if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
 | 
			
		||||
		conn->osmux.state = OSMUX_STATE_ACTIVATING;
 | 
			
		||||
		if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
 | 
			
		||||
	conn_rtp = mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
	OSMO_ASSERT(conn_rtp);
 | 
			
		||||
 | 
			
		||||
	/* If X-Osmux (remote CID) was received (-1 is wilcard), alloc next avail CID as local CID */
 | 
			
		||||
	if (remote_osmux_cid >= -1) {
 | 
			
		||||
		if (osmux_init_conn(conn_rtp) < 0) {
 | 
			
		||||
			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
 | 
			
		||||
			goto error2;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (endp->trunk->cfg->osmux == OSMUX_USAGE_ONLY) {
 | 
			
		||||
		LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
		if (remote_osmux_cid >= 0) {
 | 
			
		||||
			conn_rtp->osmux.remote_cid_present = true;
 | 
			
		||||
			conn_rtp->osmux.remote_cid = remote_osmux_cid;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_ONLY) {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "CRCX: osmux only and no osmux offered\n");
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
 | 
			
		||||
		goto error2;
 | 
			
		||||
@@ -1019,8 +1064,8 @@ mgcp_header_done:
 | 
			
		||||
		rc = set_local_cx_options(trunk->endpoints,
 | 
			
		||||
					  &endp->local_options, local_options);
 | 
			
		||||
		if (rc != 0) {
 | 
			
		||||
			LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
				 "CRCX: inavlid local connection options!\n");
 | 
			
		||||
			LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
				 "CRCX: invalid local connection options!\n");
 | 
			
		||||
			error_code = rc;
 | 
			
		||||
			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
 | 
			
		||||
			goto error2;
 | 
			
		||||
@@ -1028,8 +1073,8 @@ mgcp_header_done:
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Handle codec information and decide for a suitable codec */
 | 
			
		||||
	rc = handle_codec_info(conn, rq, have_sdp, true);
 | 
			
		||||
	mgcp_codec_summary(conn);
 | 
			
		||||
	rc = handle_codec_info(conn_rtp, rq, have_sdp, true);
 | 
			
		||||
	mgcp_codec_summary(conn_rtp);
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		error_code = rc;
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
 | 
			
		||||
@@ -1037,64 +1082,60 @@ mgcp_header_done:
 | 
			
		||||
	}
 | 
			
		||||
	/* 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);
 | 
			
		||||
	if (conn_rtp->type == MGCP_RTP_DEFAULT && strcmp(conn_rtp->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) {
 | 
			
		||||
		rc = mgcp_conn_iuup_init(conn_rtp);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
		conn_rtp->end.packet_duration_ms = pdata->cfg->force_ptime;
 | 
			
		||||
		conn_rtp->end.force_output_ptime = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mgcp_rtp_end_config(endp, 0, &conn->end);
 | 
			
		||||
 | 
			
		||||
	/* check connection mode setting */
 | 
			
		||||
	if (conn->conn->mode != MGCP_CONN_LOOPBACK
 | 
			
		||||
	    && conn->conn->mode != MGCP_CONN_RECV_ONLY
 | 
			
		||||
	    && conn->end.rtp_port == 0) {
 | 
			
		||||
		LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "CRCX: selected connection mode type requires an opposite end!\n");
 | 
			
		||||
		error_code = 527;
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
 | 
			
		||||
		goto error2;
 | 
			
		||||
	}
 | 
			
		||||
	mgcp_rtp_end_config(endp, 0, &conn_rtp->end);
 | 
			
		||||
 | 
			
		||||
	/* Find a local address for conn based on policy and initial SDP remote
 | 
			
		||||
	   information, then find a free port for it */
 | 
			
		||||
	mgcp_get_local_addr(conn->end.local_addr, conn);
 | 
			
		||||
	if (allocate_port(endp, conn) != 0) {
 | 
			
		||||
	if (mgcp_get_local_addr(conn_rtp->end.local_addr, conn_rtp) < 0) {
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
 | 
			
		||||
		goto error2;
 | 
			
		||||
	}
 | 
			
		||||
	if (allocate_port(endp, conn_rtp) != 0) {
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
 | 
			
		||||
		goto error2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (setup_rtp_processing(endp, conn) != 0) {
 | 
			
		||||
		LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
	if (setup_rtp_processing(endp, conn_rtp) != 0) {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "CRCX: could not start RTP processing!\n");
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
 | 
			
		||||
		goto error2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
 | 
			
		||||
		 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
 | 
			
		||||
	/* Notify Osmux conn that CRCX was received */
 | 
			
		||||
	if (mgcp_conn_rtp_is_osmux(conn_rtp)) {
 | 
			
		||||
		if (conn_osmux_event_rx_crcx_mdcx(conn_rtp) < 0) {
 | 
			
		||||
			LOGPCONN(conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
 | 
			
		||||
			goto error2;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_DEBUG,
 | 
			
		||||
		 "CRCX: Creating connection: port: %u\n", conn_rtp->end.local_port);
 | 
			
		||||
 | 
			
		||||
	/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
 | 
			
		||||
	OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
 | 
			
		||||
	if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
 | 
			
		||||
	    mgcp_rtp_end_remote_addr_available(&conn->end) &&
 | 
			
		||||
	if (conn->mode & MGCP_CONN_RECV_ONLY &&
 | 
			
		||||
	    trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
 | 
			
		||||
		send_dummy(endp, conn);
 | 
			
		||||
		send_dummy(endp, conn_rtp);
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
		 "CRCX: connection successfully created\n");
 | 
			
		||||
	rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
 | 
			
		||||
	mgcp_endp_update(endp);
 | 
			
		||||
 | 
			
		||||
	/* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
 | 
			
		||||
	bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
 | 
			
		||||
	return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
 | 
			
		||||
	return create_response_with_sdp(endp, conn_rtp, "CRCX", pdata->trans, add_epname, true);
 | 
			
		||||
error2:
 | 
			
		||||
	mgcp_endp_release(endp);
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
@@ -1108,21 +1149,29 @@ 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;
 | 
			
		||||
	int have_sdp = 0;
 | 
			
		||||
	char *line;
 | 
			
		||||
	const char *local_options = NULL;
 | 
			
		||||
	const char *mode = NULL;
 | 
			
		||||
	struct mgcp_conn_rtp *conn = NULL;
 | 
			
		||||
	enum mgcp_connection_mode mode = MGCP_CONN_NONE;
 | 
			
		||||
	struct mgcp_conn *conn = NULL;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp = NULL;
 | 
			
		||||
	const char *conn_id = NULL;
 | 
			
		||||
	int osmux_cid = -2;
 | 
			
		||||
	int remote_osmux_cid = -2;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	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,
 | 
			
		||||
@@ -1136,7 +1185,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR, "MDCX: selected endpoint not available!\n");
 | 
			
		||||
		return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans);
 | 
			
		||||
	}
 | 
			
		||||
	if (llist_count(&endp->conns) <= 0) {
 | 
			
		||||
	if (mgcp_endp_num_conns(endp) <= 0) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "MDCX: endpoint is not holding a connection.\n");
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
 | 
			
		||||
@@ -1166,17 +1215,14 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
 | 
			
		||||
			local_options = (const char *)line + 3;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'M':
 | 
			
		||||
			mode = (const char *)line + 3;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'Z':
 | 
			
		||||
			silent = strcasecmp("noanswer", line + 3) == 0;
 | 
			
		||||
			mode = mgcp_parse_conn_mode((const char *)line + 3);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'X':
 | 
			
		||||
			if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
 | 
			
		||||
				/* If osmux is disabled, just skip setting it up */
 | 
			
		||||
				if (!endp->trunk->cfg->osmux)
 | 
			
		||||
				if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
 | 
			
		||||
					break;
 | 
			
		||||
				osmux_cid = mgcp_osmux_setup(endp, line);
 | 
			
		||||
				remote_osmux_cid = mgcp_osmux_setup(endp, line);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			/* Ignore unknown X-headers */
 | 
			
		||||
@@ -1203,29 +1249,30 @@ mgcp_header_done:
 | 
			
		||||
		return create_err_response(endp, endp, 515, "MDCX", pdata->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn = mgcp_conn_get_rtp(endp, conn_id);
 | 
			
		||||
	conn = mgcp_endp_get_conn(endp, conn_id);
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
 | 
			
		||||
		return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mgcp_conn_watchdog_kick(conn->conn);
 | 
			
		||||
	mgcp_conn_watchdog_kick(conn);
 | 
			
		||||
 | 
			
		||||
	if (mode) {
 | 
			
		||||
		if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
 | 
			
		||||
	if (mode != MGCP_CONN_NONE) {
 | 
			
		||||
		if (mgcp_conn_set_mode(conn, mode) < 0) {
 | 
			
		||||
			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
 | 
			
		||||
			error_code = 517;
 | 
			
		||||
			goto error3;
 | 
			
		||||
		}
 | 
			
		||||
	} else
 | 
			
		||||
		conn->conn->mode = conn->conn->mode_orig;
 | 
			
		||||
	} else {
 | 
			
		||||
		conn->mode = conn->mode_orig;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Set local connection options, if present */
 | 
			
		||||
	if (local_options) {
 | 
			
		||||
		rc = set_local_cx_options(trunk->endpoints,
 | 
			
		||||
					  &endp->local_options, local_options);
 | 
			
		||||
		if (rc != 0) {
 | 
			
		||||
			LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
				 "MDCX: invalid local connection options!\n");
 | 
			
		||||
			error_code = rc;
 | 
			
		||||
			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
 | 
			
		||||
@@ -1233,91 +1280,89 @@ mgcp_header_done:
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn_rtp = mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
	OSMO_ASSERT(conn_rtp);
 | 
			
		||||
 | 
			
		||||
	/* Handle codec information and decide for a suitable codec */
 | 
			
		||||
	rc = handle_codec_info(conn, rq, have_sdp, false);
 | 
			
		||||
	mgcp_codec_summary(conn);
 | 
			
		||||
	rc = handle_codec_info(conn_rtp, rq, have_sdp, false);
 | 
			
		||||
	mgcp_codec_summary(conn_rtp);
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		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_rtp->type == MGCP_RTP_DEFAULT && strcmp(conn_rtp->end.codec->subtype_name, "VND.3GPP.IUFP") == 0)
 | 
			
		||||
		rc = mgcp_conn_iuup_init(conn_rtp);
 | 
			
		||||
 | 
			
		||||
	/* check connection mode setting */
 | 
			
		||||
	if (conn->conn->mode != MGCP_CONN_LOOPBACK
 | 
			
		||||
	    && conn->conn->mode != MGCP_CONN_RECV_ONLY
 | 
			
		||||
	    && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "MDCX: selected connection mode type requires an opposite end!\n");
 | 
			
		||||
		error_code = 527;
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
 | 
			
		||||
		goto error3;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (mgcp_conn_rtp_is_osmux(conn)) {
 | 
			
		||||
		OSMO_ASSERT(conn->osmux.cid_allocated);
 | 
			
		||||
		if (osmux_cid < -1) {
 | 
			
		||||
			LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
	if (mgcp_conn_rtp_is_osmux(conn_rtp)) {
 | 
			
		||||
		OSMO_ASSERT(conn_rtp->osmux.local_cid_allocated);
 | 
			
		||||
		if (remote_osmux_cid < -1) {
 | 
			
		||||
			LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
				 "MDCX: Failed to parse Osmux CID!\n");
 | 
			
		||||
			goto error3;
 | 
			
		||||
		} else if (osmux_cid == -1) {
 | 
			
		||||
			LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
		} else if (remote_osmux_cid == -1) {
 | 
			
		||||
			LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
				 "MDCX: wilcard in MDCX is not supported!\n");
 | 
			
		||||
			goto error3;
 | 
			
		||||
		} else if (osmux_cid != (int) conn->osmux.cid) {
 | 
			
		||||
			LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
		} else if (conn_rtp->osmux.remote_cid_present &&
 | 
			
		||||
			   remote_osmux_cid != conn_rtp->osmux.remote_cid) {
 | 
			
		||||
			LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
				"MDCX: changing already allocated CID is not supported!\n");
 | 
			
		||||
			goto error3;
 | 
			
		||||
		} else {
 | 
			
		||||
			conn_rtp->osmux.remote_cid_present = true;
 | 
			
		||||
			conn_rtp->osmux.remote_cid = remote_osmux_cid;
 | 
			
		||||
			if (conn_osmux_event_rx_crcx_mdcx(conn_rtp) < 0) {
 | 
			
		||||
				LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
					"MDCX: Osmux handling failed!\n");
 | 
			
		||||
				goto error3;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		/* TODO: In the future (when we have recvCID!=sendCID), we need to
 | 
			
		||||
		   tell Osmux code that osmux_cid is to be used as sendCID for
 | 
			
		||||
		   that conn. */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* MDCX may have provided a new remote address, which means we may need
 | 
			
		||||
	   to update our announced IP addr and re-bind our local end. This can
 | 
			
		||||
	   happen for instance if MGW initially provided an IPv4 during CRCX
 | 
			
		||||
	   ACK, and now MDCX tells us the remote has an IPv6 address. */
 | 
			
		||||
	mgcp_get_local_addr(new_local_addr, conn);
 | 
			
		||||
	if (strcmp(new_local_addr, conn->end.local_addr)) {
 | 
			
		||||
		osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
 | 
			
		||||
		mgcp_free_rtp_port(&conn->end);
 | 
			
		||||
		if (allocate_port(endp, conn) != 0) {
 | 
			
		||||
	if (mgcp_get_local_addr(new_local_addr, conn_rtp) < 0) {
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
 | 
			
		||||
		goto error3;
 | 
			
		||||
	}
 | 
			
		||||
	if (strcmp(new_local_addr, conn_rtp->end.local_addr)) {
 | 
			
		||||
		osmo_strlcpy(conn_rtp->end.local_addr, new_local_addr, sizeof(conn_rtp->end.local_addr));
 | 
			
		||||
		mgcp_rtp_end_free_port(&conn_rtp->end);
 | 
			
		||||
		if (allocate_port(endp, conn_rtp) != 0) {
 | 
			
		||||
			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
 | 
			
		||||
			goto error3;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (setup_rtp_processing(endp, conn) != 0) {
 | 
			
		||||
	if (setup_rtp_processing(endp, conn_rtp) != 0) {
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
 | 
			
		||||
		goto error3;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mgcp_rtp_end_config(endp, 1, &conn->end);
 | 
			
		||||
	mgcp_rtp_end_config(endp, 1, &conn_rtp->end);
 | 
			
		||||
 | 
			
		||||
	/* modify */
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
 | 
			
		||||
		 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_DEBUG,
 | 
			
		||||
		 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn));
 | 
			
		||||
 | 
			
		||||
	/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
 | 
			
		||||
	OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
 | 
			
		||||
	if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
 | 
			
		||||
	    mgcp_rtp_end_remote_addr_available(&conn->end) &&
 | 
			
		||||
	if (conn->mode & MGCP_CONN_RECV_ONLY &&
 | 
			
		||||
	    trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
 | 
			
		||||
		send_dummy(endp, conn);
 | 
			
		||||
		send_dummy(endp, conn_rtp);
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
 | 
			
		||||
	if (silent)
 | 
			
		||||
		goto out_silent;
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
		 "MDCX: connection successfully modified\n");
 | 
			
		||||
	mgcp_endp_update(endp);
 | 
			
		||||
	return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
 | 
			
		||||
	return create_response_with_sdp(endp, conn_rtp, "MDCX", pdata->trans, false, false);
 | 
			
		||||
error3:
 | 
			
		||||
	return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
 | 
			
		||||
 | 
			
		||||
out_silent:
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* DLCX command handler, processes the received command */
 | 
			
		||||
@@ -1326,20 +1371,27 @@ 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;
 | 
			
		||||
	char stats[1048];
 | 
			
		||||
	const char *conn_id = NULL;
 | 
			
		||||
	struct mgcp_conn_rtp *conn = NULL;
 | 
			
		||||
	struct mgcp_conn *conn = NULL;
 | 
			
		||||
	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,
 | 
			
		||||
@@ -1360,7 +1412,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
 | 
			
		||||
	if (rq->wildcarded) {
 | 
			
		||||
		int num_conns = 0;
 | 
			
		||||
		for (i = 0; i < trunk->number_endpoints; i++) {
 | 
			
		||||
			num_conns += llist_count(&trunk->endpoints[i]->conns);
 | 
			
		||||
			num_conns += mgcp_endp_num_conns(trunk->endpoints[i]);
 | 
			
		||||
			mgcp_endp_release(trunk->endpoints[i]);
 | 
			
		||||
		}
 | 
			
		||||
		rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
 | 
			
		||||
@@ -1404,9 +1456,6 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
 | 
			
		||||
				goto error3;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case 'Z':
 | 
			
		||||
			silent = strcasecmp("noanswer", line + 3) == 0;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
 | 
			
		||||
				 line[0], line[0]);
 | 
			
		||||
@@ -1425,7 +1474,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
 | 
			
		||||
	 * that we drop all connections on that specific endpoint at once.
 | 
			
		||||
	 * (See also RFC3435 Section F.7) */
 | 
			
		||||
	if (!conn_id) {
 | 
			
		||||
		int num_conns = llist_count(&endp->conns);
 | 
			
		||||
		int num_conns = mgcp_endp_num_conns(endp);
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
			 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
 | 
			
		||||
			 num_conns);
 | 
			
		||||
@@ -1442,39 +1491,33 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Find the connection */
 | 
			
		||||
	conn = mgcp_conn_get_rtp(endp, conn_id);
 | 
			
		||||
	conn = mgcp_endp_get_conn(endp, conn_id);
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
 | 
			
		||||
		goto error3;
 | 
			
		||||
	}
 | 
			
		||||
	/* save the statistics of the current connection */
 | 
			
		||||
	mgcp_format_stats(stats, sizeof(stats), conn->conn);
 | 
			
		||||
	mgcp_format_stats(stats, sizeof(stats), conn);
 | 
			
		||||
 | 
			
		||||
	/* delete connection */
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
 | 
			
		||||
		 mgcp_conn_dump(conn->conn));
 | 
			
		||||
	mgcp_conn_free(endp, conn_id);
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
 | 
			
		||||
		 mgcp_conn_dump(conn));
 | 
			
		||||
	mgcp_conn_free(conn);
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
		 "DLCX: connection successfully deleted\n");
 | 
			
		||||
 | 
			
		||||
	/* When all connections are closed, the endpoint will be released
 | 
			
		||||
	 * in order to be ready to be used by another call. */
 | 
			
		||||
	if (llist_count(&endp->conns) <= 0) {
 | 
			
		||||
	if (mgcp_endp_num_conns(endp) <= 0) {
 | 
			
		||||
		mgcp_endp_release(endp);
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
 | 
			
		||||
	if (silent)
 | 
			
		||||
		goto out_silent;
 | 
			
		||||
	return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);
 | 
			
		||||
 | 
			
		||||
error3:
 | 
			
		||||
	return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
 | 
			
		||||
 | 
			
		||||
out_silent:
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* RSIP command handler, processes the received command */
 | 
			
		||||
@@ -1490,6 +1533,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;
 | 
			
		||||
@@ -1515,6 +1564,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':
 | 
			
		||||
@@ -1569,9 +1624,8 @@ static void mgcp_keepalive_timer_cb(void *_trunk)
 | 
			
		||||
		struct mgcp_endpoint *endp = trunk->endpoints[i];
 | 
			
		||||
		llist_for_each_entry(conn, &endp->conns, entry) {
 | 
			
		||||
			if (conn->type == MGCP_CONN_TYPE_RTP &&
 | 
			
		||||
			    conn->mode == MGCP_CONN_RECV_ONLY &&
 | 
			
		||||
			     mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
 | 
			
		||||
				send_dummy(endp, &conn->u.rtp);
 | 
			
		||||
			    conn->mode == MGCP_CONN_RECV_ONLY)
 | 
			
		||||
				send_dummy(endp, mgcp_conn_get_conn_rtp(conn));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1624,13 +1678,10 @@ struct mgcp_config *mgcp_config_alloc(void)
 | 
			
		||||
 | 
			
		||||
	cfg->source_port = 2427;
 | 
			
		||||
	osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
 | 
			
		||||
	osmo_strlcpy(cfg->osmux_addr, "0.0.0.0", sizeof(cfg->osmux_addr));
 | 
			
		||||
 | 
			
		||||
	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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
 | 
			
		||||
	    { "mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints." },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
 | 
			
		||||
static const struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "mgcp",
 | 
			
		||||
	.group_description = "mgcp general statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
@@ -71,7 +71,7 @@ static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
 | 
			
		||||
	[MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
 | 
			
		||||
static const struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "crcx",
 | 
			
		||||
	.group_description = "crxc statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
@@ -97,7 +97,7 @@ static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
 | 
			
		||||
	[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
 | 
			
		||||
static const struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "mdcx",
 | 
			
		||||
	.group_description = "mdcx statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
@@ -116,7 +116,7 @@ static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
 | 
			
		||||
	[MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
 | 
			
		||||
static const struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "dlcx",
 | 
			
		||||
	.group_description = "dlcx statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
@@ -130,7 +130,7 @@ static const struct rate_ctr_desc e1_rate_ctr_desc[] = {
 | 
			
		||||
	[E1_I460_TRAU_MUX_EMPTY_CTR] = { "e1:i460", "Outbound I.460 MUX queue empty." }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
 | 
			
		||||
static const struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "e1",
 | 
			
		||||
	.group_description = "e1 statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
@@ -138,7 +138,7 @@ const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
 | 
			
		||||
	.ctr_desc = e1_rate_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
 | 
			
		||||
static const struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "all_rtp_conn",
 | 
			
		||||
	.group_description = "aggregated statistics for all rtp connections",
 | 
			
		||||
	.class_id = 1,
 | 
			
		||||
@@ -146,6 +146,14 @@ const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
 | 
			
		||||
	.ctr_desc = all_rtp_conn_rate_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_group_desc all_osmux_conn_rate_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "all_osmux_conn",
 | 
			
		||||
	.group_description = "aggregated statistics for all osmux connections",
 | 
			
		||||
	.class_id = 1,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(all_osmux_conn_rate_ctr_desc),
 | 
			
		||||
	.ctr_desc = all_osmux_conn_rate_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! allocate global rate counters
 | 
			
		||||
 *  (called once at startup).
 | 
			
		||||
 *  \param[in] cfg mgw configuration for which the rate counters are allocated.
 | 
			
		||||
@@ -191,6 +199,7 @@ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk)
 | 
			
		||||
	static atomic_uint mdcx_rate_ctr_index = 0;
 | 
			
		||||
	static atomic_uint dlcx_rate_ctr_index = 0;
 | 
			
		||||
	static atomic_uint all_rtp_conn_rate_ctr_index = 0;
 | 
			
		||||
	static atomic_uint all_osmux_conn_rate_ctr_index = 0;
 | 
			
		||||
	char ctr_name[256];
 | 
			
		||||
 | 
			
		||||
	if (ratectr->mgcp_crcx_ctr_group == NULL) {
 | 
			
		||||
@@ -229,6 +238,15 @@ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk)
 | 
			
		||||
			 trunk->trunk_nr);
 | 
			
		||||
		rate_ctr_group_set_name(ratectr->all_rtp_conn_stats, ctr_name);
 | 
			
		||||
	}
 | 
			
		||||
	if (ratectr->all_osmux_conn_stats == NULL) {
 | 
			
		||||
		ratectr->all_osmux_conn_stats = rate_ctr_group_alloc(trunk, &all_osmux_conn_rate_ctr_group_desc,
 | 
			
		||||
								   all_osmux_conn_rate_ctr_index++);
 | 
			
		||||
		if (!ratectr->all_osmux_conn_stats)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		snprintf(ctr_name, sizeof(ctr_name), "%s-%u:osmux_conn", mgcp_trunk_type_strs_str(trunk->trunk_type),
 | 
			
		||||
			 trunk->trunk_nr);
 | 
			
		||||
		rate_ctr_group_set_name(ratectr->all_osmux_conn_stats, ctr_name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* E1 specific */
 | 
			
		||||
	if (trunk->trunk_type == MGCP_TRUNK_E1 && ratectr->e1_stats == NULL) {
 | 
			
		||||
@@ -265,6 +283,10 @@ void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk)
 | 
			
		||||
		rate_ctr_group_free(ratectr->all_rtp_conn_stats);
 | 
			
		||||
		ratectr->all_rtp_conn_stats = NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if (ratectr->all_osmux_conn_stats) {
 | 
			
		||||
		rate_ctr_group_free(ratectr->all_osmux_conn_stats);
 | 
			
		||||
		ratectr->all_osmux_conn_stats = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* E1 specific */
 | 
			
		||||
	if (ratectr->e1_stats) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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, " ");
 | 
			
		||||
@@ -384,7 +383,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
		case 'm':
 | 
			
		||||
			rc = sscanf(line, "m=audio %d RTP/AVP", &port);
 | 
			
		||||
			if (rc == 1) {
 | 
			
		||||
				rtp->rtp_port = htons(port);
 | 
			
		||||
				osmo_sockaddr_set_port(&rtp->addr.u.sa, port);
 | 
			
		||||
				rtp->rtcp_port = htons(port + 1);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -432,7 +431,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
	     "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
 | 
			
		||||
	     ntohs(rtp->rtp_port), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
 | 
			
		||||
	     osmo_sockaddr_port(&rtp->addr.u.sa), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
 | 
			
		||||
	     rtp->packet_duration_ms);
 | 
			
		||||
	if (codecs_used == 0)
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_NOTICE, "none");
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -601,7 +565,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
		payload_types[0] = payload_type;
 | 
			
		||||
		if (mgcp_conn_rtp_is_osmux(conn))
 | 
			
		||||
			local_port = endp->trunk->cfg->osmux_port;
 | 
			
		||||
			local_port = endp->trunk->cfg->osmux.local_port;
 | 
			
		||||
		else
 | 
			
		||||
			local_port = conn->end.local_port;
 | 
			
		||||
		rc = add_audio(sdp, payload_types, 1, local_port);
 | 
			
		||||
@@ -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;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ __attribute__((no_sanitize("undefined")))
 | 
			
		||||
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_state *state = &conn->state;
 | 
			
		||||
	struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
 | 
			
		||||
	struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
 | 
			
		||||
 | 
			
		||||
	*expected = state->stats.cycles + state->stats.max_seq;
 | 
			
		||||
	*expected = *expected - state->stats.base_seq + 1;
 | 
			
		||||
@@ -80,10 +80,10 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
 | 
			
		||||
	int ploss;
 | 
			
		||||
	int nchars;
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
 | 
			
		||||
	struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR);
 | 
			
		||||
	struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR);
 | 
			
		||||
	struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR);
 | 
			
		||||
	struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
 | 
			
		||||
	struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
 | 
			
		||||
	struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
 | 
			
		||||
	struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
 | 
			
		||||
 | 
			
		||||
	calc_loss(conn, &expected, &ploss);
 | 
			
		||||
	jitter = calc_jitter(&conn->state);
 | 
			
		||||
@@ -99,7 +99,7 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
 | 
			
		||||
	str += nchars;
 | 
			
		||||
	str_len -= nchars;
 | 
			
		||||
 | 
			
		||||
	if (conn->conn->endp->trunk->cfg->osmux != OSMUX_USAGE_OFF) {
 | 
			
		||||
	if (conn->conn->endp->trunk->cfg->osmux.usage != OSMUX_USAGE_OFF) {
 | 
			
		||||
		/* Error Counter */
 | 
			
		||||
		nchars = snprintf(str, str_len,
 | 
			
		||||
				  "\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
 | 
			
		||||
@@ -112,9 +112,12 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
 | 
			
		||||
		str_len -= nchars;
 | 
			
		||||
 | 
			
		||||
		if (conn->osmux.state == OSMUX_STATE_ENABLED) {
 | 
			
		||||
			struct rate_ctr *osmux_chunks_rx, *osmux_octets_rx;
 | 
			
		||||
			osmux_chunks_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_CHUNKS_RX_CTR);
 | 
			
		||||
			osmux_octets_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_OCTETS_RX_CTR);
 | 
			
		||||
			snprintf(str, str_len,
 | 
			
		||||
				 "\r\nX-Osmux-ST: CR=%u, BR=%u",
 | 
			
		||||
				 conn->osmux.stats.chunks, conn->osmux.stats.octets);
 | 
			
		||||
				 "\r\nX-Osmux-ST: CR=%" PRIu64 ", BR=%" PRIu64,
 | 
			
		||||
				 osmux_chunks_rx->current, osmux_octets_rx->current);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -138,7 +141,7 @@ void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn)
 | 
			
		||||
	 * keep this option open: */
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		mgcp_format_stats_rtp(str, str_len, &conn->u.rtp);
 | 
			
		||||
		mgcp_format_stats_rtp(str, str_len, mgcp_conn_get_conn_rtp(conn));
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
@@ -129,7 +124,7 @@ static int config_write_mgcp(struct vty *vty)
 | 
			
		||||
		vty_out(vty, " rtp force-ptime %d%s", g_cfg->force_ptime,
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	switch (g_cfg->osmux) {
 | 
			
		||||
	switch (g_cfg->osmux.usage) {
 | 
			
		||||
	case OSMUX_USAGE_ON:
 | 
			
		||||
		vty_out(vty, " osmux on%s", VTY_NEWLINE);
 | 
			
		||||
		break;
 | 
			
		||||
@@ -141,17 +136,23 @@ static int config_write_mgcp(struct vty *vty)
 | 
			
		||||
		vty_out(vty, " osmux off%s", VTY_NEWLINE);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	if (g_cfg->osmux) {
 | 
			
		||||
	if (g_cfg->osmux.usage != OSMUX_USAGE_OFF) {
 | 
			
		||||
		if (g_cfg->osmux.local_addr_v4)
 | 
			
		||||
			vty_out(vty, " osmux bind-ip %s%s",
 | 
			
		||||
			g_cfg->osmux_addr, VTY_NEWLINE);
 | 
			
		||||
				g_cfg->osmux.local_addr_v4, VTY_NEWLINE);
 | 
			
		||||
		if (g_cfg->osmux.local_addr_v6)
 | 
			
		||||
			vty_out(vty, " osmux bind-ip-v6 %s%s",
 | 
			
		||||
				g_cfg->osmux.local_addr_v6, VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, " osmux batch-factor %d%s",
 | 
			
		||||
			g_cfg->osmux_batch, VTY_NEWLINE);
 | 
			
		||||
			g_cfg->osmux.batch_factor, VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, " osmux batch-size %u%s",
 | 
			
		||||
			g_cfg->osmux_batch_size, VTY_NEWLINE);
 | 
			
		||||
			g_cfg->osmux.batch_size, VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, " osmux port %u%s",
 | 
			
		||||
			g_cfg->osmux_port, VTY_NEWLINE);
 | 
			
		||||
			g_cfg->osmux.local_port, VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, " osmux dummy %s%s",
 | 
			
		||||
			g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
 | 
			
		||||
			g_cfg->osmux.dummy_padding ? "on" : "off", VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, " osmux peer-behind-nat %s%s",
 | 
			
		||||
			g_cfg->osmux.peer_behind_nat ? "on" : "off", VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (g_cfg->conn_timeout)
 | 
			
		||||
@@ -169,11 +170,11 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
 | 
			
		||||
	struct rate_ctr *rx_packets, *rx_bytes;
 | 
			
		||||
	struct rate_ctr *dropped_packets;
 | 
			
		||||
 | 
			
		||||
	tx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR);
 | 
			
		||||
	tx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR);
 | 
			
		||||
	rx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
 | 
			
		||||
	rx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR);
 | 
			
		||||
	dropped_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_DROPPED_PACKETS_CTR);
 | 
			
		||||
	tx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
 | 
			
		||||
	tx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
 | 
			
		||||
	rx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
 | 
			
		||||
	rx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
 | 
			
		||||
	dropped_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_DROPPED_PACKETS_CTR);
 | 
			
		||||
 | 
			
		||||
	vty_out(vty,
 | 
			
		||||
		"   Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s"
 | 
			
		||||
@@ -183,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,
 | 
			
		||||
@@ -194,9 +195,33 @@ 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)) {
 | 
			
		||||
		struct rate_ctr *rx_chunks, *rx_octets, *rtp_tx, *rtp_tx_dropped, *octets_tx;
 | 
			
		||||
		rx_chunks = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_CHUNKS_RX_CTR);
 | 
			
		||||
		rx_octets = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_OCTETS_RX_CTR);
 | 
			
		||||
		rtp_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_CTR);
 | 
			
		||||
		rtp_tx_dropped =  rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
 | 
			
		||||
		octets_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_AMR_OCTETS_TX_CTR);
 | 
			
		||||
		vty_out(vty,
 | 
			
		||||
		"   Osmux:%s"
 | 
			
		||||
		"    State: %s%s"
 | 
			
		||||
		"    Local CID: %d%s"
 | 
			
		||||
		"    Remote CID: %d%s"
 | 
			
		||||
		"    Chunks received: %" PRIu64 " (%" PRIu64 " bytes total)%s"
 | 
			
		||||
		"    RTP Packets encoded (Tx): %" PRIu64 " (%" PRIu64 " AMR octets total)%s"
 | 
			
		||||
		"    AMR payloads Dropped (Tx): %" PRIu64 "%s",
 | 
			
		||||
		VTY_NEWLINE, osmux_state_str(conn->osmux.state), VTY_NEWLINE,
 | 
			
		||||
		conn->osmux.local_cid_allocated ? conn->osmux.local_cid : -1, VTY_NEWLINE,
 | 
			
		||||
		conn->osmux.remote_cid_present ? conn->osmux.remote_cid : -1, VTY_NEWLINE,
 | 
			
		||||
		rx_chunks->current, rx_octets->current, VTY_NEWLINE,
 | 
			
		||||
		rtp_tx->current, octets_tx->current, VTY_NEWLINE,
 | 
			
		||||
		rtp_tx_dropped->current, VTY_NEWLINE
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
 | 
			
		||||
@@ -229,7 +254,8 @@ static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
 | 
			
		||||
			 * connection types (E1) as soon as
 | 
			
		||||
			 * the implementation is available */
 | 
			
		||||
			if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
				dump_rtp_end(vty, &conn->u.rtp);
 | 
			
		||||
				struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
				dump_rtp_end(vty, conn_rtp);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -289,6 +315,14 @@ static void dump_ratectr_trunk(struct vty *vty, struct mgcp_trunk *trunk)
 | 
			
		||||
					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d",
 | 
			
		||||
					   ratectr->all_rtp_conn_stats);
 | 
			
		||||
	}
 | 
			
		||||
	if (ratectr->all_osmux_conn_stats) {
 | 
			
		||||
		vty_out(vty, "   %s:%s",
 | 
			
		||||
			ratectr->all_osmux_conn_stats->desc->group_description,
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		vty_out_rate_ctr_group_fmt(vty,
 | 
			
		||||
					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d",
 | 
			
		||||
					   ratectr->all_osmux_conn_stats);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ratectr->e1_stats && trunk->trunk_type == MGCP_TRUNK_E1) {
 | 
			
		||||
		vty_out(vty, "   %s:%s",
 | 
			
		||||
@@ -342,7 +376,7 @@ static int mgcp_show(struct vty *vty, int argc, const char **argv,
 | 
			
		||||
	llist_for_each_entry(trunk, &g_cfg->trunks, entry)
 | 
			
		||||
		dump_trunk(vty, trunk, show_stats, active_only);
 | 
			
		||||
 | 
			
		||||
	if (g_cfg->osmux)
 | 
			
		||||
	if (g_cfg->osmux.usage != OSMUX_USAGE_OFF)
 | 
			
		||||
		vty_out(vty, "Osmux used CID: %d%s", osmux_cid_pool_count_used(),
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
@@ -475,6 +509,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -644,53 +679,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -704,6 +726,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -764,6 +787,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1028,30 +1052,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1060,6 +1071,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1072,6 +1084,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1084,6 +1097,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1281,23 +1295,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1348,10 +1358,11 @@ DEFUN(loop_conn,
 | 
			
		||||
	endp = trunk->endpoints[endp_no];
 | 
			
		||||
	int loop = atoi(argv[2]);
 | 
			
		||||
	llist_for_each_entry(conn, &endp->conns, entry) {
 | 
			
		||||
		if (conn->type == MGCP_CONN_TYPE_RTP)
 | 
			
		||||
		if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
			/* Handle it like a MDCX, switch on SSRC patching if enabled */
 | 
			
		||||
			mgcp_rtp_end_config(endp, 1, &conn->u.rtp.end);
 | 
			
		||||
		else {
 | 
			
		||||
			struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
			mgcp_rtp_end_config(endp, 1, &conn_rtp->end);
 | 
			
		||||
		} else {
 | 
			
		||||
			/* FIXME: Introduce support for other connection (E1)
 | 
			
		||||
			 * types when implementation is available */
 | 
			
		||||
			vty_out(vty, "%%Can't enable SSRC patching,"
 | 
			
		||||
@@ -1409,7 +1420,7 @@ DEFUN(tap_rtp,
 | 
			
		||||
	endp = trunk->endpoints[endp_no];
 | 
			
		||||
 | 
			
		||||
	conn_id = argv[2];
 | 
			
		||||
	conn = mgcp_conn_get_rtp(endp, conn_id);
 | 
			
		||||
	conn = mgcp_endp_get_conn_rtp(endp, conn_id);
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		vty_out(vty, "Conn ID %s is invalid.%s",
 | 
			
		||||
			conn_id, VTY_NEWLINE);
 | 
			
		||||
@@ -1539,12 +1550,12 @@ DEFUN(cfg_mgcp_osmux,
 | 
			
		||||
	OSMO_ASSERT(trunk);
 | 
			
		||||
 | 
			
		||||
	if (strcmp(argv[0], "off") == 0) {
 | 
			
		||||
		g_cfg->osmux = OSMUX_USAGE_OFF;
 | 
			
		||||
		g_cfg->osmux.usage = OSMUX_USAGE_OFF;
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
	} else if (strcmp(argv[0], "on") == 0)
 | 
			
		||||
		g_cfg->osmux = OSMUX_USAGE_ON;
 | 
			
		||||
		g_cfg->osmux.usage = OSMUX_USAGE_ON;
 | 
			
		||||
	else if (strcmp(argv[0], "only") == 0)
 | 
			
		||||
		g_cfg->osmux = OSMUX_USAGE_ONLY;
 | 
			
		||||
		g_cfg->osmux.usage = OSMUX_USAGE_ONLY;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
 | 
			
		||||
@@ -1552,12 +1563,29 @@ DEFUN(cfg_mgcp_osmux,
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_osmux_ip,
 | 
			
		||||
      cfg_mgcp_osmux_ip_cmd,
 | 
			
		||||
      "osmux bind-ip " VTY_IPV46_CMD,
 | 
			
		||||
      "osmux bind-ip " VTY_IPV4_CMD,
 | 
			
		||||
      OSMUX_STR IP_STR
 | 
			
		||||
      "IPv4 Address to bind to\n")
 | 
			
		||||
{
 | 
			
		||||
	osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v4, argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_osmux_ip_v6,
 | 
			
		||||
      cfg_mgcp_osmux_ip_v6_cmd,
 | 
			
		||||
      "osmux bind-ip-v6 " VTY_IPV6_CMD,
 | 
			
		||||
      OSMUX_STR IP_STR
 | 
			
		||||
      "IPv4 Address to bind to\n"
 | 
			
		||||
      "IPv6 Address to bind to\n")
 | 
			
		||||
{
 | 
			
		||||
	osmo_strlcpy(g_cfg->osmux_addr, argv[0], sizeof(g_cfg->osmux_addr));
 | 
			
		||||
	osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v6, argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_osmux_port,
 | 
			
		||||
      cfg_mgcp_osmux_port_cmd,
 | 
			
		||||
      "osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->osmux.local_port = atoi(argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1566,7 +1594,7 @@ DEFUN(cfg_mgcp_osmux_batch_factor,
 | 
			
		||||
      "osmux batch-factor <1-8>",
 | 
			
		||||
      OSMUX_STR "Batching factor\n" "Number of messages in the batch\n")
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->osmux_batch = atoi(argv[0]);
 | 
			
		||||
	g_cfg->osmux.batch_factor = atoi(argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1575,15 +1603,7 @@ DEFUN(cfg_mgcp_osmux_batch_size,
 | 
			
		||||
      "osmux batch-size <1-65535>",
 | 
			
		||||
      OSMUX_STR "batch size\n" "Batch size in bytes\n")
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->osmux_batch_size = atoi(argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_osmux_port,
 | 
			
		||||
      cfg_mgcp_osmux_port_cmd,
 | 
			
		||||
      "osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->osmux_port = atoi(argv[0]);
 | 
			
		||||
	g_cfg->osmux.batch_size = atoi(argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1594,9 +1614,24 @@ DEFUN(cfg_mgcp_osmux_dummy,
 | 
			
		||||
      "Disable dummy padding\n")
 | 
			
		||||
{
 | 
			
		||||
	if (strcmp(argv[0], "on") == 0)
 | 
			
		||||
		g_cfg->osmux_dummy = 1;
 | 
			
		||||
		g_cfg->osmux.dummy_padding = true;
 | 
			
		||||
	else if (strcmp(argv[0], "off") == 0)
 | 
			
		||||
		g_cfg->osmux_dummy = 0;
 | 
			
		||||
		g_cfg->osmux.dummy_padding = false;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_osmux_peer_behind_nat,
 | 
			
		||||
      cfg_mgcp_osmux_peer_behind_nat_cmd,
 | 
			
		||||
      "osmux peer-behind-nat (on|off)",
 | 
			
		||||
      OSMUX_STR "Define whether peer is behind NAT\n"
 | 
			
		||||
      "Peer is behind NAT\n"
 | 
			
		||||
      "Peer is NOT behind NAT\n")
 | 
			
		||||
{
 | 
			
		||||
	if (strcmp(argv[0], "on") == 0)
 | 
			
		||||
		g_cfg->osmux.peer_behind_nat = true;
 | 
			
		||||
	else if (strcmp(argv[0], "off") == 0)
 | 
			
		||||
		g_cfg->osmux.peer_behind_nat = false;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
@@ -1686,10 +1721,12 @@ int mgcp_vty_init(void)
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_name_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_v6_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_size_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_osmux_peer_behind_nat_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_domain_cmd);
 | 
			
		||||
@@ -1734,9 +1771,9 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct mgcp_trunk *trunk;
 | 
			
		||||
 | 
			
		||||
	cfg->osmux_port = OSMUX_PORT;
 | 
			
		||||
	cfg->osmux_batch = 4;
 | 
			
		||||
	cfg->osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX;
 | 
			
		||||
	cfg->osmux.local_port = OSMUX_DEFAULT_PORT;
 | 
			
		||||
	cfg->osmux.batch_factor = 4;
 | 
			
		||||
	cfg->osmux.batch_size = OSMUX_BATCH_DEFAULT_MAX;
 | 
			
		||||
 | 
			
		||||
	g_cfg = cfg;
 | 
			
		||||
	rc = vty_read_config_file(config_file, NULL);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										38
									
								
								src/libosmo-sdp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/libosmo-sdp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_builddir) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(TALLOC_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
SDP_LIBVERSION=1:0:0
 | 
			
		||||
 | 
			
		||||
# 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!
 | 
			
		||||
lib_LTLIBRARIES = \
 | 
			
		||||
	libosmo-sdp.la \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_sdp_la_SOURCES = \
 | 
			
		||||
	sdp_codec.c \
 | 
			
		||||
	sdp_codec_list.c \
 | 
			
		||||
	sdp_msg.c \
 | 
			
		||||
	sdp_internal.c \
 | 
			
		||||
	fmtp.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_sdp_la_LDFLAGS = \
 | 
			
		||||
	$(AM_LDFLAGS) \
 | 
			
		||||
	-version-info $(SDP_LIBVERSION) \
 | 
			
		||||
	-no-undefined \
 | 
			
		||||
	-export-symbols-regex '^osmo_' \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_sdp_la_LIBADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
							
								
								
									
										193
									
								
								src/libosmo-sdp/fmtp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								src/libosmo-sdp/fmtp.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,193 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2023-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Hofmeyr
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/sdp_strings.h>
 | 
			
		||||
#include <osmocom/sdp/fmtp.h>
 | 
			
		||||
 | 
			
		||||
/* End of current fmtp parameter. Return a pointer to the next ';' character, if present, or the terminating '\0'. */
 | 
			
		||||
static const char *osmo_sdp_fmtp_end(const char *fmtp)
 | 
			
		||||
{
 | 
			
		||||
	if (!fmtp)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	for (; *fmtp && *fmtp != ';'; fmtp++);
 | 
			
		||||
	return fmtp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Start of next fmtp parameter. Return a pointer to the first character of the next fmtp parameter's name, or the
 | 
			
		||||
 * terminating '\0'. */
 | 
			
		||||
static const char *osmo_sdp_fmtp_next(const char *fmtp)
 | 
			
		||||
{
 | 
			
		||||
	if (!fmtp)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	fmtp = osmo_sdp_fmtp_end(fmtp);
 | 
			
		||||
	for (; *fmtp && (*fmtp == ';' || isspace(*fmtp)); fmtp++);
 | 
			
		||||
	return fmtp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Parse a given SDP fmtp value string, returning the value of a specific option, if present.
 | 
			
		||||
 *
 | 
			
		||||
 * Example:
 | 
			
		||||
 *
 | 
			
		||||
 *   const char *fmtp_vals = "octet-align=1;mode-set=0,2,4,7";
 | 
			
		||||
 *
 | 
			
		||||
 *   char mode_set_str[23];
 | 
			
		||||
 *   if (osmo_sdp_fmtp_get_val(mode_set_str, sizeof(mode_set_str), fmtp_vals, "mode-set")) {
 | 
			
		||||
 *           // option 'mode-set' is present, now mode_set_str == "0,2,4,7"
 | 
			
		||||
 *           use_modeset(mode_set_str);
 | 
			
		||||
 *   } else {
 | 
			
		||||
 *           // if 'mode-set' were not present...
 | 
			
		||||
 *           use_modeset(MY_DEFAULT_MODESET);
 | 
			
		||||
 *   }
 | 
			
		||||
 *
 | 
			
		||||
 * \param[out] val  Buffer to write the option's value to.
 | 
			
		||||
 * \param[in] val_size  Space available in val.
 | 
			
		||||
 * \param[in] fmtp  fmtp value string to parse -- must not contain the "a=fmtp:N " prefix, only the value part.
 | 
			
		||||
 * \param[in] option_name  Which fmtp option to get the value for.
 | 
			
		||||
 * \return true when the option was found, false when it was not present.
 | 
			
		||||
 */
 | 
			
		||||
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name)
 | 
			
		||||
{
 | 
			
		||||
	const char *pos = fmtp;
 | 
			
		||||
	const char *end;
 | 
			
		||||
	int option_name_len = strlen(option_name);
 | 
			
		||||
	for (; pos && *pos; pos = osmo_sdp_fmtp_next(pos)) {
 | 
			
		||||
		if (!osmo_str_startswith(pos, option_name))
 | 
			
		||||
			continue;
 | 
			
		||||
		pos += option_name_len;
 | 
			
		||||
		if (*pos != '=')
 | 
			
		||||
			continue;
 | 
			
		||||
		pos++;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!pos || !*pos)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	end = osmo_sdp_fmtp_end(pos);
 | 
			
		||||
	OSMO_ASSERT(end);
 | 
			
		||||
	if (val && val_size)
 | 
			
		||||
		osmo_strlcpy(val, pos, OSMO_MIN(val_size, end - pos + 1));
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Parse a given SDP fmtp value string, returning the value of a specific integer option, if present.
 | 
			
		||||
 *
 | 
			
		||||
 * Example:
 | 
			
		||||
 *
 | 
			
		||||
 *   const char *fmtp_vals = "octet-align=1;mode-set=0,2,4,7";
 | 
			
		||||
 *   bool oa = osmo_sdp_fmtp_get_int(fmtp_vals, OSMO_SDP_AMR_OCTET_ALIGN_NAME, 1);
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in] fmtp  fmtp value string to parse -- must not contain the "a=fmtp:N " prefix, only the value part.
 | 
			
		||||
 * \param[in] option_name  Which fmtp option to get the value for.
 | 
			
		||||
 * \param[in] default_value  If option_name is not present or cannot be parsed as integer, return this instead.
 | 
			
		||||
 * \return the integer value when the option was found and actually an integer, default_value otherwise.
 | 
			
		||||
 */
 | 
			
		||||
int64_t osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int64_t default_value)
 | 
			
		||||
{
 | 
			
		||||
	char val[128];
 | 
			
		||||
	if (!osmo_sdp_fmtp_get_val(val, sizeof(val), fmtp, option_name))
 | 
			
		||||
		return default_value;
 | 
			
		||||
	if (!val[0])
 | 
			
		||||
		return default_value;
 | 
			
		||||
	int64_t i;
 | 
			
		||||
	if (osmo_str_to_int64(&i, val, 10, INT64_MIN, INT64_MAX)) {
 | 
			
		||||
		/* error parsing number */
 | 
			
		||||
		return default_value;
 | 
			
		||||
	}
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Return true if octet-align is present and set to 1 in the given AMR related fmtp value.
 | 
			
		||||
 * Default to octet-align=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":
 | 
			
		||||
 *
 | 
			
		||||
 *    octet-align: Permissible values are 0 and 1.  If 1, octet-align
 | 
			
		||||
 *                 operation SHALL be used.  If 0 or if not present,
 | 
			
		||||
 *                 bandwidth-efficient operation is employed.
 | 
			
		||||
 *
 | 
			
		||||
 * https://tools.ietf.org/html/rfc4867
 | 
			
		||||
 */
 | 
			
		||||
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp)
 | 
			
		||||
{
 | 
			
		||||
	return osmo_sdp_fmtp_get_int(fmtp, OSMO_SDP_STR_AMR_OCTET_ALIGN, 0) == 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void strip_whitespace(char *str)
 | 
			
		||||
{
 | 
			
		||||
	char *i = str;
 | 
			
		||||
	char *o = str;
 | 
			
		||||
	for (; *i; i++, o++) {
 | 
			
		||||
		while (isspace(*i))
 | 
			
		||||
			i++;
 | 
			
		||||
		*o = *i;
 | 
			
		||||
		if (!*i)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return true when the two AMR type fmtp strings can be considered equivalent.
 | 
			
		||||
 * - Omission of octet-align is equivalent to having octet-align=0 present (0 is the default).
 | 
			
		||||
 * - Omission of 'mode-set' means, match any and all codec modes. So if either a or b have no 'mode-set', it's a match.
 | 
			
		||||
 *   If both have 'mode-set' present, they must be identical to match. Do not sort the mode-set string, but strip
 | 
			
		||||
 *   whitespace.
 | 
			
		||||
 * - TODO all other parameters are currently completely ignored.
 | 
			
		||||
 */
 | 
			
		||||
bool osmo_sdp_fmtp_amr_match(const char *a, const char *b)
 | 
			
		||||
{
 | 
			
		||||
	char a_modeset[32] = {};
 | 
			
		||||
	char b_modeset[32] = {};
 | 
			
		||||
	bool a_ok;
 | 
			
		||||
	bool b_ok;
 | 
			
		||||
 | 
			
		||||
	if (!a)
 | 
			
		||||
		a = "";
 | 
			
		||||
	if (!b)
 | 
			
		||||
		b = "";
 | 
			
		||||
 | 
			
		||||
	/* octet-align=1. Omission means octet-align=0 */
 | 
			
		||||
	if (osmo_sdp_fmtp_amr_is_octet_aligned(a) != osmo_sdp_fmtp_amr_is_octet_aligned(b))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* mode-set=0,1,2,3,4,5,6,7 */
 | 
			
		||||
	a_ok = osmo_sdp_fmtp_get_val(a_modeset, sizeof(a_modeset), a, "mode-set");
 | 
			
		||||
	b_ok = osmo_sdp_fmtp_get_val(b_modeset, sizeof(b_modeset), b, "mode-set");
 | 
			
		||||
	if (a_ok && b_ok) {
 | 
			
		||||
		/* Strip whitespace: We don't know what remote SDP peers may throw at us. There could be whitespace
 | 
			
		||||
		 * around the separators like 'mode-set=2,3 ; octet-align=1', which may show up here as whitespace in
 | 
			
		||||
		 * the value string as "2,3 ", which would mismatch "2,3". */
 | 
			
		||||
		strip_whitespace(a_modeset);
 | 
			
		||||
		strip_whitespace(b_modeset);
 | 
			
		||||
		if (strcmp(a_modeset, b_modeset))
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* TODO: treat other AMR traits, see RFC4867 8.1. Maybe generically match all values that are present?
 | 
			
		||||
	 * So far we have no need for other values than octet-align and mode-set. */
 | 
			
		||||
 | 
			
		||||
	/* No mismatch found, it's a match */
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										251
									
								
								src/libosmo-sdp/sdp_codec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								src/libosmo-sdp/sdp_codec.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,251 @@
 | 
			
		||||
/* Codec management in SDP messages. */
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/fmtp.h>
 | 
			
		||||
#include <osmocom/sdp/sdp_codec.h>
 | 
			
		||||
#include <osmocom/sdp/sdp_internal.h>
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_codec *osmo_sdp_codec_alloc(void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	return talloc_zero(ctx, struct osmo_sdp_codec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_codec_set(struct osmo_sdp_codec *c,
 | 
			
		||||
		       int8_t payload_type, const char *encoding_name, unsigned int rate, const char *fmtp)
 | 
			
		||||
{
 | 
			
		||||
	c->rate = rate;
 | 
			
		||||
	osmo_sdp_codec_set_encoding_name(c, encoding_name);
 | 
			
		||||
	osmo_sdp_codec_set_fmtp(c, fmtp);
 | 
			
		||||
	c->payload_type = payload_type;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_codec_set_encoding_name(struct osmo_sdp_codec *c, const char *encoding_name)
 | 
			
		||||
{
 | 
			
		||||
	osmo_talloc_replace_string(c, &c->encoding_name, encoding_name);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_codec_set_fmtp(struct osmo_sdp_codec *c, const char *fmtp)
 | 
			
		||||
{
 | 
			
		||||
	osmo_talloc_replace_string(c, &c->fmtp, fmtp);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool osmo_sdp_codec_is_set(const struct osmo_sdp_codec *a)
 | 
			
		||||
{
 | 
			
		||||
	return a && a->encoding_name && a->encoding_name[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmo_sdp_codec_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_strbuf sb = { .buf = buf, .len = buflen };
 | 
			
		||||
	if (!codec) {
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, "NULL");
 | 
			
		||||
		return sb.chars_needed;
 | 
			
		||||
	}
 | 
			
		||||
	if (codec->encoding_name && codec->encoding_name[0])
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, "%s", codec->encoding_name);
 | 
			
		||||
	if (codec->rate != 8000)
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, "/%u", codec->rate);
 | 
			
		||||
	if (codec->fmtp && codec->fmtp[0])
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, ":%s", codec->fmtp);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type);
 | 
			
		||||
	return sb.chars_needed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *osmo_sdp_codec_to_str_c(void *ctx, const struct osmo_sdp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_NAME_C_IMPL(ctx, 32, "osmo_sdp_codec_to_str_c-ERROR", osmo_sdp_codec_to_str_buf, codec)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Parse a codec string as from osmo_sdp_codec_to_str_buf() back to an osmo_sdp_codec struct.
 | 
			
		||||
 * Write the parsed result to *dst, using ctx as talloc parent.
 | 
			
		||||
 * The input string is like <encoding_name>[:<fmtp-string>][#<payload-type-nr>]
 | 
			
		||||
 * for example:
 | 
			
		||||
 *    "FOO:my-fmtp=1;my-other-fmtp=2#42"
 | 
			
		||||
 * Note that ';' are separators only within the fmtp string. This function does not separate those. In above example,
 | 
			
		||||
 * the fmtp string part is "my-fmtp=val;my-other-fmtp=val2" and ends up in dst->ftmp as-is.
 | 
			
		||||
 * Parse at most str_len characters, or the entire string when str_len < 0 or str_len > strlen(str).
 | 
			
		||||
 * Return 0 on success, negative on failure. */
 | 
			
		||||
int osmo_sdp_codec_from_str(struct osmo_sdp_codec *dst, const char *str, int str_len)
 | 
			
		||||
{
 | 
			
		||||
	const char *pos = str;
 | 
			
		||||
	const char *str_end = str + (str_len >= 0 ? str_len : strlen(str));
 | 
			
		||||
	const char *p2;
 | 
			
		||||
 | 
			
		||||
	struct token token_encoding_name = {};
 | 
			
		||||
	struct token token_rate = {};
 | 
			
		||||
	struct token token_fmtp = {};
 | 
			
		||||
	struct token token_payload_type = {};
 | 
			
		||||
 | 
			
		||||
	struct token *new_t = NULL;
 | 
			
		||||
	/* start with the encoding name */
 | 
			
		||||
	struct token *t = &token_encoding_name;
 | 
			
		||||
	t->start = pos;
 | 
			
		||||
 | 
			
		||||
	for (; pos < str_end; pos++) {
 | 
			
		||||
		new_t = NULL;
 | 
			
		||||
		switch (*pos) {
 | 
			
		||||
		case '/':
 | 
			
		||||
			new_t = &token_rate;
 | 
			
		||||
			break;
 | 
			
		||||
		case ':':
 | 
			
		||||
			new_t = &token_fmtp;
 | 
			
		||||
			break;
 | 
			
		||||
		case '#':
 | 
			
		||||
			/* count this '#' only if there is no other one following. It might be part of a fmtp. */
 | 
			
		||||
			for (p2 = pos + 1; p2 < str_end; p2++)
 | 
			
		||||
				if (*p2 == '#')
 | 
			
		||||
					break;
 | 
			
		||||
			if (p2 < str_end && *p2 == '#')
 | 
			
		||||
				break;
 | 
			
		||||
			/* This is the last '#' in the string. Count it only when a digit follows. */
 | 
			
		||||
			if (!isdigit(pos[1]))
 | 
			
		||||
				break;
 | 
			
		||||
			new_t = &token_payload_type;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		if (!new_t)
 | 
			
		||||
			continue;
 | 
			
		||||
		/* If we already have a token for a start character, don't start it again. These may be part of a fmtp
 | 
			
		||||
		 * string. */
 | 
			
		||||
		if (new_t == t)
 | 
			
		||||
			continue;
 | 
			
		||||
		t->end = pos;
 | 
			
		||||
		t = new_t;
 | 
			
		||||
		t->start = pos + 1;
 | 
			
		||||
	}
 | 
			
		||||
	t->end = pos;
 | 
			
		||||
 | 
			
		||||
	token_copy(dst, &dst->encoding_name, &token_encoding_name);
 | 
			
		||||
	if (token_rate.start)
 | 
			
		||||
		dst->rate = atoi(token_rate.start);
 | 
			
		||||
	else
 | 
			
		||||
		dst->rate = 8000;
 | 
			
		||||
	token_copy(dst, &dst->fmtp, &token_fmtp);
 | 
			
		||||
	if (token_payload_type.start)
 | 
			
		||||
		dst->payload_type = atoi(token_payload_type.start);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Compare both payload type number and fmtp string 1:1 */
 | 
			
		||||
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_exact = {
 | 
			
		||||
	.payload_type = true,
 | 
			
		||||
	.encoding_name = true,
 | 
			
		||||
	.rate = true,
 | 
			
		||||
	.fmtp = OSMO_SDP_CMP_EXACT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Ignore payload type number; compare fmtp string by meaning when possible, else 1:1 */
 | 
			
		||||
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent = {
 | 
			
		||||
	.payload_type = false,
 | 
			
		||||
	.encoding_name = true,
 | 
			
		||||
	.rate = true,
 | 
			
		||||
	.fmtp = OSMO_SDP_CMP_EQUIVALENT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Compare only encoding name */
 | 
			
		||||
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_name = {
 | 
			
		||||
	.payload_type = false,
 | 
			
		||||
	.encoding_name = true,
 | 
			
		||||
	.rate = false,
 | 
			
		||||
	.fmtp = OSMO_SDP_CMP_IGNORE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent;
 | 
			
		||||
static inline int strcmp_safe(const char *a, const char *b)
 | 
			
		||||
{
 | 
			
		||||
	return strcmp(a ? : "", b ? : "");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Compare encoding name, rate and fmtp, returning cmp result: -1 if a < b, 0 if a == b, 1 if a > b.
 | 
			
		||||
 * Compare as defined in 'cmp':
 | 
			
		||||
 * If cmpf->payload_type is false, ignore payload_type numbers.
 | 
			
		||||
 * If cmpf->rate is false, ignore rate.
 | 
			
		||||
 * If cmpf->fmtp is OSMO_SDP_CMP_IGNORE, ignore fmtp strings.
 | 
			
		||||
 * If cmpf->fmtp is OSMO_SDP_CMP_EXACT, use strcmp() to match fmtp 1:1.
 | 
			
		||||
 * If cmpf->fmtp is OSMO_SDP_CMP_EQUIVALENT, use specific fmtp knowledge to match equivalent entries;
 | 
			
		||||
 *  - AMR fmtp matching is done by osmo_sdp_fmtp_amr_match().
 | 
			
		||||
 *  - for all others, still compare fmtp 1:1.
 | 
			
		||||
 */
 | 
			
		||||
int osmo_sdp_codec_cmp(const struct osmo_sdp_codec *a, const struct osmo_sdp_codec *b,
 | 
			
		||||
		       const struct osmo_sdp_codec_cmp_flags *cmpf)
 | 
			
		||||
{
 | 
			
		||||
	int cmp;
 | 
			
		||||
	if (a == b)
 | 
			
		||||
		return 0;
 | 
			
		||||
	if (!a)
 | 
			
		||||
		return -1;
 | 
			
		||||
	if (!b)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	if (!cmpf)
 | 
			
		||||
		cmpf = &osmo_sdp_codec_cmp_exact;
 | 
			
		||||
 | 
			
		||||
	if (cmpf->encoding_name) {
 | 
			
		||||
		cmp = strcmp_safe(a->encoding_name, b->encoding_name);
 | 
			
		||||
		if (cmp)
 | 
			
		||||
			return cmp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cmpf->rate) {
 | 
			
		||||
		cmp = OSMO_CMP(a->rate, b->rate);
 | 
			
		||||
		if (cmp)
 | 
			
		||||
			return cmp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (cmpf->fmtp) {
 | 
			
		||||
	default:
 | 
			
		||||
	case OSMO_SDP_CMP_EXACT:
 | 
			
		||||
		cmp = strcmp_safe(a->fmtp, b->fmtp);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case OSMO_SDP_CMP_EQUIVALENT:
 | 
			
		||||
		/* In case of AMR, allow logical matching; we only need to do that if the strings differ. */
 | 
			
		||||
		cmp = strcmp_safe(a->fmtp, b->fmtp);
 | 
			
		||||
		if (cmp
 | 
			
		||||
		    && !strcmp_safe("AMR", a->encoding_name)
 | 
			
		||||
		    && osmo_sdp_fmtp_amr_match(a->fmtp, b->fmtp))
 | 
			
		||||
			cmp = 0;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case OSMO_SDP_CMP_IGNORE:
 | 
			
		||||
		cmp = 0;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	if (cmp)
 | 
			
		||||
		return cmp;
 | 
			
		||||
 | 
			
		||||
	if (cmpf->payload_type)
 | 
			
		||||
		cmp = OSMO_CMP(a->payload_type, b->payload_type);
 | 
			
		||||
 | 
			
		||||
	return cmp;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										367
									
								
								src/libosmo-sdp/sdp_codec_list.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										367
									
								
								src/libosmo-sdp/sdp_codec_list.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,367 @@
 | 
			
		||||
/* Codec management in SDP messages. */
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/sdp_codec_list.h>
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_codec_list *osmo_sdp_codec_list_alloc(void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sdp_codec_list *codec_list = talloc_zero(ctx, struct osmo_sdp_codec_list);
 | 
			
		||||
	INIT_LLIST_HEAD(&codec_list->list);
 | 
			
		||||
	return codec_list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Free all items contained in this list, do not free the list itself (leave an empty list). */
 | 
			
		||||
void osmo_sdp_codec_list_free_items(struct osmo_sdp_codec_list *codec_list)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sdp_codec *c;
 | 
			
		||||
	while ((c = osmo_sdp_codec_list_first(codec_list))) {
 | 
			
		||||
		osmo_sdp_codec_list_remove_entry(c);
 | 
			
		||||
		talloc_free(c);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_codec *osmo_sdp_codec_list_add_empty(struct osmo_sdp_codec_list *codec_list)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sdp_codec *c = osmo_sdp_codec_alloc(codec_list);
 | 
			
		||||
	llist_add_tail(&c->entry, &codec_list->list);
 | 
			
		||||
	return c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int8_t osmo_sdp_codec_list_get_unused_dyn_pt_nr(const struct osmo_sdp_codec_list *codec_list, int8_t suggest_pt_nr)
 | 
			
		||||
{
 | 
			
		||||
	bool present[127 - 96 + 1] = {};
 | 
			
		||||
	const struct osmo_sdp_codec *c;
 | 
			
		||||
	bool suggest_pt_nr_exists = false;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	osmo_sdp_codec_list_foreach (c, codec_list) {
 | 
			
		||||
		if (c->payload_type >= 96 && c->payload_type <= 127)
 | 
			
		||||
			present[c->payload_type - 96] = true;
 | 
			
		||||
		if (c->payload_type == suggest_pt_nr)
 | 
			
		||||
			suggest_pt_nr_exists = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!suggest_pt_nr_exists)
 | 
			
		||||
		return suggest_pt_nr;
 | 
			
		||||
 | 
			
		||||
	/* The desired number is already taken, see which of the dynamic types is not taken yet */
 | 
			
		||||
	for (i = 96; i <= 127; i++) {
 | 
			
		||||
		/* For dynamic allocations, skip these predefined numbers, taken from enum mgcp_codecs:
 | 
			
		||||
		 * 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_CLEARMODE = 120,		3GPP TS 48.103 table 5.4.2.2.1
 | 
			
		||||
		 */
 | 
			
		||||
		if (i >= 110 && i <= 113)
 | 
			
		||||
			continue;
 | 
			
		||||
		else if (i == 120)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!present[i - 96])
 | 
			
		||||
			return i;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Allocate a new entry in codec_list and copy codec's values to it.
 | 
			
		||||
 * If once is NULL, unconditionally add a new codec entry.
 | 
			
		||||
 * If once is non-NULL, do not add a new entry when the list already contains a matching entry; for determining a match,
 | 
			
		||||
 * use the once->flags. For example, if once = &osmo_sdp_codec_cmp_equivalent, look up if codec_list has a similar
 | 
			
		||||
 * codec, and add the new entry only if it is not listed.
 | 
			
		||||
 * See osmo_sdp_codec_cmp() and osmo_sdp_fmtp_amr_match() for details.
 | 
			
		||||
 * Return the new entry, or the equivalent entry already present in the list.
 | 
			
		||||
 */
 | 
			
		||||
struct osmo_sdp_codec *osmo_sdp_codec_list_add(struct osmo_sdp_codec_list *codec_list,
 | 
			
		||||
					       const struct osmo_sdp_codec *codec,
 | 
			
		||||
					       const struct osmo_sdp_codec_cmp_flags *once, bool pick_unused_pt_nr)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sdp_codec *new_entry;
 | 
			
		||||
	int8_t payload_type;
 | 
			
		||||
 | 
			
		||||
	if (once) {
 | 
			
		||||
		struct osmo_sdp_codec *c;
 | 
			
		||||
		osmo_sdp_codec_list_foreach (c, codec_list)
 | 
			
		||||
			if (!osmo_sdp_codec_cmp(codec, c, once))
 | 
			
		||||
				return c;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Adjust payload_type number? */
 | 
			
		||||
	payload_type = codec->payload_type;
 | 
			
		||||
	if (pick_unused_pt_nr)
 | 
			
		||||
		payload_type = osmo_sdp_codec_list_get_unused_dyn_pt_nr(codec_list, payload_type);
 | 
			
		||||
 | 
			
		||||
	/* Take provided values, possibly modified payload_type */
 | 
			
		||||
	new_entry = osmo_sdp_codec_list_add_empty(codec_list);
 | 
			
		||||
	osmo_sdp_codec_set(new_entry, payload_type, codec->encoding_name, codec->rate, codec->fmtp);
 | 
			
		||||
 | 
			
		||||
	return new_entry;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Remove and free all entries from the codec_list that match the given codec according to osmo_sdp_codec_cmp(cmpf).
 | 
			
		||||
 * Return the number of entries freed. */
 | 
			
		||||
int osmo_sdp_codec_list_remove(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
 | 
			
		||||
			       const struct osmo_sdp_codec_cmp_flags *cmpf)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sdp_codec *i, *j;
 | 
			
		||||
	int count = 0;
 | 
			
		||||
	osmo_sdp_codec_list_foreach_safe (i, j, codec_list) {
 | 
			
		||||
		if (osmo_sdp_codec_cmp(i, codec, cmpf))
 | 
			
		||||
			continue;
 | 
			
		||||
		osmo_sdp_codec_list_remove_entry(i);
 | 
			
		||||
		talloc_free(i);
 | 
			
		||||
		count++;
 | 
			
		||||
	}
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Unlink an osmo_sdp_codec from an osmo_sdp_codec_list, if the codec instance is part of a list. Do not free the
 | 
			
		||||
 * struct osmo_sdp_codec.
 | 
			
		||||
 */
 | 
			
		||||
void osmo_sdp_codec_list_remove_entry(struct osmo_sdp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	/* The codec is not part of a list in these cases:
 | 
			
		||||
	 * After talloc_zero(), next == NULL.
 | 
			
		||||
	 * After llist_del(), next == LLIST_POISON1. */
 | 
			
		||||
	if (codec->entry.next != NULL
 | 
			
		||||
	    && codec->entry.next != (struct llist_head *)LLIST_POISON1)
 | 
			
		||||
		llist_del(&codec->entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int strcmp_safe(const char *a, const char *b)
 | 
			
		||||
{
 | 
			
		||||
	return strcmp(a ? : "", b ? : "");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Short single-line representation of a list of SDP audio codecs, convenient for logging.
 | 
			
		||||
 * If summarize == true, collapse variants of the same encoding_name (in practice, don't show all of the various AMR
 | 
			
		||||
 * fmtp permutations). If summarize == false, print each and every codec in full.
 | 
			
		||||
 */
 | 
			
		||||
int osmo_sdp_codec_list_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec_list *codec_list, bool summarize)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_strbuf sb = { .buf = buf, .len = buflen };
 | 
			
		||||
	const struct osmo_sdp_codec *codec;
 | 
			
		||||
	bool first;
 | 
			
		||||
 | 
			
		||||
	if (llist_empty(&codec_list->list)) {
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, "(no-codecs)");
 | 
			
		||||
		return sb.chars_needed;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!summarize) {
 | 
			
		||||
		first = true;
 | 
			
		||||
		osmo_sdp_codec_list_foreach (codec, codec_list) {
 | 
			
		||||
			if (!first)
 | 
			
		||||
				OSMO_STRBUF_PRINTF(sb, " ");
 | 
			
		||||
			OSMO_STRBUF_APPEND(sb, osmo_sdp_codec_to_str_buf, codec);
 | 
			
		||||
			first = false;
 | 
			
		||||
		}
 | 
			
		||||
		return sb.chars_needed;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* summarize */
 | 
			
		||||
	first = true;
 | 
			
		||||
	osmo_sdp_codec_list_foreach (codec, codec_list) {
 | 
			
		||||
		const struct osmo_sdp_codec *c2;
 | 
			
		||||
		int count = 0;
 | 
			
		||||
		bool various_pt = false;
 | 
			
		||||
 | 
			
		||||
		/* When this encoding name has been handled before, skip it now. */
 | 
			
		||||
		osmo_sdp_codec_list_foreach (c2, codec_list) {
 | 
			
		||||
			if (c2 == codec)
 | 
			
		||||
				break;
 | 
			
		||||
			if (!strcmp_safe(codec->encoding_name, c2->encoding_name)) {
 | 
			
		||||
				count = 1;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (count)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* Not seen this encoding_name before, count total occurences */
 | 
			
		||||
		count = 0;
 | 
			
		||||
		osmo_sdp_codec_list_foreach (c2, codec_list) {
 | 
			
		||||
			if (!strcmp_safe(codec->encoding_name, c2->encoding_name)) {
 | 
			
		||||
				count++;
 | 
			
		||||
				if (codec->payload_type != c2->payload_type)
 | 
			
		||||
					various_pt = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!first)
 | 
			
		||||
			OSMO_STRBUF_PRINTF(sb, " ");
 | 
			
		||||
		if (count > 1)
 | 
			
		||||
			OSMO_STRBUF_PRINTF(sb, "%d*", count);
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, "%s", codec->encoding_name);
 | 
			
		||||
		if (!various_pt)
 | 
			
		||||
			OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type);
 | 
			
		||||
		first = false;
 | 
			
		||||
	}
 | 
			
		||||
	return sb.chars_needed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *osmo_sdp_codec_list_to_str_c(void *ctx, const struct osmo_sdp_codec_list *codec_list, bool summarize)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_NAME_C_IMPL(ctx, 128, "osmo_sdp_codec_list_to_str_c-ERROR", osmo_sdp_codec_list_to_str_buf, codec_list, summarize)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Return first entry, or NULL if the list is empty. */
 | 
			
		||||
struct osmo_sdp_codec *osmo_sdp_codec_list_first(const struct osmo_sdp_codec_list *list)
 | 
			
		||||
{
 | 
			
		||||
	return llist_first_entry_or_null(&list->list, struct osmo_sdp_codec, entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Move entries matching 'codec' to the front of the list. Matching is done via osmo_sdp_codec_cmp(cmpf).
 | 
			
		||||
 * Return the number of matches that are now at the front of the list.
 | 
			
		||||
 */
 | 
			
		||||
int osmo_sdp_codec_list_move_to_first(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
 | 
			
		||||
				      const struct osmo_sdp_codec_cmp_flags *cmpf)
 | 
			
		||||
{
 | 
			
		||||
	struct llist_head *head = &codec_list->list;
 | 
			
		||||
	struct osmo_sdp_codec *i, *j;
 | 
			
		||||
	int matches_found = 0;
 | 
			
		||||
	osmo_sdp_codec_list_foreach_safe (i, j, codec_list) {
 | 
			
		||||
		if (osmo_sdp_codec_cmp(codec, i, cmpf))
 | 
			
		||||
			continue;
 | 
			
		||||
		/* It's a match, move to the head */
 | 
			
		||||
		osmo_sdp_codec_list_remove_entry(i);
 | 
			
		||||
		llist_add(&i->entry, head);
 | 
			
		||||
		matches_found++;
 | 
			
		||||
		/* If more matches show up later, add them *after* the one just moved to the front. */
 | 
			
		||||
		head = &i->entry;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return matches_found;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Compare two lists of SDP codecs, returning cmp result: -1 if a < b, 0 if a == b, 1 if a > b.
 | 
			
		||||
 * The two lists are compared in order, item by item, using osmo_sdp_codec_cmp(cmpf).
 | 
			
		||||
 */
 | 
			
		||||
int osmo_sdp_codec_list_cmp(const struct osmo_sdp_codec_list *a, const struct osmo_sdp_codec_list *b,
 | 
			
		||||
			    const struct osmo_sdp_codec_cmp_flags *cmpf)
 | 
			
		||||
{
 | 
			
		||||
	const struct llist_head *a_start;
 | 
			
		||||
	const struct llist_head *a_pos;
 | 
			
		||||
	const struct llist_head *b_start;
 | 
			
		||||
	const struct llist_head *b_pos;
 | 
			
		||||
	int cmp;
 | 
			
		||||
 | 
			
		||||
	/* NULL pointer == empty list */
 | 
			
		||||
	if (a && llist_empty(&a->list))
 | 
			
		||||
		a = NULL;
 | 
			
		||||
	if (b && llist_empty(&a->list))
 | 
			
		||||
		b = NULL;
 | 
			
		||||
 | 
			
		||||
	/* are one or both empty? */
 | 
			
		||||
	if (a == b)
 | 
			
		||||
		return 0;
 | 
			
		||||
	if (!a)
 | 
			
		||||
		return -1;
 | 
			
		||||
	if (!b)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	/* compare item by item */
 | 
			
		||||
	a_start = &a->list;
 | 
			
		||||
	a_pos = a_start->next;
 | 
			
		||||
	b_start = &b->list;
 | 
			
		||||
	b_pos = b_start->next;
 | 
			
		||||
 | 
			
		||||
	for (; a_pos != a_start; a_pos = a_pos->next, b_pos = b_pos->next) {
 | 
			
		||||
		const struct osmo_sdp_codec *codec_a;
 | 
			
		||||
		const struct osmo_sdp_codec *codec_b;
 | 
			
		||||
 | 
			
		||||
		if (b_pos == b_start) {
 | 
			
		||||
			/* there is an entry in a, but b has already ended. mismatch. */
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		codec_a = llist_entry(a_pos, struct osmo_sdp_codec, entry);
 | 
			
		||||
		codec_b = llist_entry(b_pos, struct osmo_sdp_codec, entry);
 | 
			
		||||
		cmp = osmo_sdp_codec_cmp(codec_a, codec_b, cmpf);
 | 
			
		||||
		if (cmp)
 | 
			
		||||
			return cmp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (b_pos != b_start) {
 | 
			
		||||
		/* 'a' has ended, but 'b' has more items. mismatch. */
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* full match. */
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Leave only those codecs in 'dst' that are also present in 'other'.
 | 
			
		||||
 * The matching is made by osmo_sdp_codec_cmp(cmpf).
 | 
			
		||||
 * If translate_payload_type_numbers has an effect if 'dst' and 'other' have mismatching payload_type numbers for the
 | 
			
		||||
 * same SDP codec descriptions. If translate_payload_type_numbers is true, take the payload_type numbers from 'other'.
 | 
			
		||||
 * If false, keep payload_type numbers in 'dst' unchanged. */
 | 
			
		||||
void osmo_sdp_codec_list_intersection(struct osmo_sdp_codec_list *dst, const struct osmo_sdp_codec_list *other,
 | 
			
		||||
				      const struct osmo_sdp_codec_cmp_flags *cmpf,
 | 
			
		||||
				      bool translate_payload_type_numbers)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sdp_codec *i, *j;
 | 
			
		||||
	osmo_sdp_codec_list_foreach_safe (i, j, dst) {
 | 
			
		||||
		struct osmo_sdp_codec *o;
 | 
			
		||||
		struct osmo_sdp_codec *match = NULL;
 | 
			
		||||
		osmo_sdp_codec_list_foreach (o, other) {
 | 
			
		||||
			if (osmo_sdp_codec_cmp(i, o, cmpf))
 | 
			
		||||
				continue;
 | 
			
		||||
			match = o;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!match) {
 | 
			
		||||
			osmo_sdp_codec_list_remove_entry(i);
 | 
			
		||||
			talloc_free(i);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (translate_payload_type_numbers)
 | 
			
		||||
			i->payload_type = match->payload_type;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find an entry for the given payload_type number in the given list of codecs. */
 | 
			
		||||
struct osmo_sdp_codec *osmo_sdp_codec_list_by_payload_type(struct osmo_sdp_codec_list *codec_list, int8_t payload_type)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sdp_codec *codec;
 | 
			
		||||
	osmo_sdp_codec_list_foreach(codec, codec_list) {
 | 
			
		||||
		if (codec->payload_type == payload_type)
 | 
			
		||||
			return codec;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool osmo_sdp_codec_list_is_empty(const struct osmo_sdp_codec_list *codec_list)
 | 
			
		||||
{
 | 
			
		||||
	if (!codec_list)
 | 
			
		||||
		return true;
 | 
			
		||||
	return llist_empty(&codec_list->list);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										121
									
								
								src/libosmo-sdp/sdp_internal.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/libosmo-sdp/sdp_internal.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/sdp_internal.h>
 | 
			
		||||
 | 
			
		||||
/* Copy a string from t->start to t->end, return as talloc allocated under ctx in *dst.
 | 
			
		||||
 * If *dst is non-NULL, talloc_free(*dst) first. */
 | 
			
		||||
void token_copy(void *ctx, char **dst, const struct token *t)
 | 
			
		||||
{
 | 
			
		||||
	size_t len;
 | 
			
		||||
	if (*dst)
 | 
			
		||||
		talloc_free(*dst);
 | 
			
		||||
	if (!t->start || !(t->end > t->start)) {
 | 
			
		||||
		*dst = NULL;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	len = t->end - t->start;
 | 
			
		||||
	*dst = talloc_size(ctx, len + 1);
 | 
			
		||||
	osmo_strlcpy(*dst, t->start, len + 1);
 | 
			
		||||
	talloc_set_name_const(*dst, *dst);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *token_chr(const struct token *src, char c)
 | 
			
		||||
{
 | 
			
		||||
	const char *pos;
 | 
			
		||||
	for (pos = src->start; *pos && pos < src->end; pos++) {
 | 
			
		||||
		if (*pos == c)
 | 
			
		||||
			return pos;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *token_chrs(const struct token *src, const char *chrs)
 | 
			
		||||
{
 | 
			
		||||
	const char *pos;
 | 
			
		||||
	for (pos = src->start; pos && *pos && pos < src->end; pos++) {
 | 
			
		||||
		if (strchr(chrs, *pos))
 | 
			
		||||
			return pos;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void token_next(struct token *t, const char *str, const char *end, const char *separators)
 | 
			
		||||
{
 | 
			
		||||
	t->start = str;
 | 
			
		||||
	while (strchr(separators, *t->start) && t->start < end)
 | 
			
		||||
		t->start++;
 | 
			
		||||
	t->end = t->start;
 | 
			
		||||
	while (!strchr(separators, *t->end) && t->end < end)
 | 
			
		||||
		t->end++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Convert the token to an integer, and return the character afer the integer string that was parsed.
 | 
			
		||||
 * The difference to osmo_str_to_int64() is that this guarantees to only access the memory from t->start to t->end. */
 | 
			
		||||
const char *token_to_int64(int64_t *result, const struct token *t, int base, int min_val, int max_val)
 | 
			
		||||
{
 | 
			
		||||
	/* copy the number section over to a temporary buffer, because t->end may terminate the string anywhere, and
 | 
			
		||||
	 * functions like strtoll (which osmo_str_to_int64() uses) are not able to stay within a strict buffer length
 | 
			
		||||
	 * unless the string is zero terminated at the buffer boundary. */
 | 
			
		||||
	char buf[32];
 | 
			
		||||
	const char *int_end = t->start;
 | 
			
		||||
	if (int_end < t->end && *int_end == '-')
 | 
			
		||||
		int_end++;
 | 
			
		||||
	while (int_end < t->end && isdigit(*int_end))
 | 
			
		||||
		int_end++;
 | 
			
		||||
	if (int_end - t->start >= sizeof(buf))
 | 
			
		||||
		return NULL;
 | 
			
		||||
	osmo_strlcpy(buf, t->start, int_end - t->start + 1);
 | 
			
		||||
	if (osmo_str_to_int64(result, buf, base, min_val, max_val) != 0)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return int_end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Convenience: like token_to_int64() but with a plain int. */
 | 
			
		||||
const char *token_to_int(int *result, const struct token *t, int base, int min_val, int max_val)
 | 
			
		||||
{
 | 
			
		||||
	int64_t val;
 | 
			
		||||
	const char *rc = token_to_int64(&val, t, base, min_val, max_val);
 | 
			
		||||
	if (val < INT_MIN) {
 | 
			
		||||
		if (result)
 | 
			
		||||
			*result = INT_MIN;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if (val > INT_MAX) {
 | 
			
		||||
		if (result)
 | 
			
		||||
			*result = INT_MAX;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if (result)
 | 
			
		||||
		*result = (int)val;
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										517
									
								
								src/libosmo-sdp/sdp_msg.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										517
									
								
								src/libosmo-sdp/sdp_msg.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,517 @@
 | 
			
		||||
/* Implementation for SDP message encoding and decoding */
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/sdp_msg.h>
 | 
			
		||||
#include <osmocom/sdp/sdp_strings.h>
 | 
			
		||||
#include <osmocom/sdp/sdp_internal.h>
 | 
			
		||||
 | 
			
		||||
static const char * const mdir_str[] = {
 | 
			
		||||
	[OSMO_SDP_MDIR_UNSET] = "-",
 | 
			
		||||
	[OSMO_SDP_MDIR_SENDONLY] = OSMO_SDP_STR_SENDONLY,
 | 
			
		||||
	[OSMO_SDP_MDIR_RECVONLY] = OSMO_SDP_STR_RECVONLY,
 | 
			
		||||
	[OSMO_SDP_MDIR_SENDRECV] = OSMO_SDP_STR_SENDRECV,
 | 
			
		||||
	[OSMO_SDP_MDIR_INACTIVE] = OSMO_SDP_STR_INACTIVE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! Convert struct osmo_sdp_msg to the actual SDP protocol representation. */
 | 
			
		||||
int osmo_sdp_msg_encode_buf(char *dst, size_t dst_size, const struct osmo_sdp_msg *sdp)
 | 
			
		||||
{
 | 
			
		||||
	const struct osmo_sdp_codec *codec;
 | 
			
		||||
	struct osmo_strbuf sb = { .buf = dst, .len = dst_size };
 | 
			
		||||
	const char *oip;
 | 
			
		||||
	char oipv;
 | 
			
		||||
	const char *ip;
 | 
			
		||||
	char ipv;
 | 
			
		||||
 | 
			
		||||
	if (!sdp) {
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, "%s", "");
 | 
			
		||||
		return sb.chars_needed;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	oip = sdp->origin.addr.ip[0] ? sdp->origin.addr.ip : "0.0.0.0";
 | 
			
		||||
	oipv = (osmo_ip_str_type(oip) == AF_INET6) ? '6' : '4';
 | 
			
		||||
 | 
			
		||||
	ip = sdp->rtp.ip[0] ? sdp->rtp.ip : "0.0.0.0";
 | 
			
		||||
	ipv = (osmo_ip_str_type(oip) == AF_INET6) ? '6' : '4';
 | 
			
		||||
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb,
 | 
			
		||||
			   "v=0\r\n"
 | 
			
		||||
			   "o=%s %s %s IN IP%c %s\r\n"
 | 
			
		||||
			   "s=%s\r\n"
 | 
			
		||||
			   "c=IN IP%c %s\r\n"
 | 
			
		||||
			   "t=%"PRId64" %"PRId64"\r\n"
 | 
			
		||||
			   "m=audio %d RTP/AVP",
 | 
			
		||||
			   sdp->origin.username ? : "libosmo-sdp",
 | 
			
		||||
			   sdp->origin.sess_id ? : "0", sdp->origin.sess_version ? : "0",
 | 
			
		||||
			   oipv, oip,
 | 
			
		||||
			   sdp->session_name ? : "-",
 | 
			
		||||
			   ipv, ip,
 | 
			
		||||
			   sdp->time_active.start,
 | 
			
		||||
			   sdp->time_active.stop,
 | 
			
		||||
			   sdp->rtp.port);
 | 
			
		||||
 | 
			
		||||
	/* Append all payload type numbers to 'm=audio <port> RTP/AVP 3 4 112' line */
 | 
			
		||||
	osmo_sdp_codec_list_foreach(codec, sdp->codecs)
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, " %d", codec->payload_type);
 | 
			
		||||
	OSMO_STRBUF_PRINTF(sb, "\r\n");
 | 
			
		||||
 | 
			
		||||
	/* Add details for all codecs */
 | 
			
		||||
	osmo_sdp_codec_list_foreach(codec, sdp->codecs) {
 | 
			
		||||
		if (!osmo_sdp_codec_is_set(codec))
 | 
			
		||||
			continue;
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_RTPMAP) "%d %s/%d\r\n", codec->payload_type, codec->encoding_name,
 | 
			
		||||
				   codec->rate > 0 ? codec->rate : 8000);
 | 
			
		||||
		if (codec->fmtp && codec->fmtp[0])
 | 
			
		||||
			OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP) "%d %s\r\n", codec->payload_type, codec->fmtp);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sdp->ptime)
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_PTIME) "%d\r\n", sdp->ptime);
 | 
			
		||||
 | 
			
		||||
	if (sdp->media_direction != OSMO_SDP_MDIR_UNSET && sdp->media_direction < ARRAY_SIZE(mdir_str))
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, "a=%s\r\n", mdir_str[sdp->media_direction]);
 | 
			
		||||
 | 
			
		||||
	return sb.chars_needed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *osmo_sdp_msg_encode_c(void *ctx, const struct osmo_sdp_msg *sdp)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_NAME_C_IMPL(ctx, 256, "osmo_sdp_msg_to_str_c-ERROR", osmo_sdp_msg_encode_buf, sdp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return the first line ending (or the end of the string) at or after the given string position. */
 | 
			
		||||
static const char *get_line_end(const struct token *src)
 | 
			
		||||
{
 | 
			
		||||
	const char *line_end = token_chrs(src, "\r\n");
 | 
			
		||||
	if (!line_end)
 | 
			
		||||
		return src->end;
 | 
			
		||||
	return line_end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* See if str starts with an attribute like "rtpmap:" or "sendrecv\r\n".
 | 
			
		||||
 * For example:
 | 
			
		||||
 *   token_is_attrib({"foo: bar"}, "foo", ':') --> " bar"
 | 
			
		||||
 *   token_is_attrib({"sendrev\n"}, "sendrecv", 0) --> "\n"
 | 
			
		||||
 *   token_is_attrib({"sendrev\n"}, "foo", 0) --> NULL
 | 
			
		||||
 * On mismatch, return NULL.
 | 
			
		||||
 * On match, return the string after the expect_next_char.
 | 
			
		||||
 * For expect_next_char == 0, match both the end of the string as well as a line ending character ('\r' or '\n'), and in
 | 
			
		||||
 * both cases return the string directly after attrib_name.
 | 
			
		||||
 */
 | 
			
		||||
static const char *token_is_attrib(const struct token *str, const char *attrib_name, char expect_next_char)
 | 
			
		||||
{
 | 
			
		||||
	const char *next_c;
 | 
			
		||||
	int attrib_name_len = strlen(attrib_name);
 | 
			
		||||
	if (str->start + attrib_name_len > str->end)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	if (!osmo_str_startswith(str->start, attrib_name))
 | 
			
		||||
		return NULL;
 | 
			
		||||
	next_c = str->start + strlen(attrib_name);
 | 
			
		||||
	if (next_c > str->end)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	if (next_c == str->end) {
 | 
			
		||||
		if (!expect_next_char)
 | 
			
		||||
			return str->end;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if (expect_next_char == *next_c)
 | 
			
		||||
		return next_c + 1;
 | 
			
		||||
	/* Treat expect_next_char == \0 as equivalent with line end */
 | 
			
		||||
	if (!expect_next_char && strchr("\r\n", *next_c))
 | 
			
		||||
		return next_c;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static enum osmo_sdp_media_direcion_e check_for_media_direction(const struct token *str)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(mdir_str); i++) {
 | 
			
		||||
		if (i == OSMO_SDP_MDIR_UNSET)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (token_is_attrib(str, mdir_str[i], 0))
 | 
			
		||||
			return i;
 | 
			
		||||
	}
 | 
			
		||||
	return OSMO_SDP_MDIR_UNSET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct osmo_sdp_codec *find_or_create_payload_type(struct osmo_sdp_msg *sdp, unsigned int payload_type)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sdp_codec *codec;
 | 
			
		||||
	codec = osmo_sdp_codec_list_by_payload_type(sdp->codecs, payload_type);
 | 
			
		||||
	if (!codec) {
 | 
			
		||||
		codec = osmo_sdp_codec_list_add_empty(sdp->codecs);
 | 
			
		||||
		codec->payload_type = payload_type;
 | 
			
		||||
		codec->rate = 8000;
 | 
			
		||||
	}
 | 
			
		||||
	return codec;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void next_token(struct token *t, const char *str, const char *end)
 | 
			
		||||
{
 | 
			
		||||
	token_next(t, str, end, " \t");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* parse a line like 'a=rtpmap:0 PCMU/8000', 'a=fmtp:112 octet-align=1; mode-set=4', 'a=ptime:20'.
 | 
			
		||||
 * The src should point at the character after 'a=', e.g. at the start of 'rtpmap', 'fmtp', 'ptime'
 | 
			
		||||
 */
 | 
			
		||||
static int sdp_parse_attrib(struct osmo_sdp_msg *sdp, const struct token *src)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int payload_type;
 | 
			
		||||
	struct osmo_sdp_codec *codec;
 | 
			
		||||
	enum osmo_sdp_media_direcion_e mdir;
 | 
			
		||||
	const char *line_end = get_line_end(src);
 | 
			
		||||
	const char *next;
 | 
			
		||||
	int nr;
 | 
			
		||||
	struct token t;
 | 
			
		||||
 | 
			
		||||
	if ((next = token_is_attrib(src, OSMO_SDP_STR_RTPMAP, ':'))) {
 | 
			
		||||
		/* "a=rtpmap:96 AMR/8000" */
 | 
			
		||||
		next_token(&t, next, line_end);
 | 
			
		||||
		next = token_to_int(&nr, &t, 10, 0, 127);
 | 
			
		||||
		if (next != t.end)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		payload_type = nr;
 | 
			
		||||
 | 
			
		||||
		token_next(&t, next, line_end, " \t/");
 | 
			
		||||
		if (t.start >= t.end)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		next = t.end;
 | 
			
		||||
 | 
			
		||||
		codec = find_or_create_payload_type(sdp, payload_type);
 | 
			
		||||
		token_copy(codec, &codec->encoding_name, &t);
 | 
			
		||||
 | 
			
		||||
		token_next(&t, next, line_end, " \t/");
 | 
			
		||||
		if (t.start >= t.end) {
 | 
			
		||||
			/* There should be a "/8000" here. If it is missing, let's not be strict about it. */
 | 
			
		||||
			codec->rate = 8000;
 | 
			
		||||
		} else {
 | 
			
		||||
			next = token_to_int(&nr, &t, 10, 0, INT_MAX);
 | 
			
		||||
			if (next != t.end)
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* optional channel number, i.e. another "/1" */
 | 
			
		||||
		token_next(&t, t.end, line_end, " \t/");
 | 
			
		||||
		if (t.end > t.start) {
 | 
			
		||||
			int channels;
 | 
			
		||||
			next = token_to_int(&channels, &t, 10, 0, INT_MAX);
 | 
			
		||||
			if (next != t.end)
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			if (channels != 1)
 | 
			
		||||
				return -ENOTSUP;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	else if ((next = token_is_attrib(src, OSMO_SDP_STR_FMTP, ':'))) {
 | 
			
		||||
		/* "a=fmtp:112 octet-align=1;mode-set=0,1,2,3" */
 | 
			
		||||
		next_token(&t, next, line_end);
 | 
			
		||||
		next = token_to_int(&nr, &t, 10, 0, 127);
 | 
			
		||||
		if (next != t.end)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		payload_type = nr;
 | 
			
		||||
 | 
			
		||||
		codec = find_or_create_payload_type(sdp, payload_type);
 | 
			
		||||
 | 
			
		||||
		/* "octet-align..." token */
 | 
			
		||||
		next_token(&t, next, line_end);
 | 
			
		||||
		if (t.start >= line_end)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		t.end = line_end;
 | 
			
		||||
		token_copy(codec, &codec->fmtp, &t);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	else if ((next = token_is_attrib(src, OSMO_SDP_STR_PTIME, ':'))) {
 | 
			
		||||
		/* "a=ptime:20" */
 | 
			
		||||
		next_token(&t, next, line_end);
 | 
			
		||||
		next = token_to_int(&nr, &t, 10, 1, INT_MAX);
 | 
			
		||||
		if (!next)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		/* RFC8866 6.4: it could also be a 'real' nr, but we don't support that. */
 | 
			
		||||
		if (next < t.end)
 | 
			
		||||
			return -ENOTSUP;
 | 
			
		||||
		sdp->ptime = nr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* "a=sendrecv" ... */
 | 
			
		||||
	else if ((mdir = check_for_media_direction(src)) != OSMO_SDP_MDIR_UNSET) {
 | 
			
		||||
		sdp->media_direction = mdir;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct value_string fixed_payload_types[] = {
 | 
			
		||||
	{ 0, "PCMU" },
 | 
			
		||||
	{ 3, "GSM" },
 | 
			
		||||
	{ 8, "PCMA" },
 | 
			
		||||
	{ 18, "G729" },
 | 
			
		||||
	{ 110, "GSM-EFR" },
 | 
			
		||||
	{ 111, "GSM-HR-08" },
 | 
			
		||||
	{ 112, "AMR" },
 | 
			
		||||
	{ 113, "AMR-WB" },
 | 
			
		||||
	{}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Parse a line like 'm=audio 16398 RTP/AVP 0 3 8 96 112', starting after the '=' */
 | 
			
		||||
static int sdp_parse_media_description(struct osmo_sdp_msg *sdp, const struct token *src)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int port;
 | 
			
		||||
	struct token t;
 | 
			
		||||
	const char *line_end = get_line_end(src);
 | 
			
		||||
	if (sscanf(src->start, "audio %u RTP/AVP", &port) < 1)
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
 | 
			
		||||
	if (port > 0xffff)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	sdp->rtp.port = port;
 | 
			
		||||
 | 
			
		||||
	/* skip "audio 12345 RTP/AVP ", i.e. 3 tokens on */
 | 
			
		||||
	next_token(&t, src->start, line_end);
 | 
			
		||||
	next_token(&t, t.end, line_end);
 | 
			
		||||
	next_token(&t, t.end, line_end);
 | 
			
		||||
 | 
			
		||||
	/* first payload type number */
 | 
			
		||||
	next_token(&t, t.end, line_end);
 | 
			
		||||
 | 
			
		||||
	/* Parse listing of payload type numbers after "RTP/AVP" */
 | 
			
		||||
	while (t.start < line_end) {
 | 
			
		||||
		unsigned int payload_type;
 | 
			
		||||
		struct osmo_sdp_codec *codec;
 | 
			
		||||
		const char *encoding_name;
 | 
			
		||||
		if (sscanf(t.start, "%u", &payload_type) < 1)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		codec = find_or_create_payload_type(sdp, payload_type);
 | 
			
		||||
 | 
			
		||||
		/* Fill in encoding name for fixed payload types */
 | 
			
		||||
		encoding_name = get_value_string_or_null(fixed_payload_types, codec->payload_type);
 | 
			
		||||
		if (encoding_name)
 | 
			
		||||
			osmo_talloc_replace_string(codec, &codec->encoding_name, encoding_name);
 | 
			
		||||
 | 
			
		||||
		next_token(&t, t.end, line_end);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* parse a line like 'c=IN IP4 192.168.11.151' starting after the '=' */
 | 
			
		||||
static int sdp_parse_connection_info(struct osmo_sdp_msg *sdp, const struct token *src)
 | 
			
		||||
{
 | 
			
		||||
	char ipv[10];
 | 
			
		||||
	char addr_str[INET6_ADDRSTRLEN];
 | 
			
		||||
	if (sscanf(src->start, "IN %s %s", ipv, addr_str) < 2)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(ipv, "IP4") && strcmp(ipv, "IP6"))
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
 | 
			
		||||
	return osmo_sockaddr_str_from_str(&sdp->rtp, addr_str, sdp->rtp.port);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* parse a line like 'o=jdoe 3724394400 3724394405 IN IP4 198.51.100.1' starting after the '=' */
 | 
			
		||||
static int sdp_parse_origin(struct osmo_sdp_msg *sdp, const struct token *src)
 | 
			
		||||
{
 | 
			
		||||
	struct token t;
 | 
			
		||||
	char addr_str[INET6_ADDRSTRLEN + 1] = {};
 | 
			
		||||
	const char *line_end = get_line_end(src);
 | 
			
		||||
 | 
			
		||||
	next_token(&t, src->start, line_end);
 | 
			
		||||
	token_copy(sdp, &sdp->origin.username, &t);
 | 
			
		||||
 | 
			
		||||
	next_token(&t, t.end, line_end);
 | 
			
		||||
	token_copy(sdp, &sdp->origin.sess_id, &t);
 | 
			
		||||
 | 
			
		||||
	next_token(&t, t.end, line_end);
 | 
			
		||||
	token_copy(sdp, &sdp->origin.sess_version, &t);
 | 
			
		||||
 | 
			
		||||
	next_token(&t, t.end, line_end);
 | 
			
		||||
	if (strncmp("IN", t.start, t.end - t.start))
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
 | 
			
		||||
	next_token(&t, t.end, line_end);
 | 
			
		||||
	if (strncmp("IP4", t.start, t.end - t.start)
 | 
			
		||||
	    && strncmp("IP6", t.start, t.end - t.start))
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
 | 
			
		||||
	next_token(&t, t.end, line_end);
 | 
			
		||||
	osmo_strlcpy(addr_str, t.start, OSMO_MIN(sizeof(addr_str), t.end - t.start + 1));
 | 
			
		||||
	return osmo_sockaddr_str_from_str(&sdp->origin.addr, addr_str, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sdp_parse_session_name(struct osmo_sdp_msg *sdp, const struct token *src)
 | 
			
		||||
{
 | 
			
		||||
	struct token t = *src;
 | 
			
		||||
	t.end = get_line_end(src);
 | 
			
		||||
	if (sdp->session_name)
 | 
			
		||||
		talloc_free(sdp->session_name);
 | 
			
		||||
	if (t.start >= t.end)
 | 
			
		||||
		sdp->session_name = NULL;
 | 
			
		||||
	else
 | 
			
		||||
		token_copy(sdp, &sdp->session_name, &t);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sdp_msg *sdp;
 | 
			
		||||
	sdp = talloc_zero(ctx, struct osmo_sdp_msg);
 | 
			
		||||
	sdp->codecs = osmo_sdp_codec_list_alloc(sdp);
 | 
			
		||||
	return sdp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Parse SDP string into struct osmo_sdp_msg. Return 0 on success, negative on error.
 | 
			
		||||
 * Return a new osmo_sdp_msg instance allocated from ctx, or NULL on error.
 | 
			
		||||
 * When NULL is returned and if ret is non-NULL, details of the error are returned in ret->*.
 | 
			
		||||
 */
 | 
			
		||||
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src_str, int src_str_len,
 | 
			
		||||
					 struct osmo_sdp_msg_decode_ret *ret)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sdp_msg *sdp;
 | 
			
		||||
	bool found_message_start = false;
 | 
			
		||||
	struct token src = {
 | 
			
		||||
		.start = src_str,
 | 
			
		||||
		.end = src_str + (src_str_len < 0 ? strlen(src_str) : src_str_len),
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (ret)
 | 
			
		||||
		*ret = (struct osmo_sdp_msg_decode_ret){};
 | 
			
		||||
 | 
			
		||||
	sdp = osmo_sdp_msg_alloc(ctx);
 | 
			
		||||
 | 
			
		||||
	for (; src.start && src.start < src.end && *src.start; src.start++) {
 | 
			
		||||
		char attrib;
 | 
			
		||||
		int rc = 0;
 | 
			
		||||
		struct token t;
 | 
			
		||||
 | 
			
		||||
		if (*src.start == '\r' || *src.start == '\n')
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		t.start = src.start;
 | 
			
		||||
		t.end = get_line_end(&src);
 | 
			
		||||
 | 
			
		||||
		if (!found_message_start) {
 | 
			
		||||
			/* An SDP message must start with a line saying "v=0". */
 | 
			
		||||
			if (strncmp("v=0", t.start, t.end - t.start)) {
 | 
			
		||||
				/* report the error */
 | 
			
		||||
				if (ret) {
 | 
			
		||||
					*ret = (struct osmo_sdp_msg_decode_ret){
 | 
			
		||||
						.rc = -EINVAL,
 | 
			
		||||
						.error = {
 | 
			
		||||
							.at_input_str = t.start,
 | 
			
		||||
							.at_input_str_len = t.end - t.start,
 | 
			
		||||
						},
 | 
			
		||||
						.src_remain = t.end,
 | 
			
		||||
					};
 | 
			
		||||
				}
 | 
			
		||||
				talloc_free(sdp);
 | 
			
		||||
				return NULL;
 | 
			
		||||
			}
 | 
			
		||||
			found_message_start = true;
 | 
			
		||||
			goto next_line;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Expecting only lines starting with 'X='. Not being too strict about it is probably alright. */
 | 
			
		||||
		if (t.start + 1 >= t.end || t.start[1] != '=')
 | 
			
		||||
			goto next_line;
 | 
			
		||||
 | 
			
		||||
		attrib = *t.start;
 | 
			
		||||
		t.start += 2;
 | 
			
		||||
		switch (attrib) {
 | 
			
		||||
			/* a=... */
 | 
			
		||||
			case 'a':
 | 
			
		||||
				rc = sdp_parse_attrib(sdp, &t);
 | 
			
		||||
				break;
 | 
			
		||||
			case 'm':
 | 
			
		||||
				rc = sdp_parse_media_description(sdp, &t);
 | 
			
		||||
				break;
 | 
			
		||||
			case 'c':
 | 
			
		||||
				rc = sdp_parse_connection_info(sdp, &t);
 | 
			
		||||
				break;
 | 
			
		||||
			case 'o':
 | 
			
		||||
				rc = sdp_parse_origin(sdp, &t);
 | 
			
		||||
				break;
 | 
			
		||||
			case 's':
 | 
			
		||||
				rc = sdp_parse_session_name(sdp, &t);
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				/* ignore any other parameters */
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (rc) {
 | 
			
		||||
			if (ret) {
 | 
			
		||||
				/* shift back to include the 'x=' part as well */
 | 
			
		||||
				t.start -= 2;
 | 
			
		||||
				*ret = (struct osmo_sdp_msg_decode_ret){
 | 
			
		||||
					.rc = rc,
 | 
			
		||||
					.error = {
 | 
			
		||||
						.at_input_str = t.start,
 | 
			
		||||
						.at_input_str_len = t.end - t.start,
 | 
			
		||||
					},
 | 
			
		||||
					.src_remain = t.end,
 | 
			
		||||
				};
 | 
			
		||||
			}
 | 
			
		||||
			talloc_free(sdp);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
next_line:
 | 
			
		||||
		src.start = t.end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sdp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Short single-line representation of an SDP message, convenient for logging.
 | 
			
		||||
 * To obtain a valid SDP message, use osmo_sdp_msg_encode_buf() instead.
 | 
			
		||||
 */
 | 
			
		||||
int osmo_sdp_msg_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_msg *sdp, bool summarize)
 | 
			
		||||
{
 | 
			
		||||
       struct osmo_strbuf sb = { .buf = buf, .len = buflen };
 | 
			
		||||
       if (!sdp) {
 | 
			
		||||
               OSMO_STRBUF_PRINTF(sb, "NULL");
 | 
			
		||||
               return sb.chars_needed;
 | 
			
		||||
       }
 | 
			
		||||
 | 
			
		||||
       OSMO_STRBUF_PRINTF(sb, OSMO_SOCKADDR_STR_FMT, OSMO_SOCKADDR_STR_FMT_ARGS(&sdp->rtp));
 | 
			
		||||
       OSMO_STRBUF_PRINTF(sb, "{");
 | 
			
		||||
       OSMO_STRBUF_APPEND(sb, osmo_sdp_codec_list_to_str_buf, sdp->codecs, summarize);
 | 
			
		||||
       OSMO_STRBUF_PRINTF(sb, "}");
 | 
			
		||||
       return sb.chars_needed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *osmo_sdp_msg_to_str_c(void *ctx, const struct osmo_sdp_msg *sdp, bool summarize)
 | 
			
		||||
{
 | 
			
		||||
       OSMO_NAME_C_IMPL(ctx, 128, "sdp_msg_to_str_c-ERROR", osmo_sdp_msg_to_str_buf, sdp, summarize)
 | 
			
		||||
}
 | 
			
		||||
@@ -25,12 +25,6 @@ osmo_mgw_SOURCES = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
osmo_mgw_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
 | 
			
		||||
	$(LIBOSMOCTRL_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(LIBOSMOTRAU_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,6 @@
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_ctrl.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
@@ -51,7 +50,7 @@
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/ctrl/control_if.h>
 | 
			
		||||
#include <osmocom/ctrl/control_vty.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/telnet_interface.h>
 | 
			
		||||
@@ -83,7 +82,7 @@ static int daemonize = 0;
 | 
			
		||||
 | 
			
		||||
const char *osmomgw_copyright =
 | 
			
		||||
	"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
 | 
			
		||||
	"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
 | 
			
		||||
	"Copyright (C) 2017-2022 by sysmocom s.f.m.c. GmbH\r\n"
 | 
			
		||||
	"Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n"
 | 
			
		||||
	"Philipp Maier\r\n\r\n"
 | 
			
		||||
	"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
 | 
			
		||||
@@ -95,7 +94,7 @@ static char *config_file = "osmo-mgw.cfg";
 | 
			
		||||
/* used by msgb and mgcp */
 | 
			
		||||
void *tall_mgw_ctx = NULL;
 | 
			
		||||
 | 
			
		||||
static void print_help()
 | 
			
		||||
static void print_help(void)
 | 
			
		||||
{
 | 
			
		||||
	printf("Some useful options:\n");
 | 
			
		||||
	printf(" -h --help is printing this text.\n");
 | 
			
		||||
@@ -281,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",
 | 
			
		||||
@@ -295,13 +319,22 @@ static const struct log_info_cat log_categories[] = {
 | 
			
		||||
		  .name = "DRTP",
 | 
			
		||||
		  .description = "RTP stream handling",
 | 
			
		||||
		  .color = "\033[1;30m",
 | 
			
		||||
		  .enabled = 1,.loglevel = LOGL_NOTICE,
 | 
			
		||||
		  .enabled = 1,
 | 
			
		||||
		  .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DE1] = {
 | 
			
		||||
		  .name = "DE1",
 | 
			
		||||
		  .description = "E1 line handling",
 | 
			
		||||
		  .color = "\033[1;31m",
 | 
			
		||||
		  .enabled = 1,.loglevel = LOGL_NOTICE,
 | 
			
		||||
		  .enabled = 1,
 | 
			
		||||
		  .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DOSMUX] = {
 | 
			
		||||
		  .name = "DOSMUX",
 | 
			
		||||
		  .description = "Osmux (Osmocom RTP multiplexing)",
 | 
			
		||||
		  .color = "\033[1;32m",
 | 
			
		||||
		  .enabled = 1,
 | 
			
		||||
		  .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -320,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);
 | 
			
		||||
@@ -352,12 +387,11 @@ 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;
 | 
			
		||||
 | 
			
		||||
	cfg->ctrl = mgw_ctrl_interface_setup(cfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
 | 
			
		||||
	cfg->ctrl = ctrl_interface_setup(cfg, OSMO_CTRL_PORT_MGW, NULL);
 | 
			
		||||
	if (!cfg->ctrl) {
 | 
			
		||||
		fprintf(stderr, "Failed to init the control interface on %s:%u. Exiting\n",
 | 
			
		||||
			ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	mgcp_client \
 | 
			
		||||
	mgcp \
 | 
			
		||||
	sdp \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
 | 
			
		||||
@@ -34,11 +35,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 = \
 | 
			
		||||
@@ -33,7 +34,7 @@ mgcp_test_SOURCES = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
mgcp_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
@@ -44,3 +45,6 @@ mgcp_test_LDADD = \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	-lm  \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
update_exp:
 | 
			
		||||
	$(builddir)/mgcp_test >$(srcdir)/mgcp_test.ok
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -73,7 +73,7 @@ v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -101,7 +101,8 @@ Testing MDCX4_ADDR000
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983216 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
@@ -111,20 +112,21 @@ c=IN IP4 0.0.0.0
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983217 1@mgw MGCP 1.0
 | 
			
		||||
MDCX 18983217 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
@@ -134,7 +136,7 @@ c=IN IP4 5.6.7.8
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -148,7 +150,8 @@ Testing MDCX4_PT1
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983218 1@mgw MGCP 1.0
 | 
			
		||||
---------8<---------
 | 
			
		||||
M: SENDRECV
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20-40, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
@@ -158,7 +161,7 @@ c=IN IP4 5.6.7.8
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
t=0 0
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -172,7 +175,8 @@ Testing MDCX4_PT2
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983219 1@mgw MGCP 1.0
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20-20, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
@@ -182,7 +186,7 @@ c=IN IP4 5.6.7.8
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
c=IN IP4 5.6.7.8
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -196,7 +200,8 @@ Testing MDCX4_PT3
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983220 1@mgw MGCP 1.0
 | 
			
		||||
Testing MDCX4_PT3
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
@@ -206,7 +211,7 @@ c=IN IP4 5.6.7.8
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
o=- %s 23 IN IP4 5.6.7.8
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -220,7 +225,8 @@ Testing MDCX4_PT4
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983221 1@mgw MGCP 1.0
 | 
			
		||||
================================================
 | 
			
		||||
m: sendrecv
 | 
			
		||||
c: 2
 | 
			
		||||
i: %s
 | 
			
		||||
l: A:amr, NT:IN
 | 
			
		||||
 | 
			
		||||
@@ -230,7 +236,7 @@ c=IN IP4 5.6.7.8
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
v=0
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -244,7 +250,8 @@ Testing MDCX4_SO
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983222 1@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
M: sendonly
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
@@ -254,7 +261,7 @@ c=IN IP4 5.6.7.8
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -267,7 +274,8 @@ Testing MDCX4_RO
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983223 1@mgw MGCP 1.0
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
@@ -296,9 +304,15 @@ Response matches our expectations.
 | 
			
		||||
Testing CRCX_ZYN
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
@@ -414,7 +428,7 @@ v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
================================================
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -479,7 +493,7 @@ v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
Testing CRCX
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -525,6 +539,184 @@ 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:20
 | 
			
		||||
 | 
			
		||||
---------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<---------
 | 
			
		||||
RQNT 186908782 null@mgw MGCP 1.0
 | 
			
		||||
X: B244F267488
 | 
			
		||||
S: D/9
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_PORT_0
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 3 1@mgw MGCP 1.0
 | 
			
		||||
m: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 0 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_PORT_0_IUFP
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 4 1@mgw MGCP 1.0
 | 
			
		||||
m: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 0 RTP/AVP 96
 | 
			
		||||
a=rtpmap:96 VND.3GPP.IUFP/16000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_PORT_0_IUFP_SENDRECV
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 4 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 0 RTP/AVP 96
 | 
			
		||||
a=rtpmap:96 VND.3GPP.IUFP/16000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_PORT_0_IUFP_SENDRECV2
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 4 1@mgw MGCP 1.0
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
a=sendrecv
 | 
			
		||||
m=audio 0 RTP/AVP 96
 | 
			
		||||
a=rtpmap:96 VND.3GPP.IUFP/16000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_PORT_0_IUFP_SENDRECV3
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 4 1@mgw MGCP 1.0
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 0 RTP/AVP 96
 | 
			
		||||
a=rtpmap:96 VND.3GPP.IUFP/16000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
@@ -538,7 +730,7 @@ v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
================================================
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -556,7 +748,7 @@ v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -668,7 +860,7 @@ v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
@@ -1122,7 +1314,7 @@ c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 18 97
 | 
			
		||||
a=rtpmap:18 G729/8000
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
@@ -1139,7 +1331,7 @@ m=audio 5904 RTP/AVP 18 97 101
 | 
			
		||||
a=rtpmap:18 G729/8000
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=rtpmap:101 FOO/8000
 | 
			
		||||
---------8<---------
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
@@ -1156,7 +1348,7 @@ m=audio 5904 RTP/AVP
 | 
			
		||||
a=rtpmap:18 G729/8000
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=rtpmap:101 FOO/8000
 | 
			
		||||
---------8<---------
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
@@ -1173,7 +1365,7 @@ m=audio 5904 RTP/AVP 18
 | 
			
		||||
a=rtpmap:18 G729/8000
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=rtpmap:101 FOO/8000
 | 
			
		||||
---------8<---------
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
@@ -1255,7 +1447,7 @@ v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
Testing no sequence flow on initial packet
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -1276,7 +1468,7 @@ p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
p10, aPCMU -> (null)
 | 
			
		||||
'10,a :PCMU' -> '(null)'
 | 
			
		||||
 | 
			
		||||
p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
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
 | 
			
		||||
@@ -1286,13 +1478,15 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    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
 | 
			
		||||
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
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
    conn[1]: codec:AMR, pt:96
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[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
 | 
			
		||||
@@ -1302,13 +1496,15 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    0:  97 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2:  96 AMR/8000/1 octet-aligned=1  -> rc=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
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:PCMU, pt:0
 | 
			
		||||
    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: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
 | 
			
		||||
@@ -1318,12 +1514,15 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    0:  97 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2:  96 AMR/8000/1 octet-aligned=1  -> rc=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:
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:PCMU, pt:0
 | 
			
		||||
    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)
 | 
			
		||||
@@ -1331,9 +1530,13 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    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, 96) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 96
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 97) -> 96
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    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
 | 
			
		||||
@@ -1341,31 +1544,116 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    2: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    (none)
 | 
			
		||||
    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
 | 
			
		||||
 - 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]):
 | 
			
		||||
    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
 | 
			
		||||
    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
 | 
			
		||||
    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]):
 | 
			
		||||
    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:
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=unset  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
			
		||||
#5: test AMR with differing octet-aligned settings
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
 - 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:
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    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]):
 | 
			
		||||
    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_pt_translate(conn1, conn0, 122) -> 111
 | 
			
		||||
#6: test AMR with missing octet-aligned settings (defaults to 0)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
 - 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
 | 
			
		||||
 - 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!
 | 
			
		||||
#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
 | 
			
		||||
@@ -1374,14 +1662,16 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    0:  97 GSM-HR-08/8000  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000  -> rc=0
 | 
			
		||||
    2:  96 AMR/8000 octet-aligned=1  -> rc=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
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:PCMU, pt:0
 | 
			
		||||
    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: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
 | 
			
		||||
@@ -1390,14 +1680,16 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    0:  97 GSM-HR-08  -> rc=0
 | 
			
		||||
    1:   0 PCMU  -> rc=0
 | 
			
		||||
    2:  96 AMR octet-aligned=1  -> rc=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
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:PCMU, pt:0
 | 
			
		||||
    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: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
 | 
			
		||||
@@ -1405,10 +1697,15 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 GSM-HR-08/8000/2  -> rc=-22
 | 
			
		||||
    1: 121 GSM-HR-08/8000/1  -> rc=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[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:GSM-HR-08, pt:111
 | 
			
		||||
    conn[1]: codec:GSM-HR-08, pt:121
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:GSM-HR-08, pt:121
 | 
			
		||||
    conn[0]: codec:GSM-HR-08, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
 | 
			
		||||
Testing test_conn_id_matching
 | 
			
		||||
needle='23AB' found '000023AB'
 | 
			
		||||
 
 | 
			
		||||
@@ -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");
 | 
			
		||||
@@ -315,7 +311,7 @@ void test_mgcp_msg(void)
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_mgcp_client_cancel()
 | 
			
		||||
void test_mgcp_client_cancel(void)
 | 
			
		||||
{
 | 
			
		||||
	mgcp_trans_id_t trans_id;
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
@@ -330,8 +326,10 @@ void test_mgcp_client_cancel()
 | 
			
		||||
		.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()
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
@@ -487,7 +485,7 @@ static struct sdp_section_start_test sdp_section_start_tests[] = {
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void test_sdp_section_start()
 | 
			
		||||
void test_sdp_section_start(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	int failures = 0;
 | 
			
		||||
@@ -531,7 +529,7 @@ void test_sdp_section_start()
 | 
			
		||||
	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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										63
									
								
								tests/sdp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								tests/sdp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_builddir)/include \
 | 
			
		||||
	-I$(top_srcdir) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	-ggdb3 \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	-no-install \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	sdp_fmtp_test.ok \
 | 
			
		||||
	sdp_fmtp_test.err \
 | 
			
		||||
	sdp_codec_test.ok \
 | 
			
		||||
	sdp_codec_test.err \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
check_PROGRAMS = \
 | 
			
		||||
	sdp_fmtp_test \
 | 
			
		||||
	sdp_codec_test \
 | 
			
		||||
	sdp_msg_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
sdp_fmtp_test_SOURCES = \
 | 
			
		||||
	sdp_fmtp_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
sdp_fmtp_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
sdp_codec_test_SOURCES = \
 | 
			
		||||
	sdp_codec_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
sdp_codec_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
sdp_msg_test_SOURCES = \
 | 
			
		||||
	sdp_msg_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
sdp_msg_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
update_exp:
 | 
			
		||||
	$(builddir)/sdp_fmtp_test >$(srcdir)/sdp_fmtp_test.ok 2>$(srcdir)/sdp_fmtp_test.err
 | 
			
		||||
	$(builddir)/sdp_codec_test >$(srcdir)/sdp_codec_test.ok 2>$(srcdir)/sdp_codec_test.err
 | 
			
		||||
	$(builddir)/sdp_msg_test >$(srcdir)/sdp_msg_test.ok 2>$(srcdir)/sdp_msg_test.err
 | 
			
		||||
							
								
								
									
										645
									
								
								tests/sdp/sdp_codec_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										645
									
								
								tests/sdp/sdp_codec_test.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,645 @@
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/sdp_codec_list.h>
 | 
			
		||||
#include <osmocom/sdp/fmtp.h>
 | 
			
		||||
 | 
			
		||||
void *test_ctx = NULL;
 | 
			
		||||
 | 
			
		||||
static void report_callback(const void *ptr, int depth, int max_depth, int is_ref, void *priv)
 | 
			
		||||
{
 | 
			
		||||
        const char *name = talloc_get_name(ptr);
 | 
			
		||||
	printf(" |%*s%3zu %s\n", depth, "", talloc_total_blocks(ptr), name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Print a talloc report that is reproducible for test output verification. It contains no pointer addresses. */
 | 
			
		||||
#define report(CTX) _report(CTX, #CTX)
 | 
			
		||||
static void _report(void *ctx, const char *label)
 | 
			
		||||
{
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
	fflush(stderr);
 | 
			
		||||
	printf("%s\n", label);
 | 
			
		||||
        talloc_report_depth_cb(ctx, 0, 100, report_callback, NULL);
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct codec_test {
 | 
			
		||||
	struct osmo_sdp_codec set;
 | 
			
		||||
	int expect_rc;
 | 
			
		||||
	const char *expect_str;
 | 
			
		||||
	bool expect_is_set;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct codec_test codec_tests[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.set = { 23, "encoding-name", 8000, NULL },
 | 
			
		||||
		.expect_str = "encoding-name#23",
 | 
			
		||||
		.expect_is_set = true,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.set = { 112, "AMR", 8000, "octet-align=1;mode-set=0,2,4" },
 | 
			
		||||
		.expect_str = "AMR:octet-align=1;mode-set=0,2,4#112",
 | 
			
		||||
		.expect_is_set = true,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.set = { 96, "AMR", 8000, "mode-set=0,2,4;octet-align=1" },
 | 
			
		||||
		.expect_str = "AMR:mode-set=0,2,4;octet-align=1#96",
 | 
			
		||||
		.expect_is_set = true,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.set = { 114, "AMR", 8000, "mode-set=0,2,4" },
 | 
			
		||||
		.expect_str = "AMR:mode-set=0,2,4#114",
 | 
			
		||||
		.expect_is_set = true,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.set = { 97, "AMR", 8000, "mode-set=0,2,4;octet-align=0" },
 | 
			
		||||
		.expect_str = "AMR:mode-set=0,2,4;octet-align=0#97",
 | 
			
		||||
		.expect_is_set = true,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.set = { 98, "AMR", 8000, "octet-align=1" },
 | 
			
		||||
		.expect_str = "AMR:octet-align=1#98",
 | 
			
		||||
		.expect_is_set = true,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.set = { 96, "AMR-WB", 16000 },
 | 
			
		||||
		.expect_str = "AMR-WB/16000#96",
 | 
			
		||||
		.expect_is_set = true,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.set = { 3, "GSM", 8000 },
 | 
			
		||||
		.expect_str = "GSM#3",
 | 
			
		||||
		.expect_is_set = true,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.set = { },
 | 
			
		||||
		.expect_str = "/0#0",
 | 
			
		||||
		.expect_is_set = false,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.set = { 112, NULL, 8000, "octet-align=1" },
 | 
			
		||||
		.expect_str = ":octet-align=1#112",
 | 
			
		||||
		.expect_is_set = false,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.set = { 112, "", 8000, "octet-align=1" },
 | 
			
		||||
		.expect_str = ":octet-align=1#112",
 | 
			
		||||
		.expect_is_set = false,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void test_codec(void)
 | 
			
		||||
{
 | 
			
		||||
	void *ctx = talloc_named_const(test_ctx, 0, __func__);
 | 
			
		||||
 | 
			
		||||
	struct codec_test *t;
 | 
			
		||||
	struct codec_test *t2;
 | 
			
		||||
 | 
			
		||||
	printf("\n\n--- %s()\n", __func__);
 | 
			
		||||
	printf("- osmo_sdp_codec_set():\n");
 | 
			
		||||
	for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
 | 
			
		||||
		struct osmo_sdp_codec *codec = osmo_sdp_codec_alloc(ctx);
 | 
			
		||||
		char *str;
 | 
			
		||||
		bool is_set;
 | 
			
		||||
 | 
			
		||||
		osmo_sdp_codec_set(codec, t->set.payload_type, t->set.encoding_name, t->set.rate, t->set.fmtp);
 | 
			
		||||
 | 
			
		||||
		str = osmo_sdp_codec_to_str_c(ctx, codec);
 | 
			
		||||
		printf("osmo_sdp_codec_set [%d] '%s'\n", (int)(t - codec_tests), str);
 | 
			
		||||
		if (strcmp(str, t->expect_str))
 | 
			
		||||
			printf("  *** ERROR: expected '%s'\n", t->expect_str);
 | 
			
		||||
 | 
			
		||||
		if (!osmo_sdp_codec_cmp(codec, &t->set, &osmo_sdp_codec_cmp_exact))
 | 
			
		||||
			printf("  osmo_sdp_codec_cmp() ok\n");
 | 
			
		||||
		else
 | 
			
		||||
			printf("  osmo_sdp_codec_cmp() *** ERROR: mismatches original values\n");
 | 
			
		||||
 | 
			
		||||
		is_set = osmo_sdp_codec_is_set(codec);
 | 
			
		||||
		printf("  osmo_sdp_codec_is_set() = %s\n", is_set ? "true" : "false");
 | 
			
		||||
		if (is_set != t->expect_is_set)
 | 
			
		||||
			printf("    *** ERROR: expected is_set = %s\n", t->expect_is_set ? "true" : "false");
 | 
			
		||||
 | 
			
		||||
		if (is_set != osmo_sdp_codec_is_set(&t->set))
 | 
			
		||||
			printf("    *** ERROR: is_set(copy) != is_set(orig)\n");
 | 
			
		||||
 | 
			
		||||
		talloc_free(str);
 | 
			
		||||
		talloc_free(codec);
 | 
			
		||||
		if (talloc_total_blocks(ctx) != 1)
 | 
			
		||||
			printf("  *** ERROR: ctx has %zu items, should be 1\n", talloc_total_blocks(ctx));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printf("\n- osmo_sdp_codec_cmp(equivalent):\n");
 | 
			
		||||
	for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
 | 
			
		||||
		for (t2 = codec_tests; (t2 - codec_tests) < ARRAY_SIZE(codec_tests); t2++) {
 | 
			
		||||
			int cmp = osmo_sdp_codec_cmp(&t->set, &t2->set, &osmo_sdp_codec_cmp_equivalent);
 | 
			
		||||
			int reverse_cmp = osmo_sdp_codec_cmp(&t2->set, &t->set, &osmo_sdp_codec_cmp_equivalent);
 | 
			
		||||
			printf("  %s %s %s %s %s\n",
 | 
			
		||||
			       osmo_sdp_codec_to_str_c(ctx, &t->set),
 | 
			
		||||
			       (cmp == 0) ? "=="
 | 
			
		||||
			       : ((cmp < 0) ? "<" : ">"),
 | 
			
		||||
			       osmo_sdp_codec_to_str_c(ctx, &t2->set),
 | 
			
		||||
			       (reverse_cmp == 0) ? "=="
 | 
			
		||||
			       : ((reverse_cmp < 0) ? "<" : ">"),
 | 
			
		||||
			       osmo_sdp_codec_to_str_c(ctx, &t->set));
 | 
			
		||||
 | 
			
		||||
			if (reverse_cmp != -cmp)
 | 
			
		||||
				printf("    *** ERROR: osmo_sdp_codec_cmp(reverse args) == %d, expected %d\n",
 | 
			
		||||
				       reverse_cmp, -cmp);
 | 
			
		||||
 | 
			
		||||
			talloc_free_children(ctx);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printf("\n- osmo_sdp_codec_from_str():\n");
 | 
			
		||||
	for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
 | 
			
		||||
		struct osmo_sdp_codec *codec = osmo_sdp_codec_alloc(ctx);
 | 
			
		||||
		int rc = osmo_sdp_codec_from_str(codec, t->expect_str, -1);
 | 
			
		||||
		printf("  osmo_sdp_codec_from_str('%s') rc=%d",
 | 
			
		||||
		       t->expect_str, rc);
 | 
			
		||||
		if (!rc) {
 | 
			
		||||
			printf(" res=%s", osmo_sdp_codec_to_str_c(ctx, codec));
 | 
			
		||||
			rc = osmo_sdp_codec_cmp(codec, &t->set, &osmo_sdp_codec_cmp_exact);
 | 
			
		||||
			if (rc)
 | 
			
		||||
				printf(" *** ERROR: osmo_sdp_codec_cmp(res,orig) = %d", rc);
 | 
			
		||||
		}
 | 
			
		||||
		printf("\n");
 | 
			
		||||
		talloc_free_children(ctx);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_codec_list(void)
 | 
			
		||||
{
 | 
			
		||||
	void *list_ctx = talloc_named_const(test_ctx, 0, __func__);
 | 
			
		||||
	void *print_ctx = talloc_named_const(test_ctx, 0, "print");
 | 
			
		||||
	int i;
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct osmo_sdp_codec *codec;
 | 
			
		||||
 | 
			
		||||
	const struct osmo_sdp_codec all_codecs[] = {
 | 
			
		||||
		{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
 | 
			
		||||
		{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
		{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct osmo_sdp_codec_list *codec_list;
 | 
			
		||||
 | 
			
		||||
	printf("\n\n--- %s()\n", __func__);
 | 
			
		||||
 | 
			
		||||
	codec_list = osmo_sdp_codec_list_alloc(list_ctx);
 | 
			
		||||
	printf("osmo_sdp_codec_list_first() = %s\n",
 | 
			
		||||
	       osmo_sdp_codec_to_str_c(print_ctx, osmo_sdp_codec_list_first(codec_list)));
 | 
			
		||||
	report(list_ctx);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
 | 
			
		||||
		struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, false);
 | 
			
		||||
		printf("[%d] osmo_sdp_codec_list_add(%s)\n", i, osmo_sdp_codec_to_str_c(print_ctx, added));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	i = 0;
 | 
			
		||||
	osmo_sdp_codec_list_foreach(codec, codec_list) {
 | 
			
		||||
		printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
 | 
			
		||||
	}
 | 
			
		||||
	printf("osmo_sdp_codec_list_first() = %s\n",
 | 
			
		||||
	       osmo_sdp_codec_to_str_c(print_ctx, osmo_sdp_codec_list_first(codec_list)));
 | 
			
		||||
	report(list_ctx);
 | 
			
		||||
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
 | 
			
		||||
 | 
			
		||||
	printf("\n");
 | 
			
		||||
	printf("- add same entries again with once=exact, nothing should change\n");
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
 | 
			
		||||
		struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i],
 | 
			
		||||
								       &osmo_sdp_codec_cmp_exact, false);
 | 
			
		||||
		printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
 | 
			
		||||
	}
 | 
			
		||||
	i = 0;
 | 
			
		||||
	osmo_sdp_codec_list_foreach(codec, codec_list) {
 | 
			
		||||
		printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
 | 
			
		||||
	}
 | 
			
		||||
	report(list_ctx);
 | 
			
		||||
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
 | 
			
		||||
 | 
			
		||||
	printf("\n");
 | 
			
		||||
	printf("- add same entries again with once=NULL, duplicates are added\n");
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
 | 
			
		||||
		struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, false);
 | 
			
		||||
		printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
 | 
			
		||||
	}
 | 
			
		||||
	i = 0;
 | 
			
		||||
	osmo_sdp_codec_list_foreach(codec, codec_list) {
 | 
			
		||||
		printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
 | 
			
		||||
	}
 | 
			
		||||
	report(list_ctx);
 | 
			
		||||
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
 | 
			
		||||
 | 
			
		||||
	printf("\n");
 | 
			
		||||
	printf("- add same entries again with once=NULL,pick_unused_pt_nr=true, duplicates are added with new #nr\n");
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
 | 
			
		||||
		struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, true);
 | 
			
		||||
		printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
 | 
			
		||||
	}
 | 
			
		||||
	i = 0;
 | 
			
		||||
	osmo_sdp_codec_list_foreach(codec, codec_list) {
 | 
			
		||||
		printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
 | 
			
		||||
	}
 | 
			
		||||
	report(list_ctx);
 | 
			
		||||
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
 | 
			
		||||
 | 
			
		||||
	printf("\n");
 | 
			
		||||
	printf("- remove all 'GSM#3' entries, with osmo_sdp_codec_cmp_exact\n");
 | 
			
		||||
	rc = osmo_sdp_codec_list_remove(codec_list, &all_codecs[1], &osmo_sdp_codec_cmp_exact);
 | 
			
		||||
	printf("  osmo_sdp_codec_list_remove() = %d\n", rc);
 | 
			
		||||
	i = 0;
 | 
			
		||||
	osmo_sdp_codec_list_foreach(codec, codec_list) {
 | 
			
		||||
		printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
 | 
			
		||||
	}
 | 
			
		||||
	report(list_ctx);
 | 
			
		||||
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
 | 
			
		||||
 | 
			
		||||
	printf("- remove all 'GSM' entries, with osmo_sdp_codec_cmp_equivalent\n");
 | 
			
		||||
	rc = osmo_sdp_codec_list_remove(codec_list, &all_codecs[1], &osmo_sdp_codec_cmp_equivalent);
 | 
			
		||||
	printf("  osmo_sdp_codec_list_remove() = %d\n", rc);
 | 
			
		||||
	i = 0;
 | 
			
		||||
	osmo_sdp_codec_list_foreach(codec, codec_list) {
 | 
			
		||||
		printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
 | 
			
		||||
	}
 | 
			
		||||
	report(list_ctx);
 | 
			
		||||
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
 | 
			
		||||
 | 
			
		||||
	rc = osmo_sdp_codec_list_move_to_first(codec_list, &all_codecs[0], &osmo_sdp_codec_cmp_equivalent);
 | 
			
		||||
	printf("- osmo_sdp_codec_list_move_to_first('%s', equivalent) = %d\n",
 | 
			
		||||
	       osmo_sdp_codec_to_str_c(print_ctx, &all_codecs[0]), rc);
 | 
			
		||||
	i = 0;
 | 
			
		||||
	osmo_sdp_codec_list_foreach(codec, codec_list) {
 | 
			
		||||
		printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
 | 
			
		||||
	}
 | 
			
		||||
	report(list_ctx);
 | 
			
		||||
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	printf("- osmo_sdp_codec_list_free_items()\n");
 | 
			
		||||
	osmo_sdp_codec_list_free_items(codec_list);
 | 
			
		||||
	i = 0;
 | 
			
		||||
	osmo_sdp_codec_list_foreach(codec, codec_list) {
 | 
			
		||||
		printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
 | 
			
		||||
	}
 | 
			
		||||
	printf("  %d entries\n", i);
 | 
			
		||||
	report(list_ctx);
 | 
			
		||||
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
 | 
			
		||||
	printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
 | 
			
		||||
	       osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
 | 
			
		||||
 | 
			
		||||
	talloc_free(print_ctx);
 | 
			
		||||
	talloc_free(list_ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct osmo_sdp_codec_list *init_codec_list(void *ctx, const struct osmo_sdp_codec *init_array)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sdp_codec_list *dst = osmo_sdp_codec_list_alloc(ctx);
 | 
			
		||||
	for (; osmo_sdp_codec_is_set(init_array); init_array++)
 | 
			
		||||
		osmo_sdp_codec_list_add(dst, init_array, NULL, false);
 | 
			
		||||
	return dst;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_codec_list_cmp(void)
 | 
			
		||||
{
 | 
			
		||||
	void *ctx = talloc_named_const(test_ctx, 0, __func__);
 | 
			
		||||
	void *print_ctx = talloc_named_const(test_ctx, 0, "print");
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	const struct osmo_sdp_codec codec_a[] = {
 | 
			
		||||
		{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
 | 
			
		||||
		{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
		{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
		{}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const struct osmo_sdp_codec codec_b[][5] = {
 | 
			
		||||
		/* same */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
 | 
			
		||||
			{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		/* different payload_type */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
 | 
			
		||||
			{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		/* AMR fmtp in different order */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "mode-set=0,2,4;octet-align=1" },
 | 
			
		||||
			{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		/* different AMR mode-set */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=7" },
 | 
			
		||||
			{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		/* empty AMR mode-set */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1" },
 | 
			
		||||
			{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		/* different AMR octet-align */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=0;mode-set=0,2,4" },
 | 
			
		||||
			{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		/* omitted AMR octet-align is identical to octet-align=0 */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "mode-set=0,2,4" },
 | 
			
		||||
			{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		/* different order */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		/* one less item */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
 | 
			
		||||
			{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		/* one more item */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
 | 
			
		||||
			{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const struct osmo_sdp_codec_cmp_flags *test_cmpf[] = {
 | 
			
		||||
		&osmo_sdp_codec_cmp_name,
 | 
			
		||||
		&osmo_sdp_codec_cmp_equivalent,
 | 
			
		||||
		&osmo_sdp_codec_cmp_exact,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	printf("\n\n--- %s()\n", __func__);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(codec_b); i++) {
 | 
			
		||||
		struct osmo_sdp_codec_list *list_a = init_codec_list(ctx, codec_a);
 | 
			
		||||
		struct osmo_sdp_codec_list *list_b = init_codec_list(ctx, codec_b[i]);
 | 
			
		||||
		int j;
 | 
			
		||||
 | 
			
		||||
		printf("A = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
 | 
			
		||||
		printf("B = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
 | 
			
		||||
 | 
			
		||||
		for (j = 0; j < ARRAY_SIZE(test_cmpf); j++) {
 | 
			
		||||
			const struct osmo_sdp_codec_cmp_flags *cmpf = test_cmpf[j];
 | 
			
		||||
			int cmp = osmo_sdp_codec_list_cmp(list_a, list_b, cmpf);
 | 
			
		||||
			int reverse_cmp = osmo_sdp_codec_list_cmp(list_b, list_a, cmpf);
 | 
			
		||||
			printf("  cmpf[%d]: payload_type=%s rate=%s fmtp=%d: A %s B %s A\n",
 | 
			
		||||
			       j,
 | 
			
		||||
			       cmpf->payload_type ? "true" : "false",
 | 
			
		||||
			       cmpf->rate ? "true" : "false",
 | 
			
		||||
			       cmpf->fmtp,
 | 
			
		||||
			       (cmp == 0) ? "=="
 | 
			
		||||
				       : ((cmp < 0) ? "<" : ">"),
 | 
			
		||||
			       (reverse_cmp == 0) ? "=="
 | 
			
		||||
				       : ((reverse_cmp < 0) ? "<" : ">"));
 | 
			
		||||
 | 
			
		||||
			if (reverse_cmp != -cmp)
 | 
			
		||||
				printf("    *** ERROR: osmo_sdp_codec_list_cmp(reverse args) == %d, expected %d\n",
 | 
			
		||||
				       reverse_cmp, -cmp);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		talloc_free(list_a);
 | 
			
		||||
		talloc_free(list_b);
 | 
			
		||||
 | 
			
		||||
		if (talloc_total_blocks(ctx) != 1) {
 | 
			
		||||
			printf("ERROR: memleak:\n");
 | 
			
		||||
			report(ctx);
 | 
			
		||||
		}
 | 
			
		||||
		printf("\n");
 | 
			
		||||
 | 
			
		||||
		talloc_free_children(print_ctx);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(print_ctx);
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_codec_list_intersection(void)
 | 
			
		||||
{
 | 
			
		||||
	void *ctx = talloc_named_const(test_ctx, 0, __func__);
 | 
			
		||||
	void *print_ctx = talloc_named_const(test_ctx, 0, "print");
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	const struct osmo_sdp_codec codec_a[] = {
 | 
			
		||||
		{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
 | 
			
		||||
		{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
		{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
		{}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const struct osmo_sdp_codec codec_b[][5] = {
 | 
			
		||||
		/* same */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
 | 
			
		||||
			{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		/* same in different order */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
 | 
			
		||||
			{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		/* two matches */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		/* no match */
 | 
			
		||||
		{
 | 
			
		||||
			{ .payload_type = 97, .encoding_name = "AMR-WB", .rate = 16000 },
 | 
			
		||||
			{ .payload_type = 98, .encoding_name = "FOO", .rate = 8000 },
 | 
			
		||||
			{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	printf("\n\n--- %s()\n", __func__);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(codec_b); i++) {
 | 
			
		||||
		struct osmo_sdp_codec_list *list_a = init_codec_list(ctx, codec_a);
 | 
			
		||||
		struct osmo_sdp_codec_list *list_b = init_codec_list(ctx, codec_b[i]);
 | 
			
		||||
 | 
			
		||||
		printf("A = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
 | 
			
		||||
		printf("B = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
 | 
			
		||||
 | 
			
		||||
		osmo_sdp_codec_list_intersection(list_a, list_b, &osmo_sdp_codec_cmp_equivalent, false);
 | 
			
		||||
		printf("osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)\n = %s\n",
 | 
			
		||||
		       osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
 | 
			
		||||
 | 
			
		||||
		talloc_free(list_a);
 | 
			
		||||
		list_a = init_codec_list(ctx, codec_a);
 | 
			
		||||
 | 
			
		||||
		osmo_sdp_codec_list_intersection(list_a, list_b, &osmo_sdp_codec_cmp_equivalent, true);
 | 
			
		||||
		printf("osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)\n = %s\n",
 | 
			
		||||
		       osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
 | 
			
		||||
 | 
			
		||||
		talloc_free(list_a);
 | 
			
		||||
		list_a = init_codec_list(ctx, codec_a);
 | 
			
		||||
 | 
			
		||||
		osmo_sdp_codec_list_intersection(list_b, list_a, &osmo_sdp_codec_cmp_equivalent, false);
 | 
			
		||||
		printf("osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)\n = %s\n",
 | 
			
		||||
		       osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
 | 
			
		||||
 | 
			
		||||
		talloc_free(list_a);
 | 
			
		||||
		list_a = init_codec_list(ctx, codec_a);
 | 
			
		||||
 | 
			
		||||
		osmo_sdp_codec_list_intersection(list_b, list_a, &osmo_sdp_codec_cmp_equivalent, true);
 | 
			
		||||
		printf("osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)\n = %s\n",
 | 
			
		||||
		       osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
 | 
			
		||||
 | 
			
		||||
		talloc_free(list_a);
 | 
			
		||||
		talloc_free(list_b);
 | 
			
		||||
 | 
			
		||||
		if (talloc_total_blocks(ctx) != 1) {
 | 
			
		||||
			printf("ERROR: memleak:\n");
 | 
			
		||||
			report(ctx);
 | 
			
		||||
		}
 | 
			
		||||
		printf("\n");
 | 
			
		||||
 | 
			
		||||
		talloc_free_children(print_ctx);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(print_ctx);
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct my_obj {
 | 
			
		||||
	struct osmo_sdp_codec *codec;
 | 
			
		||||
	struct osmo_sdp_codec_list *codec_list;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct my_obj *my_obj_alloc(void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct my_obj *o = talloc_zero(ctx, struct my_obj);
 | 
			
		||||
	o->codec_list = osmo_sdp_codec_list_alloc(o);
 | 
			
		||||
	return o;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_obj_members(void)
 | 
			
		||||
{
 | 
			
		||||
	void *ctx = talloc_named_const(test_ctx, 0, __func__);
 | 
			
		||||
	void *print_ctx = talloc_named_const(test_ctx, 0, "print");
 | 
			
		||||
	int i;
 | 
			
		||||
	struct osmo_sdp_codec *codec;
 | 
			
		||||
 | 
			
		||||
	struct my_obj *o;
 | 
			
		||||
 | 
			
		||||
	printf("\n\n--- %s()\n", __func__);
 | 
			
		||||
	o = my_obj_alloc(ctx);
 | 
			
		||||
 | 
			
		||||
	o->codec = osmo_sdp_codec_alloc(o);
 | 
			
		||||
	osmo_sdp_codec_set(o->codec, 96, "AMR", 8000, "octet-align=1");
 | 
			
		||||
 | 
			
		||||
	printf("o->codec = %s\n", osmo_sdp_codec_to_str_c(print_ctx, o->codec));
 | 
			
		||||
	report(ctx);
 | 
			
		||||
 | 
			
		||||
	osmo_sdp_codec_list_add(o->codec_list, o->codec, false, false);
 | 
			
		||||
	osmo_sdp_codec_list_add(o->codec_list, o->codec, false, true);
 | 
			
		||||
	i = 0;
 | 
			
		||||
	osmo_sdp_codec_list_foreach(codec, o->codec_list) {
 | 
			
		||||
		printf("o->codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	report(ctx);
 | 
			
		||||
	printf("talloc_free(o)\n");
 | 
			
		||||
	talloc_free(o);
 | 
			
		||||
	report(ctx);
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
	talloc_free(print_ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef void (*test_func_t)(void);
 | 
			
		||||
test_func_t test_func[] = {
 | 
			
		||||
	test_codec,
 | 
			
		||||
	test_codec_list,
 | 
			
		||||
	test_codec_list_cmp,
 | 
			
		||||
	test_codec_list_intersection,
 | 
			
		||||
	test_obj_members,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	test_ctx = talloc_named_const(NULL, 0, "sdp_codec_test");
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(test_func); i++) {
 | 
			
		||||
 | 
			
		||||
		test_func[i]();
 | 
			
		||||
 | 
			
		||||
		if (talloc_total_blocks(test_ctx) != 1) {
 | 
			
		||||
			talloc_report_full(test_ctx, stderr);
 | 
			
		||||
			printf("ERROR after test %d: memory leak\n", i);
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(test_ctx);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								tests/sdp/sdp_codec_test.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/sdp/sdp_codec_test.err
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										538
									
								
								tests/sdp/sdp_codec_test.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										538
									
								
								tests/sdp/sdp_codec_test.ok
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,538 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- test_codec()
 | 
			
		||||
- osmo_sdp_codec_set():
 | 
			
		||||
osmo_sdp_codec_set [0] 'encoding-name#23'
 | 
			
		||||
  osmo_sdp_codec_cmp() ok
 | 
			
		||||
  osmo_sdp_codec_is_set() = true
 | 
			
		||||
osmo_sdp_codec_set [1] 'AMR:octet-align=1;mode-set=0,2,4#112'
 | 
			
		||||
  osmo_sdp_codec_cmp() ok
 | 
			
		||||
  osmo_sdp_codec_is_set() = true
 | 
			
		||||
osmo_sdp_codec_set [2] 'AMR:mode-set=0,2,4;octet-align=1#96'
 | 
			
		||||
  osmo_sdp_codec_cmp() ok
 | 
			
		||||
  osmo_sdp_codec_is_set() = true
 | 
			
		||||
osmo_sdp_codec_set [3] 'AMR:mode-set=0,2,4#114'
 | 
			
		||||
  osmo_sdp_codec_cmp() ok
 | 
			
		||||
  osmo_sdp_codec_is_set() = true
 | 
			
		||||
osmo_sdp_codec_set [4] 'AMR:mode-set=0,2,4;octet-align=0#97'
 | 
			
		||||
  osmo_sdp_codec_cmp() ok
 | 
			
		||||
  osmo_sdp_codec_is_set() = true
 | 
			
		||||
osmo_sdp_codec_set [5] 'AMR:octet-align=1#98'
 | 
			
		||||
  osmo_sdp_codec_cmp() ok
 | 
			
		||||
  osmo_sdp_codec_is_set() = true
 | 
			
		||||
osmo_sdp_codec_set [6] 'AMR-WB/16000#96'
 | 
			
		||||
  osmo_sdp_codec_cmp() ok
 | 
			
		||||
  osmo_sdp_codec_is_set() = true
 | 
			
		||||
osmo_sdp_codec_set [7] 'GSM#3'
 | 
			
		||||
  osmo_sdp_codec_cmp() ok
 | 
			
		||||
  osmo_sdp_codec_is_set() = true
 | 
			
		||||
osmo_sdp_codec_set [8] '/0#0'
 | 
			
		||||
  osmo_sdp_codec_cmp() ok
 | 
			
		||||
  osmo_sdp_codec_is_set() = false
 | 
			
		||||
osmo_sdp_codec_set [9] ':octet-align=1#112'
 | 
			
		||||
  osmo_sdp_codec_cmp() ok
 | 
			
		||||
  osmo_sdp_codec_is_set() = false
 | 
			
		||||
osmo_sdp_codec_set [10] ':octet-align=1#112'
 | 
			
		||||
  osmo_sdp_codec_cmp() ok
 | 
			
		||||
  osmo_sdp_codec_is_set() = false
 | 
			
		||||
 | 
			
		||||
- osmo_sdp_codec_cmp(equivalent):
 | 
			
		||||
  encoding-name#23 == encoding-name#23 == encoding-name#23
 | 
			
		||||
  encoding-name#23 > AMR:octet-align=1;mode-set=0,2,4#112 < encoding-name#23
 | 
			
		||||
  encoding-name#23 > AMR:mode-set=0,2,4;octet-align=1#96 < encoding-name#23
 | 
			
		||||
  encoding-name#23 > AMR:mode-set=0,2,4#114 < encoding-name#23
 | 
			
		||||
  encoding-name#23 > AMR:mode-set=0,2,4;octet-align=0#97 < encoding-name#23
 | 
			
		||||
  encoding-name#23 > AMR:octet-align=1#98 < encoding-name#23
 | 
			
		||||
  encoding-name#23 > AMR-WB/16000#96 < encoding-name#23
 | 
			
		||||
  encoding-name#23 > GSM#3 < encoding-name#23
 | 
			
		||||
  encoding-name#23 > /0#0 < encoding-name#23
 | 
			
		||||
  encoding-name#23 > :octet-align=1#112 < encoding-name#23
 | 
			
		||||
  encoding-name#23 > :octet-align=1#112 < encoding-name#23
 | 
			
		||||
  AMR:octet-align=1;mode-set=0,2,4#112 < encoding-name#23 > AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  AMR:octet-align=1;mode-set=0,2,4#112 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4#114 < AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1#98 == AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  AMR:octet-align=1;mode-set=0,2,4#112 < AMR-WB/16000#96 > AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  AMR:octet-align=1;mode-set=0,2,4#112 < GSM#3 > AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  AMR:octet-align=1;mode-set=0,2,4#112 > /0#0 < AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=1#96 < encoding-name#23 > AMR:mode-set=0,2,4;octet-align=1#96
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:mode-set=0,2,4;octet-align=1#96
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=1#96 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:mode-set=0,2,4;octet-align=1#96
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4#114 < AMR:mode-set=0,2,4;octet-align=1#96
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:mode-set=0,2,4;octet-align=1#96
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1#98 == AMR:mode-set=0,2,4;octet-align=1#96
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=1#96 < AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=1#96
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=1#96 < GSM#3 > AMR:mode-set=0,2,4;octet-align=1#96
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=1#96 > /0#0 < AMR:mode-set=0,2,4;octet-align=1#96
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96
 | 
			
		||||
  AMR:mode-set=0,2,4#114 < encoding-name#23 > AMR:mode-set=0,2,4#114
 | 
			
		||||
  AMR:mode-set=0,2,4#114 < AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4#114
 | 
			
		||||
  AMR:mode-set=0,2,4#114 < AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4#114
 | 
			
		||||
  AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4#114
 | 
			
		||||
  AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4#114
 | 
			
		||||
  AMR:mode-set=0,2,4#114 < AMR:octet-align=1#98 > AMR:mode-set=0,2,4#114
 | 
			
		||||
  AMR:mode-set=0,2,4#114 < AMR-WB/16000#96 > AMR:mode-set=0,2,4#114
 | 
			
		||||
  AMR:mode-set=0,2,4#114 < GSM#3 > AMR:mode-set=0,2,4#114
 | 
			
		||||
  AMR:mode-set=0,2,4#114 > /0#0 < AMR:mode-set=0,2,4#114
 | 
			
		||||
  AMR:mode-set=0,2,4#114 > :octet-align=1#112 < AMR:mode-set=0,2,4#114
 | 
			
		||||
  AMR:mode-set=0,2,4#114 > :octet-align=1#112 < AMR:mode-set=0,2,4#114
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=0#97 < encoding-name#23 > AMR:mode-set=0,2,4;octet-align=0#97
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4;octet-align=0#97
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=0#97 < AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4;octet-align=0#97
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4;octet-align=0#97
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4;octet-align=0#97
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1#98 > AMR:mode-set=0,2,4;octet-align=0#97
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=0#97 < AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=0#97
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=0#97 < GSM#3 > AMR:mode-set=0,2,4;octet-align=0#97
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=0#97 > /0#0 < AMR:mode-set=0,2,4;octet-align=0#97
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97
 | 
			
		||||
  AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97
 | 
			
		||||
  AMR:octet-align=1#98 < encoding-name#23 > AMR:octet-align=1#98
 | 
			
		||||
  AMR:octet-align=1#98 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1#98
 | 
			
		||||
  AMR:octet-align=1#98 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1#98
 | 
			
		||||
  AMR:octet-align=1#98 > AMR:mode-set=0,2,4#114 < AMR:octet-align=1#98
 | 
			
		||||
  AMR:octet-align=1#98 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1#98
 | 
			
		||||
  AMR:octet-align=1#98 == AMR:octet-align=1#98 == AMR:octet-align=1#98
 | 
			
		||||
  AMR:octet-align=1#98 < AMR-WB/16000#96 > AMR:octet-align=1#98
 | 
			
		||||
  AMR:octet-align=1#98 < GSM#3 > AMR:octet-align=1#98
 | 
			
		||||
  AMR:octet-align=1#98 > /0#0 < AMR:octet-align=1#98
 | 
			
		||||
  AMR:octet-align=1#98 > :octet-align=1#112 < AMR:octet-align=1#98
 | 
			
		||||
  AMR:octet-align=1#98 > :octet-align=1#112 < AMR:octet-align=1#98
 | 
			
		||||
  AMR-WB/16000#96 < encoding-name#23 > AMR-WB/16000#96
 | 
			
		||||
  AMR-WB/16000#96 > AMR:octet-align=1;mode-set=0,2,4#112 < AMR-WB/16000#96
 | 
			
		||||
  AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=1#96 < AMR-WB/16000#96
 | 
			
		||||
  AMR-WB/16000#96 > AMR:mode-set=0,2,4#114 < AMR-WB/16000#96
 | 
			
		||||
  AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR-WB/16000#96
 | 
			
		||||
  AMR-WB/16000#96 > AMR:octet-align=1#98 < AMR-WB/16000#96
 | 
			
		||||
  AMR-WB/16000#96 == AMR-WB/16000#96 == AMR-WB/16000#96
 | 
			
		||||
  AMR-WB/16000#96 < GSM#3 > AMR-WB/16000#96
 | 
			
		||||
  AMR-WB/16000#96 > /0#0 < AMR-WB/16000#96
 | 
			
		||||
  AMR-WB/16000#96 > :octet-align=1#112 < AMR-WB/16000#96
 | 
			
		||||
  AMR-WB/16000#96 > :octet-align=1#112 < AMR-WB/16000#96
 | 
			
		||||
  GSM#3 < encoding-name#23 > GSM#3
 | 
			
		||||
  GSM#3 > AMR:octet-align=1;mode-set=0,2,4#112 < GSM#3
 | 
			
		||||
  GSM#3 > AMR:mode-set=0,2,4;octet-align=1#96 < GSM#3
 | 
			
		||||
  GSM#3 > AMR:mode-set=0,2,4#114 < GSM#3
 | 
			
		||||
  GSM#3 > AMR:mode-set=0,2,4;octet-align=0#97 < GSM#3
 | 
			
		||||
  GSM#3 > AMR:octet-align=1#98 < GSM#3
 | 
			
		||||
  GSM#3 > AMR-WB/16000#96 < GSM#3
 | 
			
		||||
  GSM#3 == GSM#3 == GSM#3
 | 
			
		||||
  GSM#3 > /0#0 < GSM#3
 | 
			
		||||
  GSM#3 > :octet-align=1#112 < GSM#3
 | 
			
		||||
  GSM#3 > :octet-align=1#112 < GSM#3
 | 
			
		||||
  /0#0 < encoding-name#23 > /0#0
 | 
			
		||||
  /0#0 < AMR:octet-align=1;mode-set=0,2,4#112 > /0#0
 | 
			
		||||
  /0#0 < AMR:mode-set=0,2,4;octet-align=1#96 > /0#0
 | 
			
		||||
  /0#0 < AMR:mode-set=0,2,4#114 > /0#0
 | 
			
		||||
  /0#0 < AMR:mode-set=0,2,4;octet-align=0#97 > /0#0
 | 
			
		||||
  /0#0 < AMR:octet-align=1#98 > /0#0
 | 
			
		||||
  /0#0 < AMR-WB/16000#96 > /0#0
 | 
			
		||||
  /0#0 < GSM#3 > /0#0
 | 
			
		||||
  /0#0 == /0#0 == /0#0
 | 
			
		||||
  /0#0 < :octet-align=1#112 > /0#0
 | 
			
		||||
  /0#0 < :octet-align=1#112 > /0#0
 | 
			
		||||
  :octet-align=1#112 < encoding-name#23 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < AMR:mode-set=0,2,4#114 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < AMR:octet-align=1#98 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < AMR-WB/16000#96 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < GSM#3 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 > /0#0 < :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < encoding-name#23 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < AMR:mode-set=0,2,4#114 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < AMR:octet-align=1#98 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < AMR-WB/16000#96 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 < GSM#3 > :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 > /0#0 < :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
 | 
			
		||||
  :octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
 | 
			
		||||
 | 
			
		||||
- osmo_sdp_codec_from_str():
 | 
			
		||||
  osmo_sdp_codec_from_str('encoding-name#23') rc=0 res=encoding-name#23
 | 
			
		||||
  osmo_sdp_codec_from_str('AMR:octet-align=1;mode-set=0,2,4#112') rc=0 res=AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  osmo_sdp_codec_from_str('AMR:mode-set=0,2,4;octet-align=1#96') rc=0 res=AMR:mode-set=0,2,4;octet-align=1#96
 | 
			
		||||
  osmo_sdp_codec_from_str('AMR:mode-set=0,2,4#114') rc=0 res=AMR:mode-set=0,2,4#114
 | 
			
		||||
  osmo_sdp_codec_from_str('AMR:mode-set=0,2,4;octet-align=0#97') rc=0 res=AMR:mode-set=0,2,4;octet-align=0#97
 | 
			
		||||
  osmo_sdp_codec_from_str('AMR:octet-align=1#98') rc=0 res=AMR:octet-align=1#98
 | 
			
		||||
  osmo_sdp_codec_from_str('AMR-WB/16000#96') rc=0 res=AMR-WB/16000#96
 | 
			
		||||
  osmo_sdp_codec_from_str('GSM#3') rc=0 res=GSM#3
 | 
			
		||||
  osmo_sdp_codec_from_str('/0#0') rc=0 res=/0#0
 | 
			
		||||
  osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112
 | 
			
		||||
  osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- test_codec_list()
 | 
			
		||||
osmo_sdp_codec_list_first() = NULL
 | 
			
		||||
list_ctx
 | 
			
		||||
 |  2 test_codec_list
 | 
			
		||||
 |   1 struct osmo_sdp_codec_list
 | 
			
		||||
[0] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
 | 
			
		||||
[1] osmo_sdp_codec_list_add(GSM#3)
 | 
			
		||||
[2] osmo_sdp_codec_list_add(GSM-HR-08#111)
 | 
			
		||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
codec_list[1] = GSM#3
 | 
			
		||||
codec_list[2] = GSM-HR-08#111
 | 
			
		||||
osmo_sdp_codec_list_first() = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
list_ctx
 | 
			
		||||
 |  9 test_codec_list
 | 
			
		||||
 |   8 struct osmo_sdp_codec_list
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=true):
 | 
			
		||||
 'AMR#112 GSM#3 GSM-HR-08#111'
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=false):
 | 
			
		||||
 'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
 | 
			
		||||
 | 
			
		||||
- add same entries again with once=exact, nothing should change
 | 
			
		||||
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
 | 
			
		||||
[] osmo_sdp_codec_list_add(GSM#3)
 | 
			
		||||
[] osmo_sdp_codec_list_add(GSM-HR-08#111)
 | 
			
		||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
codec_list[1] = GSM#3
 | 
			
		||||
codec_list[2] = GSM-HR-08#111
 | 
			
		||||
list_ctx
 | 
			
		||||
 |  9 test_codec_list
 | 
			
		||||
 |   8 struct osmo_sdp_codec_list
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=true):
 | 
			
		||||
 'AMR#112 GSM#3 GSM-HR-08#111'
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=false):
 | 
			
		||||
 'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
 | 
			
		||||
 | 
			
		||||
- add same entries again with once=NULL, duplicates are added
 | 
			
		||||
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
 | 
			
		||||
[] osmo_sdp_codec_list_add(GSM#3)
 | 
			
		||||
[] osmo_sdp_codec_list_add(GSM-HR-08#111)
 | 
			
		||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
codec_list[1] = GSM#3
 | 
			
		||||
codec_list[2] = GSM-HR-08#111
 | 
			
		||||
codec_list[3] = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
codec_list[4] = GSM#3
 | 
			
		||||
codec_list[5] = GSM-HR-08#111
 | 
			
		||||
list_ctx
 | 
			
		||||
 | 16 test_codec_list
 | 
			
		||||
 |  15 struct osmo_sdp_codec_list
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=true):
 | 
			
		||||
 '2*AMR#112 2*GSM#3 2*GSM-HR-08#111'
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=false):
 | 
			
		||||
 'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
 | 
			
		||||
 | 
			
		||||
- add same entries again with once=NULL,pick_unused_pt_nr=true, duplicates are added with new #nr
 | 
			
		||||
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#96)
 | 
			
		||||
[] osmo_sdp_codec_list_add(GSM#97)
 | 
			
		||||
[] osmo_sdp_codec_list_add(GSM-HR-08#98)
 | 
			
		||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
codec_list[1] = GSM#3
 | 
			
		||||
codec_list[2] = GSM-HR-08#111
 | 
			
		||||
codec_list[3] = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
codec_list[4] = GSM#3
 | 
			
		||||
codec_list[5] = GSM-HR-08#111
 | 
			
		||||
codec_list[6] = AMR:octet-align=1;mode-set=0,2,4#96
 | 
			
		||||
codec_list[7] = GSM#97
 | 
			
		||||
codec_list[8] = GSM-HR-08#98
 | 
			
		||||
list_ctx
 | 
			
		||||
 | 23 test_codec_list
 | 
			
		||||
 |  22 struct osmo_sdp_codec_list
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=true):
 | 
			
		||||
 '3*AMR 3*GSM 3*GSM-HR-08'
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=false):
 | 
			
		||||
 'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98'
 | 
			
		||||
 | 
			
		||||
- remove all 'GSM#3' entries, with osmo_sdp_codec_cmp_exact
 | 
			
		||||
  osmo_sdp_codec_list_remove() = 2
 | 
			
		||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
codec_list[1] = GSM-HR-08#111
 | 
			
		||||
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
codec_list[3] = GSM-HR-08#111
 | 
			
		||||
codec_list[4] = AMR:octet-align=1;mode-set=0,2,4#96
 | 
			
		||||
codec_list[5] = GSM#97
 | 
			
		||||
codec_list[6] = GSM-HR-08#98
 | 
			
		||||
list_ctx
 | 
			
		||||
 | 19 test_codec_list
 | 
			
		||||
 |  18 struct osmo_sdp_codec_list
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=true):
 | 
			
		||||
 '3*AMR 3*GSM-HR-08 GSM#97'
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=false):
 | 
			
		||||
 'AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98'
 | 
			
		||||
- remove all 'GSM' entries, with osmo_sdp_codec_cmp_equivalent
 | 
			
		||||
  osmo_sdp_codec_list_remove() = 1
 | 
			
		||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
codec_list[1] = GSM-HR-08#111
 | 
			
		||||
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
codec_list[3] = GSM-HR-08#111
 | 
			
		||||
codec_list[4] = AMR:octet-align=1;mode-set=0,2,4#96
 | 
			
		||||
codec_list[5] = GSM-HR-08#98
 | 
			
		||||
list_ctx
 | 
			
		||||
 | 17 test_codec_list
 | 
			
		||||
 |  16 struct osmo_sdp_codec_list
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=true):
 | 
			
		||||
 '3*AMR 3*GSM-HR-08'
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=false):
 | 
			
		||||
 'AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM-HR-08#98'
 | 
			
		||||
- osmo_sdp_codec_list_move_to_first('AMR:octet-align=1;mode-set=0,2,4#112', equivalent) = 3
 | 
			
		||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
codec_list[1] = AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#96
 | 
			
		||||
codec_list[3] = GSM-HR-08#111
 | 
			
		||||
codec_list[4] = GSM-HR-08#111
 | 
			
		||||
codec_list[5] = GSM-HR-08#98
 | 
			
		||||
list_ctx
 | 
			
		||||
 | 17 test_codec_list
 | 
			
		||||
 |  16 struct osmo_sdp_codec_list
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
 |    2 struct osmo_sdp_codec
 | 
			
		||||
 |     1 GSM-HR-08
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1;mode-set=0,2,4
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=true):
 | 
			
		||||
 '3*AMR 3*GSM-HR-08'
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=false):
 | 
			
		||||
 'AMR:octet-align=1;mode-set=0,2,4#112 AMR:octet-align=1;mode-set=0,2,4#112 AMR:octet-align=1;mode-set=0,2,4#96 GSM-HR-08#111 GSM-HR-08#111 GSM-HR-08#98'
 | 
			
		||||
- osmo_sdp_codec_list_free_items()
 | 
			
		||||
  0 entries
 | 
			
		||||
list_ctx
 | 
			
		||||
 |  2 test_codec_list
 | 
			
		||||
 |   1 struct osmo_sdp_codec_list
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=true):
 | 
			
		||||
 '(no-codecs)'
 | 
			
		||||
osmo_sdp_codec_list_to_str_c(summarize=false):
 | 
			
		||||
 '(no-codecs)'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- test_codec_list_cmp()
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
  cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
 | 
			
		||||
  cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
 | 
			
		||||
  cmpf[2]: payload_type=true rate=true fmtp=2: A == B == A
 | 
			
		||||
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = AMR:octet-align=1;mode-set=0,2,4#96 GSM#3 GSM-HR-08#111
 | 
			
		||||
  cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
 | 
			
		||||
  cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
 | 
			
		||||
  cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
 | 
			
		||||
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = AMR:mode-set=0,2,4;octet-align=1#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
  cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
 | 
			
		||||
  cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
 | 
			
		||||
  cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
 | 
			
		||||
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = AMR:octet-align=1;mode-set=7#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
  cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
 | 
			
		||||
  cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
 | 
			
		||||
  cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
 | 
			
		||||
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = AMR:octet-align=1#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
  cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
 | 
			
		||||
  cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
 | 
			
		||||
  cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
 | 
			
		||||
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = AMR:octet-align=0;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
  cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
 | 
			
		||||
  cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
 | 
			
		||||
  cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
 | 
			
		||||
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = AMR:mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
  cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
 | 
			
		||||
  cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
 | 
			
		||||
  cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
 | 
			
		||||
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112
 | 
			
		||||
  cmpf[0]: payload_type=false rate=false fmtp=0: A < B > A
 | 
			
		||||
  cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
 | 
			
		||||
  cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
 | 
			
		||||
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3
 | 
			
		||||
  cmpf[0]: payload_type=false rate=false fmtp=0: A > B < A
 | 
			
		||||
  cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
 | 
			
		||||
  cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
 | 
			
		||||
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 GSM-EFR#110
 | 
			
		||||
  cmpf[0]: payload_type=false rate=false fmtp=0: A < B > A
 | 
			
		||||
  cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
 | 
			
		||||
  cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- test_codec_list_intersection()
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
 | 
			
		||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
 | 
			
		||||
 = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
 | 
			
		||||
 = AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
 | 
			
		||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
 | 
			
		||||
 = AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
 | 
			
		||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
 | 
			
		||||
 = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = GSM-HR-08#98 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97
 | 
			
		||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
 | 
			
		||||
 = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
 | 
			
		||||
 = AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
 | 
			
		||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
 | 
			
		||||
 = GSM-HR-08#98 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97
 | 
			
		||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
 | 
			
		||||
 = GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3
 | 
			
		||||
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = GSM-HR-08#98 GSM-EFR#110 GSM#97
 | 
			
		||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
 | 
			
		||||
 = GSM#3 GSM-HR-08#111
 | 
			
		||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
 | 
			
		||||
 = GSM#97 GSM-HR-08#98
 | 
			
		||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
 | 
			
		||||
 = GSM-HR-08#98 GSM#97
 | 
			
		||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
 | 
			
		||||
 = GSM-HR-08#111 GSM#3
 | 
			
		||||
 | 
			
		||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
 | 
			
		||||
B = AMR-WB/16000#97 FOO#98 GSM-EFR#110
 | 
			
		||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
 | 
			
		||||
 = (no-codecs)
 | 
			
		||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
 | 
			
		||||
 = (no-codecs)
 | 
			
		||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
 | 
			
		||||
 = (no-codecs)
 | 
			
		||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
 | 
			
		||||
 = (no-codecs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- test_obj_members()
 | 
			
		||||
o->codec = AMR:octet-align=1#96
 | 
			
		||||
ctx
 | 
			
		||||
 |  6 test_obj_members
 | 
			
		||||
 |   5 struct my_obj
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
 |    1 struct osmo_sdp_codec_list
 | 
			
		||||
o->codec_list[0] = AMR:octet-align=1#96
 | 
			
		||||
o->codec_list[1] = AMR:octet-align=1#97
 | 
			
		||||
ctx
 | 
			
		||||
 | 12 test_obj_members
 | 
			
		||||
 |  11 struct my_obj
 | 
			
		||||
 |    3 struct osmo_sdp_codec
 | 
			
		||||
 |     1 octet-align=1
 | 
			
		||||
 |     1 AMR
 | 
			
		||||
 |    7 struct osmo_sdp_codec_list
 | 
			
		||||
 |     3 struct osmo_sdp_codec
 | 
			
		||||
 |      1 octet-align=1
 | 
			
		||||
 |      1 AMR
 | 
			
		||||
 |     3 struct osmo_sdp_codec
 | 
			
		||||
 |      1 octet-align=1
 | 
			
		||||
 |      1 AMR
 | 
			
		||||
talloc_free(o)
 | 
			
		||||
ctx
 | 
			
		||||
 |  1 test_obj_members
 | 
			
		||||
							
								
								
									
										126
									
								
								tests/sdp/sdp_fmtp_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								tests/sdp/sdp_fmtp_test.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/fmtp.h>
 | 
			
		||||
 | 
			
		||||
struct get_val_test {
 | 
			
		||||
	const char *fmtp_string;
 | 
			
		||||
	const char *val_name;
 | 
			
		||||
	bool expect_rc;
 | 
			
		||||
	const char *expect_val;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct get_val_test get_val_tests[] = {
 | 
			
		||||
	{
 | 
			
		||||
		"foo=123;bar=success;baz=456", "foo",
 | 
			
		||||
		true, "123"
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"foo=123;bar=success;baz=456", "bar",
 | 
			
		||||
		true, "success"
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"foo=123;bar=success;baz=456", "baz",
 | 
			
		||||
		true, "456"
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void test_get_val(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	printf("\n--- %s()\n", __func__);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(get_val_tests); i++) {
 | 
			
		||||
		const struct get_val_test *t = &get_val_tests[i];
 | 
			
		||||
		char val[128] = {};
 | 
			
		||||
		bool rc = osmo_sdp_fmtp_get_val(val, sizeof(val), t->fmtp_string, t->val_name);
 | 
			
		||||
		bool ok;
 | 
			
		||||
		printf("osmo_sdp_fmtp_get_val('%s', '%s') rc=%s",
 | 
			
		||||
		       t->fmtp_string, t->val_name,
 | 
			
		||||
		       rc ? "true" : "false");
 | 
			
		||||
		if (rc)
 | 
			
		||||
			printf(" val='%s'", val);
 | 
			
		||||
		ok = true;
 | 
			
		||||
		if (rc != t->expect_rc) {
 | 
			
		||||
			printf(" ERROR: expected rc=%s", t->expect_rc ? "true" : "false");
 | 
			
		||||
			ok = false;
 | 
			
		||||
		}
 | 
			
		||||
		if (t->expect_val && strcmp(val, t->expect_val)) {
 | 
			
		||||
			printf(" ERROR: expected val='%s'", t->expect_val);
 | 
			
		||||
			ok = false;
 | 
			
		||||
		}
 | 
			
		||||
		if (ok)
 | 
			
		||||
			printf(" ok");
 | 
			
		||||
		printf("\n");
 | 
			
		||||
	}
 | 
			
		||||
	printf("\n--- %s() DONE\n", __func__);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct get_int_test {
 | 
			
		||||
	const char *fmtp_string;
 | 
			
		||||
	const char *val_name;
 | 
			
		||||
	int64_t defval;
 | 
			
		||||
	int64_t expect_rc;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct get_int_test get_int_tests[] = {
 | 
			
		||||
	{
 | 
			
		||||
		"foo=123;bar=success;baz=456", "foo", -1,
 | 
			
		||||
		123
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"foo=123;bar=success;baz=456", "bar", -1,
 | 
			
		||||
		-1
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"foo=123;bar=success;baz=456", "baz", -1,
 | 
			
		||||
		456
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void test_get_int(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	printf("\n--- %s()\n", __func__);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(get_int_tests); i++) {
 | 
			
		||||
		const struct get_int_test *t = &get_int_tests[i];
 | 
			
		||||
		int64_t rc = osmo_sdp_fmtp_get_int(t->fmtp_string, t->val_name, t->defval);
 | 
			
		||||
		printf("osmo_sdp_fmtp_get_int('%s', '%s') rc=%"PRId64,
 | 
			
		||||
		       t->fmtp_string, t->val_name, rc);
 | 
			
		||||
		if (rc != t->expect_rc) {
 | 
			
		||||
			printf(" ERROR: expected rc=%"PRId64, t->expect_rc);
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			printf(" ok");
 | 
			
		||||
		}
 | 
			
		||||
		printf("\n");
 | 
			
		||||
	}
 | 
			
		||||
	printf("\n--- %s() DONE\n", __func__);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct log_info_cat log_categories[] = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct log_info log_info = {
 | 
			
		||||
        .cat = log_categories,
 | 
			
		||||
        .num_cat = ARRAY_SIZE(log_categories),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(void)
 | 
			
		||||
{
 | 
			
		||||
	void *ctx = talloc_named_const(NULL, 1, "sdp_fmtp_test");
 | 
			
		||||
 | 
			
		||||
	osmo_init_logging2(ctx, &log_info);
 | 
			
		||||
	log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
 | 
			
		||||
	log_set_print_timestamp(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_use_color(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category_hex(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category(osmo_stderr_target, 1);
 | 
			
		||||
 | 
			
		||||
	test_get_val();
 | 
			
		||||
	test_get_int();
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								tests/sdp/sdp_fmtp_test.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/sdp/sdp_fmtp_test.err
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										14
									
								
								tests/sdp/sdp_fmtp_test.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/sdp/sdp_fmtp_test.ok
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
 | 
			
		||||
--- test_get_val()
 | 
			
		||||
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'foo') rc=true val='123' ok
 | 
			
		||||
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'bar') rc=true val='success' ok
 | 
			
		||||
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'baz') rc=true val='456' ok
 | 
			
		||||
 | 
			
		||||
--- test_get_val() DONE
 | 
			
		||||
 | 
			
		||||
--- test_get_int()
 | 
			
		||||
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'foo') rc=123 ok
 | 
			
		||||
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'bar') rc=-1 ok
 | 
			
		||||
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'baz') rc=456 ok
 | 
			
		||||
 | 
			
		||||
--- test_get_int() DONE
 | 
			
		||||
							
								
								
									
										769
									
								
								tests/sdp/sdp_msg_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										769
									
								
								tests/sdp/sdp_msg_test.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,769 @@
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/sdp_msg.h>
 | 
			
		||||
 | 
			
		||||
void *test_ctx = NULL;
 | 
			
		||||
 | 
			
		||||
static void report_callback(const void *ptr, int depth, int max_depth, int is_ref, void *priv)
 | 
			
		||||
{
 | 
			
		||||
        const char *name = talloc_get_name(ptr);
 | 
			
		||||
	printf(" |%*s%3zu %s\n", depth, "", talloc_total_blocks(ptr), name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Print a talloc report that is reproducible for test output verification. It contains no pointer addresses. */
 | 
			
		||||
#define report(CTX) _report(CTX, #CTX)
 | 
			
		||||
static void _report(void *ctx, const char *label)
 | 
			
		||||
{
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
	fflush(stderr);
 | 
			
		||||
	printf("%s\n", label);
 | 
			
		||||
        talloc_report_depth_cb(ctx, 0, 100, report_callback, NULL);
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void dump_sdp(const char *str, const char *prefix)
 | 
			
		||||
{
 | 
			
		||||
	while (str && *str) {
 | 
			
		||||
		const char *line_end = str;
 | 
			
		||||
		while (*line_end && *line_end != '\r' && *line_end != '\n')
 | 
			
		||||
			line_end++;
 | 
			
		||||
		while (*line_end == '\r' || *line_end == '\n')
 | 
			
		||||
			line_end++;
 | 
			
		||||
		printf("%s%s\n", prefix, osmo_escape_str(str, line_end - str));
 | 
			
		||||
		str = line_end;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct sdp_msg_test_data {
 | 
			
		||||
	const char *sdp_input;
 | 
			
		||||
	const char *expect_sdp_str;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct sdp_msg_test_data sdp_msg_tests[] = {
 | 
			
		||||
	{
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=- 5628250 5628250 IN IP4 192.168.11.121\r\n"
 | 
			
		||||
		"s=-\r\n"
 | 
			
		||||
		"c=IN IP4 192.168.11.121\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 10020 RTP/AVP 18 0 2 4 8 96 97 98 100 101\r\n"
 | 
			
		||||
		"a=rtpmap:18 G729/8000\r\n"
 | 
			
		||||
		"a=rtpmap:0 PCMU/8000\r\n"
 | 
			
		||||
		"a=rtpmap:2 G726-32/8000\r\n"
 | 
			
		||||
		"a=rtpmap:4 G723/8000\r\n"
 | 
			
		||||
		"a=rtpmap:8 PCMA/8000\r\n"
 | 
			
		||||
		"a=rtpmap:96 G726-40/8000\r\n"
 | 
			
		||||
		"a=rtpmap:97 G726-24/8000\r\n"
 | 
			
		||||
		"a=rtpmap:98 G726-16/8000\r\n"
 | 
			
		||||
		"a=rtpmap:100 NSE/8000\r\n"
 | 
			
		||||
		"a=fmtp:100 192-193\r\n"
 | 
			
		||||
		"a=rtpmap:101 telephone-event/8000\r\n"
 | 
			
		||||
		"a=fmtp:101 0-15\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		"a=sendrecv\r\n"
 | 
			
		||||
		,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
 | 
			
		||||
		"s=FooBar\r\n"
 | 
			
		||||
		"c=IN IP4 192.168.11.151\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 16398 RTP/AVP 98\r\n"
 | 
			
		||||
		"a=rtpmap:98 AMR/8000\r\n"
 | 
			
		||||
		"a=fmtp:98 octet-align=1; mode-set=4\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		"a=rtcp:16399 IN IP4 192.168.11.151\r\n"
 | 
			
		||||
		,
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
 | 
			
		||||
		"s=FooBar\r\n"
 | 
			
		||||
		"c=IN IP4 192.168.11.151\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 16398 RTP/AVP 98\r\n"
 | 
			
		||||
		"a=rtpmap:98 AMR/8000\r\n"
 | 
			
		||||
		"a=fmtp:98 octet-align=1; mode-set=4\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		/* The rtcp line is dropped, not supported yet */
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
 | 
			
		||||
		"s=FooBar\r\n"
 | 
			
		||||
		"c=IN IP4 192.168.11.140\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
 | 
			
		||||
		"a=rtpmap:18 G729/8000\r\n"
 | 
			
		||||
		"a=rtpmap:0 PCMU/8000\r\n"
 | 
			
		||||
		"a=rtpmap:4 G723/8000\r\n"
 | 
			
		||||
		"a=rtpmap:8 PCMA/8000\r\n"
 | 
			
		||||
		"a=rtpmap:101 telephone-event/8000\r\n"
 | 
			
		||||
		"a=fmtp:101 0-15\r\n"
 | 
			
		||||
		"a=sendrecv\r\n"
 | 
			
		||||
		"a=rtcp:30437\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		,
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
 | 
			
		||||
		"s=FooBar\r\n"
 | 
			
		||||
		"c=IN IP4 192.168.11.140\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
 | 
			
		||||
		"a=rtpmap:18 G729/8000\r\n"
 | 
			
		||||
		"a=rtpmap:0 PCMU/8000\r\n"
 | 
			
		||||
		"a=rtpmap:4 G723/8000\r\n"
 | 
			
		||||
		"a=rtpmap:8 PCMA/8000\r\n"
 | 
			
		||||
		"a=rtpmap:101 telephone-event/8000\r\n"
 | 
			
		||||
		"a=fmtp:101 0-15\r\n"
 | 
			
		||||
		/* a=sendrecv ends up further below */
 | 
			
		||||
		/* The rtcp line is dropped, not supported yet */
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		"a=sendrecv\r\n"
 | 
			
		||||
		,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
 | 
			
		||||
		"s=FooBar\r\n"
 | 
			
		||||
		"c=IN IP4 192.168.11.140\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
 | 
			
		||||
		"a=rtpmap:18 G729/8000\r\n"
 | 
			
		||||
		"a=rtpmap:0 PCMU/8000\r\n"
 | 
			
		||||
		"a=rtpmap:4 G723/8000\r\n"
 | 
			
		||||
		"a=rtpmap:8 PCMA/8000\r\n"
 | 
			
		||||
		"a=rtpmap:101 telephone-event/8000\r\n"
 | 
			
		||||
		"a=fmtp:101 0-15\r\n"
 | 
			
		||||
		"a=recvonly\r\n"
 | 
			
		||||
		"a=rtcp:30437\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		,
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
 | 
			
		||||
		"s=FooBar\r\n"
 | 
			
		||||
		"c=IN IP4 192.168.11.140\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
 | 
			
		||||
		"a=rtpmap:18 G729/8000\r\n"
 | 
			
		||||
		"a=rtpmap:0 PCMU/8000\r\n"
 | 
			
		||||
		"a=rtpmap:4 G723/8000\r\n"
 | 
			
		||||
		"a=rtpmap:8 PCMA/8000\r\n"
 | 
			
		||||
		"a=rtpmap:101 telephone-event/8000\r\n"
 | 
			
		||||
		"a=fmtp:101 0-15\r\n"
 | 
			
		||||
		/* a=recvonly ends up further below */
 | 
			
		||||
		/* The rtcp line is dropped, not supported yet */
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		"a=recvonly\r\n"
 | 
			
		||||
		,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
 | 
			
		||||
		"s=FooBar\r\n"
 | 
			
		||||
		"c=IN IP4 192.168.11.140\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
 | 
			
		||||
		"a=rtpmap:18 G729/8000\r\n"
 | 
			
		||||
		"a=rtpmap:0 PCMU/8000\r\n"
 | 
			
		||||
		"a=rtpmap:4 G723/8000\r\n"
 | 
			
		||||
		"a=rtpmap:8 PCMA/8000\r\n"
 | 
			
		||||
		"a=rtpmap:101 telephone-event/8000\r\n"
 | 
			
		||||
		"a=fmtp:101 0-15\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		"a=sendonly\r\n"
 | 
			
		||||
		,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
 | 
			
		||||
		"s=FooBar\r\n"
 | 
			
		||||
		"c=IN IP4 192.168.11.140\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
 | 
			
		||||
		"a=rtpmap:18 G729/8000\r\n"
 | 
			
		||||
		"a=rtpmap:0 PCMU/8000\r\n"
 | 
			
		||||
		"a=rtpmap:4 G723/8000\r\n"
 | 
			
		||||
		"a=rtpmap:8 PCMA/8000\r\n"
 | 
			
		||||
		"a=rtpmap:101 telephone-event/8000\r\n"
 | 
			
		||||
		"a=fmtp:101 0-15\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		"a=inactive\r\n"
 | 
			
		||||
		,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void test_parse_and_compose(void)
 | 
			
		||||
{
 | 
			
		||||
	void *ctx = talloc_named_const(test_ctx, 0, __func__);
 | 
			
		||||
	void *print_ctx = talloc_named_const(test_ctx, 0, "print");
 | 
			
		||||
	int i;
 | 
			
		||||
	bool ok = true;
 | 
			
		||||
 | 
			
		||||
	printf("\n\n%s\n", __func__);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(sdp_msg_tests); i++) {
 | 
			
		||||
		const struct sdp_msg_test_data *t = &sdp_msg_tests[i];
 | 
			
		||||
		struct osmo_sdp_msg *sdp_msg;
 | 
			
		||||
		char str[1024];
 | 
			
		||||
		const char *expect;
 | 
			
		||||
		struct osmo_sdp_msg_decode_ret ret;
 | 
			
		||||
 | 
			
		||||
		printf("\n[%d]\n", i);
 | 
			
		||||
		dump_sdp(t->sdp_input, "sdp input: ");
 | 
			
		||||
 | 
			
		||||
		sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp_input, -1, &ret);
 | 
			
		||||
 | 
			
		||||
		if (ret.rc) {
 | 
			
		||||
			printf("ERROR: %s at %s\n", strerror(abs(ret.rc)),
 | 
			
		||||
			       osmo_quote_cstr_c(print_ctx, ret.error.at_input_str, ret.error.at_input_str_len));
 | 
			
		||||
			ok = false;
 | 
			
		||||
		}
 | 
			
		||||
		printf("parsed SDP message %s\n", osmo_sdp_msg_to_str_c(print_ctx, sdp_msg, false));
 | 
			
		||||
 | 
			
		||||
		osmo_sdp_msg_encode_buf(str, sizeof(str), sdp_msg);
 | 
			
		||||
		dump_sdp(str, "osmo_sdp_msg_encode_buf: ");
 | 
			
		||||
		expect = t->expect_sdp_str ? : t->sdp_input;
 | 
			
		||||
		if (strcmp(str, expect)) {
 | 
			
		||||
			int j;
 | 
			
		||||
			ok = false;
 | 
			
		||||
			printf("ERROR:\n");
 | 
			
		||||
			dump_sdp(expect, "expect: ");
 | 
			
		||||
			for (j = 0; expect[j]; j++) {
 | 
			
		||||
				if (expect[j] != str[j]) {
 | 
			
		||||
					printf("ERROR at position %d, at:\n", j);
 | 
			
		||||
					dump_sdp(str + j, "     mismatch: ");
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else
 | 
			
		||||
			printf("[%d] ok\n", i);
 | 
			
		||||
 | 
			
		||||
		report(ctx);
 | 
			
		||||
		printf("talloc_free(sdp_msg)\n");
 | 
			
		||||
		talloc_free(sdp_msg);
 | 
			
		||||
		report(ctx);
 | 
			
		||||
 | 
			
		||||
		if (talloc_total_blocks(ctx) != 1) {
 | 
			
		||||
			printf("ERROR: memleak\n");
 | 
			
		||||
			talloc_free_children(ctx);
 | 
			
		||||
		}
 | 
			
		||||
		printf("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ok);
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
	talloc_free(print_ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct intersect_test_data {
 | 
			
		||||
	const char *descr;
 | 
			
		||||
	const char *sdp_msg_a;
 | 
			
		||||
	const char *sdp_msg_b;
 | 
			
		||||
	const char *expect_intersection;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define SDP_1 \
 | 
			
		||||
		"v=0\r\n" \
 | 
			
		||||
		"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
 | 
			
		||||
		"s=GSM Call\r\n" \
 | 
			
		||||
		"c=IN IP4 23.42.23.42\r\n" \
 | 
			
		||||
		"t=0 0\r\n" \
 | 
			
		||||
		"m=audio 30436 RTP/AVP 112 3 111 110\r\n" \
 | 
			
		||||
		"a=rtpmap:112 AMR/8000\r\n" \
 | 
			
		||||
		"a=fmtp:112 octet-align=1\r\n" \
 | 
			
		||||
		"a=rtpmap:3 GSM/8000\r\n" \
 | 
			
		||||
		"a=rtpmap:111 GSM-HR-08/8000\r\n" \
 | 
			
		||||
		"a=rtpmap:110 GSM-EFR/8000\r\n" \
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
 | 
			
		||||
#define SDP_2 \
 | 
			
		||||
		"v=0\r\n" \
 | 
			
		||||
		"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
 | 
			
		||||
		"s=GSM Call\r\n" \
 | 
			
		||||
		"c=IN IP4 23.42.23.42\r\n" \
 | 
			
		||||
		"t=0 0\r\n" \
 | 
			
		||||
		"m=audio 30436 RTP/AVP 112 110\r\n" \
 | 
			
		||||
		"a=rtpmap:112 AMR/8000\r\n" \
 | 
			
		||||
		"a=fmtp:112 octet-align=1\r\n" \
 | 
			
		||||
		"a=rtpmap:110 GSM-EFR/8000\r\n" \
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
 | 
			
		||||
#define SDP_3 \
 | 
			
		||||
		"v=0\r\n" \
 | 
			
		||||
		"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
 | 
			
		||||
		"s=GSM Call\r\n" \
 | 
			
		||||
		"c=IN IP4 23.42.23.42\r\n" \
 | 
			
		||||
		"t=0 0\r\n" \
 | 
			
		||||
		"m=audio 30436 RTP/AVP 3 111 123\r\n" \
 | 
			
		||||
		"a=rtpmap:3 GSM/8000\r\n" \
 | 
			
		||||
		"a=rtpmap:111 GSM-HR-08/8000\r\n" \
 | 
			
		||||
		"a=rtpmap:123 FOO/8000\r\n" \
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
 | 
			
		||||
#define SDP_4 \
 | 
			
		||||
		"v=0\r\n" \
 | 
			
		||||
		"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
 | 
			
		||||
		"s=GSM Call\r\n" \
 | 
			
		||||
		"c=IN IP4 23.42.23.42\r\n" \
 | 
			
		||||
		"t=0 0\r\n" \
 | 
			
		||||
		"m=audio 30436 RTP/AVP 3 111\r\n" \
 | 
			
		||||
		"a=rtpmap:3 GSM/8000\r\n" \
 | 
			
		||||
		"a=rtpmap:111 GSM-HR-08/8000\r\n" \
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
 | 
			
		||||
#define SDP_5 \
 | 
			
		||||
		"v=0\r\n" \
 | 
			
		||||
		"o=libosmo-sdp 0 0 IN IP4 0.0.0.0\r\n" \
 | 
			
		||||
		"s=GSM Call\r\n" \
 | 
			
		||||
		"c=IN IP4 0.0.0.0\r\n" \
 | 
			
		||||
		"t=0 0\r\n" \
 | 
			
		||||
		"m=audio 0 RTP/AVP 112 113 110 3 111\r\n" \
 | 
			
		||||
		"a=rtpmap:112 AMR/8000\r\n" \
 | 
			
		||||
		"a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n" \
 | 
			
		||||
		"a=rtpmap:113 AMR-WB/8000\r\n" \
 | 
			
		||||
		"a=fmtp:113 octet-align=1\r\n" \
 | 
			
		||||
		"a=rtpmap:110 GSM-EFR/8000\r\n" \
 | 
			
		||||
		"a=rtpmap:3 GSM/8000\r\n" \
 | 
			
		||||
		"a=rtpmap:111 GSM-HR-08/8000\r\n" \
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
 | 
			
		||||
static const struct intersect_test_data intersect_tests[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "identical codecs lead to no change",
 | 
			
		||||
		.sdp_msg_a = SDP_1,
 | 
			
		||||
		.sdp_msg_b =
 | 
			
		||||
			"v=0\r\n"
 | 
			
		||||
			"c=IN IP4 5.6.7.8\r\n"
 | 
			
		||||
			"m=audio 12345 RTP/AVP 112 3 111 110\r\n"
 | 
			
		||||
			"a=rtpmap:112 AMR/8000\r\n"
 | 
			
		||||
			"a=fmtp:112 octet-align=1\r\n"
 | 
			
		||||
			"a=rtpmap:3 GSM/8000\r\n"
 | 
			
		||||
			"a=rtpmap:111 GSM-HR-08/8000\r\n"
 | 
			
		||||
			"a=rtpmap:110 GSM-EFR/8000\r\n"
 | 
			
		||||
			,
 | 
			
		||||
		.expect_intersection = SDP_1,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "identical codecs in different order also lead to no change",
 | 
			
		||||
		.sdp_msg_a = SDP_1,
 | 
			
		||||
		.sdp_msg_b =
 | 
			
		||||
			"v=0\r\n"
 | 
			
		||||
			"c=IN IP4 5.6.7.8\r\n" \
 | 
			
		||||
			"m=audio 12345 RTP/AVP 3 110 111 112\r\n"
 | 
			
		||||
			"a=rtpmap:3 GSM/8000\r\n"
 | 
			
		||||
			"a=rtpmap:110 GSM-EFR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:111 GSM-HR-08/8000\r\n"
 | 
			
		||||
			"a=rtpmap:112 AMR/8000\r\n"
 | 
			
		||||
			"a=fmtp:112 octet-align=1\r\n"
 | 
			
		||||
			,
 | 
			
		||||
		.expect_intersection = SDP_1,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "identical codecs with mismatching payload type numbers also lead to no change",
 | 
			
		||||
		.sdp_msg_a = SDP_1,
 | 
			
		||||
		.sdp_msg_b =
 | 
			
		||||
			"v=0\r\n"
 | 
			
		||||
			"c=IN IP4 5.6.7.8\r\n" \
 | 
			
		||||
			"m=audio 12345 RTP/AVP 96 97 98 99\r\n"
 | 
			
		||||
			"a=rtpmap:96 GSM/8000\r\n"
 | 
			
		||||
			"a=rtpmap:97 GSM-EFR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:98 GSM-HR-08/8000\r\n"
 | 
			
		||||
			"a=rtpmap:99 AMR/8000\r\n"
 | 
			
		||||
			"a=fmtp:99 octet-align=1\r\n"
 | 
			
		||||
			,
 | 
			
		||||
		.expect_intersection = SDP_1,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "identical codecs plus some extra codecs also lead to no change" ,
 | 
			
		||||
		.sdp_msg_a = SDP_1,
 | 
			
		||||
		.sdp_msg_b =
 | 
			
		||||
			"v=0\r\n"
 | 
			
		||||
			"c=IN IP4 5.6.7.8\r\n" \
 | 
			
		||||
			"m=audio 12345 RTP/AVP 8 0 96 97 98 99\r\n"
 | 
			
		||||
			"a=rtpmap:8 PCMA/8000\r\n"
 | 
			
		||||
			"a=rtpmap:0 PCMU/8000\r\n"
 | 
			
		||||
			"a=rtpmap:96 GSM/8000\r\n"
 | 
			
		||||
			"a=rtpmap:97 GSM-EFR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:98 GSM-HR-08/8000\r\n"
 | 
			
		||||
			"a=rtpmap:99 AMR/8000\r\n"
 | 
			
		||||
			"a=fmtp:99 octet-align=1\r\n"
 | 
			
		||||
			,
 | 
			
		||||
		.expect_intersection = SDP_1,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "some codecs removed",
 | 
			
		||||
		.sdp_msg_a = SDP_1,
 | 
			
		||||
		.sdp_msg_b = SDP_2,
 | 
			
		||||
		.expect_intersection = SDP_2,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "other codecs removed",
 | 
			
		||||
		.sdp_msg_a = SDP_1,
 | 
			
		||||
		.sdp_msg_b = SDP_3,
 | 
			
		||||
		.expect_intersection = SDP_4,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "all codecs removed",
 | 
			
		||||
		.sdp_msg_a = SDP_1,
 | 
			
		||||
		.sdp_msg_b =
 | 
			
		||||
			"v=0\r\n"
 | 
			
		||||
			"s=empty"
 | 
			
		||||
			,
 | 
			
		||||
		.expect_intersection =
 | 
			
		||||
			"v=0\r\n"
 | 
			
		||||
			"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
 | 
			
		||||
			"s=GSM Call\r\n"
 | 
			
		||||
			"c=IN IP4 23.42.23.42\r\n"
 | 
			
		||||
			"t=0 0\r\n"
 | 
			
		||||
			"m=audio 30436 RTP/AVP\r\n"
 | 
			
		||||
			"a=ptime:20\r\n"
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "some real world test case",
 | 
			
		||||
		.sdp_msg_a = SDP_5,
 | 
			
		||||
		.sdp_msg_b = SDP_5,
 | 
			
		||||
		.expect_intersection = SDP_5,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char *sdp_msg_logstr(const struct osmo_sdp_msg *sdp_msg)
 | 
			
		||||
{
 | 
			
		||||
	static char buf[1024];
 | 
			
		||||
	osmo_sdp_msg_encode_buf(buf, sizeof(buf), sdp_msg);
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_intersect(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	bool ok = true;
 | 
			
		||||
	void *ctx = talloc_named_const(test_ctx, 0, __func__);
 | 
			
		||||
 | 
			
		||||
	printf("\n\n%s\n", __func__);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(intersect_tests); i++) {
 | 
			
		||||
		const struct intersect_test_data *t = &intersect_tests[i];
 | 
			
		||||
		struct osmo_sdp_msg *sdp_msg_a = NULL;
 | 
			
		||||
		struct osmo_sdp_msg *sdp_msg_b = NULL;
 | 
			
		||||
		char str[1024];
 | 
			
		||||
		printf("\n[%d] %s\n", i, t->descr);
 | 
			
		||||
		dump_sdp(t->sdp_msg_a, "SDP A: ");
 | 
			
		||||
		dump_sdp(t->sdp_msg_b, " SDP B: ");
 | 
			
		||||
 | 
			
		||||
		sdp_msg_a = osmo_sdp_msg_decode(ctx, t->sdp_msg_a, -1, NULL);
 | 
			
		||||
		if (!sdp_msg_a) {
 | 
			
		||||
			printf("ERROR parsing SDP A\n");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		dump_sdp(sdp_msg_logstr(sdp_msg_a), "parsed SDP A: ");
 | 
			
		||||
 | 
			
		||||
		struct osmo_sdp_msg_decode_ret r;
 | 
			
		||||
		sdp_msg_b = osmo_sdp_msg_decode(ctx, t->sdp_msg_b, -1, &r);
 | 
			
		||||
		if (!sdp_msg_b) {
 | 
			
		||||
			printf("ERROR parsing SDP B\n");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		dump_sdp(sdp_msg_logstr(sdp_msg_b), "parsed SDP B: ");
 | 
			
		||||
 | 
			
		||||
		osmo_sdp_codec_list_intersection(sdp_msg_a->codecs, sdp_msg_b->codecs,
 | 
			
		||||
						 &osmo_sdp_codec_cmp_equivalent,
 | 
			
		||||
						 false);
 | 
			
		||||
		osmo_sdp_msg_encode_buf(str, sizeof(str), sdp_msg_a);
 | 
			
		||||
		dump_sdp(str, "intersection(a,b): ");
 | 
			
		||||
		if (strcmp(str, t->expect_intersection)) {
 | 
			
		||||
			int j;
 | 
			
		||||
			ok = false;
 | 
			
		||||
			printf("ERROR:\n");
 | 
			
		||||
			dump_sdp(t->expect_intersection, "expect_intersection: ");
 | 
			
		||||
			for (j = 0; t->expect_intersection[j]; j++) {
 | 
			
		||||
				if (t->expect_intersection[j] != str[j]) {
 | 
			
		||||
					printf("ERROR at position %d, at:\n", j);
 | 
			
		||||
					dump_sdp(str + j, "     mismatch: ");
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else
 | 
			
		||||
			printf("[%d] ok\n", i);
 | 
			
		||||
 | 
			
		||||
		report(ctx);
 | 
			
		||||
		printf("talloc_free(sdp_msg_a)\n");
 | 
			
		||||
		talloc_free(sdp_msg_a);
 | 
			
		||||
		report(ctx);
 | 
			
		||||
		printf("talloc_free(sdp_msg_b)\n");
 | 
			
		||||
		talloc_free(sdp_msg_b);
 | 
			
		||||
		report(ctx);
 | 
			
		||||
 | 
			
		||||
		if (talloc_total_blocks(ctx) != 1) {
 | 
			
		||||
			printf("ERROR: memleak\n");
 | 
			
		||||
			talloc_free_children(ctx);
 | 
			
		||||
		}
 | 
			
		||||
		printf("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ok);
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct sdp_select_test_data {
 | 
			
		||||
	const char *sdp;
 | 
			
		||||
	const struct osmo_sdp_codec_cmp_flags *cmpf;
 | 
			
		||||
	const struct osmo_sdp_codec select;
 | 
			
		||||
	const char *expect_sdp;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct osmo_sdp_codec_cmp_flags pt_only = { .payload_type = true };
 | 
			
		||||
 | 
			
		||||
static const struct sdp_select_test_data sdp_select_tests[] = {
 | 
			
		||||
	{
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"s=GSM Call\r\n"
 | 
			
		||||
		"c=IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
 | 
			
		||||
		"a=rtpmap:112 AMR/8000\r\n"
 | 
			
		||||
		"a=fmtp:112 octet-align=1\r\n"
 | 
			
		||||
		"a=rtpmap:3 GSM/8000\r\n"
 | 
			
		||||
		"a=rtpmap:111 GSM-HR-08/8000\r\n"
 | 
			
		||||
		"a=rtpmap:110 GSM-EFR/8000\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		,
 | 
			
		||||
		&pt_only,
 | 
			
		||||
		{ .payload_type = 112, },
 | 
			
		||||
		NULL
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"s=GSM Call\r\n"
 | 
			
		||||
		"c=IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
 | 
			
		||||
		"a=rtpmap:112 AMR/8000\r\n"
 | 
			
		||||
		"a=fmtp:112 octet-align=1\r\n"
 | 
			
		||||
		"a=rtpmap:3 GSM/8000\r\n"
 | 
			
		||||
		"a=rtpmap:111 GSM-HR-08/8000\r\n"
 | 
			
		||||
		"a=rtpmap:110 GSM-EFR/8000\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		,
 | 
			
		||||
		&pt_only,
 | 
			
		||||
		{ .payload_type = 3, },
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"s=GSM Call\r\n"
 | 
			
		||||
		"c=IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 3 112 111 110\r\n"
 | 
			
		||||
		"a=rtpmap:3 GSM/8000\r\n"
 | 
			
		||||
		"a=rtpmap:112 AMR/8000\r\n"
 | 
			
		||||
		"a=fmtp:112 octet-align=1\r\n"
 | 
			
		||||
		"a=rtpmap:111 GSM-HR-08/8000\r\n"
 | 
			
		||||
		"a=rtpmap:110 GSM-EFR/8000\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"s=GSM Call\r\n"
 | 
			
		||||
		"c=IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
 | 
			
		||||
		"a=rtpmap:112 AMR/8000\r\n"
 | 
			
		||||
		"a=fmtp:112 octet-align=1\r\n"
 | 
			
		||||
		"a=rtpmap:3 GSM/8000\r\n"
 | 
			
		||||
		"a=rtpmap:111 GSM-HR-08/8000\r\n"
 | 
			
		||||
		"a=rtpmap:110 GSM-EFR/8000\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		,
 | 
			
		||||
		&pt_only,
 | 
			
		||||
		{ .payload_type = 111, },
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"s=GSM Call\r\n"
 | 
			
		||||
		"c=IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 111 112 3 110\r\n"
 | 
			
		||||
		"a=rtpmap:111 GSM-HR-08/8000\r\n"
 | 
			
		||||
		"a=rtpmap:112 AMR/8000\r\n"
 | 
			
		||||
		"a=fmtp:112 octet-align=1\r\n"
 | 
			
		||||
		"a=rtpmap:3 GSM/8000\r\n"
 | 
			
		||||
		"a=rtpmap:110 GSM-EFR/8000\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"s=GSM Call\r\n"
 | 
			
		||||
		"c=IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
 | 
			
		||||
		"a=rtpmap:112 AMR/8000\r\n"
 | 
			
		||||
		"a=fmtp:112 octet-align=1\r\n"
 | 
			
		||||
		"a=rtpmap:3 GSM/8000\r\n"
 | 
			
		||||
		"a=rtpmap:111 GSM-HR-08/8000\r\n"
 | 
			
		||||
		"a=rtpmap:110 GSM-EFR/8000\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
		,
 | 
			
		||||
		&pt_only,
 | 
			
		||||
		{ .payload_type = 110, },
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"s=GSM Call\r\n"
 | 
			
		||||
		"c=IN IP4 23.42.23.42\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 30436 RTP/AVP 110 112 3 111\r\n"
 | 
			
		||||
		"a=rtpmap:110 GSM-EFR/8000\r\n"
 | 
			
		||||
		"a=rtpmap:112 AMR/8000\r\n"
 | 
			
		||||
		"a=fmtp:112 octet-align=1\r\n"
 | 
			
		||||
		"a=rtpmap:3 GSM/8000\r\n"
 | 
			
		||||
		"a=rtpmap:111 GSM-HR-08/8000\r\n"
 | 
			
		||||
		"a=ptime:20\r\n"
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void test_select(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	bool ok = true;
 | 
			
		||||
	void *ctx = talloc_named_const(test_ctx, 0, __func__);
 | 
			
		||||
	void *print_ctx = talloc_named_const(test_ctx, 0, "print");
 | 
			
		||||
 | 
			
		||||
	printf("\n\n%s\n", __func__);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(sdp_select_tests); i++) {
 | 
			
		||||
		const struct sdp_select_test_data *t = &sdp_select_tests[i];
 | 
			
		||||
		struct osmo_sdp_msg *sdp_msg;
 | 
			
		||||
		char buf[1024];
 | 
			
		||||
		const char *expect_sdp;
 | 
			
		||||
 | 
			
		||||
		printf("\n[%d]\n", i);
 | 
			
		||||
		sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp, -1, NULL);
 | 
			
		||||
		if (!sdp_msg) {
 | 
			
		||||
			printf("ERROR parsing SDP\n");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		printf("SDP: %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, sdp_msg->codecs, false));
 | 
			
		||||
 | 
			
		||||
		printf("Select: %s\n", osmo_sdp_codec_to_str_c(print_ctx, &t->select));
 | 
			
		||||
		osmo_sdp_codec_list_move_to_first(sdp_msg->codecs, &t->select, t->cmpf);
 | 
			
		||||
 | 
			
		||||
		printf("SDP: %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, sdp_msg->codecs, false));
 | 
			
		||||
		osmo_sdp_msg_encode_buf(buf, sizeof(buf), sdp_msg);
 | 
			
		||||
 | 
			
		||||
		expect_sdp = t->expect_sdp ? : t->sdp;
 | 
			
		||||
		if (strcmp(buf, expect_sdp)) {
 | 
			
		||||
			int j;
 | 
			
		||||
			ok = false;
 | 
			
		||||
			printf("ERROR:\n");
 | 
			
		||||
			dump_sdp(buf, "selection result: ");
 | 
			
		||||
			dump_sdp(expect_sdp, "expect result: ");
 | 
			
		||||
			for (j = 0; expect_sdp[j]; j++) {
 | 
			
		||||
				if (expect_sdp[j] != buf[j]) {
 | 
			
		||||
					printf("ERROR at position %d, at:\n", j);
 | 
			
		||||
					dump_sdp(buf + j, "     mismatch: ");
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else
 | 
			
		||||
			printf("[%d] ok\n", i);
 | 
			
		||||
 | 
			
		||||
		report(ctx);
 | 
			
		||||
		printf("talloc_free(sdp_msg)\n");
 | 
			
		||||
		talloc_free(sdp_msg);
 | 
			
		||||
		report(ctx);
 | 
			
		||||
		if (talloc_total_blocks(ctx) != 1) {
 | 
			
		||||
			printf("ERROR: memleak\n");
 | 
			
		||||
			talloc_free_children(ctx);
 | 
			
		||||
		}
 | 
			
		||||
		printf("\n");
 | 
			
		||||
		talloc_free_children(print_ctx);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ok);
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
	talloc_free(print_ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct my_obj {
 | 
			
		||||
	struct osmo_sdp_msg *sdp_msg;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct my_obj *my_obj_alloc(void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct my_obj *o = talloc_zero(ctx, struct my_obj);
 | 
			
		||||
	return o;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_obj_members(void)
 | 
			
		||||
{
 | 
			
		||||
	void *ctx = talloc_named_const(test_ctx, 0, __func__);
 | 
			
		||||
	void *print_ctx = talloc_named_const(test_ctx, 0, "print");
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	struct my_obj *o;
 | 
			
		||||
 | 
			
		||||
	printf("\n\n--- %s()\n", __func__);
 | 
			
		||||
	o = my_obj_alloc(ctx);
 | 
			
		||||
 | 
			
		||||
	o->sdp_msg = osmo_sdp_msg_alloc(o);
 | 
			
		||||
 | 
			
		||||
	printf("o->sdp_msg = '%s'\n", osmo_sdp_msg_encode_c(print_ctx, o->sdp_msg));
 | 
			
		||||
	report(ctx);
 | 
			
		||||
 | 
			
		||||
	const struct osmo_sdp_codec all_codecs[] = {
 | 
			
		||||
		{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
 | 
			
		||||
		{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
 | 
			
		||||
		{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(all_codecs); i++)
 | 
			
		||||
		osmo_sdp_codec_list_add(o->sdp_msg->codecs, &all_codecs[i], false, false);
 | 
			
		||||
 | 
			
		||||
	printf("o->sdp_msg = '%s'\n", osmo_sdp_msg_encode_c(print_ctx, o->sdp_msg));
 | 
			
		||||
 | 
			
		||||
	report(ctx);
 | 
			
		||||
	printf("talloc_free(o)\n");
 | 
			
		||||
	talloc_free(o);
 | 
			
		||||
	report(ctx);
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
	talloc_free(print_ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef void (*test_func_t)(void);
 | 
			
		||||
 | 
			
		||||
static const test_func_t test_func[] = {
 | 
			
		||||
	test_parse_and_compose,
 | 
			
		||||
	test_intersect,
 | 
			
		||||
	test_select,
 | 
			
		||||
	test_obj_members,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	test_ctx = talloc_named_const(NULL, 0, "sdp_codec_test");
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(test_func); i++) {
 | 
			
		||||
 | 
			
		||||
		test_func[i]();
 | 
			
		||||
 | 
			
		||||
		if (talloc_total_blocks(test_ctx) != 1) {
 | 
			
		||||
			talloc_report_full(test_ctx, stderr);
 | 
			
		||||
			printf("ERROR after test %d: memory leak\n", i);
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(test_ctx);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								tests/sdp/sdp_msg_test.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/sdp/sdp_msg_test.err
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										1321
									
								
								tests/sdp/sdp_msg_test.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1321
									
								
								tests/sdp/sdp_msg_test.ok
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -13,3 +13,24 @@ AT_KEYWORDS([mgcp])
 | 
			
		||||
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
 | 
			
		||||
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
 | 
			
		||||
AT_CLEANUP
 | 
			
		||||
 | 
			
		||||
AT_SETUP([sdp_fmtp])
 | 
			
		||||
AT_KEYWORDS([sdp_fmtp])
 | 
			
		||||
cat $abs_srcdir/sdp/sdp_fmtp_test.ok > expout
 | 
			
		||||
cat $abs_srcdir/sdp/sdp_fmtp_test.err > experr
 | 
			
		||||
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_fmtp_test], [], [expout], [experr])
 | 
			
		||||
AT_CLEANUP
 | 
			
		||||
 | 
			
		||||
AT_SETUP([sdp_codec])
 | 
			
		||||
AT_KEYWORDS([sdp_codec])
 | 
			
		||||
cat $abs_srcdir/sdp/sdp_codec_test.ok > expout
 | 
			
		||||
cat $abs_srcdir/sdp/sdp_codec_test.err > experr
 | 
			
		||||
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_codec_test], [], [expout], [experr])
 | 
			
		||||
AT_CLEANUP
 | 
			
		||||
 | 
			
		||||
AT_SETUP([sdp_msg])
 | 
			
		||||
AT_KEYWORDS([sdp_msg])
 | 
			
		||||
cat $abs_srcdir/sdp/sdp_msg_test.ok > expout
 | 
			
		||||
cat $abs_srcdir/sdp/sdp_msg_test.err > experr
 | 
			
		||||
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_msg_test], [], [expout], [experr])
 | 
			
		||||
AT_CLEANUP
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user