mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-10-31 12:03:50 +00:00 
			
		
		
		
	Compare commits
	
		
			396 Commits
		
	
	
		
			1.0.2
			...
			osmith/fix
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 832bcdf631 | ||
|  | 73f9c02f49 | ||
|  | 6a25a61142 | ||
|  | 1dbbed169a | ||
|  | a8f27abe12 | ||
|  | ca2aec0235 | ||
|  | e827831514 | ||
|  | 923d60bb12 | ||
|  | f2bf8dc8c8 | ||
|  | 055ded74de | ||
|  | 3ff71284fa | ||
|  | cc0b97e197 | ||
|  | 8c69e29820 | ||
|  | 843d9038ce | ||
|  | d071a30238 | ||
|  | 3ab8ca4d84 | ||
|  | 23f4048b57 | ||
|  | 401b740ccd | ||
|  | a468b0f57f | ||
|  | b0cfa7272e | ||
|  | 683e05f60b | ||
|  | 16b637bf1b | ||
|  | 2698540c1e | ||
|  | d2f5e69d3e | ||
|  | ce64f18587 | ||
|  | 667fa59b0c | ||
|  | 5a6220f43b | ||
|  | 7c6dd3c2c3 | ||
|  | 740af6ed44 | ||
|  | 782d607962 | ||
|  | 50e52e45f9 | ||
|  | a2d10216ea | ||
|  | f54eb96338 | ||
|  | a2b1c5e6f6 | ||
|  | c5c1430a1c | ||
|  | f7de9aea7b | ||
|  | 199c5e965e | ||
|  | 06a49fc04b | ||
|  | 6349bd422a | ||
|  | 796a4a1325 | ||
|  | 2cf6652ba2 | ||
|  | 9b508f6b53 | ||
|  | 6e26c70f46 | ||
|  | 9a34592c09 | ||
|  | 7eb6f2cb56 | ||
|  | 83fd8a5692 | ||
|  | 17058484d1 | ||
|  | 166077ea48 | ||
|  | 0c6c3c1da6 | ||
|  | fe9a1fe03b | ||
|  | 1e4a34d45e | ||
|  | 189f29e939 | ||
|  | d2ce444008 | ||
|  | ef9420adf5 | ||
|  | cb96e05a91 | ||
|  | f6387dc766 | ||
|  | 1b1d7ed98f | ||
|  | df7d97e4b4 | ||
|  | 85978dadab | ||
|  | 9aaaab6b3b | ||
|  | c1ad553d86 | ||
|  | c1bf4694e7 | ||
|  | 1442c5a99e | ||
|  | dac2ca7e21 | ||
|  | 375252279c | ||
|  | 295570c631 | ||
|  | a93c6e9263 | ||
|  | 30907dc9d8 | ||
|  | 14f8a08f44 | ||
|  | c63f15a9a7 | ||
|  | ca538fc5eb | ||
|  | 6be2c49538 | ||
|  | 91088c305f | ||
|  | 900cd6518a | ||
|  | fa810e8ccd | ||
|  | 497611ae84 | ||
|  | 2b89617aad | ||
|  | b542b0457b | ||
|  | 120568651a | ||
|  | f1d301a9a6 | ||
|  | c9a6280c94 | ||
|  | b5583cde41 | ||
|  | 6c1cd63a57 | ||
|  | 0d7ba56f5c | ||
|  | 3ac604e3ad | ||
|  | 9fb8ddf00e | ||
|  | 182ca3bad4 | ||
|  | f027f17dcb | ||
|  | ac772d8b0c | ||
|  | 9d9f44ac71 | ||
|  | 84274e4e9b | ||
|  | c5479fe086 | ||
|  | 538d2c53d9 | ||
|  | 8de58e79b8 | ||
|  | 5e8d7995d1 | ||
|  | 3239f6212e | ||
|  | 209eb9f103 | ||
|  | ef6304e4a1 | ||
|  | 21cdbfc196 | ||
|  | fc8067348b | ||
|  | 9f11dc5616 | ||
|  | ca0818c760 | ||
|  | c12bfb7ffc | ||
|  | 228e591589 | ||
|  | 58128258b0 | ||
|  | e7ae69a7be | ||
|  | 7e37ce6008 | ||
|  | dbe09dd515 | ||
|  | 217d31d62f | ||
|  | 47642f245d | ||
|  | cb760bdebe | ||
|  | e3f8bca424 | ||
|  | 9fc8a02196 | ||
|  | e36b775eae | ||
|  | 622dd61764 | ||
|  | e81c1176aa | ||
|  | ed0c1aa9d5 | ||
|  | ac69ea9cdf | ||
|  | 0a40379214 | ||
|  | 72bc8da537 | ||
|  | c3132fd508 | ||
|  | 96c3107daf | ||
|  | ad21a0e166 | ||
|  | 32d15cc8ba | ||
|  | 1fd50e5306 | ||
|  | 6802374574 | ||
|  | 82c1d1eb20 | ||
|  | 5a5169e15c | ||
|  | 7c0b70a94e | ||
|  | ca6a8495e1 | ||
|  | c0a2196f91 | ||
|  | da026abc27 | ||
|  | 27780313ac | ||
|  | e5ed946bdb | ||
|  | c319b79baa | ||
|  | 2818db8b33 | ||
|  | 0415de74b8 | ||
|  | 4075b8805d | ||
|  | 8ab3fbbaf2 | ||
|  | cc39218dce | ||
|  | 8dbc9ed408 | ||
|  | ba25eab0fa | ||
|  | aa823bf24b | ||
|  | a714abfc91 | ||
|  | 9270e91c09 | ||
|  | b7974e2fa4 | ||
|  | 1e174875bf | ||
|  | c26b665c0c | ||
|  | 0479b2a15f | ||
|  | ff6606cacb | ||
|  | 407b1f186d | ||
|  | de2a4d7c22 | ||
|  | 48aff62341 | ||
|  | 852ba86949 | ||
|  | 662fa421c6 | ||
|  | d14163e74f | ||
|  | 11b4810142 | ||
|  | 9ecceb651b | ||
|  | 426a9d9103 | ||
|  | 17bf603222 | ||
|  | bcd52e5724 | ||
|  | 956242dcec | ||
|  | e686675f7d | ||
|  | 4ca0b0d1fa | ||
|  | efd645e5a8 | ||
|  | f0504e86b3 | ||
|  | e28b673fba | ||
|  | a77eade744 | ||
|  | 6531726a02 | ||
|  | 31b4729f27 | ||
|  | a729db62a9 | ||
|  | 5336f57f05 | ||
|  | eb72ff058f | ||
|  | 8a91d2c04e | ||
|  | 55e0dcf254 | ||
|  | 23e7bf1c00 | ||
|  | 10d487e13f | ||
|  | 40f5033cf9 | ||
|  | b1bb1fa187 | ||
|  | 1d121483f6 | ||
|  | 124441af1f | ||
|  | b861db97ea | ||
|  | 08e07046f9 | ||
|  | a5e0cf09a6 | ||
|  | f2388eab71 | ||
|  | 120865833c | ||
|  | e6d8e91b3a | ||
|  | 475f868b7f | ||
|  | 0063ca2fb0 | ||
|  | 610fda6108 | ||
|  | 352eed09ae | ||
|  | 0a89e92a5e | ||
|  | dde80f3f61 | ||
|  | 9a7ccc3746 | ||
|  | 910189d0a1 | ||
|  | 8482e8374c | ||
|  | fee4fa9492 | ||
|  | 4dba769577 | ||
|  | da895b1151 | ||
|  | a74c0ea2db | ||
|  | 6a26c16eb0 | ||
|  | af8e00ffe8 | ||
|  | 6931f9a7a4 | ||
|  | 544448abea | ||
|  | e547bdd1ce | ||
|  | d1562a7766 | ||
|  | 304b3eb328 | ||
|  | 9cd5233609 | ||
|  | 35a382968c | ||
|  | 7066af825a | ||
|  | acc10353fe | ||
|  | bca0ef6cd9 | ||
|  | cede2a4b7c | ||
|  | 337209a6ea | ||
|  | 56e0443e1c | ||
|  | b1a1b4000e | ||
|  | 8838c62fe9 | ||
|  | 704c4f0adf | ||
|  | bc0346e080 | ||
|  | 5928dc9345 | ||
|  | 04da5e5e98 | ||
|  | 54b4f82f91 | ||
|  | 3d7b58d77a | ||
|  | 604410cd13 | ||
|  | b340f90c9e | ||
|  | 3c8ccb6724 | ||
|  | d4e6aa42ca | ||
|  | e6df0e47e7 | ||
|  | 54eb0e1204 | ||
|  | b38fb8911f | ||
|  | 1b3a385b9d | ||
|  | dbd70c7b68 | ||
|  | a19547b7a1 | ||
|  | 06823731d8 | ||
|  | ed1cff5ab9 | ||
|  | f2321b7a72 | ||
|  | 4219904cb2 | ||
|  | b2753f2044 | ||
|  | ba61f68137 | ||
|  | 9e1d164469 | ||
|  | 0ec1d4e17c | ||
|  | d761d355f9 | ||
|  | 8d064dfd24 | ||
|  | 7181cc1f02 | ||
|  | 60f8e31a2f | ||
|  | 086c3f3c67 | ||
|  | d0b470d1a9 | ||
|  | 77f76d0be5 | ||
|  | 892dec0be9 | ||
|  | c430d19112 | ||
|  | 7b8e419d92 | ||
|  | 4f7613eeb1 | ||
|  | 01f039538e | ||
|  | d2e3a52230 | ||
|  | 2924825bc6 | ||
|  | bd86f94a09 | ||
|  | 1083533db4 | ||
|  | 0793d2f5d5 | ||
|  | a8c6a9c37a | ||
|  | 2da99a2946 | ||
|  | c3eed40f00 | ||
|  | 515e341ff6 | ||
|  | 8bda7a7766 | ||
|  | df5d219f39 | ||
|  | 5656fbf49d | ||
|  | 19d640e806 | ||
|  | 207ab51270 | ||
|  | c3cc654add | ||
|  | b911b879d8 | ||
|  | af07f66ca3 | ||
|  | dd0c522cd4 | ||
|  | 37d11c80da | ||
|  | 1355d7e4f7 | ||
|  | fdd603c4c8 | ||
|  | 1fc6999a77 | ||
|  | 220f487238 | ||
|  | edc00f4ea7 | ||
|  | a49e32a1ab | ||
|  | dd4ede34ad | ||
|  | 3aa815755d | ||
|  | 3261dc7540 | ||
|  | 771b26a043 | ||
|  | 36a81129ec | ||
|  | a390d0ba52 | ||
|  | b759473d10 | ||
|  | c3cfae2cf8 | ||
|  | 3cbfb8a53c | ||
|  | 275ac97036 | ||
|  | abe8c897fd | ||
|  | e9d645b3b3 | ||
|  | 7f0966c13d | ||
|  | 55295f7b07 | ||
|  | 9d25d7a2e6 | ||
|  | 490cbaa89e | ||
|  | eb0bde09bb | ||
|  | 3b12e1b011 | ||
|  | 2138779559 | ||
|  | 12943ea0c1 | ||
|  | 6efb43a210 | ||
|  | 03cc48474c | ||
|  | 5672563f7e | ||
|  | 106743c66b | ||
|  | 8890dfa634 | ||
|  | 10f32dbfbf | ||
|  | e35eeae8e0 | ||
|  | abbb6b9088 | ||
|  | 3f35a3708a | ||
|  | 33eafe050b | ||
|  | 1d1b98f4a4 | ||
|  | a0ac30faa5 | ||
|  | 49e3d5a9c9 | ||
|  | 3338135ead | ||
|  | 5dbfc78aff | ||
|  | 7bc5552512 | ||
|  | a330b864e5 | ||
|  | 7df419b434 | ||
|  | 7cedfd753b | ||
|  | 23b8e29835 | ||
|  | 1c3287f1db | ||
|  | 922ae9a5bf | ||
|  | 189d6bf4d4 | ||
|  | c8f37cb4d6 | ||
|  | c0dcc3c60c | ||
|  | 230e4fc270 | ||
|  | ead2f60f73 | ||
|  | f8bfbe8b14 | ||
|  | ffd75e420c | ||
|  | 01d24a3281 | ||
|  | 0be3ce66c0 | ||
|  | fcd0655176 | ||
|  | 48454983c4 | ||
|  | 9a3543a8d4 | ||
|  | 465446b4b4 | ||
|  | b597b4fc9d | ||
|  | d20910c655 | ||
|  | 677f4ad968 | ||
|  | 7c20c9de5a | ||
|  | 839fc954d0 | ||
|  | d164b05655 | ||
|  | a896f9cdeb | ||
|  | 1b0cf6fa1c | ||
|  | 9bf7c53779 | ||
|  | 6be0fdb5e4 | ||
|  | 641c4d47b9 | ||
|  | c1b9fa158e | ||
|  | 6a421f1b6f | ||
|  | f1889d8640 | ||
|  | 2982e42d93 | ||
|  | ddf1f9d7d5 | ||
|  | 653c4ff354 | ||
|  | 4efce88a62 | ||
|  | e7d27aeae1 | ||
|  | e6f172dd57 | ||
|  | b969455941 | ||
|  | 54dd4b3f72 | ||
|  | dbd88e5167 | ||
|  | 87203f2a37 | ||
|  | 1cb1e38dbc | ||
|  | e726d4fad2 | ||
|  | c341388b30 | ||
|  | 7bf4ce3aed | ||
|  | f4c0e37352 | ||
|  | 06da85ed3a | ||
|  | 1dc6be6a82 | ||
|  | 8348abb372 | ||
|  | d8d7b4b188 | ||
|  | 31c4305225 | ||
|  | c7a228aceb | ||
|  | e472b4e39a | ||
|  | 8970c497bc | ||
|  | 1e0b9f872c | ||
|  | 8863f7ae64 | ||
|  | 1ae2d5ebf4 | ||
|  | 5852a78d2e | ||
|  | 59e2d518fd | ||
|  | 062b0f0eee | ||
|  | 71c619ee05 | ||
|  | e3b5de2285 | ||
|  | f96e46fcce | ||
|  | 88d4dae645 | ||
|  | 8042539444 | ||
|  | 6ba4406c47 | ||
|  | 1e940d615a | ||
|  | fde78ad412 | ||
|  | 3d9b656a1c | ||
|  | 2152f8523e | ||
|  | c5ecebb711 | ||
|  | 020e89bce9 | ||
|  | 6779354245 | ||
|  | 87bd9be0b0 | ||
|  | f83ec56212 | ||
|  | 1c8d67d7f5 | ||
|  | 9d4f1d57f3 | ||
|  | d95ab1e9ad | ||
|  | a1fa2f4bc3 | ||
|  | c221b7ad01 | 
							
								
								
									
										50
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										50
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -2,26 +2,20 @@ debian/*.log | ||||
| *.o | ||||
| *.lo | ||||
| *.a | ||||
| *.la | ||||
| .deps | ||||
| Makefile | ||||
| Makefile.in | ||||
| bscconfig.h | ||||
| bscconfig.h.in | ||||
| openbsc.pc | ||||
| src/osmo-nitb/osmo-nitb | ||||
| src/osmo-bsc_mgcp/osmo-bsc_mgcp | ||||
| src/osmo-bsc/osmo-bsc | ||||
| src/utils/meas_vis | ||||
| src/utils/meas_json | ||||
| src/utils/osmo-meas-pcap2db | ||||
| src/utils/osmo-meas-udp2db | ||||
| src/utils/smpp_mirror | ||||
| src/osmo-mgw/osmo-mgw | ||||
| *.*~ | ||||
| *.sw? | ||||
| .libs | ||||
| *.pyc | ||||
| *.gcda | ||||
| *.gcno | ||||
| *.pc | ||||
|  | ||||
| #configure | ||||
| aclocal.m4 | ||||
| @@ -38,38 +32,16 @@ missing | ||||
| stamp-h1 | ||||
| libtool | ||||
| ltmain.sh | ||||
| m4/*.m4 | ||||
|  | ||||
| # git-version-gen magic | ||||
| .tarball-version | ||||
| .version | ||||
|  | ||||
|  | ||||
| # apps and app data | ||||
| hlr.sqlite3 | ||||
| src/utils/bs11_config | ||||
| src/ipaccess/ipaccess-config | ||||
| src/ipaccess/abisip-find | ||||
| src/ipaccess/ipaccess-firmware | ||||
| src/ipaccess/ipaccess-proxy | ||||
| src/utils/isdnsync | ||||
| src/nat/bsc_nat | ||||
| src/gprs/osmo-sgsn | ||||
| src/gprs/osmo-gbproxy | ||||
| src/gprs/osmo-gtphub | ||||
| src/osmo-bsc_nat/osmo-bsc_nat | ||||
| src/libcommon/gsup_test_client | ||||
| src/osmo-msc/osmo-msc | ||||
|  | ||||
| #tests | ||||
| tests/testsuite.dir | ||||
| tests/*/*_test | ||||
| # ignore compiled binaries like msc_vlr_test_foo; do not ignore | ||||
| # msc_vlr_test_foo.{c,ok,err}, but do still ignore the corresponding .o object | ||||
| # files: | ||||
| tests/msc_vlr/msc_vlr_test_* | ||||
| !tests/msc_vlr/msc_vlr_test_*.* | ||||
| tests/msc_vlr/msc_vlr_test_*.o | ||||
|  | ||||
|  | ||||
| tests/atconfig | ||||
| tests/atlocal | ||||
| @@ -77,7 +49,15 @@ tests/package.m4 | ||||
| tests/testsuite | ||||
| tests/testsuite.log | ||||
|  | ||||
| gsn_restart | ||||
| src/openbsc.cfg* | ||||
| writtenconfig/ | ||||
| gtphub_restart_count | ||||
|  | ||||
| # manuals | ||||
| doc/manuals/*.html | ||||
| doc/manuals/*.svg | ||||
| doc/manuals/*.pdf | ||||
| doc/manuals/*__*.png | ||||
| doc/manuals/*.check | ||||
| doc/manuals/generated/ | ||||
| doc/manuals/osmomsc-usermanual.xml | ||||
| doc/manuals/common | ||||
| doc/manuals/build | ||||
|   | ||||
| @@ -18,13 +18,15 @@ SUBDIRS = \ | ||||
|  | ||||
| pkgconfigdir = $(libdir)/pkgconfig | ||||
| pkgconfig_DATA = \ | ||||
| 	libosmo-legacy-mgcp.pc \ | ||||
| 	libosmo-mgcp-client.pc \ | ||||
| 	$(NULL) | ||||
|  | ||||
| BUILT_SOURCES = $(top_srcdir)/.version | ||||
| EXTRA_DIST = git-version-gen osmoappdesc.py .version | ||||
|  | ||||
| AM_DISTCHECK_CONFIGURE_FLAGS = \ | ||||
| 	--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) | ||||
|  | ||||
| @RELMAKE@ | ||||
|  | ||||
| $(top_srcdir)/.version: | ||||
|   | ||||
							
								
								
									
										4
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								README
									
									
									
									
									
								
							| @@ -19,10 +19,6 @@ 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. | ||||
|  | ||||
| The libosmo-mgcp library exposes MGCP server utilities used by e.g. OsmoBSC-NAT | ||||
| (found in osmo-bsc.git) to navigate RTP streams through a NAT. | ||||
| (At time of writing, this is still called libosmo-legacy-mgcp.) | ||||
|  | ||||
| Find OsmoMGW issue tracker and wiki online at | ||||
| https://osmocom.org/projects/osmo-mgw | ||||
| https://osmocom.org/projects/osmo-mgw/wiki | ||||
|   | ||||
							
								
								
									
										35
									
								
								TODO-RELEASE
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								TODO-RELEASE
									
									
									
									
									
								
							| @@ -1,9 +1,26 @@ | ||||
| # When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install | ||||
| # according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info | ||||
| # In short: | ||||
| # 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:0. | ||||
| # 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 | ||||
| # 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. | ||||
| # | ||||
| #library		what		description / commit summary line | ||||
|   | ||||
							
								
								
									
										134
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -39,43 +39,47 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""]) | ||||
| AC_SUBST(LIBRARY_DL) | ||||
|  | ||||
|  | ||||
| PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.9.5) | ||||
| PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0) | ||||
| PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.0.1) | ||||
| PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0) | ||||
| PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.6.0) | ||||
|  | ||||
| # Enable/disable transcoding within osmo-bsc_mgcp? | ||||
| AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])], | ||||
|     [osmo_ac_mgcp_transcoding="$enableval"],[osmo_ac_mgcp_transcoding="no"]) | ||||
| AC_ARG_WITH([g729], [AS_HELP_STRING([--with-g729], [Enable G.729 encoding/decoding.])], [osmo_ac_with_g729="$withval"],[osmo_ac_with_g729="no"]) | ||||
|  | ||||
| if test "$osmo_ac_mgcp_transcoding" = "yes" ; then | ||||
|     AC_SEARCH_LIBS([gsm_create], [gsm], [LIBRARY_GSM="$LIBS";LIBS=""], [AC_MSG_ERROR([--enable-mgcp-transcoding: cannot find usable libgsm])]) | ||||
|     AC_SUBST(LIBRARY_GSM) | ||||
|     if test "$osmo_ac_with_g729" = "yes" ; then | ||||
| 	PKG_CHECK_MODULES(LIBBCG729, libbcg729 >= 0.1, [AC_DEFINE([HAVE_BCG729], [1], [Use bgc729 decoder/encoder])]) | ||||
|     fi | ||||
|     AC_DEFINE(BUILD_MGCP_TRANSCODING, 1, [Define if we want to build the MGCP gateway with transcoding support]) | ||||
| AC_ARG_ENABLE(sanitize, | ||||
| 	[AS_HELP_STRING( | ||||
| 		[--enable-sanitize], | ||||
| 		[Compile with address sanitizer enabled], | ||||
| 	)], | ||||
| 	[sanitize=$enableval], [sanitize="no"]) | ||||
| if test x"$sanitize" = x"yes" | ||||
| then | ||||
| 	CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined" | ||||
| 	CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" | ||||
| fi | ||||
|  | ||||
| AC_ARG_ENABLE(werror, | ||||
| 	[AS_HELP_STRING( | ||||
| 		[--enable-werror], | ||||
| 		[Turn all compiler warnings into errors, with exceptions: | ||||
| 		 a) deprecation (allow upstream to mark deprecation without breaking builds); | ||||
| 		 b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds) | ||||
| 		] | ||||
| 	)], | ||||
| 	[werror=$enableval], [werror="no"]) | ||||
| if test x"$werror" = x"yes" | ||||
| then | ||||
| 	WERROR_FLAGS="-Werror" | ||||
| 	WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" | ||||
| 	WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" | ||||
| 	CFLAGS="$CFLAGS $WERROR_FLAGS" | ||||
| 	CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" | ||||
| fi | ||||
| AM_CONDITIONAL(BUILD_MGCP_TRANSCODING, test "x$osmo_ac_mgcp_transcoding" = "xyes") | ||||
| AC_SUBST(osmo_ac_mgcp_transcoding) | ||||
|  | ||||
| dnl Checks for typedefs, structures and compiler characteristics | ||||
|  | ||||
| # The following test is taken from WebKit's webkit.m4 | ||||
| saved_CFLAGS="$CFLAGS" | ||||
| CFLAGS="$CFLAGS -fvisibility=hidden " | ||||
| AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) | ||||
| AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])], | ||||
|       [ AC_MSG_RESULT([yes]) | ||||
|         SYMBOL_VISIBILITY="-fvisibility=hidden"], | ||||
|         AC_MSG_RESULT([no])) | ||||
| CFLAGS="$saved_CFLAGS" | ||||
| AC_SUBST(SYMBOL_VISIBILITY) | ||||
|  | ||||
| AX_CHECK_COMPILE_FLAG([-Werror=implicit], [CFLAGS="$CFLAGS -Werror=implicit"]) | ||||
| AX_CHECK_COMPILE_FLAG([-Werror=maybe-uninitialized], [CFLAGS="$CFLAGS -Werror=maybe-uninitialized"]) | ||||
| AX_CHECK_COMPILE_FLAG([-Werror=memset-transposed-args], [CFLAGS="$CFLAGS -Werror=memset-transposed-args"]) | ||||
| AX_CHECK_COMPILE_FLAG([-Werror=null-dereference], [CFLAGS="$CFLAGS -Werror=null-dereference"]) | ||||
| AX_CHECK_COMPILE_FLAG([-Wnull-dereference], [CFLAGS="$CFLAGS -Wnull-dereference"]) | ||||
| AX_CHECK_COMPILE_FLAG([-Werror=sizeof-array-argument], [CFLAGS="$CFLAGS -Werror=sizeof-array-argument"]) | ||||
| AX_CHECK_COMPILE_FLAG([-Werror=sizeof-pointer-memaccess], [CFLAGS="$CFLAGS -Werror=sizeof-pointer-memaccess"]) | ||||
|  | ||||
| @@ -113,25 +117,87 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests]) | ||||
| AC_MSG_RESULT([$enable_ext_tests]) | ||||
| AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes") | ||||
|  | ||||
| # Generate manuals | ||||
| AC_ARG_ENABLE(manuals, | ||||
| 	[AS_HELP_STRING( | ||||
| 		[--enable-manuals], | ||||
| 		[Generate manual PDFs [default=no]], | ||||
| 	)], | ||||
| 	[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"]) | ||||
| AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"]) | ||||
| AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals" | ||||
| 	fallback]) | ||||
| if test x"$osmo_ac_build_manuals" = x"yes" | ||||
| then | ||||
| 	# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback) | ||||
| 	if test -n "$OSMO_GSM_MANUALS_DIR"; then | ||||
| 		echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)" | ||||
| 	else | ||||
| 		OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)" | ||||
| 		if test -n "$OSMO_GSM_MANUALS_DIR"; then | ||||
| 			echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)" | ||||
| 		else | ||||
| 			OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals" | ||||
| 			echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)" | ||||
| 		fi | ||||
| 	fi | ||||
| 	if ! test -d "$OSMO_GSM_MANUALS_DIR"; then | ||||
| 		AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.") | ||||
| 	fi | ||||
|  | ||||
| 	# Find and run check-depends | ||||
| 	CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh" | ||||
| 	if ! test -x "$CHECK_DEPENDS"; then | ||||
| 		CHECK_DEPENDS="osmo-gsm-manuals-check-depends" | ||||
| 	fi | ||||
| 	if ! $CHECK_DEPENDS; then | ||||
| 		AC_MSG_ERROR("missing dependencies for --enable-manuals") | ||||
| 	fi | ||||
|  | ||||
| 	# Put in Makefile with absolute path | ||||
| 	OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")" | ||||
| 	AC_SUBST([OSMO_GSM_MANUALS_DIR]) | ||||
| fi | ||||
|  | ||||
| # https://www.freedesktop.org/software/systemd/man/daemon.html | ||||
| AC_ARG_WITH([systemdsystemunitdir], | ||||
|      [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],, | ||||
|      [with_systemdsystemunitdir=auto]) | ||||
| AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [ | ||||
|      def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) | ||||
|  | ||||
|      AS_IF([test "x$def_systemdsystemunitdir" = "x"], | ||||
|    [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"], | ||||
|     [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])]) | ||||
|     with_systemdsystemunitdir=no], | ||||
|    [with_systemdsystemunitdir="$def_systemdsystemunitdir"])]) | ||||
| AS_IF([test "x$with_systemdsystemunitdir" != "xno"], | ||||
|       [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])]) | ||||
| AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"]) | ||||
|  | ||||
| AC_MSG_RESULT([CFLAGS="$CFLAGS"]) | ||||
| AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"]) | ||||
|  | ||||
| dnl Generate the output | ||||
| AM_CONFIG_HEADER(bscconfig.h) | ||||
|  | ||||
| AC_OUTPUT( | ||||
|     libosmo-legacy-mgcp.pc | ||||
|     libosmo-mgcp-client.pc | ||||
|     include/Makefile | ||||
|     include/osmocom/Makefile | ||||
|     include/osmocom/legacy_mgcp/Makefile | ||||
|     include/osmocom/mgcp_client/Makefile | ||||
|     include/osmocom/mgcp/Makefile | ||||
|     src/Makefile | ||||
|     src/libosmo-legacy-mgcp/Makefile | ||||
|     src/libosmo-mgcp-client/Makefile | ||||
|     src/osmo-bsc_mgcp/Makefile | ||||
|     src/libosmo-mgcp/Makefile | ||||
|     src/osmo-mgw/Makefile | ||||
|     tests/Makefile | ||||
|     tests/atlocal | ||||
|     tests/legacy_mgcp/Makefile | ||||
|     tests/mgcp_client/Makefile | ||||
|     tests/mgcp/Makefile | ||||
|     doc/Makefile | ||||
|     doc/examples/Makefile | ||||
|     doc/manuals/Makefile | ||||
|     contrib/Makefile | ||||
|     contrib/systemd/Makefile | ||||
|     Makefile) | ||||
|   | ||||
| @@ -1 +1,3 @@ | ||||
| SUBDIRS = systemd | ||||
|  | ||||
| EXTRA_DIST = ipa.py | ||||
|   | ||||
| @@ -1,5 +1,10 @@ | ||||
| #!/usr/bin/env bash | ||||
| # jenkins build helper script for openbsc.  This is how we build on jenkins.osmocom.org | ||||
| # | ||||
| # environment variables: | ||||
| # * WITH_MANUALS: build manual PDFs if set to "1" | ||||
| # * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1") | ||||
| # | ||||
|  | ||||
| if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then | ||||
| 	echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !" | ||||
| @@ -14,19 +19,27 @@ deps="$base/deps" | ||||
| inst="$deps/install" | ||||
| export deps inst | ||||
|  | ||||
| mkdir "$deps" || true | ||||
| rm -rf "$inst" | ||||
| osmo-clean-workspace.sh | ||||
|  | ||||
| mkdir "$deps" || true | ||||
| osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false | ||||
|  | ||||
| verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]") | ||||
|  | ||||
| export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" | ||||
| export LD_LIBRARY_PATH="$inst/lib" | ||||
| export PATH="$inst/bin:$PATH" | ||||
|  | ||||
| osmo-build-dep.sh libosmo-abis | ||||
| osmo-build-dep.sh libosmo-netif | ||||
|  | ||||
| # Additional configure options and depends | ||||
| CONFIG="" | ||||
| if [ "$WITH_MANUALS" = "1" ]; then | ||||
| 	osmo-build-dep.sh osmo-gsm-manuals | ||||
| 	CONFIG="--enable-manuals" | ||||
| fi | ||||
|  | ||||
| set +x | ||||
| echo | ||||
| echo | ||||
| @@ -37,11 +50,18 @@ set -x | ||||
|  | ||||
| cd "$base" | ||||
| autoreconf --install --force | ||||
| ./configure $MGCP --enable-vty-tests --enable-external-tests | ||||
| ./configure --enable-vty-tests --enable-external-tests --enable-werror $CONFIG | ||||
| $MAKE $PARALLEL_MAKE | ||||
| LD_LIBRARY_PATH="$inst/lib" $MAKE check \ | ||||
|   || cat-testlogs.sh | ||||
| LD_LIBRARY_PATH="$inst/lib" \ | ||||
|   DISTCHECK_CONFIGURE_FLAGS="$MGCP --enable-vty-tests --enable-external-tests" \ | ||||
|   DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests $CONFIG" \ | ||||
|   $MAKE distcheck \ | ||||
|   || cat-testlogs.sh | ||||
|  | ||||
| if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then | ||||
| 	make -C "$base/doc/manuals" publish | ||||
| fi | ||||
|  | ||||
| $MAKE maintainer-clean | ||||
| osmo-clean-workspace.sh | ||||
|   | ||||
							
								
								
									
										6
									
								
								contrib/systemd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								contrib/systemd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| EXTRA_DIST = osmo-mgw.service | ||||
|  | ||||
| if HAVE_SYSTEMD | ||||
| systemdsystemunit_DATA = \ | ||||
|   osmo-mgw.service | ||||
| endif | ||||
| @@ -1,11 +0,0 @@ | ||||
| [Unit] | ||||
| Description=OpenBSC MGCP | ||||
|  | ||||
| [Service] | ||||
| Type=simple | ||||
| Restart=always | ||||
| ExecStart=/usr/bin/osmo-bsc_mgcp -s -c /etc/osmocom/osmo-bsc-mgcp.cfg | ||||
| RestartSec=2 | ||||
|  | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
							
								
								
									
										11
									
								
								contrib/systemd/osmo-mgw.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								contrib/systemd/osmo-mgw.service
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| [Unit] | ||||
| Description=Osmocom Media Gateway (MGW) | ||||
|  | ||||
| [Service] | ||||
| Type=simple | ||||
| Restart=always | ||||
| ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg | ||||
| RestartSec=2 | ||||
|  | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
							
								
								
									
										422
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										422
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,425 @@ | ||||
| osmo-mgw (1.6.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Oliver Smith ] | ||||
|   * Cosmetic: fix spaces/tabs in mgcp_requests[] | ||||
|   * Inactive connection cleanup (disabled by default) | ||||
|   * debian: create -doc subpackage with pdf manuals | ||||
|   * vty: allow 0 as conn-timeout to disable it | ||||
|   * vty: update desc of conn-timeout | ||||
|   * manuals: update VTY documentation | ||||
|   * "make dist" fix for: no rule to make mgcp_common.h | ||||
|   * contrib/jenkins.sh: run "make maintainer-clean" | ||||
|  | ||||
|   [ Philipp Maier ] | ||||
|   * Add option to GSM HR frames to RFC5593 representation | ||||
|   * mgcp_sdp: cosmetic: correct typo in comment | ||||
|   * mgco_client: cosmetic: remove excess whitespace | ||||
|   * mgcp_sdp: mgcp_sdp.c does not include own header file | ||||
|   * mgcp_sdp: untangle parsing of a= parameters | ||||
|   * mgcp_network: use mgcp_rtp_codec in downlink format callback | ||||
|   * AMR: Add function to convert between bw-effient and octet aligned mode | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * log: don't spam with "can not patch PT" messages | ||||
|   * mgcp client: allow setting conn mode for CRCX and MDCX | ||||
|   * make codec_table public as osmo_mgcpc_codec_names | ||||
|   * move MGW endpoint FSM from osmo-bsc to here | ||||
|   * fix: multiple initial CRCX | ||||
|   * constify map_codec_to_pt() ptmap arg | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * mgcp-client: whitespace cleanup in mgcp_client.c | ||||
|   * mgcp-client: Sanitize implementation of mgcp_client_rtpbridge_wildcard | ||||
|   * libosmo-mgcp: Use trunk type during endpoint allocation | ||||
|   * cosmetic: Fix typos in comment | ||||
|   * cosmetic: tests: mgcp_client_test: clean trailing whitespace | ||||
|   * mgcp_msg: Log faulty line on Osmux parsing error | ||||
|   * cosmetic: handle_modify_con: Fix indentation level | ||||
|   * Introduce log fmt helpers LOGPENDP and LOGPCONN | ||||
|   * create_response_with_sdp: Fix inclusion of X-Osmux | ||||
|   * osmux: Cleanup of CID alloc pool APIs | ||||
|   * mgcp_osmux.c: osmux_enable_endpoint: Fix incorrect return check | ||||
|   * osmux: Log osmux socket during osmux_init | ||||
|   * mgcp-cli: Change osmo_mgcpc_ep_fsm name to avoid collision with old osmo-bsc | ||||
|   * osmux: Document func and return different rc upon osmux init failure | ||||
|   * osmux: Fix reception of legacy dummy payloads | ||||
|   * osmux: Use LOGPCONN in several log calls | ||||
|   * cosmetic: mgcp_udp_send: Document port param is in network byte order | ||||
|   * cosmetic: osmux: Document network byte order in port variable | ||||
|   * vty: Allow enabling Osmux | ||||
|   * osmux: Allocate CID during CRCX | ||||
|   * osmux: Introduce mgcp_conn_rtp_is_osmux() helper | ||||
|   * osmux: Mark conn_rtp->type as osmux during CRCX | ||||
|   * mgcp-cli: Allow submitting X-Osmux on CRCX request | ||||
|   * mgcp-cli: Parse X-Osmux on CRCX response | ||||
|   * osmux: Handle Osmux MGCP extension in MDCX messages | ||||
|   * mgcp-cli: Allow submitting X-Osmux on MDCX request | ||||
|   * mgcp-cli: Parse X-Osmux on MDCX response | ||||
|   * osmux: Drop unneeded OSMUX_STATE_NEGOTIATING | ||||
|   * mgcp-cli: endpoint_fsm: Add API to retrieve Osmux CID from MGW | ||||
|   * osmux: Provide correct local port during mgcp resp | ||||
|   * osmux: Use remote port to send osmux frames | ||||
|   * osmux: Improve logging around osmux enabling events | ||||
|   * osmux: Delay osmux enable of conn until remote addr is configured by MDCX | ||||
|   * osmux: Redo read/write osmux glue code to have data routed correctly | ||||
|   * mgw, mgcp-li: Handle X-Osmux param name as case insensitive | ||||
|   * osmux: Use DUMMY ft msg as per Osmux spec | ||||
|   * osmux: Fix loopback for Osmux connections | ||||
|   * osmux: Fix CID release for non-enabled connections | ||||
|   * osmux: Fix hardcoded rtp payload_type 98 in osmux conn | ||||
|   * mgcp-cli: Validate osmux cid value during mgcp_msg_gen | ||||
|   * mgw: Support uppercase LCO options | ||||
|   * mgw: Support lowercase header parameters | ||||
|   * mgcp-cli: Support lowercase header parameters | ||||
|   * mgw: Support receiving uppercase connection mode | ||||
|   * mgw: Support receiving lowercase LCO codec | ||||
|   * mgw: Make check of duplicated LCO fields case insensitive | ||||
|   * mgw: Allow receiving lowercase MGCP header keyword | ||||
|   * mgw: Allow receiving lowercase X-Osmo-Ign Callid field | ||||
|   * mgw: Allow receiving uppercase noanswer keyword | ||||
|   * doc: X-Osmo-IGN: small formatting and typo fixes | ||||
|   * doc: Add Osmux documentation to OsmoMGW User Manual | ||||
|   * Catch unsigned integer MGCP parsing errors with strtoul | ||||
|   * Fix return variable of strtoul() | ||||
|   * Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds | ||||
|   * configure.ac: Require libosmo-netif 0.6.0 | ||||
|  | ||||
|   [ Alexander Couzens ] | ||||
|   * mgcp_internal: LOGPENDP: ensure *endp is not NULL | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * handle NULL return of rate_ctr_group_alloc() | ||||
|   * update .gitignore | ||||
|   * mgcp_sdp: Don't check if an unsigned int is below 0 | ||||
|  | ||||
|   [ Vadim Yanitskiy ] | ||||
|   * configure.ac: drop useless check for -fvisibility=hidden | ||||
|  | ||||
|   [ Daniel Willmann ] | ||||
|   * manuals: Add script to regenerate vty/counter documentation | ||||
|   * manuals: Update vty/counter documentation | ||||
|  | ||||
|   [ Hoernchen ] | ||||
|   * turn -Werror=null-dereference into a warning | ||||
|  | ||||
|  -- Pau Espin Pedrol <pespin@sysmocom.de>  Wed, 07 Aug 2019 16:52:58 +0200 | ||||
|  | ||||
| osmo-mgw (1.5.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * gitignore: Filter *.pc | ||||
|   * configure: Find correct libgsm's gsm.h header | ||||
|   * vty: Fix typo writing bts-jitter-buffer-delay-{min,max} | ||||
|   * Remove libosmo-legacy-mgcp and osmo-bsc-mgcp | ||||
|   * debian: Remove dangling symlink to osmo-bsc-mgcp.service | ||||
|   * Install systemd services with autotools | ||||
|   * Install sample cfg file to /etc/osmocom | ||||
|   * mgcp: Fix osmux_cid_bitmap static array size calculation | ||||
|   * mgcp_osmux: Use define to calculate rtp_ssrc_winlen | ||||
|   * osmux: Avoid initing output without enabling osmux | ||||
|   * mgcp: Log endpoint nr consistently as hex | ||||
|   * osmux_send_dummy: Avoid logging incorrectly and sending if osmux not enabled | ||||
|   * osmux: Don't process regular osmux frames if disabled by cfg | ||||
|   * osmux: Move parse_cid of legacy dummy frames to own function | ||||
|   * osmux: Make func handling dummy frames independent of endp type | ||||
|   * osmux: allow enabling osmux only on correct activating state | ||||
|   * osmux: Improve checks around activating and using enabled osmux | ||||
|   * osmux.h: Document enum osmux_state | ||||
|   * osmux: Avoid processing further frames if conn not found | ||||
|  | ||||
|   [ Philipp Maier ] | ||||
|   * mgcp_client_fsm: allow ptmap in mgcp_client_fsm as well | ||||
|   * mgcp_network: translate payload type numbers in RTP packets | ||||
|   * mgcp_client: use IETF source port as for MGCP | ||||
|   * mgcp_client: increment local port number when port is in use | ||||
|   * mgcp_test: release endpoints after use | ||||
|   * network: do not patch PT of RTCP packets | ||||
|   * network: check packets before further processing | ||||
|   * Cosmetic: remove misplaced line break | ||||
|   * mgcp_sdp: restructure mgcp_write_response_sdp() (rtpmap) | ||||
|   * mgcp_sdp: restructure mgcp_write_response_sdp() (audio) | ||||
|   * mgcp_client: check local port only once | ||||
|   * mgcp_client_fsm: switch to MGCP_CONN_RECV_SEND in add_audio() | ||||
|   * mgcp_protocol: increase buffer space for codec name in LCO | ||||
|   * osmo-mgw: Add vty reference manual | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * fix handling of "Wrong domain name" error | ||||
|   * interpret domain '*' as 'allow any domain' | ||||
|   * cosmetic: log: fix "CallIDs does not match" | ||||
|   * fix 3G hack: allow any IP for loopback and 0.0.0.0 | ||||
|   * cosmetic: drop code dup in mgcp_client_fsm.c CRCX | ||||
|   * add X-Osmo-IGN MGCP header to ignore CallID | ||||
|   * X-Osmo-IGN: rather parse items by token, not char | ||||
|   * mgcp_test: fix log of conn_id presence | ||||
|   * mgcp_test: fix get_conn_id_from_response() CI length | ||||
|   * mgcp_client_test: cosmetically re-arrange reply_to() args | ||||
|   * mgcp_client_test: use "\r\n\r\n" instead of "\n\n" | ||||
|   * mgcp_client_test: also verify received conn_id | ||||
|   * mgcp_client_test: test long conn_id | ||||
|   * mgcp_client: error on too long conn id | ||||
|   * mgcp_common: rename to MGCP_CONN_ID_MAXLEN | ||||
|   * doc: fix mgcp_verify_ci() return val doc | ||||
|   * mgcp_verify_ci(): return meaningful error codes | ||||
|   * fix mgcp_verify_ci(): off-by-one in max len check | ||||
|   * generate shorter 'I:' conn IDs | ||||
|   * mgcp_conn_get(): compare conn Id ('I:') case insensitively | ||||
|   * mgcp_conn_get(): match conn Id ('I:') despite leading zeros | ||||
|   * cosmetic: mgcp_test: fix get_conn_id_from_response() | ||||
|   * comment: indicate struct type for mgcp_endpoint.conns | ||||
|   * log: avoid logging early media as error | ||||
|   * fix osmo-mgw -s; fixes osmo-mgw.service using -s | ||||
|   * Importing history from osmo-gsm-manuals.git | ||||
|   * OsmoMGW: update VTY reference | ||||
|   * OsmoMGW: document the 'X-Osmo-IGN' MGCP extension | ||||
|   * mgw: update vty reference | ||||
|   * drop/replace very weird logging in mgcp_client.c | ||||
|   * check_rtp: on IP:port errors, log the IP and port | ||||
|   * osmo-mgw: err-log: include expected domain name | ||||
|   * mgcp_client_vty: fix missing talloc_free | ||||
|   * mgcp_client: drop a bunch of dead code | ||||
|   * mgcp_client: logging tweaks | ||||
|   * mgcp_client: make domain part of endpoint configurable | ||||
|   * mgcp_client: tweak some log levels INFO -> {DEBUG,ERROR} | ||||
|  | ||||
|   [ Stefan Sperling ] | ||||
|   * add VTY commands which show specific mgcp endpoints | ||||
|   * add MGCP CRCX command statistics to osmo-mgw | ||||
|   * show RTP TX/RX stats in 'mgcp show stats' output | ||||
|   * use local variable for rate counters in handle_create_con() | ||||
|   * add more mgcp crxc error counters | ||||
|   * add MDCX command statistics to osmo-mgw | ||||
|   * add aggregated rtp connection stats to osmo-mgw | ||||
|   * add DLCX command statistics to osmo-mgw | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * debian/rules: Don't overwrite .tarball-version | ||||
|   * check_rtp_origin(): Don't memcmp sockadd_in and in_addr | ||||
|   * check_rtp_origin(): Avoid using memcmp for comparing integer types | ||||
|   * vty-ref: Update URI of docbook 5.0 schema | ||||
|  | ||||
|   [ Daniel Willmann ] | ||||
|   * mgw: Add new VTY reference | ||||
|   * Add initial OsmoMGW manual | ||||
|  | ||||
|   [ Oliver Smith ] | ||||
|   * build manuals moved here from osmo-gsm-manuals.git | ||||
|   * jenkins.sh: remove leftover MGCP env variable | ||||
|   * Fix DISTCHECK_CONFIGURE_FLAGS override | ||||
|   * contrib/jenkins.sh: build and publish manuals | ||||
|   * contrib: fix makedistcheck with disabled systemd | ||||
|  | ||||
|  -- Harald Welte <laforge@gnumonks.org>  Sun, 20 Jan 2019 15:02:18 +0100 | ||||
|  | ||||
| osmo-mgw (1.4.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Philipp Maier ] | ||||
|   * network: independently initalize state->out_stream | ||||
|   * stats: use libosmocore rate counter for in/out_stream.err_ts_counter | ||||
|   * mgcp_sdp: correct apidoc of mgcp_parse_sdp_data | ||||
|   * vty: clean up rtp port-range command | ||||
|   * sdp: remove unused alt_codec field from struct mgcp_rtp_end | ||||
|   * sdp: remove circular inclusion | ||||
|   * protocol: Try whole port range on port allocation | ||||
|   * client: do not start connections in loopback mode | ||||
|   * mgcp_network: do not log destination invalid ip/port as error | ||||
|   * cosmetic: fix log output | ||||
|   * conn: call talloc_free before setting the pointer to NULL | ||||
|   * protocol: do not change LCO, when no LCO are present | ||||
|   * protocol: reject illegal lco options | ||||
|   * cosmetic: fix typo | ||||
|   * mgw: clean up codec negotiation (sdp) | ||||
|   * client: add features to generate and parse codec information | ||||
|   * mgcp_internal: remove unused struct member | ||||
|   * stats: replace packet statistic counters with libosmocore rate counters | ||||
|   * stat+vty: fix printing of rate counter values | ||||
|   * protocol: prevent unnecessary null pointer deref | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * legacy-mgcp: Add jitter buffer on the uplink receiver | ||||
|   * legacy-mgcp: switch to new osmux output APIs | ||||
|   * mgcp: mgcp_osmux: use conn_bts when forwarding pkts from bsc_nat | ||||
|   * mgcp: switch to new osmux output APIs | ||||
|   * debian: Package installed example doc files | ||||
|   * gitignore: Add m4 scripts from m4 subdir | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * api doc: fix parameter name for mgcp_conn_create() | ||||
|   * mgcp-client: add mgcp_conn_get_ci() | ||||
|   * mgcp_client_fsm: improve error logging | ||||
|   * cosmetic: fix doxygen comment markers | ||||
|   * cosmetic: mgcp_network.c: merge one LOGPC to its preceding LOGP | ||||
|   * IuUP hack: make RTP patching less general | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * cosmetic: fix typo in log message: 'abrupt' instead of 'aprupt' | ||||
|  | ||||
|   [ Daniel Willmann ] | ||||
|   * git-version-gen: Don't check for .git directory | ||||
|  | ||||
|  -- Pau Espin Pedrol <pespin@sysmocom.de>  Fri, 27 Jul 2018 19:05:22 +0200 | ||||
|  | ||||
| osmo-mgw (1.3.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * contrib: Add osmo-mgw systemd service | ||||
|   * legacy: mgcp_protocol: Don't print osmux stats if it is off | ||||
|   * mgcp_stat: Don't print osmux stats if it is off | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * fix segfault: DLCX for unknown endpoint: dont try to log NULL endpoint | ||||
|   * MGCP endpoints: parse as decimal, not hex | ||||
|   * add --enable-sanitize config option | ||||
|   * legacy_mgcp: mgcp_test: sanitize: free msgb_ctx | ||||
|   * mgcp_test: test_packet_error_detection: sanitize: free all conns | ||||
|   * mgcp_test: test_no_cycle: sanitize: free endp | ||||
|   * mgcp_test: sanitize: free msgb_ctx | ||||
|   * mgcp_client: don't configure "bts base" | ||||
|   * Revert "mgcp_client: don't configure "bts base"" until osmo-msc is ready | ||||
|   * mgcp_client: add transaction cleanup | ||||
|   * mgcp_client_test makefile: add update_exp target | ||||
|   * cosmetic: mgcp_network: typo in log | ||||
|   * osmo-mgw: Add talloc context introspection via VTY | ||||
|   * mgcp_client: show failure by MGCP SDP section parsing test | ||||
|   * mgcp_client: cosmetic: clean up SDP params parsing | ||||
|   * mgcp_client: detect SDP section-start parsing errors | ||||
|   * compiler warning: ignore deprecated in mgcp_client_test.c | ||||
|   * configure: add --enable-werror | ||||
|   * jenkins.sh: add --enable-werror to configure flags | ||||
|   * cosmetic: mgcp, legacy_mgcp: drop unused vty.h definitions | ||||
|   * use osmo_init_logging2() with proper talloc ctx | ||||
|  | ||||
|   [ Philipp Maier ] | ||||
|   * osmux: fix nullpointer dereference | ||||
|   * cosmetic: guard dead osmux vty code with ifdef | ||||
|   * cosmetic: remove prefix "net" from rtp related vty commands | ||||
|   * doc: update sample config file | ||||
|   * cosmetic: use correct VTY port number constant | ||||
|   * vty: simplify endpoint allocation | ||||
|   * vty: do not change number_endpoints at runtime | ||||
|   * MGCP: Connection Identifiers are hex strings | ||||
|   * libosmo-mgcp: Connection Identifiers are allocated by MGW, not CA | ||||
|   * client: use osmo_strlcpy instead of strncpy | ||||
|   * cosmetic: fix sourcecode formatting | ||||
|   * cosmetic: clearly mark endpoint numbers as hex | ||||
|   * client: use string as connection identifier | ||||
|   * conn: remove assertions | ||||
|   * mgcp_test: fix wrong strcmp() parameters | ||||
|   * mgcp_test: fix nullpointer dereference | ||||
|   * mgcp_test: add returncode check | ||||
|   * mgcp_test: fix possible double free | ||||
|   * mcgp_client: mgcp_msg_gen(): add checks to verify params | ||||
|   * network: use originating RTP packet address for loopback | ||||
|   * client: mgcp_response_parse_params: check rtp port | ||||
|   * mgcp: allow endpoints beginning from zero | ||||
|   * client/common: move constant MGCP_ENDPOINT_MAXLEN | ||||
|   * mgcp: make domain name configurable | ||||
|   * cosmetic: protocol: remove unnecessary nul termination | ||||
|   * client: do not insist on \n\n when parsing MGCP messages | ||||
|   * main: display mgcp ip/port | ||||
|   * client: make callid in MDCX mandatory | ||||
|   * client: add missing mandatory SDP fields | ||||
|   * mgcp: permit wildcarded endpoint assignment (CRCX) | ||||
|   * mgcp: add prefix to virtual trunk | ||||
|   * client: eliminate destructive parameter parsing | ||||
|   * client: eliminate destructive head parsing | ||||
|   * cosmetic: client: add doxygen comments | ||||
|   * protocol: fix problem with line break and OSMUX | ||||
|   * protocol: fix missing carriage return | ||||
|   * client: fix sdp parameter ordering | ||||
|   * protocol: check the packetization in local cx options | ||||
|   * cosmetic: remove spaces from pointer symbol | ||||
|   * client: Do not accept endpoint ids with wildcards in responses | ||||
|   * client: do not accept endpoint ids without @ character in responses | ||||
|   * client: prohibit endpoint ids without @ character | ||||
|   * protocol: on wildcarded CRCX return endpoint number as hex | ||||
|   * msg: fix response code on exhausted endp resources | ||||
|   * cosmetic: move mgcp_release_endp() to mgcp_ep.c | ||||
|   * client: use heap to store mgcp_response | ||||
|   * ep: move endpoint struct and define to mgcp_ep.h | ||||
|   * cosmetic: rename mgcp_release_endp to mgcp_endp_release | ||||
|   * cosmetic: rename mgcp_ep.c/h to mgcp_endp.c/h | ||||
|   * protocol: reject DLCX/CRCX/MDCX on unsupported parameters | ||||
|   * protocol: exit cleanly when local cx options check fails | ||||
|   * cosmetic: Add missing \n on log line | ||||
|   * protocol: check requested connection mode | ||||
|   * protocol: fix tagging of wildcarded requests | ||||
|   * protocol: prohibit wildcarded requests for MDCX and DLCX | ||||
|   * mgcp: fix use-after-free and add callback for endpoint cleanup | ||||
|   * client: add an optional FSM interface | ||||
|   * mgcp_client_fsm: Add FSM event names | ||||
|   * cosmetic: mgcp_client_fsm: rename enums | ||||
|   * cosmetic: rename function .._conn_reset() to .._conn_init() | ||||
|   * mgcp_conn: do not touch u.rtp in mgcp_conn_alloc() | ||||
|   * cosmetic: rename .._codec_reset() to .._codec_init() | ||||
|   * mgcp_conn: add function mgcp_rtp_conn_cleanup() | ||||
|   * stats: use libosmocore rate counter for in/out_stream.err_ts_counter | ||||
|  | ||||
|   [ Alexander Couzens ] | ||||
|   * debian/control: correct library dependency of osmo-mgw against libosmo-mgcp1 | ||||
|   * debian: include systemd service osmo-mgw.service | ||||
|   * Revert "stats: use libosmocore rate counter for in/out_stream.err_ts_counter" | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * cosmetic: fix whitespaces; we use tabs for indentation | ||||
|   * Fix possible buffer overflow in mgcp_conn_dump() | ||||
|   * osmo-mgw: Update copyright statement | ||||
|   * osmo-mgw: Config file is osmo-mgw.cfg, and not mgcp.cfg | ||||
|   * osmo-mgw: Fix copyright notice | ||||
|   * strct mgcp_rtp_state: Group + document struct members related to patching | ||||
|   * mgcp_rtp_state: grup 'stats' members into sub-structure | ||||
|   * mgcp_rtp_end: Group statistics members into 'stats' sub-struct | ||||
|   * libosmo-mgcp: Cosmetic spelling fixes in comments | ||||
|   * mgcp_msg: We must parse endpoint numbers as hex, not decimal! | ||||
|   * mgcp_internal.h: document more struct members with comments | ||||
|   * centralize handling of common errors like "endpoint not found" | ||||
|   * Return proper MGCP Error codes, as per spec | ||||
|   * osmo-mgw: Use libosmocore socket abstraction | ||||
|   * osmo-bsc_mgcp: Add LIBOSMONETIF_{CFLAGS,LIBS} | ||||
|   * libosmo-mgcp-client is GPLv2+, not AGPLv3+ | ||||
|   * Turn libosmo-mgcp into local, non-installed library | ||||
|  | ||||
|   [ Stefan Sperling ] | ||||
|   * enable osmo_fsm vty commands in libosmo-mgcp-client vty | ||||
|  | ||||
|  -- Pau Espin Pedrol <pespin@sysmocom.de>  Thu, 03 May 2018 17:40:35 +0200 | ||||
|  | ||||
| osmo-mgw (1.2.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * jenkins: use osmo-clean-workspace.sh before and after build | ||||
|   * vty: skip installing cmds now always installed by default | ||||
|   * mgcp-client vty: use name 'mgw' instead of 'mgcpgw' | ||||
|   * mgcp client: vty: tweak doc strings | ||||
|  | ||||
|   [ Philipp Maier ] | ||||
|   * sdp: refactoring sdp parser/generator | ||||
|   * cosmetic: rename bts_codec to codec_str | ||||
|   * cosmetic: fix coding style for mgcp_parse_sdp_data() | ||||
|   * cosmetic: fix commenting style | ||||
|   * cosmetic: correct whitespaces | ||||
|   * client: fix stderror logging in unit-test | ||||
|   * client: add unified function to generate MGCP messages | ||||
|   * client: add ip address parsing to the client | ||||
|   * protocol: allow wildcarded DLCX | ||||
|   * mgcp: remove port/timeslot calculator functions from  mgcp.h | ||||
|   * network: add separate log category | ||||
|   * cosmetic: make dummy packet handling more explicit | ||||
|   * network: autdetect rtp bind ip-address | ||||
|   * network: fix rtp packet length | ||||
|   * network: remove unused return code | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * mgcp_client_vty.c: Fix VTY compatibility with 'mgcpgw bts-base' | ||||
|  | ||||
|  -- Harald Welte <laforge@gnumonks.org>  Fri, 10 Nov 2017 11:10:23 +0900 | ||||
|  | ||||
| osmo-mgw (1.1.0) unstable; urgency=medium | ||||
|  | ||||
|   * New upstream release | ||||
|  | ||||
|  -- Harald Welte <lafore@gnumonks.org>  Sat, 28 Oct 2017 12:48:41 +0200 | ||||
|  | ||||
| osmo-mgw (1.0.2) unstable; urgency=low | ||||
|  | ||||
|   * First release after major rename. | ||||
|   | ||||
							
								
								
									
										33
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -7,7 +7,8 @@ Build-Depends: debhelper (>=9), | ||||
|                pkg-config, | ||||
|                autotools-dev, | ||||
|                libosmocore-dev, | ||||
|                libosmo-netif-dev | ||||
|                libosmo-netif-dev, | ||||
|                osmo-gsm-manuals-dev | ||||
| Standards-Version: 3.9.8 | ||||
| Vcs-Git: git://git.osmocom.org/osmo-mgw.git | ||||
| Vcs-Browser: https://git.osmocom.org/osmo-mgw/ | ||||
| @@ -16,25 +17,10 @@ Homepage: https://osmocom.org/projects/osmo-mgw | ||||
| Package: osmo-mgw | ||||
| Architecture: any | ||||
| Multi-Arch: foreign | ||||
| Depends: libosmo-legacy-mgcp0, ${misc:Depends}, ${shlibs:Depends} | ||||
| Depends: ${misc:Depends}, ${shlibs:Depends} | ||||
| Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks | ||||
|  | ||||
| Package: libosmo-legacy-mgcp0 | ||||
| Section: libs | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| Pre-Depends: ${misc:Pre-Depends} | ||||
| Depends: ${misc:Depends}, ${shlibs:Depends} | ||||
| Description: libosmo-legacy-mgcp: Osmocom's Media Gateway server library | ||||
|  | ||||
| Package: libosmo-legacy-mgcp-dev | ||||
| Section: libdevel | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| Depends: libosmo-legacy-mgcp0 (= ${binary:Version}), ${misc:Depends} | ||||
| Description: libosmo-legacy-mgcp: Osmocom's Media Gateway server library | ||||
|  | ||||
| Package: libosmo-mgcp-client1 | ||||
| Package: libosmo-mgcp-client6 | ||||
| Section: libs | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| @@ -46,5 +32,14 @@ Package: libosmo-mgcp-client-dev | ||||
| Section: libdevel | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| Depends: libosmo-mgcp-client1 (= ${binary:Version}), ${misc:Depends} | ||||
| Depends: libosmo-mgcp-client6 (= ${binary:Version}), ${misc:Depends} | ||||
| Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities | ||||
|  | ||||
| Package: osmo-mgw-doc | ||||
| Architecture: all | ||||
| Section: doc | ||||
| Priority: optional | ||||
| Depends: ${misc:Depends} | ||||
| Description: ${misc:Package} PDF documentation | ||||
|  Various manuals: user manual, VTY reference manual and/or | ||||
|  protocol/interface manuals. | ||||
|   | ||||
							
								
								
									
										29
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							| @@ -21,11 +21,12 @@ License:   AGPL-3.0+ | ||||
|  You should have received a copy of the GNU Affero General Public License | ||||
|  along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| Files:     src/libosmo-legacy-mgcp/g711common.h | ||||
| Copyright: 2000 Abramo Bagnara <abramo@alsa-project.org> | ||||
| Files:     src/libosmo-mgcp-client/* include/osmocom/mgcp_client/* | ||||
| Copyright: 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de> | ||||
|            Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c) | ||||
|            2009 by Harald Welte <laforge@gnumonks.org> | ||||
|            2009-2011 by Holger Hans Peter Freyther | ||||
| License:   GPL-2.0+ | ||||
|  Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org> | ||||
|  . | ||||
|  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 | ||||
| @@ -37,13 +38,7 @@ License:   GPL-2.0+ | ||||
|  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, write to the Free Software | ||||
|  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  . | ||||
|  The FSF address in the above text is the old one. | ||||
|  . | ||||
|  On Debian systems, the complete text of the GNU General Public License | ||||
|  Version 2 can be found in `/usr/share/common-licenses/GPL-2'. | ||||
|  along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| Files:     tests/vty_test_runner.py | ||||
| Copyright: 2013 Holger Hans Peter Freyther | ||||
| @@ -68,15 +63,3 @@ License:   GPL-3.0+ | ||||
| Files:     osmoappdesc.py | ||||
| Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com> | ||||
| License:   GPL-3.0+ | ||||
|  | ||||
| Files:     src/libosmo-legacy-mgcp/mgcp_osmux.c | ||||
| Copyright: 2012-2013 On Waves ehf <http://www.on-waves.com> | ||||
|            2012-2013 Pablo Neira Ayuso <pablo@gnumonks.org> | ||||
| License:   AGPL-3.0+ | ||||
|  All rights not specifically granted under this license are 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. | ||||
|  | ||||
|   | ||||
							
								
								
									
										4
									
								
								debian/libosmo-legacy-mgcp-dev.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/libosmo-legacy-mgcp-dev.install
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +0,0 @@ | ||||
| usr/include/osmocom/legacy_mgcp | ||||
| usr/lib/*/*legacy-mgcp*.so | ||||
| usr/lib/*/*legacy-mgcp*.a | ||||
| usr/lib/*/pkgconfig/*legacy-mgcp*.pc | ||||
							
								
								
									
										1
									
								
								debian/libosmo-legacy-mgcp0.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/libosmo-legacy-mgcp0.install
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | ||||
| usr/lib/*/*legacy-mgcp*.so.* | ||||
							
								
								
									
										1
									
								
								debian/libosmo-mgcp-client1.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/libosmo-mgcp-client1.install
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | ||||
| usr/lib/*/*mgcp-client*.so.* | ||||
							
								
								
									
										1
									
								
								debian/libosmo-mgcp-client6.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/libosmo-mgcp-client6.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| usr/lib/*/libosmo-mgcp-client.so.* | ||||
							
								
								
									
										1
									
								
								debian/osmo-mgw-doc.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-mgw-doc.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| usr/share/doc/osmo-mgw-doc/*.pdf | ||||
							
								
								
									
										5
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,4 @@ | ||||
| usr/bin | ||||
| etc/osmocom/osmo-mgw.cfg | ||||
| lib/systemd/system/osmo-mgw.service | ||||
| usr/bin/osmo-mgw | ||||
| usr/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw.cfg | ||||
|   | ||||
							
								
								
									
										14
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							| @@ -14,7 +14,7 @@ CFLAGS += -g | ||||
|  | ||||
| # main packaging script based on dh7 syntax | ||||
| %: | ||||
| 	dh $@ --with autoreconf  | ||||
| 	dh $@ --with autoreconf | ||||
|  | ||||
| # debmake generated override targets | ||||
| # Set options for ./configure | ||||
| @@ -26,8 +26,14 @@ CFLAGS += -g | ||||
| #override_dh_install: | ||||
| #	dh_install --list-missing -X.la -X.pyc -X.pyo | ||||
|  | ||||
| override_dh_autoreconf: | ||||
| 	echo $(VERSION) > .tarball-version | ||||
| 	dh_autoreconf | ||||
| override_dh_auto_test: | ||||
| 	dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false) | ||||
|  | ||||
| override_dh_auto_configure: | ||||
| 	dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals | ||||
|  | ||||
| # Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers) | ||||
| override_dh_compress: | ||||
| 	dh_compress -X.pdf | ||||
|  | ||||
| # See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| SUBDIRS = \ | ||||
| 	examples \ | ||||
| 	manuals \ | ||||
| 	$(NULL) | ||||
|   | ||||
| @@ -1,3 +1,11 @@ | ||||
| OSMOCONF_FILES = \ | ||||
| 	osmo-mgw/osmo-mgw.cfg | ||||
|  | ||||
| osmoconfdir = $(sysconfdir)/osmocom | ||||
| osmoconf_DATA = $(OSMOCONF_FILES) | ||||
|  | ||||
| EXTRA_DIST = $(OSMOCONF_FILES) | ||||
|  | ||||
| CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,' | ||||
|  | ||||
| dist-hook: | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| ! | ||||
| ! MGCP configuration example | ||||
| ! | ||||
| mgcp | ||||
|  !local ip 10.23.24.2 | ||||
|  !bts ip 10.24.24.1 | ||||
|  !bind ip 10.23.24.1 | ||||
|  bind port 2427 | ||||
|  rtp base 4000 | ||||
|  rtp force-ptime 20 | ||||
|  sdp audio payload number 98 | ||||
|  sdp audio payload name AMR/8000 | ||||
|  number endpoints 31 | ||||
|  no rtcp-omit | ||||
							
								
								
									
										18
									
								
								doc/examples/osmo-mgw/osmo-mgw.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								doc/examples/osmo-mgw/osmo-mgw.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| ! | ||||
| ! MGCP configuration example | ||||
| ! | ||||
| mgcp | ||||
|   bind ip 127.0.0.1 | ||||
|   rtp port-range 4002 16000 | ||||
|   rtp bind-ip 10.9.1.122 | ||||
|   rtp ip-probing | ||||
|   rtp ip-tos 184 | ||||
|   bind port 2427 | ||||
|   sdp audio payload number 98 | ||||
|   sdp audio payload name GSM | ||||
|   number endpoints 31 | ||||
|   loop 0 | ||||
|   force-realloc 1 | ||||
|   rtcp-omit | ||||
|   rtp-patch ssrc | ||||
|   rtp-patch timestamp | ||||
							
								
								
									
										18
									
								
								doc/manuals/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								doc/manuals/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| EXTRA_DIST = osmomgw-usermanual.adoc \ | ||||
|     osmomgw-usermanual-docinfo.xml \ | ||||
|     osmomgw-vty-reference.xml \ | ||||
|     regen_doc.sh \ | ||||
|     chapters \ | ||||
|     vty | ||||
|  | ||||
| if BUILD_MANUALS | ||||
|   ASCIIDOC = osmomgw-usermanual.adoc | ||||
|   ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc | ||||
|   include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc | ||||
|  | ||||
|   VTY_REFERENCE = osmomgw-vty-reference.xml | ||||
|   include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc | ||||
|  | ||||
|   OSMO_REPOSITORY = osmo-mgw | ||||
|   include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc | ||||
| endif | ||||
							
								
								
									
										57
									
								
								doc/manuals/chapters/configuration.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								doc/manuals/chapters/configuration.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| == Configuring OsmoMGW | ||||
|  | ||||
| A basic configation of OsmoMGW mainly consists of specifying the IP address | ||||
| and port on which to listen to MGCP commands, but changing the port range at | ||||
| which the RTP streams terminate as well as limiting operation to a single call | ||||
| agent can be done as well as changing the number of endpoints. | ||||
|  | ||||
| === Configuring MGCP | ||||
|  | ||||
| By default OsmoMGW listens for MGCP on port 2427 on any IP address. If a call | ||||
| agent address is configured then OsmoMGW will only answer MGCP commands from | ||||
| that IP port 2727, otherwise all sources are handled. A domain can be | ||||
| specified | ||||
|  | ||||
| .Example: MGCP configuration | ||||
| ---- | ||||
| OsmoMGW(config-mgcp)# bind ip 127.0.0.1 | ||||
| OsmoMGW(config-mgcp)# bind port 2427 | ||||
| OsmoMGW(config-mgcp)# call-agent ip 127.0.0.1 | ||||
| OsmoMGW(config-mgcp)# domain mgw-bsc | ||||
| OsmoMGW(config-mgcp)# local ip 127.0.0.1 | ||||
| ---- | ||||
|  | ||||
| === Configuring the trunk | ||||
|  | ||||
| The first trunk (trunk 0) is considered a virtual trunk in OsmoMGW. All | ||||
| endpoints of type "rtpbridge" are routed here. The virtual trunk is configured | ||||
| in the config-mgcp context. | ||||
|  | ||||
| All other trunks are configured in the config-mgcp-trunk context, but the | ||||
| commands used are identical. Right now trunks are considered only for ds/e1 | ||||
| type endpoints which are not yet implemented. Don't use trunks other than the | ||||
| "virtual" trunk 0. | ||||
|  | ||||
| .Example: MGCP trunk configuration | ||||
| ---- | ||||
| OsmoMGW(config-mgcp)# number endpoints 63 <1> | ||||
| OsmoMGW(config-mgcp)# rtp bind-ip 10.0.0.1 <2> | ||||
| OsmoMGW(config-mgcp)# rtp port-range 12000-14000 <3> | ||||
| ---- | ||||
| <1> Maximum number of endpoints that can be allocated at once | ||||
| <2> Use this IP when binding RTP endpoints | ||||
| <3> Use ports in this range when binding RTP endpoints | ||||
|  | ||||
| There are some options to tweak how RTP forwarding behaves in OsmoMGW: | ||||
|  | ||||
| .Example: MGCP trunk rtp options | ||||
| ---- | ||||
| OsmoMGW(config-mgcp)# rtp keep-alive 30 <1> | ||||
| OsmoMGW(config-mgcp)# rtp-patch ssrc <2> | ||||
| OsmoMGW(config-mgcp)# rtp-patch timestamp <3> | ||||
| ---- | ||||
| <1> Send dummy UDP packets periodically to RTP destination | ||||
| <2> Hide SSRC changes | ||||
| <3> Ensure RTP timestamp is aligned with frame duration | ||||
|  | ||||
|  | ||||
							
								
								
									
										4
									
								
								doc/manuals/chapters/counters.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								doc/manuals/chapters/counters.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| [[counters]] | ||||
| == Counters | ||||
|  | ||||
| include::./counters_generated.adoc[] | ||||
							
								
								
									
										83
									
								
								doc/manuals/chapters/counters_generated.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								doc/manuals/chapters/counters_generated.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
|  | ||||
| // autogenerated by show asciidoc counters | ||||
| These counters and their description based on OsmoMGW 1.5.0.64-189f (OsmoMGW). | ||||
|  | ||||
| === Rate Counters | ||||
|  | ||||
| // generating tables for rate_ctr_group | ||||
| // rate_ctr_group table aggregated statistics for all rtp connections | ||||
| .all_rtp_conn - aggregated statistics for all rtp connections | ||||
| [options="header"] | ||||
| |=== | ||||
| | Name | Reference | Description | ||||
| | all_rtp:err_tstmp_in | <<all_rtp_conn_all_rtp:err_tstmp_in>> | Total inbound rtp-stream timestamp errors. | ||||
| | all_rtp:err_tstmp_out | <<all_rtp_conn_all_rtp:err_tstmp_out>> | Total outbound rtp-stream timestamp errors. | ||||
| | all_rtp:packets_rx | <<all_rtp_conn_all_rtp:packets_rx>> | Total inbound rtp packets. | ||||
| | all_rtp:octets_rx | <<all_rtp_conn_all_rtp:octets_rx>> | Total inbound rtp octets. | ||||
| | all_rtp:packets_tx | <<all_rtp_conn_all_rtp:packets_tx>> | Total outbound rtp packets. | ||||
| | all_rtp:octets_tx | <<all_rtp_conn_all_rtp:octets_tx>> | Total outbound rtp octets. | ||||
| | all_rtp:dropped | <<all_rtp_conn_all_rtp:dropped>> | Total dropped rtp packets. | ||||
| | all_rtp:num_closed_conns | <<all_rtp_conn_all_rtp:num_closed_conns>> | Total number of rtp connections closed. | ||||
| |=== | ||||
| // rate_ctr_group table dlcx statistics | ||||
| .dlcx - dlcx statistics | ||||
| [options="header"] | ||||
| |=== | ||||
| | Name | Reference | Description | ||||
| | dlcx:success | <<dlcx_dlcx:success>> | DLCX command processed successfully. | ||||
| | dlcx:wildcard | <<dlcx_dlcx:wildcard>> | wildcard names in DLCX commands are unsupported. | ||||
| | dlcx:no_conn | <<dlcx_dlcx:no_conn>> | endpoint specified in DLCX command has no active connections. | ||||
| | dlcx:callid | <<dlcx_dlcx:callid>> | CallId specified in DLCX command mismatches endpoint's CallId . | ||||
| | dlcx:connid | <<dlcx_dlcx:connid>> | connection ID specified in DLCX command does not exist on endpoint. | ||||
| | dlcx:unhandled_param | <<dlcx_dlcx:unhandled_param>> | unhandled parameter in DLCX command. | ||||
| | dlcx:rejected | <<dlcx_dlcx:rejected>> | connection deletion rejected by policy. | ||||
| | dlcx:deferred | <<dlcx_dlcx:deferred>> | connection deletion deferred by policy. | ||||
| |=== | ||||
| // rate_ctr_group table mdcx statistics | ||||
| .mdcx - mdcx statistics | ||||
| [options="header"] | ||||
| |=== | ||||
| | Name | Reference | Description | ||||
| | mdcx:success | <<mdcx_mdcx:success>> | MDCX command processed successfully. | ||||
| | mdcx:wildcard | <<mdcx_mdcx:wildcard>> | wildcard endpoint names in MDCX commands are unsupported. | ||||
| | mdcx:no_conn | <<mdcx_mdcx:no_conn>> | endpoint specified in MDCX command has no active connections. | ||||
| | mdcx:callid | <<mdcx_mdcx:callid>> | invalid CallId specified in MDCX command. | ||||
| | mdcx:connid | <<mdcx_mdcx:connid>> | invalid connection ID specified in MDCX command. | ||||
| | crcx:unhandled_param | <<mdcx_crcx:unhandled_param>> | unhandled parameter in MDCX command. | ||||
| | mdcx:no_connid | <<mdcx_mdcx:no_connid>> | no connection ID specified in MDCX command. | ||||
| | mdcx:conn_not_found | <<mdcx_mdcx:conn_not_found>> | connection specified in MDCX command does not exist. | ||||
| | mdcx:invalid_mode | <<mdcx_mdcx:invalid_mode>> | invalid connection mode in MDCX command. | ||||
| | mdcx:conn_opt | <<mdcx_mdcx:conn_opt>> | connection options invalid. | ||||
| | mdcx:no_remote_conn_desc | <<mdcx_mdcx:no_remote_conn_desc>> | no opposite end specified for connection. | ||||
| | mdcx:start_rtp_failure | <<mdcx_mdcx:start_rtp_failure>> | failure to start RTP processing. | ||||
| | mdcx:conn_rejected | <<mdcx_mdcx:conn_rejected>> | connection rejected by policy. | ||||
| | mdcx:conn_deferred | <<mdcx_mdcx:conn_deferred>> | connection deferred by policy. | ||||
| |=== | ||||
| // rate_ctr_group table crxc statistics | ||||
| .crcx - crxc statistics | ||||
| [options="header"] | ||||
| |=== | ||||
| | Name | Reference | Description | ||||
| | crcx:success | <<crcx_crcx:success>> | CRCX command processed successfully. | ||||
| | crcx:bad_action | <<crcx_crcx:bad_action>> | bad action in CRCX command. | ||||
| | crcx:unhandled_param | <<crcx_crcx:unhandled_param>> | unhandled parameter in CRCX command. | ||||
| | crcx:missing_callid | <<crcx_crcx:missing_callid>> | missing CallId in CRCX command. | ||||
| | crcx:invalid_mode | <<crcx_crcx:invalid_mode>> | invalid connection mode in CRCX command. | ||||
| | crcx:limit_exceeded | <<crcx_crcx:limit_exceeded>> | limit of concurrent connections was reached. | ||||
| | crcx:unkown_callid | <<crcx_crcx:unkown_callid>> | unknown CallId in CRCX command. | ||||
| | crcx:alloc_conn_fail | <<crcx_crcx:alloc_conn_fail>> | connection allocation failure. | ||||
| | crcx:no_remote_conn_desc | <<crcx_crcx:no_remote_conn_desc>> | no opposite end specified for connection. | ||||
| | crcx:start_rtp_failure | <<crcx_crcx:start_rtp_failure>> | failure to start RTP processing. | ||||
| | crcx:conn_rejected | <<crcx_crcx:conn_rejected>> | connection rejected by policy. | ||||
| | crcx:no_osmux | <<crcx_crcx:no_osmux>> | no osmux offered by peer. | ||||
| | crcx:conn_opt | <<crcx_crcx:conn_opt>> | connection options invalid. | ||||
| | crcx:codec_nego | <<crcx_crcx:codec_nego>> | codec negotiation failure. | ||||
| | crcx:bind_port | <<crcx_crcx:bind_port>> | port bind failure. | ||||
| |=== | ||||
| == Osmo Stat Items | ||||
|  | ||||
| // generating tables for osmo_stat_items | ||||
| == Osmo Counters | ||||
|  | ||||
| // generating tables for osmo_counters | ||||
| // there are no ungrouped osmo_counters | ||||
							
								
								
									
										72
									
								
								doc/manuals/chapters/mgcp_extensions.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								doc/manuals/chapters/mgcp_extensions.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| == MGCP Extensions | ||||
|  | ||||
| The MGCP protocol is extendable. The following non-standard extensions are | ||||
| understood by OsmoMGW. | ||||
|  | ||||
| === `X-Osmo-IGN` | ||||
|  | ||||
| `X-Osmo-IGN` indicates to OsmoMGW that specific items of an endpoint should be | ||||
| ignored, so that it is lenient on mismatching values that would normally | ||||
| indicate collisions or configuration errors. | ||||
|  | ||||
| ==== `X-Osmo-IGN` Format | ||||
|  | ||||
| The value part of `X-Osmo-IGN` must be one or more items separated by one or more | ||||
| spaces. Each item consists of one or more non-whitespace characters. | ||||
|  | ||||
| .Example: `X-Osmo-IGN` format with three ficticious items "X", "abc" and "123". | ||||
| ---- | ||||
| X-Osmo-IGN: X abc 123 | ||||
| ---- | ||||
|  | ||||
| `X-Osmo-IGN` must be issued in the MGCP header section (typically as its last item), | ||||
| before the SDP section starts. | ||||
|  | ||||
| ==== Supported `X-Osmo-IGN` Items | ||||
|  | ||||
| Currently, the following `X-Osmo-IGN` items are supported: | ||||
|  | ||||
| * `C`: ignore CallID mismatches, i.e. differing "C" values between connections | ||||
|   on the same endpoint. | ||||
|  | ||||
| .Note: | ||||
| `X-Osmo-IGN` does not support ignoring mismatches on the domain part of | ||||
| an endpoint name, e.g. ignoring a mismatch on "example.com" in | ||||
| `rtpbridge/123abc@example.com`. Instead, you may globally configure OsmoMGW | ||||
| with `mgcp` / `domain *` to permit all domain parts. | ||||
|  | ||||
| ===== `X-Osmo-IGN: C` | ||||
|  | ||||
| By default, OsmoMGW verifies that all CallIDs ("C" values) match for all | ||||
| connections on any one given endpoint. To ignore CallID mismatches, pass a `C` | ||||
| in the `X-Osmo-IGN` header, for the first or the second `CRCX` on an endpoint. | ||||
| When the `X-Osmo-IGN: C` is sent for any one `CRCX` on an endpoint, CallID | ||||
| mismatches will be ignored for that and all subsequent messages for that | ||||
| endpoint. Hence this header only needs to be included once per endpoint, in any | ||||
| `CRCX` message that precedes or coincides with a CallID mismatch. | ||||
|  | ||||
| This is particularly useful for a BSC that is connected to an A/SCCPlite MSC, | ||||
| where the BSC and MSC each are expected to configure their respective own | ||||
| connection on a shared endpoint. For A/SCCPlite, it is impossible for the BSC | ||||
| to know the CallID that the MSC will use, so CallID mismatches are inevitable. | ||||
| See also OsmoBSC, which will by default pass the `X-Osmo-IGN: C` header for | ||||
| endpoints associated with an A/SCCPlite MSC. | ||||
|  | ||||
| .Example: `CRCX` message that instructs OsmoMGW to ignore CallID mismatches | ||||
| ---- | ||||
| CRCX 2 rtpbridge/123abc@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| L: p:20 | ||||
| X-Osmo-IGN: C | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 97 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| a=ptime:40 | ||||
| ---- | ||||
|  | ||||
| === `X-Osmux` | ||||
|  | ||||
| See <<mgcp-extension-osmux>> | ||||
							
								
								
									
										100
									
								
								doc/manuals/chapters/overview.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								doc/manuals/chapters/overview.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| [[overview]] | ||||
| == Overview | ||||
|  | ||||
| This manual should help you getting started with OsmoMGW. It will cover | ||||
| aspects of configuring and running the media gateway. | ||||
|  | ||||
| [[intro_overview]] | ||||
| === About OsmoMGW | ||||
|  | ||||
| OsmoMGW is the Osmocom implementation of a media gateway to handle user | ||||
| plane (voice) traffic in cellular networks. It can connect and optionally | ||||
| transcode RTP voice streams between different network elements such as BTSs | ||||
| and external entities like SIP. It is typically co-located with both OsmoBSC | ||||
| and OsmoMSC and controlled by them via MGCP, the Media Gateway Control | ||||
| Protocol. | ||||
|  | ||||
| [[fig-bsc]] | ||||
| .OsmoMGW used with OsmoBSC | ||||
| [graphviz] | ||||
| ---- | ||||
| digraph G { | ||||
|     rankdir = LR; | ||||
|     OsmoBTS -> OsmoBSC [label="Abis/IP"]; | ||||
|     OsmoBSC -> OsmoMSC [label="3GPP AoIP"]; | ||||
|     OsmoBSC -> OsmoMGW [label="MGCP"]; | ||||
|     OsmoBTS -> OsmoMGW [label="RTP",dir=both]; | ||||
|     OsmoMGW -> OsmoMSC [label="RTP",dir=both]; | ||||
|     {rank=same OsmoBSC OsmoMGW} | ||||
|     OsmoMGW [color=red]; | ||||
| } | ||||
| ---- | ||||
|  | ||||
| [[fig-msc]] | ||||
| .OsmoMGW used with OsmoMSC | ||||
| [graphviz] | ||||
| ---- | ||||
| digraph G { | ||||
|     rankdir = LR; | ||||
|     BTS -> BSC [label="Abis"]; | ||||
|     BSC -> OsmoMSC [label="3GPP AoIP"]; | ||||
|     OsmoMSC -> OsmoMGW [label="MGCP"]; | ||||
|     BSC -> OsmoMGW [label="RTP",dir=both]; | ||||
|     OsmoMSC -> OsmoSIP [label="MNCC"]; | ||||
|     OsmoSIP -> PBX [label="SIP Trunk"]; | ||||
|     OsmoMGW -> PBX [label="RTP",dir=both]; | ||||
|     {rank=same OsmoMSC OsmoMGW} | ||||
|     OsmoSIP [label="osmo-sip-connector"]; | ||||
|     OsmoMGW [color=red]; | ||||
|  | ||||
|     hNodeB -> OsmoHNBGW [label="Iuh"]; | ||||
|     OsmoHNBGW -> OsmoMSC [label="IuCS"]; | ||||
|     hNodeB -> OsmoMGW [label="RTP",dir=both]; | ||||
| } | ||||
| ---- | ||||
|  | ||||
| === Software Components | ||||
|  | ||||
| OsmoMGW contains a variety of different software components, which we’ll | ||||
| quickly describe in this section. | ||||
|  | ||||
| ==== MGCP Implementation | ||||
|  | ||||
| OsmoMGW brings its own MGCP implementation through which OsmoMGW is | ||||
| controlled. | ||||
|  | ||||
| The commands implemented are CRCX, MDCX, DLCX and RSIP. The command AUEP is | ||||
| implemented as a stub and will simply respond with OK. | ||||
|  | ||||
| ==== RTP implementation | ||||
|  | ||||
| Support for IuUP which is used in 3G cells is quite lacking at the moment. | ||||
| 3G<->3G and 2G<->2G calls should work, but 3G<->2G does not. | ||||
|  | ||||
| ==== Audio transcoder | ||||
|  | ||||
| Transcoding is currently not supported in OsmoMGW. | ||||
|  | ||||
| === Limitations | ||||
|  | ||||
| Osmux is not yet supported in OsmoMGW. | ||||
|  | ||||
| At the moment (July 2018), OsmoMGW only implements RTP proxy / RTP bridge | ||||
| type endpoints, to each of which two RTP connections can be established. | ||||
| We are planning to add endpoint types for: | ||||
|  | ||||
| - classic E1/T1 timeslots (64kBps alaw/ulaw) | ||||
| - classic E1/T1 16k sub-slots with TRAU frames for classic BTS support | ||||
| - announcement/playout end-points | ||||
| - conference endpoints | ||||
|  | ||||
| === Additional resources | ||||
|  | ||||
| You can find the OsmoMGW issue tracker and wiki online at | ||||
|  | ||||
| - https://osmocom.org/projects/osmomgw | ||||
| - https://osmocom.org/projects/osmomgw/wiki | ||||
|  | ||||
| RFC 3435 for MGCP is located at | ||||
|  | ||||
| - https://tools.ietf.org/html/rfc3435 | ||||
							
								
								
									
										25
									
								
								doc/manuals/chapters/running.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								doc/manuals/chapters/running.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| == Running OsmoMGW | ||||
|  | ||||
| The OsmoMGW executable (`osmo-mgw`) offers the following command-line | ||||
| arguments: | ||||
|  | ||||
| === SYNOPSIS | ||||
|  | ||||
| *osmo-mgw* [-h|-V] [-D] [-c 'CONFIGFILE'] [-s] | ||||
|  | ||||
| === OPTIONS | ||||
|  | ||||
| *-h, --help*:: | ||||
| 	Print a short help message about the supported options | ||||
| *-V, --version*:: | ||||
| 	Print the compile-time version number of the program | ||||
| *-D, --daemonize*:: | ||||
| 	Fork the process as a daemon into background. | ||||
| *-c, --config-file 'CONFIGFILE'*:: | ||||
| 	Specify the file and path name of the configuration file to be | ||||
| 	used. If none is specified, use `osmo-mgw.cfg` in the current | ||||
| 	working directory. | ||||
| *-s, --disable-color*:: | ||||
| 	Disable colors for logging to stderr. This has mostly been | ||||
| 	deprecated by VTY based logging configuration, see <<logging>> | ||||
| 	for more information. | ||||
							
								
								
									
										47
									
								
								doc/manuals/osmomgw-usermanual-docinfo.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								doc/manuals/osmomgw-usermanual-docinfo.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| <revhistory> | ||||
|   <revision> | ||||
|     <revnumber>1</revnumber> | ||||
|     <date>July 24th, 2018</date> | ||||
|     <authorinitials>DW</authorinitials> | ||||
|     <revremark> | ||||
|       Initial version | ||||
|     </revremark> | ||||
|   </revision> | ||||
| </revhistory> | ||||
|  | ||||
| <authorgroup> | ||||
|   <author> | ||||
|     <firstname>Daniel</firstname> | ||||
|     <surname>Willmann</surname> | ||||
|     <email>dwillmann@sysmocom.de</email> | ||||
|     <authorinitials>DW</authorinitials> | ||||
|     <affiliation> | ||||
|       <shortaffil>sysmocom</shortaffil> | ||||
|       <orgname>sysmocom - s.f.m.c. GmbH</orgname> | ||||
|     </affiliation> | ||||
|   </author> | ||||
| </authorgroup> | ||||
|  | ||||
| <copyright> | ||||
|   <year>2018</year> | ||||
|   <holder>sysmocom - s.f.m.c. GmbH</holder> | ||||
| </copyright> | ||||
|  | ||||
| <legalnotice> | ||||
|   <para> | ||||
| 	Permission is granted to copy, distribute and/or modify this | ||||
| 	document under the terms of the GNU Free Documentation License, | ||||
| 	Version 1.3 or any later version published by the Free Software | ||||
| 	Foundation; with the Invariant Sections being just 'Foreword', | ||||
| 	'Acknowledgements' and 'Preface', with no Front-Cover Texts, | ||||
| 	and no Back-Cover Texts.  A copy of the license is included in | ||||
| 	the section entitled "GNU Free Documentation License". | ||||
|   </para> | ||||
|   <para> | ||||
| 	The Asciidoc source code of this manual can be found at | ||||
| 	<ulink url="http://git.osmocom.org/osmo-gsm-manuals/"> | ||||
| 		http://git.osmocom.org/osmo-gsm-manuals/ | ||||
| 	</ulink> | ||||
|   </para> | ||||
| </legalnotice> | ||||
|  | ||||
							
								
								
									
										33
									
								
								doc/manuals/osmomgw-usermanual.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								doc/manuals/osmomgw-usermanual.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| :gfdl-enabled: | ||||
| :program-name: OsmoMGW | ||||
|  | ||||
| OsmoMGW User Manual | ||||
| ==================== | ||||
| Daniel Willmann <dwillmann@sysmocom.de> | ||||
|  | ||||
|  | ||||
| include::./common/chapters/preface.adoc[] | ||||
|  | ||||
| include::{srcdir}/chapters/overview.adoc[] | ||||
|  | ||||
| include::{srcdir}/chapters/running.adoc[] | ||||
|  | ||||
| include::./common/chapters/vty.adoc[] | ||||
|  | ||||
| include::./common/chapters/logging.adoc[] | ||||
|  | ||||
| include::{srcdir}/chapters/configuration.adoc[] | ||||
|  | ||||
| include::{srcdir}/chapters/mgcp_extensions.adoc[] | ||||
|  | ||||
| include::./common/chapters/osmux/osmux.adoc[] | ||||
|  | ||||
| //include::{srcdir}/chapters/counters.adoc[] | ||||
|  | ||||
| include::./common/chapters/port_numbers.adoc[] | ||||
|  | ||||
| include::./common/chapters/bibliography.adoc[] | ||||
|  | ||||
| include::./common/chapters/glossary.adoc[] | ||||
|  | ||||
| include::./common/chapters/gfdl.adoc[] | ||||
							
								
								
									
										38
									
								
								doc/manuals/osmomgw-vty-reference.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								doc/manuals/osmomgw-vty-reference.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!-- | ||||
|   ex:ts=2:sw=42sts=2:et | ||||
|   -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||||
| --> | ||||
| <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN" | ||||
| "http://docbook.org/xml/5.0/dtd/docbook.dtd" [ | ||||
| <!ENTITY chapter-vty      SYSTEM      "./common/chapters/vty.xml" > | ||||
| <!ENTITY sections-vty     SYSTEM      "generated/docbook_vty.xml"  > | ||||
| ]> | ||||
|  | ||||
| <book> | ||||
|   <info> | ||||
|     <revhistory> | ||||
|         <revision> | ||||
|             <revnumber>v1</revnumber> | ||||
|             <date>12th December 2017</date> | ||||
|             <authorinitials>pm</authorinitials> | ||||
|             <revremark>Initial</revremark> | ||||
|         </revision> | ||||
|     </revhistory> | ||||
|  | ||||
|     <title>OsmoMGW VTY Reference</title> | ||||
|  | ||||
|     <copyright> | ||||
|       <year>2017</year> | ||||
|     </copyright> | ||||
|  | ||||
|     <legalnotice> | ||||
|       <para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved. | ||||
|       </para> | ||||
|     </legalnotice> | ||||
|   </info> | ||||
|  | ||||
|   <!-- Main chapters--> | ||||
|   &chapter-vty; | ||||
| </book> | ||||
|  | ||||
							
								
								
									
										17
									
								
								doc/manuals/regen_doc.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								doc/manuals/regen_doc.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| #!/bin/sh -x | ||||
|  | ||||
| if [ -z "$DOCKER_PLAYGROUND" ]; then | ||||
| 	echo "You need to set DOCKER_PLAYGROUND" | ||||
| 	exit 1 | ||||
| fi | ||||
|  | ||||
| SCRIPT=$(realpath "$0") | ||||
| MANUAL_DIR=$(dirname "$SCRIPT") | ||||
|  | ||||
| COMMIT=${COMMIT:-$(git log -1 --format=format:%H)} | ||||
|  | ||||
| cd "$DOCKER_PLAYGROUND/scripts" || exit 1 | ||||
|  | ||||
| OSMO_MGW_BRANCH=$COMMIT ./regen_doc.sh osmo-mgw 4243 \ | ||||
| 	"$MANUAL_DIR/chapters/counters_generated.adoc" \ | ||||
| 	"$MANUAL_DIR/vty/mgw_vty_reference.xml" | ||||
							
								
								
									
										2
									
								
								doc/manuals/vty/mgw_vty_additions.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								doc/manuals/vty/mgw_vty_additions.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| <vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'> | ||||
| </vtydoc> | ||||
							
								
								
									
										1677
									
								
								doc/manuals/vty/mgw_vty_reference.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1677
									
								
								doc/manuals/vty/mgw_vty_reference.xml
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -92,8 +92,8 @@ fi | ||||
| if test -n "$v" | ||||
| then | ||||
|     : # use $v | ||||
| elif test -d ./.git \ | ||||
|     && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ | ||||
| elif | ||||
|        v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ | ||||
| 	  || git describe --abbrev=4 HEAD 2>/dev/null` \ | ||||
|     && case $v in | ||||
| 	 [0-9]*) ;; | ||||
|   | ||||
| @@ -3,8 +3,17 @@ SUBDIRS = \ | ||||
| 	$(NULL) | ||||
|  | ||||
| nobase_include_HEADERS = \ | ||||
| 	osmocom/legacy_mgcp/mgcp.h \ | ||||
| 	osmocom/legacy_mgcp/mgcp_internal.h \ | ||||
| 	osmocom/legacy_mgcp/osmux.h \ | ||||
| 	osmocom/mgcp_client/mgcp_client.h \ | ||||
| 	osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \ | ||||
| 	osmocom/mgcp_client/mgcp_client_fsm.h \ | ||||
| 	osmocom/mgcp/mgcp.h \ | ||||
| 	osmocom/mgcp/mgcp_common.h \ | ||||
| 	osmocom/mgcp/mgcp_internal.h \ | ||||
| 	osmocom/mgcp/osmux.h \ | ||||
| 	$(NULL) | ||||
|  | ||||
| # This gets copied during make from osmocom/mgcp/mgcp_common.h. Therefore it is not included in the source tree and we | ||||
| # don't need to distribute it (OS#4084). | ||||
| nobase_nodist_include_HEADERS = \ | ||||
| 	osmocom/mgcp_client/mgcp_common.h \ | ||||
| 	$(NULL) | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| SUBDIRS = \ | ||||
| 	legacy_mgcp \ | ||||
| 	mgcp_client \ | ||||
| 	mgcp \ | ||||
| 	$(NULL) | ||||
|   | ||||
| @@ -1,4 +0,0 @@ | ||||
| noinst_HEADERS = \ | ||||
| 	mgcp_transcode.h \ | ||||
| 	vty.h \ | ||||
| 	$(NULL) | ||||
| @@ -1,337 +0,0 @@ | ||||
| /* MGCP Private Data */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2012 by On-Waves | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string.h> | ||||
|  | ||||
| #include <osmocom/core/select.h> | ||||
|  | ||||
| #define CI_UNUSED 0 | ||||
|  | ||||
| enum mgcp_trunk_type { | ||||
| 	MGCP_TRUNK_VIRTUAL, | ||||
| 	MGCP_TRUNK_E1, | ||||
| }; | ||||
|  | ||||
| struct mgcp_rtp_stream_state { | ||||
| 	uint32_t ssrc; | ||||
| 	uint16_t last_seq; | ||||
| 	uint32_t last_timestamp; | ||||
| 	uint32_t err_ts_counter; | ||||
| 	int32_t last_tsdelta; | ||||
| 	uint32_t last_arrival_time; | ||||
| }; | ||||
|  | ||||
| struct mgcp_rtp_state { | ||||
| 	int initialized; | ||||
| 	int patch_ssrc; | ||||
|  | ||||
| 	uint32_t orig_ssrc; | ||||
|  | ||||
| 	int seq_offset; | ||||
|  | ||||
| 	int32_t  timestamp_offset; | ||||
| 	uint32_t packet_duration; | ||||
|  | ||||
| 	struct mgcp_rtp_stream_state in_stream; | ||||
| 	struct mgcp_rtp_stream_state out_stream; | ||||
|  | ||||
| 	/* jitter and packet loss calculation */ | ||||
| 	int stats_initialized; | ||||
| 	uint16_t stats_base_seq; | ||||
| 	uint16_t stats_max_seq; | ||||
| 	uint32_t stats_ssrc; | ||||
| 	uint32_t stats_jitter; | ||||
| 	int32_t stats_transit; | ||||
| 	int stats_cycles; | ||||
| 	bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */ | ||||
| }; | ||||
|  | ||||
| struct mgcp_rtp_codec { | ||||
| 	uint32_t rate; | ||||
| 	int channels; | ||||
| 	uint32_t frame_duration_num; | ||||
| 	uint32_t frame_duration_den; | ||||
|  | ||||
| 	int payload_type; | ||||
| 	char *audio_name; | ||||
| 	char *subtype_name; | ||||
| }; | ||||
|  | ||||
| struct mgcp_rtp_end { | ||||
| 	/* statistics */ | ||||
| 	unsigned int packets; | ||||
| 	unsigned int octets; | ||||
| 	unsigned int dropped_packets; | ||||
| 	struct in_addr addr; | ||||
|  | ||||
| 	/* in network byte order */ | ||||
| 	int rtp_port, rtcp_port; | ||||
|  | ||||
| 	/* audio codec information */ | ||||
| 	struct mgcp_rtp_codec codec; | ||||
| 	struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */ | ||||
|  | ||||
| 	/* per endpoint data */ | ||||
| 	int  frames_per_packet; | ||||
| 	uint32_t packet_duration_ms; | ||||
| 	char *fmtp_extra; | ||||
| 	int output_enabled; | ||||
| 	int force_output_ptime; | ||||
|  | ||||
| 	/* RTP patching */ | ||||
| 	int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ | ||||
| 	int force_aligned_timing; | ||||
| 	void *rtp_process_data; | ||||
|  | ||||
| 	/* | ||||
| 	 * Each end has a socket... | ||||
| 	 */ | ||||
| 	struct osmo_fd rtp; | ||||
| 	struct osmo_fd rtcp; | ||||
|  | ||||
| 	int local_port; | ||||
| 	int local_alloc; | ||||
| }; | ||||
|  | ||||
| enum { | ||||
| 	MGCP_TAP_BTS_IN, | ||||
| 	MGCP_TAP_BTS_OUT, | ||||
| 	MGCP_TAP_NET_IN, | ||||
| 	MGCP_TAP_NET_OUT, | ||||
|  | ||||
| 	/* last element */ | ||||
| 	MGCP_TAP_COUNT | ||||
| }; | ||||
|  | ||||
| struct mgcp_rtp_tap { | ||||
| 	int enabled; | ||||
| 	struct sockaddr_in forward; | ||||
| }; | ||||
|  | ||||
| struct mgcp_lco { | ||||
| 	char *string; | ||||
| 	char *codec; | ||||
| 	int pkt_period_min; /* time in ms */ | ||||
| 	int pkt_period_max; /* time in ms */ | ||||
| }; | ||||
|  | ||||
| enum mgcp_type { | ||||
| 	MGCP_RTP_DEFAULT	= 0, | ||||
| 	MGCP_RTP_TRANSCODED, | ||||
| 	MGCP_OSMUX_BSC, | ||||
| 	MGCP_OSMUX_BSC_NAT, | ||||
| }; | ||||
|  | ||||
| #include <osmocom/legacy_mgcp/osmux.h> | ||||
|  | ||||
| struct mgcp_endpoint { | ||||
| 	int allocated; | ||||
| 	uint32_t ci; | ||||
| 	char *callid; | ||||
| 	struct mgcp_lco local_options; | ||||
| 	int conn_mode; | ||||
| 	int orig_mode; | ||||
|  | ||||
| 	/* backpointer */ | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct mgcp_trunk_config *tcfg; | ||||
|  | ||||
| 	/* port status for bts/net */ | ||||
| 	struct mgcp_rtp_end bts_end; | ||||
| 	struct mgcp_rtp_end net_end; | ||||
|  | ||||
| 	/* | ||||
| 	 * For transcoding we will send from the local_port | ||||
| 	 * of trans_bts and it will arrive at trans_net from | ||||
| 	 * where we will forward it to the network. | ||||
| 	 */ | ||||
| 	struct mgcp_rtp_end trans_bts; | ||||
| 	struct mgcp_rtp_end trans_net; | ||||
| 	enum mgcp_type type; | ||||
|  | ||||
| 	/* sequence bits */ | ||||
| 	struct mgcp_rtp_state net_state; | ||||
| 	struct mgcp_rtp_state bts_state; | ||||
|  | ||||
| 	/* fields for re-transmission */ | ||||
| 	char *last_trans; | ||||
| 	char *last_response; | ||||
|  | ||||
| 	/* tap for the endpoint */ | ||||
| 	struct mgcp_rtp_tap taps[MGCP_TAP_COUNT]; | ||||
|  | ||||
| 	struct { | ||||
| 		/* Osmux state: disabled, activating, active */ | ||||
| 		enum osmux_state state; | ||||
| 		/* Allocated Osmux circuit ID for this endpoint */ | ||||
| 		int allocated_cid; | ||||
| 		/* Used Osmux circuit ID for this endpoint */ | ||||
| 		uint8_t cid; | ||||
| 		/* handle to batch messages */ | ||||
| 		struct osmux_in_handle *in; | ||||
| 		/* handle to unbatch messages */ | ||||
| 		struct osmux_out_handle out; | ||||
| 		/* statistics */ | ||||
| 		struct { | ||||
| 			uint32_t chunks; | ||||
| 			uint32_t octets; | ||||
| 		} stats; | ||||
| 	} osmux; | ||||
| }; | ||||
|  | ||||
| #define for_each_line(line, save)			\ | ||||
| 	for (line = strline_r(NULL, &save); line;\ | ||||
| 	     line = strline_r(NULL, &save)) | ||||
|  | ||||
| static inline char *strline_r(char *str, char **saveptr) | ||||
| { | ||||
| 	char *result; | ||||
|  | ||||
| 	if (str) | ||||
| 		*saveptr = str; | ||||
|  | ||||
| 	result = *saveptr; | ||||
|  | ||||
| 	if (*saveptr != NULL) { | ||||
| 		*saveptr = strpbrk(*saveptr, "\r\n"); | ||||
|  | ||||
| 		if (*saveptr != NULL) { | ||||
| 			char *eos = *saveptr; | ||||
|  | ||||
| 			if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n') | ||||
| 				(*saveptr)++; | ||||
| 			(*saveptr)++; | ||||
| 			if ((*saveptr)[0] == '\0') | ||||
| 				*saveptr = NULL; | ||||
|  | ||||
| 			*eos = '\0'; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| #define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints)) | ||||
|  | ||||
| /** | ||||
|  * Internal structure while parsing a request | ||||
|  */ | ||||
| struct mgcp_parse_data { | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	char *trans; | ||||
| 	char *save; | ||||
| 	int found; | ||||
| }; | ||||
|  | ||||
| int mgcp_send_dummy(struct mgcp_endpoint *endp); | ||||
| int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port); | ||||
| int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port); | ||||
| int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *enp, int rtp_port); | ||||
| int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *enp, int rtp_port); | ||||
| int mgcp_free_rtp_port(struct mgcp_rtp_end *end); | ||||
|  | ||||
| /* For transcoding we need to manage an in and an output that are connected */ | ||||
| static inline int endp_back_channel(int endpoint) | ||||
| { | ||||
| 	return endpoint + 60; | ||||
| } | ||||
|  | ||||
| struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index); | ||||
| struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index); | ||||
|  | ||||
| void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, | ||||
| 			 struct mgcp_rtp_end *rtp); | ||||
| uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp, | ||||
| 				  struct mgcp_rtp_end *rtp); | ||||
|  | ||||
| void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *, | ||||
| 			uint32_t *expected, int *loss); | ||||
| uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *); | ||||
|  | ||||
| /* 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_setup_rtp_processing_default(struct mgcp_endpoint *endp, | ||||
| 				      struct mgcp_rtp_end *dst_end, | ||||
| 				      struct mgcp_rtp_end *src_end); | ||||
|  | ||||
| void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, | ||||
| 					  int *payload_type, | ||||
| 					  const char**subtype_name, | ||||
| 					  const char**fmtp_extra); | ||||
|  | ||||
| /* internal RTP Annex A counting */ | ||||
| void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, | ||||
| 			const uint16_t seq, const int32_t transit, | ||||
| 			const uint32_t ssrc); | ||||
|  | ||||
| int mgcp_set_ip_tos(int fd, int tos); | ||||
|  | ||||
| enum { | ||||
| 	MGCP_DEST_NET = 0, | ||||
| 	MGCP_DEST_BTS, | ||||
| }; | ||||
|  | ||||
|  | ||||
| #define MGCP_DUMMY_LOAD 0x23 | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * SDP related information | ||||
|  */ | ||||
| /* Assume audio frame length of 20ms */ | ||||
| #define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20 | ||||
| #define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000 | ||||
| #define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20 | ||||
| #define DEFAULT_RTP_AUDIO_DEFAULT_RATE  8000 | ||||
| #define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1 | ||||
|  | ||||
| #define PTYPE_UNDEFINED (-1) | ||||
| int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p); | ||||
| int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec, | ||||
| 			int payload_type, const char *audio_name); | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Internal network related | ||||
|  */ | ||||
| static inline const char *mgcp_net_src_addr(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	if (endp->cfg->net_ports.bind_addr) | ||||
| 		return endp->cfg->net_ports.bind_addr; | ||||
| 	return endp->cfg->source_addr; | ||||
| } | ||||
|  | ||||
| static inline const char *mgcp_bts_src_addr(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	if (endp->cfg->bts_ports.bind_addr) | ||||
| 		return endp->cfg->bts_ports.bind_addr; | ||||
| 	return endp->cfg->source_addr; | ||||
| } | ||||
|  | ||||
| int mgcp_msg_terminate_nul(struct msgb *msg); | ||||
| @@ -1,90 +0,0 @@ | ||||
| /* | ||||
|  * (C) 2014 by On-Waves | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #ifndef OPENBSC_MGCP_TRANSCODE_H | ||||
| #define OPENBSC_MGCP_TRANSCODE_H | ||||
|  | ||||
| #include "bscconfig.h" | ||||
|  | ||||
| #include <gsm.h> | ||||
| #ifdef HAVE_BCG729 | ||||
| #include <bcg729/decoder.h> | ||||
| #include <bcg729/encoder.h> | ||||
| #endif | ||||
|  | ||||
| enum audio_format { | ||||
| 	AF_INVALID, | ||||
| 	AF_S16, | ||||
| 	AF_L16, | ||||
| 	AF_GSM, | ||||
| 	AF_G729, | ||||
| 	AF_PCMA, | ||||
| 	AF_PCMU | ||||
| }; | ||||
|  | ||||
|  | ||||
| struct mgcp_process_rtp_state { | ||||
| 	/* decoding */ | ||||
| 	enum audio_format src_fmt; | ||||
| 	union { | ||||
| 		gsm gsm_handle; | ||||
| #ifdef HAVE_BCG729 | ||||
| 		bcg729DecoderChannelContextStruct *g729_dec; | ||||
| #endif | ||||
| 	} src; | ||||
| 	size_t src_frame_size; | ||||
| 	size_t src_samples_per_frame; | ||||
|  | ||||
| 	/* processing */ | ||||
|  | ||||
| 	/* encoding */ | ||||
| 	enum audio_format dst_fmt; | ||||
| 	union { | ||||
| 		gsm gsm_handle; | ||||
| #ifdef HAVE_BCG729 | ||||
| 		bcg729EncoderChannelContextStruct *g729_enc; | ||||
| #endif | ||||
| 	} dst; | ||||
| 	size_t dst_frame_size; | ||||
| 	size_t dst_samples_per_frame; | ||||
| 	int dst_packet_duration; | ||||
|  | ||||
| 	int is_running; | ||||
| 	uint16_t next_seq; | ||||
| 	uint32_t next_time; | ||||
| 	int16_t samples[10*160]; | ||||
| 	size_t sample_cnt; | ||||
| 	size_t sample_offs; | ||||
| }; | ||||
|  | ||||
|  | ||||
| int mgcp_transcoding_setup(struct mgcp_endpoint *endp, | ||||
| 			   struct mgcp_rtp_end *dst_end, | ||||
| 			   struct mgcp_rtp_end *src_end); | ||||
|  | ||||
| void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, | ||||
| 					  int *payload_type, | ||||
| 					  const char**audio_name, | ||||
| 					  const char**fmtp_extra); | ||||
|  | ||||
| int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, | ||||
| 				 struct mgcp_rtp_end *dst_end, | ||||
| 				 char *data, int *len, int buf_size); | ||||
|  | ||||
| int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst); | ||||
| #endif /* OPENBSC_MGCP_TRANSCODE_H */ | ||||
| @@ -1,41 +0,0 @@ | ||||
| #ifndef _OPENBSC_OSMUX_H_ | ||||
| #define _OPENBSC_OSMUX_H_ | ||||
|  | ||||
| #include <osmocom/netif/osmux.h> | ||||
|  | ||||
| #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_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port); | ||||
| void osmux_disable_endpoint(struct mgcp_endpoint *endp); | ||||
| void osmux_allocate_cid(struct mgcp_endpoint *endp); | ||||
| void osmux_release_cid(struct mgcp_endpoint *endp); | ||||
|  | ||||
| int osmux_xfrm_to_rtp(struct mgcp_endpoint *endp, int type, char *buf, int rc); | ||||
| int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp); | ||||
|  | ||||
| int osmux_send_dummy(struct mgcp_endpoint *endp); | ||||
|  | ||||
| int osmux_get_cid(void); | ||||
| void osmux_put_cid(uint8_t osmux_cid); | ||||
| int osmux_used_cid(void); | ||||
|  | ||||
| enum osmux_state { | ||||
| 	OSMUX_STATE_DISABLED = 0, | ||||
| 	OSMUX_STATE_NEGOTIATING, | ||||
| 	OSMUX_STATE_ACTIVATING, | ||||
| 	OSMUX_STATE_ENABLED, | ||||
| }; | ||||
|  | ||||
| enum osmux_usage { | ||||
| 	OSMUX_USAGE_OFF = 0, | ||||
| 	OSMUX_USAGE_ON = 1, | ||||
| 	OSMUX_USAGE_ONLY = 2, | ||||
| }; | ||||
|  | ||||
| #endif | ||||
| @@ -1,31 +0,0 @@ | ||||
| #ifndef OPENBSC_VTY_H | ||||
| #define OPENBSC_VTY_H | ||||
|  | ||||
| #include <osmocom/vty/vty.h> | ||||
| #include <osmocom/vty/buffer.h> | ||||
| #include <osmocom/vty/command.h> | ||||
|  | ||||
| struct gsm_network; | ||||
| struct vty; | ||||
|  | ||||
| void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *); | ||||
|  | ||||
| struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base); | ||||
|  | ||||
| extern struct cmd_element cfg_description_cmd; | ||||
| extern struct cmd_element cfg_no_description_cmd; | ||||
|  | ||||
| enum mgcp_vty_node { | ||||
| 	MGCP_NODE = _LAST_OSMOVTY_NODE + 1, | ||||
| 	TRUNK_NODE, | ||||
| }; | ||||
|  | ||||
| struct log_info; | ||||
| int bsc_vty_init(struct gsm_network *network); | ||||
| int bsc_vty_init_extra(void); | ||||
|  | ||||
| void msc_vty_init(struct gsm_network *msc_network); | ||||
|  | ||||
| struct gsm_network *gsmnet_from_vty(struct vty *vty); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										10
									
								
								include/osmocom/mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								include/osmocom/mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| noinst_HEADERS = \ | ||||
| 	vty.h \ | ||||
| 	mgcp_msg.h \ | ||||
| 	mgcp_conn.h \ | ||||
| 	mgcp_stat.h \ | ||||
| 	mgcp_endp.h \ | ||||
| 	mgcp_sdp.h \ | ||||
| 	mgcp_codec.h \ | ||||
| 	debug.h \ | ||||
| 	$(NULL) | ||||
							
								
								
									
										35
									
								
								include/osmocom/mgcp/debug.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								include/osmocom/mgcp/debug.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| /* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Philipp Maier | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <osmocom/core/linuxlist.h> | ||||
|  | ||||
| #define DEBUG | ||||
| #include <osmocom/core/logging.h> | ||||
|  | ||||
| /* Debug Areas of the code */ | ||||
| enum { | ||||
| 	DRTP, | ||||
| 	Debug_LastEntry, | ||||
| }; | ||||
|  | ||||
| extern const struct log_info log_info; | ||||
| @@ -20,46 +20,22 @@ | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef OPENBSC_MGCP_H | ||||
| #define OPENBSC_MGCP_H | ||||
| #pragma once | ||||
| 
 | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/core/write_queue.h> | ||||
| #include <osmocom/core/timer.h> | ||||
| #include <osmocom/core/logging.h> | ||||
| 
 | ||||
| #include <osmocom/mgcp/mgcp_common.h> | ||||
| 
 | ||||
| #include <arpa/inet.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/socket.h> | ||||
| #include <netinet/in.h> | ||||
| 
 | ||||
| #define RTP_PORT_DEFAULT 4000 | ||||
| #define RTP_PORT_NET_DEFAULT 16000 | ||||
| 
 | ||||
| /**
 | ||||
|  * Calculate the RTP audio port for the given multiplex | ||||
|  * and the direction. This allows a semi static endpoint | ||||
|  * to port calculation removing the need for the BSC | ||||
|  * and the MediaGateway to communicate. | ||||
|  * | ||||
|  * Port usage explained: | ||||
|  *       base + (multiplex * 2) + 0 == local port to wait for network packets | ||||
|  *       base + (multiplex * 2) + 1 == local port for rtcp | ||||
|  * | ||||
|  * The above port will receive packets from the BTS that need | ||||
|  * to be patched and forwarded to the network. | ||||
|  * The above port will receive packets from the network that | ||||
|  * need to be patched and forwarded to the BTS. | ||||
|  * | ||||
|  * We assume to have a static BTS IP address so we can differentiate | ||||
|  * network and BTS. | ||||
|  * | ||||
|  */ | ||||
| static inline int rtp_calculate_port(int multiplex, int base) | ||||
| { | ||||
| 	return base + (multiplex * 2); | ||||
| } | ||||
| 
 | ||||
| #define RTP_PORT_DEFAULT_RANGE_START 16002 | ||||
| #define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64 | ||||
| 
 | ||||
| /*
 | ||||
|  * Handling of MGCP Endpoints and the MGCP Config | ||||
| @@ -98,37 +74,99 @@ typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone); | ||||
| typedef int (*mgcp_processing)(struct mgcp_endpoint *endp, | ||||
| 			       struct mgcp_rtp_end *dst_end, | ||||
| 			       char *data, int *len, int buf_size); | ||||
| 
 | ||||
| struct mgcp_conn_rtp; | ||||
| 
 | ||||
| typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp, | ||||
| 				     struct mgcp_rtp_end *dst_end, | ||||
| 				     struct mgcp_rtp_end *src_end); | ||||
| 				     struct mgcp_conn_rtp *conn_dst, | ||||
| 				     struct mgcp_conn_rtp *conn_src); | ||||
| 
 | ||||
| struct mgcp_rtp_codec; | ||||
| 
 | ||||
| typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp, | ||||
| 				int *payload_type, | ||||
| 				const char**subtype_name, | ||||
| 				const char**fmtp_extra); | ||||
| 
 | ||||
| #define PORT_ALLOC_STATIC	0 | ||||
| #define PORT_ALLOC_DYNAMIC	1 | ||||
| 				const struct mgcp_rtp_codec **codec, | ||||
| 				const char **fmtp_extra, | ||||
| 				struct mgcp_conn_rtp *conn); | ||||
| 
 | ||||
| /**
 | ||||
|  * This holds information on how to allocate ports | ||||
|  */ | ||||
| struct mgcp_port_range { | ||||
| 	int mode; | ||||
| 
 | ||||
| 	/* addr or NULL to fall-back to default */ | ||||
| 	char *bind_addr; | ||||
| 
 | ||||
| 	/* pre-allocated from a base? */ | ||||
| 	int base_port; | ||||
| 
 | ||||
| 	/* dynamically allocated */ | ||||
| 	int range_start; | ||||
| 	int range_end; | ||||
| 	int last_port; | ||||
| 
 | ||||
| 	/* set to true to enable automatic probing
 | ||||
| 	 * of the local bind IP-Address, bind_addr | ||||
| 	 * (or its fall back) is used when automatic | ||||
| 	 * probing fails */ | ||||
| 	bool bind_addr_probe; | ||||
| }; | ||||
| 
 | ||||
| /* There are up to three modes in which the keep-alive dummy packet can be
 | ||||
|  * sent. The behaviour is controlled via the keepalive_interval member of the | ||||
|  * trunk config. If that member is set to 0 (MGCP_KEEPALIVE_NEVER) no dummy- | ||||
|  * packet is sent at all and the timer that sends regular dummy packets | ||||
|  * is no longer scheduled. If the keepalive_interval is set to -1, only | ||||
|  * one dummy packet is sent when an CRCX or an MDCX is performed. No timer | ||||
|  * is scheduled. For all vales greater 0, the timer is scheduled and the | ||||
|  * value is used as interval. See also mgcp_keepalive_timer_cb(), | ||||
|  * handle_modify_con(), and handle_create_con() */ | ||||
| #define MGCP_KEEPALIVE_ONCE (-1) | ||||
| #define MGCP_KEEPALIVE_NEVER 0 | ||||
| 
 | ||||
| /* Global MCGP CRCX related rate counters */ | ||||
| enum { | ||||
| 	MGCP_CRCX_SUCCESS, | ||||
| 	MGCP_CRCX_FAIL_BAD_ACTION, | ||||
| 	MGCP_CRCX_FAIL_UNHANDLED_PARAM, | ||||
| 	MGCP_CRCX_FAIL_MISSING_CALLID, | ||||
| 	MGCP_CRCX_FAIL_INVALID_MODE, | ||||
| 	MGCP_CRCX_FAIL_LIMIT_EXCEEDED, | ||||
| 	MGCP_CRCX_FAIL_UNKNOWN_CALLID, | ||||
| 	MGCP_CRCX_FAIL_ALLOC_CONN, | ||||
| 	MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC, | ||||
| 	MGCP_CRCX_FAIL_START_RTP, | ||||
| 	MGCP_CRCX_FAIL_REJECTED_BY_POLICY, | ||||
| 	MGCP_CRCX_FAIL_NO_OSMUX, | ||||
| 	MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS, | ||||
| 	MGCP_CRCX_FAIL_CODEC_NEGOTIATION, | ||||
| 	MGCP_CRCX_FAIL_BIND_PORT, | ||||
| }; | ||||
| 
 | ||||
| /* Global MCGP MDCX related rate counters */ | ||||
| enum { | ||||
| 	MGCP_MDCX_SUCCESS, | ||||
| 	MGCP_MDCX_FAIL_WILDCARD, | ||||
| 	MGCP_MDCX_FAIL_NO_CONN, | ||||
| 	MGCP_MDCX_FAIL_INVALID_CALLID, | ||||
| 	MGCP_MDCX_FAIL_INVALID_CONNID, | ||||
| 	MGCP_MDCX_FAIL_UNHANDLED_PARAM, | ||||
| 	MGCP_MDCX_FAIL_NO_CONNID, | ||||
| 	MGCP_MDCX_FAIL_CONN_NOT_FOUND, | ||||
| 	MGCP_MDCX_FAIL_INVALID_MODE, | ||||
| 	MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS, | ||||
| 	MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC, | ||||
| 	MGCP_MDCX_FAIL_START_RTP, | ||||
| 	MGCP_MDCX_FAIL_REJECTED_BY_POLICY, | ||||
| 	MGCP_MDCX_DEFERRED_BY_POLICY | ||||
| }; | ||||
| 
 | ||||
| /* Global MCGP DLCX related rate counters */ | ||||
| enum { | ||||
| 	MGCP_DLCX_SUCCESS, | ||||
| 	MGCP_DLCX_FAIL_WILDCARD, | ||||
| 	MGCP_DLCX_FAIL_NO_CONN, | ||||
| 	MGCP_DLCX_FAIL_INVALID_CALLID, | ||||
| 	MGCP_DLCX_FAIL_INVALID_CONNID, | ||||
| 	MGCP_DLCX_FAIL_UNHANDLED_PARAM, | ||||
| 	MGCP_DLCX_FAIL_REJECTED_BY_POLICY, | ||||
| 	MGCP_DLCX_DEFERRED_BY_POLICY, | ||||
| }; | ||||
| 
 | ||||
| struct mgcp_trunk_config { | ||||
| 	struct llist_head entry; | ||||
| @@ -153,6 +191,7 @@ struct mgcp_trunk_config { | ||||
| 	/* RTP patching */ | ||||
| 	int force_constant_ssrc; /* 0: don't, 1: once */ | ||||
| 	int force_aligned_timing; | ||||
| 	bool rfc5993_hr_convert; | ||||
| 
 | ||||
| 	/* spec handling */ | ||||
| 	int force_realloc; | ||||
| @@ -160,8 +199,22 @@ struct mgcp_trunk_config { | ||||
| 	/* timer */ | ||||
| 	struct osmo_timer_list keepalive_timer; | ||||
| 
 | ||||
| 	/* When set, incoming RTP packets are not filtered
 | ||||
| 	 * when ports and ip-address do not match (debug) */ | ||||
| 	int rtp_accept_all; | ||||
| 
 | ||||
| 	unsigned int number_endpoints; | ||||
| 	int vty_number_endpoints; | ||||
| 	struct mgcp_endpoint *endpoints; | ||||
| 
 | ||||
| 	/* Rate counter group which contains stats for processed CRCX commands. */ | ||||
| 	struct rate_ctr_group *mgcp_crcx_ctr_group; | ||||
| 	/* Rate counter group which contains stats for processed MDCX commands. */ | ||||
| 	struct rate_ctr_group *mgcp_mdcx_ctr_group; | ||||
| 	/* Rate counter group which contains stats for processed DLCX commands. */ | ||||
| 	struct rate_ctr_group *mgcp_dlcx_ctr_group; | ||||
| 	/* Rate counter group which aggregates stats of individual RTP connections. */ | ||||
| 	struct rate_ctr_group *all_rtp_conn_stats; | ||||
| }; | ||||
| 
 | ||||
| enum mgcp_role { | ||||
| @@ -169,35 +222,12 @@ enum mgcp_role { | ||||
| 	MGCP_BSC_NAT, | ||||
| }; | ||||
| 
 | ||||
| enum mgcp_connection_mode { | ||||
| 	MGCP_CONN_NONE = 0, | ||||
| 	MGCP_CONN_RECV_ONLY = 1, | ||||
| 	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, | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
| struct mgcp_config { | ||||
| 	int source_port; | ||||
| 	char *local_ip; | ||||
| 	char *source_addr; | ||||
| 	char *bts_ip; | ||||
| 	char *call_agent_addr; | ||||
| 
 | ||||
| 	struct in_addr bts_in; | ||||
| 
 | ||||
| 	/* transcoder handling */ | ||||
| 	char *transcoder_ip; | ||||
| 	struct in_addr transcoder_in; | ||||
| 	int transcoder_remote_base; | ||||
| 
 | ||||
| 	/* RTP processing */ | ||||
| 	mgcp_processing rtp_processing_cb; | ||||
| 	mgcp_processing_setup setup_rtp_processing_cb; | ||||
| @@ -206,12 +236,10 @@ struct mgcp_config { | ||||
| 
 | ||||
| 	struct osmo_wqueue gw_fd; | ||||
| 
 | ||||
| 	struct mgcp_port_range bts_ports; | ||||
| 	struct mgcp_port_range net_ports; | ||||
| 	struct mgcp_port_range transcoder_ports; | ||||
| 	int endp_dscp; | ||||
| 
 | ||||
| 	int bts_force_ptime; | ||||
| 	int force_ptime; | ||||
| 
 | ||||
| 	mgcp_change change_cb; | ||||
| 	mgcp_policy policy_cb; | ||||
| @@ -226,10 +254,6 @@ struct mgcp_config { | ||||
| 	struct mgcp_trunk_config trunk; | ||||
| 	struct llist_head trunks; | ||||
| 
 | ||||
| 	/* only used for start with a static configuration */ | ||||
| 	int last_net_port; | ||||
| 	int last_bts_port; | ||||
| 
 | ||||
| 	enum mgcp_role role; | ||||
| 
 | ||||
| 	/* osmux translator: 0 means disabled, 1 means enabled */ | ||||
| @@ -250,6 +274,11 @@ struct mgcp_config { | ||||
| 	 * message. | ||||
| 	 */ | ||||
| 	uint16_t osmux_dummy; | ||||
| 	/* domain name of the media gateway */ | ||||
| 	char domain[255+1]; | ||||
| 
 | ||||
| 	/* time after which inactive connections (CIs) get closed */ | ||||
| 	int conn_timeout; | ||||
| }; | ||||
| 
 | ||||
| /* config management */ | ||||
| @@ -258,12 +287,6 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, | ||||
| 		      enum mgcp_role role); | ||||
| int mgcp_vty_init(void); | ||||
| int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg); | ||||
| void mgcp_release_endp(struct mgcp_endpoint *endp); | ||||
| void mgcp_initialize_endp(struct mgcp_endpoint *endp); | ||||
| int mgcp_reset_transcoder(struct mgcp_config *cfg); | ||||
| void mgcp_format_stats(struct mgcp_endpoint *endp, char *stats, size_t size); | ||||
| int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os, uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter); | ||||
| 
 | ||||
| void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval); | ||||
| 
 | ||||
| /*
 | ||||
| @@ -271,29 +294,10 @@ void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval); | ||||
|  */ | ||||
| struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg); | ||||
| 
 | ||||
| /* adc helper */ | ||||
| static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot) | ||||
| { | ||||
| 	if (timeslot == 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Timeslot should not be 0\n"); | ||||
| 		timeslot = 255; | ||||
| 	} | ||||
| 
 | ||||
| 	return timeslot + (32 * multiplex); | ||||
| } | ||||
| 
 | ||||
| static inline void mgcp_endpoint_to_timeslot(int endpoint, int *multiplex, int *timeslot) | ||||
| { | ||||
| 	*multiplex = endpoint / 32; | ||||
| 	*timeslot = endpoint % 32; | ||||
| } | ||||
| 
 | ||||
| int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint); | ||||
| int mgcp_send_reset_all(struct mgcp_config *cfg); | ||||
| 
 | ||||
| 
 | ||||
| int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port); | ||||
| int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, struct sockaddr_in *addr, char *buf, int rc); | ||||
| int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										7
									
								
								include/osmocom/mgcp/mgcp_codec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								include/osmocom/mgcp/mgcp_codec.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| void mgcp_codec_summary(struct mgcp_conn_rtp *conn); | ||||
| void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn); | ||||
| int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, 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); | ||||
							
								
								
									
										107
									
								
								include/osmocom/mgcp/mgcp_common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								include/osmocom/mgcp/mgcp_common.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| /* MGCP common implementations. | ||||
|  * These are used in libosmo-mgcp as well as libosmo-mgcp-client. | ||||
|  * To avoid interdependency, these are implemented in .h file only. */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2012 by On-Waves | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| /* Two copies of this file are kept in osmocom/mgcp/ and osmocom/mgcp_client/. | ||||
|  * Since both are by definition identical, use the old header exclusion ifdefs | ||||
|  * instead of '#pragma once' to avoid including both of these files. | ||||
|  * Though at the time of writing there are no such users, this allows including | ||||
|  * both libosmo-mgcp and libosmo-mgcp-client headers in the same file. */ | ||||
| #ifndef OSMO_MGCP_COMMON_H | ||||
| #define OSMO_MGCP_COMMON_H | ||||
|  | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/core/logging.h> | ||||
|  | ||||
| #define for_each_non_empty_line(line, save)			\ | ||||
| 	for (line = strtok_r(NULL, "\r\n", &save); line;	\ | ||||
| 	     line = strtok_r(NULL, "\r\n", &save)) | ||||
|  | ||||
| enum mgcp_connection_mode { | ||||
| 	MGCP_CONN_NONE = 0, | ||||
| 	MGCP_CONN_RECV_ONLY = 1, | ||||
| 	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, | ||||
| }; | ||||
|  | ||||
| #define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:" | ||||
| #define MGCP_X_OSMO_OSMUX_HEADER "X-Osmux:" | ||||
|  | ||||
| /* Values should be bitwise-OR-able */ | ||||
| enum mgcp_x_osmo_ign { | ||||
| 	MGCP_X_OSMO_IGN_NONE = 0, | ||||
| 	MGCP_X_OSMO_IGN_CALLID = 1, | ||||
| }; | ||||
|  | ||||
| /* Codec parameters (communicated via SDP/fmtp) */ | ||||
| struct mgcp_codec_param { | ||||
| 	bool amr_octet_aligned_present; | ||||
| 	bool amr_octet_aligned; | ||||
| }; | ||||
|  | ||||
| /* 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 */ | ||||
| 	if (tail[-1] == '\0') | ||||
| 		/* nothing to do */; | ||||
| 	else if (msgb_tailroom(msg) > 0) | ||||
| 		tail[0] = '\0'; | ||||
| 	else if (tail[-1] == '\r' || tail[-1] == '\n') | ||||
| 		tail[-1] = '\0'; | ||||
| 	else { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " | ||||
| 		     "Length: %d, Buffer size: %d\n", | ||||
| 		     msgb_l2len(msg), msg->data_len); | ||||
| 		return -ENOTSUP; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Maximum length of the comment field */ | ||||
| #define MGCP_COMMENT_MAXLEN 256 | ||||
|  | ||||
| /* Maximum allowed String length of Connection Identifiers as per spec | ||||
|  * (see also RFC3435 2.1.3.2 Names of Connections), plus one for '\0'. */ | ||||
| #define MGCP_CONN_ID_MAXLEN 32+1 | ||||
|  | ||||
| /* Deprecated: old name of MGCP_CONN_ID_MAXLEN. */ | ||||
| #define MGCP_CONN_ID_LENGTH MGCP_CONN_ID_MAXLEN | ||||
|  | ||||
| /* String length of Endpoint Identifiers. | ||||
| /  (see also RFC3435 section 3.2.1.3) */ | ||||
| #define MGCP_ENDPOINT_MAXLEN (255*2+1+1) | ||||
|  | ||||
| /* A prefix to denote the virtual trunk (RTP on both ends) */ | ||||
| #define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/" | ||||
|  | ||||
| /* Maximal number of payload types / codecs that can be negotiated via SDP at | ||||
|  * at once. */ | ||||
| #define MGCP_MAX_CODECS 10 | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										80
									
								
								include/osmocom/mgcp/mgcp_conn.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								include/osmocom/mgcp/mgcp_conn.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| /* Message connection list handling */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Philipp Maier | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/core/linuxlist.h> | ||||
| #include <osmocom/core/rate_ctr.h> | ||||
| #include <inttypes.h> | ||||
|  | ||||
| /* RTP connection related counters */ | ||||
| enum { | ||||
| 	IN_STREAM_ERR_TSTMP_CTR, | ||||
| 	OUT_STREAM_ERR_TSTMP_CTR, | ||||
|         RTP_PACKETS_RX_CTR, | ||||
|         RTP_OCTETS_RX_CTR, | ||||
|         RTP_PACKETS_TX_CTR, | ||||
|         RTP_OCTETS_TX_CTR, | ||||
|         RTP_DROPPED_PACKETS_CTR, | ||||
|         RTP_NUM_CONNECTIONS, | ||||
| }; | ||||
|  | ||||
| /* 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_rate_ctr_desc[] = { | ||||
| 	[IN_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:in", "Inbound rtp-stream timestamp errors."}, | ||||
| 	[OUT_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:out", "Outbound rtp-stream timestamp errors."}, | ||||
| 	[RTP_PACKETS_RX_CTR] = {"rtp:packets_rx", "Inbound rtp packets."}, | ||||
| 	[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."} | ||||
| }; | ||||
|  | ||||
| /* Aggregated RTP connection stats. These are updated when an RTP connection is freed. | ||||
|  * Must be kept in sync with mgcp_conn_rate_ctr_desc above */ | ||||
| static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = { | ||||
| 	[IN_STREAM_ERR_TSTMP_CTR] = {"all_rtp:err_tstmp_in", "Total inbound rtp-stream timestamp errors."}, | ||||
| 	[OUT_STREAM_ERR_TSTMP_CTR] = {"all_rtp:err_tstmp_out", "Total outbound rtp-stream timestamp errors."}, | ||||
| 	[RTP_PACKETS_RX_CTR] = {"all_rtp:packets_rx", "Total inbound rtp packets."}, | ||||
| 	[RTP_OCTETS_RX_CTR] = {"all_rtp:octets_rx", "Total inbound rtp octets."}, | ||||
| 	[RTP_PACKETS_TX_CTR] = {"all_rtp:packets_tx", "Total outbound rtp packets."}, | ||||
| 	[RTP_OCTETS_TX_CTR] = {"all_rtp:octets_tx", "Total outbound rtp octets."}, | ||||
| 	[RTP_DROPPED_PACKETS_CTR] = {"all_rtp:dropped", "Total dropped rtp packets."}, | ||||
|  | ||||
| 	/* This last counter does not exist in per-connection stats, only here. */ | ||||
| 	[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."} | ||||
| }; | ||||
|  | ||||
| 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); | ||||
| char *mgcp_conn_dump(struct mgcp_conn *conn); | ||||
| struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn); | ||||
							
								
								
									
										101
									
								
								include/osmocom/mgcp/mgcp_endp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								include/osmocom/mgcp/mgcp_endp.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| /* Endpoint types */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Philipp Maier | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| struct sockaddr_in; | ||||
| struct mgcp_conn; | ||||
| struct mgcp_endpoint; | ||||
|  | ||||
| /* Callback type for RTP dispatcher functions | ||||
|    (e.g mgcp_dispatch_rtp_bridge_cb, see below) */ | ||||
| typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr, | ||||
| 				     char *buf, unsigned int buf_size, | ||||
| 				     struct mgcp_conn *conn); | ||||
|  | ||||
| /* Callback type for endpoint specific cleanup actions. This function | ||||
|  * is automatically executed when a connection is freed (see mgcp_conn_free() | ||||
|  * in mgcp_conn.c). Depending on the type of the endpoint there may be endpoint | ||||
|  * specific things to take care of once a connection has been removed. */ | ||||
| typedef void (*mgcp_cleanup_cp) (struct mgcp_endpoint *endp, | ||||
| 				 struct mgcp_conn *conn); | ||||
|  | ||||
| /*! MGCP endpoint properties */ | ||||
| struct mgcp_endpoint_type { | ||||
| 	/*! maximum number of connections */ | ||||
| 	int max_conns; | ||||
|  | ||||
| 	/*! callback that defines how to dispatch incoming RTP data */ | ||||
| 	mgcp_dispatch_rtp_cb dispatch_rtp_cb; | ||||
|  | ||||
| 	/*! callback that implements endpoint specific cleanup actions */ | ||||
| 	mgcp_cleanup_cp cleanup_cb; | ||||
| }; | ||||
|  | ||||
| /*! MGCP endpoint typeset */ | ||||
| struct mgcp_endpoint_typeset { | ||||
| 	struct mgcp_endpoint_type rtp; | ||||
| }; | ||||
|  | ||||
| /*! static MGCP endpoint typeset (pre-initalized, read-only) */ | ||||
| extern const struct mgcp_endpoint_typeset ep_typeset; | ||||
|  | ||||
| /*! MGCP endpoint model */ | ||||
| struct mgcp_endpoint { | ||||
|  | ||||
| 	/*! Call identifier string (as supplied by the call agant) */ | ||||
| 	char *callid; | ||||
|  | ||||
| 	/*! Local connection options (see mgcp_internal.h) */ | ||||
| 	struct mgcp_lco local_options; | ||||
|  | ||||
| 	/*! List of struct mgcp_conn, of the connections active on this endpoint */ | ||||
| 	struct llist_head conns; | ||||
|  | ||||
| 	/*! Backpointer to the MGW configuration */ | ||||
| 	struct mgcp_config *cfg; | ||||
|  | ||||
| 	/*! Backpointer to the Trunk specific configuration */ | ||||
| 	struct mgcp_trunk_config *tcfg; | ||||
|  | ||||
| 	/*! Endpoint properties (see above) */ | ||||
| 	const struct mgcp_endpoint_type *type; | ||||
|  | ||||
| 	/*! Last MGCP transmission (in case re-transmission is required) */ | ||||
| 	char *last_trans; | ||||
|  | ||||
| 	/*! Last MGCP response (in case re-transmission is required) */ | ||||
| 	char *last_response; | ||||
|  | ||||
| 	/*! Memorize if this endpoint was choosen by the MGW (wildcarded, true) | ||||
| 	 *   or if the user has choosen the particular endpoint explicitly. */ | ||||
| 	bool wildcarded_req; | ||||
|  | ||||
| 	/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */ | ||||
| 	uint32_t x_osmo_ign; | ||||
| }; | ||||
|  | ||||
| /*! Extract endpoint number for a given endpoint */ | ||||
| #define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints)) | ||||
|  | ||||
| void mgcp_endp_release(struct mgcp_endpoint *endp); | ||||
							
								
								
									
										352
									
								
								include/osmocom/mgcp/mgcp_internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										352
									
								
								include/osmocom/mgcp/mgcp_internal.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,352 @@ | ||||
| /* MGCP Private Data */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2012 by On-Waves | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string.h> | ||||
| #include <inttypes.h> | ||||
| #include <osmocom/core/select.h> | ||||
| #include <osmocom/mgcp/mgcp.h> | ||||
| #include <osmocom/core/linuxlist.h> | ||||
| #include <osmocom/core/counter.h> | ||||
| #include <osmocom/core/rate_ctr.h> | ||||
|  | ||||
| #define CI_UNUSED 0 | ||||
|  | ||||
| /* FIXME: This this is only needed to compile the currently | ||||
|  * broken OSMUX support. Remove when fixed */ | ||||
| #define CONN_ID_BTS "0" | ||||
| #define CONN_ID_NET "1" | ||||
|  | ||||
| enum mgcp_trunk_type { | ||||
| 	MGCP_TRUNK_VIRTUAL, | ||||
| 	MGCP_TRUNK_E1, | ||||
| }; | ||||
|  | ||||
| struct mgcp_rtp_stream_state { | ||||
| 	uint32_t ssrc; | ||||
| 	uint16_t last_seq; | ||||
| 	uint32_t last_timestamp; | ||||
| 	struct rate_ctr *err_ts_ctr; | ||||
| 	int32_t last_tsdelta; | ||||
| 	uint32_t last_arrival_time; | ||||
| }; | ||||
|  | ||||
| struct mgcp_rtp_state { | ||||
| 	/* has this state structure been initialized? */ | ||||
| 	int initialized; | ||||
|  | ||||
| 	struct { | ||||
| 		/* are we patching the SSRC value? */ | ||||
| 		int patch_ssrc; | ||||
| 		/* original SSRC (to which we shall patch any different SSRC) */ | ||||
| 		uint32_t orig_ssrc; | ||||
| 		/* offset to apply on the sequence number */ | ||||
| 		int seq_offset; | ||||
| 		/* offset to apply on the timestamp number */ | ||||
| 		int32_t timestamp_offset; | ||||
| 	} patch; | ||||
|  | ||||
| 	/* duration of a packet (FIXME: in which unit?) */ | ||||
| 	uint32_t packet_duration; | ||||
|  | ||||
| 	struct mgcp_rtp_stream_state in_stream; | ||||
| 	struct mgcp_rtp_stream_state out_stream; | ||||
|  | ||||
| 	/* jitter and packet loss calculation */ | ||||
| 	struct { | ||||
| 		int initialized; | ||||
| 		uint16_t base_seq; | ||||
| 		uint16_t max_seq; | ||||
| 		uint32_t ssrc; | ||||
| 		uint32_t jitter; | ||||
| 		int32_t transit; | ||||
| 		int cycles; | ||||
| 	} stats; | ||||
|  | ||||
| 	bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */ | ||||
| }; | ||||
|  | ||||
| struct mgcp_rtp_codec { | ||||
| 	uint32_t rate; | ||||
| 	int channels; | ||||
| 	uint32_t frame_duration_num; | ||||
| 	uint32_t frame_duration_den; | ||||
|  | ||||
| 	int payload_type; | ||||
| 	char *audio_name; | ||||
| 	char *subtype_name; | ||||
|  | ||||
| 	bool param_present; | ||||
| 	struct mgcp_codec_param param; | ||||
| }; | ||||
|  | ||||
| /* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */ | ||||
| struct mgcp_rtp_end { | ||||
| 	/* local IP address of the RTP socket */ | ||||
| 	struct in_addr addr; | ||||
|  | ||||
| 	/* in network byte order */ | ||||
| 	int rtp_port, rtcp_port; | ||||
|  | ||||
| 	/* currently selected audio codec */ | ||||
| 	struct mgcp_rtp_codec *codec; | ||||
|  | ||||
| 	/* array with assigned audio codecs to choose from (SDP) */ | ||||
| 	struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS]; | ||||
|  | ||||
| 	/* number of assigned audio codecs (SDP) */ | ||||
| 	unsigned int codecs_assigned; | ||||
|  | ||||
| 	/* per endpoint data */ | ||||
| 	int  frames_per_packet; | ||||
| 	uint32_t packet_duration_ms; | ||||
| 	int maximum_packet_time; /* -1: not set */ | ||||
| 	char *fmtp_extra; | ||||
| 	/* are we transmitting packets (1) or dropping (0) outbound packets */ | ||||
| 	int output_enabled; | ||||
| 	/* FIXME: This parameter can be set + printed, but is nowhere used! */ | ||||
| 	int force_output_ptime; | ||||
|  | ||||
| 	/* RTP patching */ | ||||
| 	int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ | ||||
| 	/* should we perform align_rtp_timestamp_offset() (1) or not (0) */ | ||||
| 	int force_aligned_timing; | ||||
| 	bool rfc5993_hr_convert; | ||||
|  | ||||
| 	/* Each end has a separate socket for RTP and RTCP */ | ||||
| 	struct osmo_fd rtp; | ||||
| 	struct osmo_fd rtcp; | ||||
|  | ||||
| 	/* local UDP port number of the RTP socket; RTCP is +1 */ | ||||
| 	int local_port; | ||||
| }; | ||||
|  | ||||
| struct mgcp_rtp_tap { | ||||
| 	/* is this tap active (1) or not (0) */ | ||||
| 	int enabled; | ||||
| 	/* IP/port to which we're forwarding the tapped data */ | ||||
| 	struct sockaddr_in forward; | ||||
| }; | ||||
|  | ||||
| struct mgcp_lco { | ||||
| 	char *string; | ||||
| 	char *codec; | ||||
| 	int pkt_period_min; /* time in ms */ | ||||
| 	int pkt_period_max; /* time in ms */ | ||||
| }; | ||||
|  | ||||
| /* 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, | ||||
| }; | ||||
|  | ||||
| #include <osmocom/mgcp/osmux.h> | ||||
| struct mgcp_conn; | ||||
|  | ||||
| /* MGCP connection (RTP) */ | ||||
| struct mgcp_conn_rtp { | ||||
|  | ||||
| 	/* Backpointer to conn struct */ | ||||
| 	struct mgcp_conn *conn; | ||||
|  | ||||
| 	/* Specific connection type */ | ||||
| 	enum mgcp_conn_rtp_type type; | ||||
|  | ||||
| 	/* Port status */ | ||||
| 	struct mgcp_rtp_end end; | ||||
|  | ||||
| 	/* Sequence bits */ | ||||
| 	struct mgcp_rtp_state state; | ||||
|  | ||||
| 	/* taps for the rtp connection; one per direction */ | ||||
| 	struct mgcp_rtp_tap tap_in; | ||||
| 	struct mgcp_rtp_tap tap_out; | ||||
|  | ||||
| 	/* Osmux states (optional) */ | ||||
| 	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 */ | ||||
| 		struct osmux_in_handle *in; | ||||
| 		/* handle to unbatch messages */ | ||||
| 		struct osmux_out_handle out; | ||||
| 		/* statistics */ | ||||
| 		struct { | ||||
| 			uint32_t chunks; | ||||
| 			uint32_t octets; | ||||
| 		} stats; | ||||
| 	} osmux; | ||||
|  | ||||
| 	struct rate_ctr_group *rate_ctr_group; | ||||
| }; | ||||
|  | ||||
| /*! Connection type, specifies which member of the union "u" in mgcp_conn | ||||
|  *  contains a useful connection description (currently only RTP) */ | ||||
| enum mgcp_conn_type { | ||||
| 	MGCP_CONN_TYPE_RTP, | ||||
| }; | ||||
|  | ||||
| /*! MGCP connection (untyped) */ | ||||
| struct mgcp_conn { | ||||
| 	/*! list head */ | ||||
| 	struct llist_head entry; | ||||
|  | ||||
| 	/*! Backpointer to the endpoint where the conn belongs to */ | ||||
| 	struct mgcp_endpoint *endp; | ||||
|  | ||||
| 	/*! type of the connection (union) */ | ||||
| 	enum mgcp_conn_type type; | ||||
|  | ||||
| 	/*! mode of the connection */ | ||||
| 	enum mgcp_connection_mode mode; | ||||
|  | ||||
| 	/*! copy of the mode to restore the original setting (VTY) */ | ||||
| 	enum mgcp_connection_mode mode_orig; | ||||
|  | ||||
| 	/*! connection id to identify the connection */ | ||||
| 	char id[MGCP_CONN_ID_MAXLEN]; | ||||
|  | ||||
| 	/*! human readable name (vty, logging) */ | ||||
| 	char name[256]; | ||||
|  | ||||
| 	/*! activity tracker (for cleaning up inactive connections) */ | ||||
| 	struct osmo_timer_list watchdog; | ||||
|  | ||||
| 	/*! union with connection description */ | ||||
| 	union { | ||||
| 		struct mgcp_conn_rtp rtp; | ||||
| 	} u; | ||||
|  | ||||
| 	/*! pointer to optional private data */ | ||||
| 	void *priv; | ||||
| }; | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
|  | ||||
| struct mgcp_endpoint_type; | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Internal structure while parsing a request | ||||
|  */ | ||||
| struct mgcp_parse_data { | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	char *trans; | ||||
| 	char *save; | ||||
| }; | ||||
|  | ||||
| int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, | ||||
| 	      char *buf, int rc, struct mgcp_conn_rtp *conn_src, | ||||
| 	      struct mgcp_conn_rtp *conn_dst); | ||||
| int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn); | ||||
| int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf, | ||||
| 				unsigned int buf_size, struct mgcp_conn *conn); | ||||
| void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn); | ||||
| int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port, | ||||
| 			   struct mgcp_conn_rtp *conn); | ||||
| void mgcp_free_rtp_port(struct mgcp_rtp_end *end); | ||||
|  | ||||
| /* For transcoding we need to manage an in and an output that are connected */ | ||||
| static inline int endp_back_channel(int endpoint) | ||||
| { | ||||
| 	return endpoint + 60; | ||||
| } | ||||
|  | ||||
| struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index); | ||||
| struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index); | ||||
|  | ||||
| char *get_lco_identifier(const char *options); | ||||
| int check_local_cx_options(void *ctx, const char *options); | ||||
| void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, | ||||
| 			 struct mgcp_rtp_end *rtp); | ||||
| uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp, | ||||
| 				  struct mgcp_rtp_end *rtp); | ||||
|  | ||||
| /* 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_setup_rtp_processing_default(struct mgcp_endpoint *endp, | ||||
| 				      struct mgcp_conn_rtp *conn_dst, | ||||
| 				      struct mgcp_conn_rtp *conn_src); | ||||
|  | ||||
| void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, | ||||
| 					  const struct mgcp_rtp_codec **codec, | ||||
| 					  const char **fmtp_extra, | ||||
| 					  struct mgcp_conn_rtp *conn); | ||||
|  | ||||
| /* internal RTP Annex A counting */ | ||||
| void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, | ||||
| 			const uint16_t seq, const int32_t transit, | ||||
| 			const uint32_t ssrc); | ||||
|  | ||||
| int mgcp_set_ip_tos(int fd, int tos); | ||||
|  | ||||
| /* 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; | ||||
| } | ||||
|  | ||||
| enum { | ||||
| 	MGCP_DEST_NET = 0, | ||||
| 	MGCP_DEST_BTS, | ||||
| }; | ||||
|  | ||||
|  | ||||
| #define MGCP_DUMMY_LOAD 0x23 | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * SDP related information | ||||
|  */ | ||||
| /* Assume audio frame length of 20ms */ | ||||
| #define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20 | ||||
| #define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000 | ||||
| #define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20 | ||||
| #define DEFAULT_RTP_AUDIO_DEFAULT_RATE  8000 | ||||
| #define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1 | ||||
|  | ||||
| #define PTYPE_UNDEFINED (-1) | ||||
|  | ||||
| void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn); | ||||
| void mgcp_conn_watchdog_kick(struct mgcp_conn *conn); | ||||
|  | ||||
| #define LOGPENDP(endp, cat, level, fmt, args...) \ | ||||
| LOGP(cat, level, "endpoint:0x%x " fmt, \ | ||||
|      endp ? ENDPOINT_NUMBER(endp) : -1, \ | ||||
|      ## args) | ||||
|  | ||||
| #define LOGPCONN(conn, cat, level, fmt, args...) \ | ||||
| LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \ | ||||
|          (conn)->id, \ | ||||
|          ## args) | ||||
							
								
								
									
										56
									
								
								include/osmocom/mgcp/mgcp_msg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								include/osmocom/mgcp/mgcp_msg.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| /* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ | ||||
| /* Message parser/generator utilities */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2012 by On-Waves | ||||
|  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * 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 | ||||
|  | ||||
| #include <stdint.h> | ||||
|  | ||||
| struct mgcp_conn; | ||||
| struct mgcp_parse_data; | ||||
| struct mgcp_endpoint; | ||||
|  | ||||
| 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); | ||||
|  | ||||
| int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data); | ||||
|  | ||||
| int mgcp_parse_osmux_cid(const char *line); | ||||
|  | ||||
| int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line); | ||||
|  | ||||
| int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid); | ||||
|  | ||||
| int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id); | ||||
|  | ||||
| char *mgcp_strline(char *str, char **saveptr); | ||||
|  | ||||
| #define for_each_line(line, save)\ | ||||
| 	for (line = mgcp_strline(NULL, &save); line;\ | ||||
| 	     line = mgcp_strline(NULL, &save)) | ||||
|  | ||||
| #define for_each_non_empty_line(line, save)\ | ||||
| 	for (line = strtok_r(NULL, "\r\n", &save); line;\ | ||||
| 	     line = strtok_r(NULL, "\r\n", &save)) | ||||
							
								
								
									
										31
									
								
								include/osmocom/mgcp/mgcp_sdp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								include/osmocom/mgcp/mgcp_sdp.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| /* | ||||
|  * SDP generation and parsing | ||||
|  * | ||||
|  * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2014 by On-Waves | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp, | ||||
| 			struct mgcp_conn_rtp *conn, | ||||
| 			struct mgcp_parse_data *p); | ||||
|  | ||||
| int mgcp_write_response_sdp(const struct mgcp_endpoint *endp, | ||||
| 			    const struct mgcp_conn_rtp *conn, struct msgb *sdp, | ||||
| 			    const char *addr); | ||||
							
								
								
									
										36
									
								
								include/osmocom/mgcp/mgcp_stat.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								include/osmocom/mgcp/mgcp_stat.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| /* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ | ||||
| /* The statistics generator */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2012 by On-Waves | ||||
|  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * 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 | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <inttypes.h> | ||||
|  | ||||
| void mgcp_format_stats(char *str, size_t str_len,  struct mgcp_conn *conn); | ||||
|  | ||||
| /* Exposed for test purposes only, do not use actively */ | ||||
| void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss); | ||||
|  | ||||
| /* Exposed for test purposes only, do not use actively */ | ||||
| uint32_t calc_jitter(struct mgcp_rtp_state *); | ||||
							
								
								
									
										39
									
								
								include/osmocom/mgcp/osmux.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								include/osmocom/mgcp/osmux.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| #pragma once | ||||
|  | ||||
|  | ||||
| #include <osmocom/netif/osmux.h> | ||||
| 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 in_addr *addr, uint16_t port); | ||||
| 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); | ||||
|  | ||||
| void osmux_cid_pool_get(uint8_t osmux_cid); | ||||
| int osmux_cid_pool_get_next(void); | ||||
| void osmux_cid_pool_put(uint8_t osmux_cid); | ||||
| bool osmux_cid_pool_allocated(uint8_t osmux_cid); | ||||
| 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 */ | ||||
| }; | ||||
|  | ||||
| enum osmux_usage { | ||||
| 	OSMUX_USAGE_OFF = 0, | ||||
| 	OSMUX_USAGE_ON = 1, | ||||
| 	OSMUX_USAGE_ONLY = 2, | ||||
| }; | ||||
							
								
								
									
										8
									
								
								include/osmocom/mgcp/vty.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								include/osmocom/mgcp/vty.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/vty/command.h> | ||||
|  | ||||
| enum mgcp_vty_node { | ||||
| 	MGCP_NODE = _LAST_OSMOVTY_NODE + 1, | ||||
| 	TRUNK_NODE, | ||||
| }; | ||||
| @@ -1,3 +1,13 @@ | ||||
| BUILT_SOURCES = \ | ||||
| 	mgcp_common.h \ | ||||
| 	$(NULL) | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	mgcp_client_internal.h \ | ||||
| 	$(NULL) | ||||
|  | ||||
| mgcp_common.h: $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h | ||||
| 	echo -e "/*\n\n   DO NOT EDIT THIS FILE!\n   THIS IS OVERWRITTEN DURING BUILD\n   This is an automatic copy of <osmocom/mgcp/mgcp_common.h>\n\n */" > mgcp_common.h | ||||
| 	cat $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h >> mgcp_common.h | ||||
|  | ||||
| CLEANFILES = mgcp_common.h | ||||
|   | ||||
| @@ -1,12 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <arpa/inet.h> | ||||
|  | ||||
| #include <osmocom/mgcp_client/mgcp_common.h> | ||||
|  | ||||
| /* See also: RFC 3435, chapter 3.5 Transmission over UDP */ | ||||
| #define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0" | ||||
| #define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0 | ||||
| #define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727 | ||||
| #define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1" | ||||
| #define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427 | ||||
|  | ||||
| #define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n" | ||||
|  | ||||
| struct msgb; | ||||
| struct vty; | ||||
| struct mgcp_client; | ||||
| @@ -16,23 +22,104 @@ struct mgcp_client_conf { | ||||
| 	int local_port; | ||||
| 	const char *remote_addr; | ||||
| 	int remote_port; | ||||
| 	uint16_t first_endpoint; | ||||
| 	uint16_t last_endpoint; | ||||
| 	uint16_t bts_base; | ||||
|  | ||||
| 	/* By default, we are always addressing the MGW with e.g. 'rtpbridge/123@mgw'. | ||||
| 	 * If this is nonempty, the contained name will be used instead of 'mgw'. */ | ||||
| 	char endpoint_domain_name[MGCP_ENDPOINT_MAXLEN]; | ||||
| }; | ||||
|  | ||||
| typedef unsigned int mgcp_trans_id_t; | ||||
|  | ||||
| /*! Enumeration of the codec types that mgcp_client is able to handle. */ | ||||
| enum mgcp_codecs { | ||||
| 	CODEC_PCMU_8000_1 = 0, | ||||
| 	CODEC_GSM_8000_1 = 3, | ||||
| 	CODEC_PCMA_8000_1 = 8, | ||||
| 	CODEC_G729_8000_1 = 18, | ||||
| 	CODEC_GSMEFR_8000_1 = 110, | ||||
| 	CODEC_GSMHR_8000_1 = 111, | ||||
| 	CODEC_AMR_8000_1 = 112, | ||||
| 	CODEC_AMRWB_16000_1 = 113, | ||||
| }; | ||||
| /* Note: when new codec types are added, the corresponding value strings | ||||
|  * in mgcp_client.c (codec_table) must be updated as well. Enumerations | ||||
|  * in enum mgcp_codecs must correspond to a valid payload type. However, | ||||
|  * this is an internal assumption that is made to avoid lookup tables. | ||||
|  * The API-User should not rely on this coincidence! */ | ||||
|  | ||||
| extern const struct value_string osmo_mgcpc_codec_names[]; | ||||
| static inline const char *osmo_mgcpc_codec_name(enum mgcp_codecs val) | ||||
| { return get_value_string(osmo_mgcpc_codec_names, val); } | ||||
|  | ||||
| /*! Structure to build a payload type map to allow the defiition custom payload | ||||
|  *  types. */ | ||||
| struct ptmap { | ||||
| 	/*! codec for which a payload type number should be defined */ | ||||
| 	enum mgcp_codecs codec; | ||||
|  | ||||
| 	/*! payload type number (96-127) */ | ||||
| 	unsigned int pt; | ||||
| }; | ||||
|  | ||||
| struct mgcp_response_head { | ||||
|        int response_code; | ||||
|        mgcp_trans_id_t trans_id; | ||||
|        const char *comment; | ||||
| 	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[INET_ADDRSTRLEN]; | ||||
| 	unsigned int ptime; | ||||
| 	enum mgcp_codecs codecs[MGCP_MAX_CODECS]; | ||||
| 	unsigned int codecs_len; | ||||
| 	struct ptmap ptmap[MGCP_MAX_CODECS]; | ||||
| 	unsigned int ptmap_len; | ||||
| }; | ||||
|  | ||||
| enum mgcp_verb { | ||||
| 	MGCP_VERB_CRCX, | ||||
| 	MGCP_VERB_MDCX, | ||||
| 	MGCP_VERB_DLCX, | ||||
| 	MGCP_VERB_AUEP, | ||||
| 	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); | ||||
| @@ -48,8 +135,8 @@ const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp); | ||||
| uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp); | ||||
| uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp); | ||||
|  | ||||
| int mgcp_client_next_endpoint(struct mgcp_client *client); | ||||
| void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client); | ||||
| const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp); | ||||
| const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp); | ||||
|  | ||||
| /* Invoked when an MGCP response is received or sending failed.  When the | ||||
|  * response is passed as NULL, this indicates failure during transmission. */ | ||||
| @@ -58,16 +145,21 @@ 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_crcx(struct mgcp_client *mgcp, | ||||
| 			   uint16_t rtp_endpoint, unsigned int call_id, | ||||
| 			   enum mgcp_connection_mode 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); | ||||
|  | ||||
| struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp, | ||||
| 			   uint16_t rtp_endpoint, const char *rtp_conn_addr, | ||||
| 			   uint16_t rtp_port, enum mgcp_connection_mode mode); | ||||
| extern const struct value_string mgcp_client_connection_mode_strs[]; | ||||
| static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode) | ||||
| { | ||||
| 	return get_value_string(mgcp_client_connection_mode_strs, mode); | ||||
| } | ||||
|  | ||||
| struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint, | ||||
| 			   unsigned int call_id); | ||||
| enum mgcp_codecs map_str_to_codec(const char *str); | ||||
| unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len, | ||||
| 			     enum mgcp_codecs codec); | ||||
| enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len, | ||||
| 				 unsigned int pt); | ||||
|   | ||||
							
								
								
									
										51
									
								
								include/osmocom/mgcp_client/mgcp_client_endpoint_fsm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								include/osmocom/mgcp_client/mgcp_client_endpoint_fsm.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| /* FSM to manage multiple connections of an MGW endpoint */ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/mgcp_client/mgcp_client_fsm.h> | ||||
|  | ||||
| #define LOG_MGCPC_EP(ep, level, fmt, args...) do { \ | ||||
| 	LOGPFSML(ep->fi, level, "%s " fmt, \ | ||||
| 		 osmo_mgcpc_ep_name(ep), ## args); \ | ||||
| 	} while(0) | ||||
|  | ||||
| struct osmo_mgcpc_ep; | ||||
| struct osmo_mgcpc_ep_ci; | ||||
| struct osmo_tdef; | ||||
|  | ||||
| struct osmo_mgcpc_ep *osmo_mgcpc_ep_alloc(struct osmo_fsm_inst *parent, uint32_t parent_term_event, | ||||
| 					  struct mgcp_client *mgcp_client, | ||||
| 					  const struct osmo_tdef *T_defs, | ||||
| 					  const char *fsm_id, | ||||
| 					  const char *endpoint_str_fmt, ...); | ||||
|  | ||||
| struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_add(struct osmo_mgcpc_ep *ep, const char *label_fmt, ...); | ||||
| const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci); | ||||
| bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest); | ||||
| bool osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(const struct osmo_mgcpc_ep_ci *ci, uint8_t* cid); | ||||
|  | ||||
| void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci, | ||||
| 			      enum mgcp_verb verb, const struct mgcp_conn_peer *verb_info, | ||||
| 			      struct osmo_fsm_inst *notify, | ||||
| 			      uint32_t event_success, uint32_t event_failure, | ||||
| 			      void *notify_data); | ||||
|  | ||||
| void osmo_mgcpc_ep_cancel_notify(struct osmo_mgcpc_ep *ep, struct osmo_fsm_inst *notify); | ||||
| struct osmo_mgcpc_ep *osmo_mgcpc_ep_ci_ep(struct osmo_mgcpc_ep_ci *ci); | ||||
|  | ||||
| /*! Dispatch a DLCX for the given connection. | ||||
|  * \param ci  Connection identifier as obtained from osmo_mgcpc_ep_ci_add(). | ||||
|  */ | ||||
| static inline void osmo_mgcpc_ep_ci_dlcx(struct osmo_mgcpc_ep_ci *ci) | ||||
| { | ||||
| 	osmo_mgcpc_ep_ci_request(ci, MGCP_VERB_DLCX, NULL, NULL, 0, 0, NULL); | ||||
| } | ||||
|  | ||||
| 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_ci_name(const struct osmo_mgcpc_ep_ci *ci); | ||||
| const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci); | ||||
|  | ||||
| extern const struct value_string osmo_mgcp_verb_names[]; | ||||
| static inline const char *osmo_mgcp_verb_name(enum mgcp_verb val) | ||||
| { return get_value_string(osmo_mgcp_verb_names, val); } | ||||
							
								
								
									
										73
									
								
								include/osmocom/mgcp_client/mgcp_client_fsm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								include/osmocom/mgcp_client/mgcp_client_fsm.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/mgcp_client/mgcp_common.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client.h> | ||||
| #include <osmocom/gsm/protocol/gsm_04_08.h> | ||||
|  | ||||
| /*! This struct organizes the connection infromation one connection side | ||||
|  *  (either remote or local). It is used to pass parameters (local) to the FSM | ||||
|  *  and get responses (remote) from the FSM as pointer attached to the FSM | ||||
|  *  event. | ||||
|  *  | ||||
|  *  When modifiying a connection, the endpoint and call_id members may be left | ||||
|  *  unpopulated. The call_id field is ignored in this case. If an endpoint | ||||
|  *  identifier is supplied it is checked against the internal state to make | ||||
|  *  sure it is correct. */ | ||||
| struct mgcp_conn_peer { | ||||
| 	/*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */ | ||||
| 	char addr[INET_ADDRSTRLEN]; | ||||
|  | ||||
| 	/*! RTP connection IP-Port (optional)  */ | ||||
| 	uint16_t port; | ||||
|  | ||||
| 	/*! RTP endpoint */ | ||||
| 	char endpoint[MGCP_ENDPOINT_MAXLEN]; | ||||
|  | ||||
| 	/*! CALL ID (unique per connection) */ | ||||
| 	unsigned int call_id; | ||||
|  | ||||
| 	/*! RTP packetization interval (optional) */ | ||||
| 	unsigned int ptime; | ||||
|  | ||||
| 	/*! RTP codec list (optional) */ | ||||
| 	enum mgcp_codecs codecs[MGCP_MAX_CODECS]; | ||||
|  | ||||
| 	/*! Number of codecs in RTP codec list (optional) */ | ||||
| 	unsigned int codecs_len; | ||||
|  | ||||
| 	/*! RTP payload type map (optional, only needed when payload types are | ||||
| 	 * used that differ from what IANA/3GPP defines) */ | ||||
| 	struct ptmap ptmap[MGCP_MAX_CODECS]; | ||||
|  | ||||
| 	/*! RTP payload type map length (optional, only needed when payload | ||||
| 	 * types are used that differ from what IANA/3GPP defines) */ | ||||
| 	unsigned int ptmap_len; | ||||
|  | ||||
| 	/*! If nonzero, send 'X-Osmo-IGN:' header. This is useful e.g. for SCCPlite MSCs where the MSC is | ||||
| 	 * known to issue incoherent or unknown CallIDs / to issue CRCX commands with a different domain | ||||
| 	 * name than the BSC. An OsmoMGW will then ignore these and not fail on mismatches. */ | ||||
| 	uint32_t x_osmo_ign; | ||||
|  | ||||
| 	/*! send 'X-Osmux: %d' header (or "*" as wildcard). */ | ||||
| 	bool x_osmo_osmux_use; | ||||
| 	/*! -1 means send wildcard. */ | ||||
| 	int x_osmo_osmux_cid; | ||||
|  | ||||
| 	/*! If left MGCP_CONN_NONE, use MGCP_CONN_RECV_ONLY or MGCP_CONN_RECV_SEND, depending on whether an audio RTP | ||||
| 	 * address is set. If != MGCP_CONN_NONE, force this conn mode. */ | ||||
| 	enum mgcp_connection_mode conn_mode; | ||||
|  | ||||
| 	/*! If the codec requires additional format parameters (fmtp), those cann be set here, see also | ||||
| 	 * mgcp_common.h */ | ||||
| 	bool param_present; | ||||
| 	struct mgcp_codec_param param; | ||||
| }; | ||||
|  | ||||
| struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt, | ||||
| 				       uint32_t parent_evt, struct mgcp_conn_peer *conn_peer); | ||||
| int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer); | ||||
| void mgcp_conn_delete(struct osmo_fsm_inst *fi); | ||||
|  | ||||
| const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi); | ||||
|  | ||||
| const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info); | ||||
| @@ -1,5 +1,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/core/write_queue.h> | ||||
|  | ||||
| #define MSGB_CB_MGCP_TRANS_ID 0 | ||||
|  | ||||
| struct mgcp_client { | ||||
|   | ||||
| @@ -1,10 +0,0 @@ | ||||
| prefix=@prefix@ | ||||
| exec_prefix=@exec_prefix@ | ||||
| libdir=@libdir@ | ||||
| includedir=@includedir@ | ||||
|  | ||||
| Name: Osmocom legacy Media Gateway Control Protocol library | ||||
| Description: C Utility Library | ||||
| Version: @VERSION@ | ||||
| Libs: -L${libdir} -losmo-legacy-mgcp | ||||
| Cflags: -I${includedir}/ | ||||
| @@ -1,4 +1,4 @@ | ||||
| #!/usr/bin/env python | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com> | ||||
| # This program is free software: you can redistribute it and/or modify | ||||
| @@ -16,13 +16,13 @@ | ||||
|  | ||||
|  | ||||
| app_configs = { | ||||
|     "mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"], | ||||
|     "osmo-mgw": ["doc/examples/osmo-mgw/osmo-mgw.cfg"], | ||||
| } | ||||
|  | ||||
| apps = [(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp"), | ||||
| apps = [(4243, "src/osmo-mgw/osmo-mgw", "OsmoMGW", "osmo-mgw"), | ||||
|         ] | ||||
|  | ||||
| vty_command = ["./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "-c", | ||||
|                "doc/examples/osmo-bsc_mgcp/mgcp.cfg"] | ||||
| vty_command = ["./src/osmo-mgw/osmo-mgw", "-c", | ||||
|                "doc/examples/osmo-mgw/osmo-mgw.cfg"] | ||||
|  | ||||
| vty_app = apps[0] | ||||
|   | ||||
| @@ -21,11 +21,11 @@ AM_LDFLAGS = \ | ||||
|  | ||||
| # Libraries | ||||
| SUBDIRS = \ | ||||
| 	libosmo-legacy-mgcp \ | ||||
| 	libosmo-mgcp-client \ | ||||
| 	libosmo-mgcp \ | ||||
| 	$(NULL) | ||||
|  | ||||
| # Programs | ||||
| SUBDIRS += \ | ||||
| 	osmo-bsc_mgcp \ | ||||
| 	osmo-mgw \ | ||||
| 	$(NULL) | ||||
|   | ||||
| @@ -1,51 +0,0 @@ | ||||
| AM_CPPFLAGS = \ | ||||
| 	$(all_includes) \ | ||||
| 	-I$(top_srcdir)/include \ | ||||
| 	-I$(top_builddir) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOVTY_CFLAGS) \ | ||||
| 	$(LIBOSMONETIF_CFLAGS) \ | ||||
| 	$(COVERAGE_CFLAGS) \ | ||||
| 	$(LIBBCG729_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	$(COVERAGE_LDFLAGS) \ | ||||
| 	$(LIBBCG729_LIBS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| # This is _NOT_ the library release version, it's an API version. | ||||
| # Please read Chapter 6 "Library interface versions" of the libtool | ||||
| # documentation before making any modification | ||||
| LEGACY_MGCP_LIBVERSION=0:0:0 | ||||
|  | ||||
| lib_LTLIBRARIES = \ | ||||
| 	libosmo-legacy-mgcp.la \ | ||||
| 	$(NULL) | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	g711common.h \ | ||||
| 	$(NULL) | ||||
|  | ||||
| libosmo_legacy_mgcp_la_SOURCES = \ | ||||
| 	mgcp_common.c \ | ||||
| 	mgcp_protocol.c \ | ||||
| 	mgcp_network.c \ | ||||
| 	mgcp_vty.c \ | ||||
| 	mgcp_osmux.c \ | ||||
| 	mgcp_sdp.c \ | ||||
| 	$(NULL) | ||||
| if BUILD_MGCP_TRANSCODING | ||||
| libosmo_legacy_mgcp_la_SOURCES += \ | ||||
| 	mgcp_transcode.c \ | ||||
| 	$(NULL) | ||||
| endif | ||||
|  | ||||
| libosmo_legacy_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LEGACY_MGCP_LIBVERSION) | ||||
| @@ -1,54 +0,0 @@ | ||||
| /* Media Gateway Control Protocol Media Gateway: RFC 3435 */ | ||||
| /* Implementations useful both for the MGCP GW as well as MGCP GW clients */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de> | ||||
|  * 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 <errno.h> | ||||
|  | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/legacy_mgcp/mgcp.h> | ||||
|  | ||||
| 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_LOOPBACK, "loopback" }, | ||||
| 	{ 0, NULL } | ||||
| }; | ||||
|  | ||||
| /* Ensure that the msg->l2h is NUL terminated. */ | ||||
| int mgcp_msg_terminate_nul(struct msgb *msg) | ||||
| { | ||||
| 	unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */ | ||||
| 	if (tail[-1] == '\0') | ||||
| 		/* nothing to do */; | ||||
| 	else if (msgb_tailroom(msg) > 0) | ||||
| 		tail[0] = '\0'; | ||||
| 	else if (tail[-1] == '\r' || tail[-1] == '\n') | ||||
| 		tail[-1] = '\0'; | ||||
| 	else { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " | ||||
| 		     "Length: %d, Buffer size: %d\n", | ||||
| 		     msgb_l2len(msg), msg->data_len); | ||||
| 		return -ENOTSUP; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,583 +0,0 @@ | ||||
| /* | ||||
|  * (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org> | ||||
|  * (C) 2012-2013 by On Waves ehf <http://www.on-waves.com> | ||||
|  * All rights not specifically granted under this license are 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. | ||||
|  */ | ||||
|  | ||||
| #include <stdio.h> /* for printf */ | ||||
| #include <string.h> /* for memcpy */ | ||||
| #include <stdlib.h> /* for abs */ | ||||
| #include <inttypes.h> /* for PRIu64 */ | ||||
| #include <netinet/in.h> | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/core/talloc.h> | ||||
|  | ||||
| #include <osmocom/netif/osmux.h> | ||||
| #include <osmocom/netif/rtp.h> | ||||
|  | ||||
| #include <osmocom/legacy_mgcp/mgcp.h> | ||||
| #include <osmocom/legacy_mgcp/mgcp_internal.h> | ||||
| #include <osmocom/legacy_mgcp/osmux.h> | ||||
|  | ||||
| static struct osmo_fd osmux_fd; | ||||
|  | ||||
| static LLIST_HEAD(osmux_handle_list); | ||||
|  | ||||
| struct osmux_handle { | ||||
| 	struct llist_head head; | ||||
| 	struct osmux_in_handle *in; | ||||
| 	struct in_addr rem_addr; | ||||
| 	int rem_port; | ||||
| 	int refcnt; | ||||
| }; | ||||
|  | ||||
| static void *osmux; | ||||
|  | ||||
| static void osmux_deliver(struct msgb *batch_msg, void *data) | ||||
| { | ||||
| 	struct osmux_handle *handle = data; | ||||
| 	struct sockaddr_in out = { | ||||
| 		.sin_family = AF_INET, | ||||
| 		.sin_port = handle->rem_port, | ||||
| 	}; | ||||
|  | ||||
| 	memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr)); | ||||
| 	sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0, | ||||
| 		(struct sockaddr *)&out, sizeof(out)); | ||||
| 	msgb_free(batch_msg); | ||||
| } | ||||
|  | ||||
| static struct osmux_handle * | ||||
| osmux_handle_find_get(struct in_addr *addr, int rem_port) | ||||
| { | ||||
| 	struct osmux_handle *h; | ||||
|  | ||||
| 	/* Lookup for existing OSMUX handle for this destination address. */ | ||||
| 	llist_for_each_entry(h, &osmux_handle_list, head) { | ||||
| 		if (memcmp(&h->rem_addr, addr, sizeof(struct in_addr)) == 0 && | ||||
| 		    h->rem_port == rem_port) { | ||||
| 			LOGP(DLMGCP, LOGL_DEBUG, "using existing OSMUX handle " | ||||
| 						"for addr=%s:%d\n", | ||||
| 				inet_ntoa(*addr), ntohs(rem_port)); | ||||
| 			h->refcnt++; | ||||
| 			return h; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static void osmux_handle_put(struct osmux_in_handle *in) | ||||
| { | ||||
| 	struct osmux_handle *h; | ||||
|  | ||||
| 	/* Lookup for existing OSMUX handle for this destination address. */ | ||||
| 	llist_for_each_entry(h, &osmux_handle_list, head) { | ||||
| 		if (h->in == in) { | ||||
| 			if (--h->refcnt == 0) { | ||||
| 				LOGP(DLMGCP, LOGL_INFO, | ||||
| 				     "Releasing unused osmux handle for %s:%d\n", | ||||
| 				     inet_ntoa(h->rem_addr), | ||||
| 				     ntohs(h->rem_port)); | ||||
| 				LOGP(DLMGCP, LOGL_INFO, "Stats: " | ||||
| 				     "input RTP msgs: %u bytes: %"PRIu64" " | ||||
| 				     "output osmux msgs: %u bytes: %"PRIu64"\n", | ||||
| 				     in->stats.input_rtp_msgs, | ||||
| 				     in->stats.input_rtp_bytes, | ||||
| 				     in->stats.output_osmux_msgs, | ||||
| 				     in->stats.output_osmux_bytes); | ||||
| 				llist_del(&h->head); | ||||
| 				osmux_xfrm_input_fini(h->in); | ||||
| 				talloc_free(h); | ||||
| 			} | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in); | ||||
| } | ||||
|  | ||||
| static struct osmux_handle * | ||||
| osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) | ||||
| { | ||||
| 	struct osmux_handle *h; | ||||
|  | ||||
| 	h = talloc_zero(osmux, struct osmux_handle); | ||||
| 	if (!h) | ||||
| 		return NULL; | ||||
| 	h->rem_addr = *addr; | ||||
| 	h->rem_port = rem_port; | ||||
| 	h->refcnt++; | ||||
|  | ||||
| 	h->in = talloc_zero(h, struct osmux_in_handle); | ||||
| 	if (!h->in) { | ||||
| 		talloc_free(h); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	h->in->osmux_seq = 0; /* sequence number to start OSmux message from */ | ||||
| 	h->in->batch_factor = cfg->osmux_batch; | ||||
| 	/* If batch size is zero, the library defaults to 1470 bytes. */ | ||||
| 	h->in->batch_size = cfg->osmux_batch_size; | ||||
| 	h->in->deliver = osmux_deliver; | ||||
| 	osmux_xfrm_input_init(h->in); | ||||
| 	h->in->data = h; | ||||
|  | ||||
| 	llist_add(&h->head, &osmux_handle_list); | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "created new OSMUX handle for addr=%s:%d\n", | ||||
| 		inet_ntoa(*addr), ntohs(rem_port)); | ||||
|  | ||||
| 	return h; | ||||
| } | ||||
|  | ||||
| static struct osmux_in_handle * | ||||
| osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) | ||||
| { | ||||
| 	struct osmux_handle *h; | ||||
|  | ||||
| 	h = osmux_handle_find_get(addr, rem_port); | ||||
| 	if (h != NULL) | ||||
| 		return h->in; | ||||
|  | ||||
| 	h = osmux_handle_alloc(cfg, addr, rem_port); | ||||
| 	if (h == NULL) | ||||
| 		return NULL; | ||||
|  | ||||
| 	return h->in; | ||||
| } | ||||
|  | ||||
| int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct msgb *msg; | ||||
|  | ||||
| 	msg = msgb_alloc(4096, "RTP"); | ||||
| 	if (!msg) | ||||
| 		return 0; | ||||
|  | ||||
| 	memcpy(msg->data, buf, rc); | ||||
| 	msgb_put(msg, rc); | ||||
|  | ||||
| 	while ((ret = osmux_xfrm_input(endp->osmux.in, msg, endp->osmux.cid)) > 0) { | ||||
| 		/* batch full, build and deliver it */ | ||||
| 		osmux_xfrm_input_deliver(endp->osmux.in); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct mgcp_endpoint * | ||||
| endpoint_lookup(struct mgcp_config *cfg, int cid, | ||||
| 		struct in_addr *from_addr, int type) | ||||
| { | ||||
| 	struct mgcp_endpoint *tmp = NULL; | ||||
| 	int i; | ||||
|  | ||||
| 	/* Lookup for the endpoint that corresponds to this port */ | ||||
| 	for (i=0; i<cfg->trunk.number_endpoints; i++) { | ||||
| 		struct in_addr *this; | ||||
|  | ||||
| 		tmp = &cfg->trunk.endpoints[i]; | ||||
|  | ||||
| 		if (!tmp->allocated) | ||||
| 			continue; | ||||
|  | ||||
| 		switch(type) { | ||||
| 		case MGCP_DEST_NET: | ||||
| 			this = &tmp->net_end.addr; | ||||
| 			break; | ||||
| 		case MGCP_DEST_BTS: | ||||
| 			this = &tmp->bts_end.addr; | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* Should not ever happen */ | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type); | ||||
| 			return NULL; | ||||
| 		} | ||||
|  | ||||
| 		if (tmp->osmux.cid == cid && this->s_addr == from_addr->s_addr) | ||||
| 			return tmp; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid); | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static void scheduled_tx_net_cb(struct msgb *msg, void *data) | ||||
| { | ||||
| 	struct mgcp_endpoint *endp = data; | ||||
| 	struct sockaddr_in addr = { | ||||
| 		.sin_addr = endp->net_end.addr, | ||||
| 		.sin_port = endp->net_end.rtp_port, | ||||
| 	}; | ||||
|  | ||||
| 	endp->bts_end.octets += msg->len; | ||||
| 	endp->bts_end.packets++; | ||||
|  | ||||
| 	mgcp_send(endp, MGCP_DEST_NET, 1, &addr, (char *)msg->data, msg->len); | ||||
| 	msgb_free(msg); | ||||
| } | ||||
|  | ||||
| static void scheduled_tx_bts_cb(struct msgb *msg, void *data) | ||||
| { | ||||
| 	struct mgcp_endpoint *endp = data; | ||||
| 	struct sockaddr_in addr = { | ||||
| 		.sin_addr = endp->bts_end.addr, | ||||
| 		.sin_port = endp->bts_end.rtp_port, | ||||
| 	}; | ||||
|  | ||||
| 	endp->net_end.octets += msg->len; | ||||
| 	endp->net_end.packets++; | ||||
|  | ||||
| 	mgcp_send(endp, MGCP_DEST_BTS, 1, &addr, (char *)msg->data, msg->len); | ||||
| 	msgb_free(msg); | ||||
| } | ||||
|  | ||||
| static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr) | ||||
| { | ||||
| 	struct msgb *msg; | ||||
| 	socklen_t slen = sizeof(*addr); | ||||
| 	int ret; | ||||
|  | ||||
| 	msg = msgb_alloc(4096, "OSMUX"); | ||||
| 	if (!msg) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot allocate message\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0, | ||||
| 			(struct sockaddr *)addr, &slen); | ||||
| 	if (ret <= 0) { | ||||
| 		msgb_free(msg); | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot receive message\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	msgb_put(msg, ret); | ||||
|  | ||||
| 	return msg; | ||||
| } | ||||
|  | ||||
| #define osmux_chunk_length(msg, rem) (rem - msg->len); | ||||
|  | ||||
| int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| { | ||||
| 	struct msgb *msg; | ||||
| 	struct osmux_hdr *osmuxh; | ||||
| 	struct llist_head list; | ||||
| 	struct sockaddr_in addr; | ||||
| 	struct mgcp_config *cfg = ofd->data; | ||||
| 	uint32_t rem; | ||||
|  | ||||
| 	msg = osmux_recv(ofd, &addr); | ||||
| 	if (!msg) | ||||
| 		return -1; | ||||
|  | ||||
| 	/* not any further processing dummy messages */ | ||||
| 	if (msg->data[0] == MGCP_DUMMY_LOAD) | ||||
| 		goto out; | ||||
|  | ||||
| 	rem = msg->len; | ||||
| 	while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) { | ||||
| 		struct mgcp_endpoint *endp; | ||||
|  | ||||
| 		/* Yes, we use MGCP_DEST_NET to locate the origin */ | ||||
| 		endp = endpoint_lookup(cfg, osmuxh->circuit_id, | ||||
| 				       &addr.sin_addr, MGCP_DEST_NET); | ||||
| 		if (!endp) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Cannot find an endpoint for circuit_id=%d\n", | ||||
| 			     osmuxh->circuit_id); | ||||
| 			goto out; | ||||
| 		} | ||||
| 		endp->osmux.stats.octets += osmux_chunk_length(msg, rem); | ||||
| 		endp->osmux.stats.chunks++; | ||||
| 		rem = msg->len; | ||||
|  | ||||
| 		osmux_xfrm_output(osmuxh, &endp->osmux.out, &list); | ||||
| 		osmux_tx_sched(&list, scheduled_tx_bts_cb, endp); | ||||
| 	} | ||||
| out: | ||||
| 	msgb_free(msg); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* This is called from the bsc-nat */ | ||||
| static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr, | ||||
| 			      struct msgb *msg) | ||||
| { | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	uint8_t osmux_cid; | ||||
|  | ||||
| 	if (msg->len < 1 + sizeof(osmux_cid)) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Discarding truncated Osmux dummy load\n"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n", | ||||
| 	     inet_ntoa(addr->sin_addr)); | ||||
|  | ||||
| 	if (!cfg->osmux) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "bsc wants to use Osmux but bsc-nat did not request it\n"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	/* extract the osmux CID from the dummy message */ | ||||
| 	memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid)); | ||||
|  | ||||
| 	endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS); | ||||
| 	if (!endp) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Cannot find endpoint for Osmux CID %d\n", osmux_cid); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (endp->osmux.state == OSMUX_STATE_ENABLED) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (osmux_enable_endpoint(endp, &addr->sin_addr, addr->sin_port) < 0 ) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Could not enable osmux in endpoint %d\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n", | ||||
| 	     ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr), | ||||
| 	     ntohs(addr->sin_port)); | ||||
| out: | ||||
| 	msgb_free(msg); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| { | ||||
| 	struct msgb *msg; | ||||
| 	struct osmux_hdr *osmuxh; | ||||
| 	struct llist_head list; | ||||
| 	struct sockaddr_in addr; | ||||
| 	struct mgcp_config *cfg = ofd->data; | ||||
| 	uint32_t rem; | ||||
|  | ||||
| 	msg = osmux_recv(ofd, &addr); | ||||
| 	if (!msg) | ||||
| 		return -1; | ||||
|  | ||||
| 	/* not any further processing dummy messages */ | ||||
| 	if (msg->data[0] == MGCP_DUMMY_LOAD) | ||||
| 		return osmux_handle_dummy(cfg, &addr, msg); | ||||
|  | ||||
| 	rem = msg->len; | ||||
| 	while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) { | ||||
| 		struct mgcp_endpoint *endp; | ||||
|  | ||||
| 		/* Yes, we use MGCP_DEST_BTS to locate the origin */ | ||||
| 		endp = endpoint_lookup(cfg, osmuxh->circuit_id, | ||||
| 				       &addr.sin_addr, MGCP_DEST_BTS); | ||||
| 		if (!endp) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Cannot find an endpoint for circuit_id=%d\n", | ||||
| 			     osmuxh->circuit_id); | ||||
| 			goto out; | ||||
| 		} | ||||
| 		endp->osmux.stats.octets += osmux_chunk_length(msg, rem); | ||||
| 		endp->osmux.stats.chunks++; | ||||
| 		rem = msg->len; | ||||
|  | ||||
| 		osmux_xfrm_output(osmuxh, &endp->osmux.out, &list); | ||||
| 		osmux_tx_sched(&list, scheduled_tx_net_cb, endp); | ||||
| 	} | ||||
| out: | ||||
| 	msgb_free(msg); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int osmux_init(int role, struct mgcp_config *cfg) | ||||
| { | ||||
| 	int ret; | ||||
|  | ||||
| 	switch(role) { | ||||
| 	case OSMUX_ROLE_BSC: | ||||
| 		osmux_fd.cb = osmux_read_from_bsc_nat_cb; | ||||
| 		break; | ||||
| 	case OSMUX_ROLE_BSC_NAT: | ||||
| 		osmux_fd.cb = osmux_read_from_bsc_cb; | ||||
| 		break; | ||||
| 	default: | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "wrong role for OSMUX\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	osmux_fd.data = cfg; | ||||
|  | ||||
| 	ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port); | ||||
| 	if (ret < 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp); | ||||
| 	osmux_fd.when |= BSC_FD_READ; | ||||
|  | ||||
| 	ret = osmo_fd_register(&osmux_fd); | ||||
| 	if (ret < 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	cfg->osmux_init = 1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port) | ||||
| { | ||||
| 	/* If osmux is enabled, initialize the output handler. This handler is | ||||
| 	 * used to reconstruct the RTP flow from osmux. The RTP SSRC is | ||||
| 	 * allocated based on the circuit ID (endp->osmux.cid), which is unique | ||||
| 	 * in the local scope to the BSC/BSC-NAT. We use it to divide the RTP | ||||
| 	 * SSRC space (2^32) by the 256 possible circuit IDs, then randomly | ||||
| 	 * select one value from that window. Thus, we have no chance to have | ||||
| 	 * overlapping RTP SSRC traveling to the BTSes behind the BSC, | ||||
| 	 * similarly, for flows traveling to the MSC. | ||||
| 	 */ | ||||
| 	static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256; | ||||
|  | ||||
| 	if (endp->osmux.state == OSMUX_STATE_DISABLED) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Endpoint %u didn't request Osmux\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	osmux_xfrm_output_init(&endp->osmux.out, | ||||
| 			       (endp->osmux.cid * rtp_ssrc_winlen) + | ||||
| 			       (random() % rtp_ssrc_winlen)); | ||||
|  | ||||
| 	endp->osmux.in = osmux_handle_lookup(endp->cfg, addr, port); | ||||
| 	if (!endp->osmux.in) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (!osmux_xfrm_input_open_circuit(endp->osmux.in, endp->osmux.cid, | ||||
| 					   endp->cfg->osmux_dummy)) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u\n", | ||||
| 		     endp->osmux.cid); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	switch (endp->cfg->role) { | ||||
| 		case MGCP_BSC_NAT: | ||||
| 			endp->type = MGCP_OSMUX_BSC_NAT; | ||||
| 			break; | ||||
| 		case MGCP_BSC: | ||||
| 			endp->type = MGCP_OSMUX_BSC; | ||||
| 			break; | ||||
| 	} | ||||
| 	endp->osmux.state = OSMUX_STATE_ENABLED; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void osmux_disable_endpoint(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	LOGP(DLMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n", | ||||
| 	     ENDPOINT_NUMBER(endp), endp->osmux.cid); | ||||
| 	osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid); | ||||
| 	endp->osmux.state = OSMUX_STATE_DISABLED; | ||||
| 	endp->osmux.cid = -1; | ||||
| 	osmux_handle_put(endp->osmux.in); | ||||
| } | ||||
|  | ||||
| void osmux_release_cid(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	if (endp->osmux.allocated_cid >= 0) | ||||
| 		osmux_put_cid(endp->osmux.allocated_cid); | ||||
| 	endp->osmux.allocated_cid = -1; | ||||
| } | ||||
|  | ||||
| void osmux_allocate_cid(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	osmux_release_cid(endp); | ||||
| 	endp->osmux.allocated_cid = osmux_get_cid(); | ||||
| } | ||||
|  | ||||
| /* We don't need to send the dummy load for osmux so often as another endpoint | ||||
|  * may have already punched the hole in the firewall. This approach is simple | ||||
|  * though. | ||||
|  */ | ||||
| int osmux_send_dummy(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	char buf[1 + sizeof(uint8_t)]; | ||||
| 	struct in_addr addr_unset = {}; | ||||
|  | ||||
| 	buf[0] = MGCP_DUMMY_LOAD; | ||||
| 	memcpy(&buf[1], &endp->osmux.cid, sizeof(endp->osmux.cid)); | ||||
|  | ||||
| 	/* Wait until we have the connection information from MDCX */ | ||||
| 	if (memcmp(&endp->net_end.addr, &addr_unset, sizeof(addr_unset)) == 0) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (endp->osmux.state == OSMUX_STATE_ACTIVATING) { | ||||
| 		if (osmux_enable_endpoint(endp, &endp->net_end.addr, | ||||
| 					  htons(endp->cfg->osmux_port)) < 0) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Could not activate osmux in endpoint %d\n", | ||||
| 			     ENDPOINT_NUMBER(endp)); | ||||
| 		} | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Osmux CID %u for %s:%u is now enabled\n", | ||||
| 		     endp->osmux.cid, inet_ntoa(endp->net_end.addr), | ||||
| 		     endp->cfg->osmux_port); | ||||
| 	} | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 	     "sending OSMUX dummy load to %s CID %u\n", | ||||
| 	     inet_ntoa(endp->net_end.addr), endp->osmux.cid); | ||||
|  | ||||
| 	return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr, | ||||
| 			     htons(endp->cfg->osmux_port), buf, sizeof(buf)); | ||||
| } | ||||
|  | ||||
| /* bsc-nat allocates/releases the Osmux circuit ID */ | ||||
| static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8]; | ||||
|  | ||||
| int osmux_used_cid(void) | ||||
| { | ||||
| 	int i, j, used = 0; | ||||
|  | ||||
| 	for (i = 0; i < sizeof(osmux_cid_bitmap); i++) { | ||||
| 		for (j = 0; j < 8; j++) { | ||||
| 			if (osmux_cid_bitmap[i] & (1 << j)) | ||||
| 				used += 1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return used; | ||||
| } | ||||
|  | ||||
| int osmux_get_cid(void) | ||||
| { | ||||
| 	int i, j; | ||||
|  | ||||
| 	for (i = 0; i < sizeof(osmux_cid_bitmap); i++) { | ||||
| 		for (j = 0; j < 8; j++) { | ||||
| 			if (osmux_cid_bitmap[i] & (1 << j)) | ||||
| 				continue; | ||||
|  | ||||
| 			osmux_cid_bitmap[i] |= (1 << j); | ||||
| 			LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 			     "Allocating Osmux CID %u from pool\n", (i * 8) + j); | ||||
| 			return (i * 8) + j; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n"); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| void osmux_put_cid(uint8_t osmux_cid) | ||||
| { | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid); | ||||
| 	osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8)); | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,305 +0,0 @@ | ||||
| /* | ||||
|  * Some SDP file parsing... | ||||
|  * | ||||
|  * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2014 by On-Waves | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <osmocom/legacy_mgcp/mgcp.h> | ||||
| #include <osmocom/legacy_mgcp/mgcp_internal.h> | ||||
|  | ||||
| #include <errno.h> | ||||
|  | ||||
| struct sdp_rtp_map { | ||||
| 	/* the type */ | ||||
| 	int payload_type; | ||||
| 	/* null, static or later dynamic codec name */ | ||||
| 	char *codec_name; | ||||
| 	/* A pointer to the original line for later parsing */ | ||||
| 	char *map_line; | ||||
|  | ||||
| 	int rate; | ||||
| 	int channels; | ||||
| }; | ||||
|  | ||||
| int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec, | ||||
| 			int payload_type, const char *audio_name) | ||||
| { | ||||
| 	int rate = codec->rate; | ||||
| 	int channels = codec->channels; | ||||
| 	char audio_codec[64]; | ||||
|  | ||||
| 	talloc_free(codec->subtype_name); | ||||
| 	codec->subtype_name = NULL; | ||||
| 	talloc_free(codec->audio_name); | ||||
| 	codec->audio_name = NULL; | ||||
|  | ||||
| 	if (payload_type != PTYPE_UNDEFINED) | ||||
| 		codec->payload_type = payload_type; | ||||
|  | ||||
| 	if (!audio_name) { | ||||
| 		switch (payload_type) { | ||||
| 		case 0: audio_name = "PCMU/8000/1"; break; | ||||
| 		case 3: audio_name = "GSM/8000/1"; break; | ||||
| 		case 8: audio_name = "PCMA/8000/1"; break; | ||||
| 		case 18: audio_name = "G729/8000/1"; break; | ||||
| 		default: | ||||
| 			 /* Payload type is unknown, don't change rate and | ||||
| 			  * channels. */ | ||||
| 			 /* TODO: return value? */ | ||||
| 			 return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (sscanf(audio_name, "%63[^/]/%d/%d", | ||||
| 		   audio_codec, &rate, &channels) < 1) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	codec->rate = rate; | ||||
| 	codec->channels = channels; | ||||
| 	codec->subtype_name = talloc_strdup(ctx, audio_codec); | ||||
| 	codec->audio_name = talloc_strdup(ctx, audio_name); | ||||
|  | ||||
| 	if (!strcmp(audio_codec, "G729")) { | ||||
| 		codec->frame_duration_num = 10; | ||||
| 		codec->frame_duration_den = 1000; | ||||
| 	} else { | ||||
| 		codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; | ||||
| 		codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; | ||||
| 	} | ||||
|  | ||||
| 	if (payload_type < 0) { | ||||
| 		payload_type = 96; | ||||
| 		if (rate == 8000 && channels == 1) { | ||||
| 			if (!strcmp(audio_codec, "GSM")) | ||||
| 				payload_type = 3; | ||||
| 			else if (!strcmp(audio_codec, "PCMA")) | ||||
| 				payload_type = 8; | ||||
| 			else if (!strcmp(audio_codec, "PCMU")) | ||||
| 				payload_type = 0; | ||||
| 			else if (!strcmp(audio_codec, "G729")) | ||||
| 				payload_type = 18; | ||||
| 		} | ||||
|  | ||||
| 		codec->payload_type = payload_type; | ||||
| 	} | ||||
|  | ||||
| 	if (channels != 1) | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 		     "Channels != 1 in SDP: '%s'\n", audio_name); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < used; ++i) { | ||||
| 		switch (codecs[i].payload_type) { | ||||
| 		case 0: | ||||
| 			codecs[i].codec_name = "PCMU"; | ||||
| 			codecs[i].rate = 8000; | ||||
| 			codecs[i].channels = 1; | ||||
| 			break; | ||||
| 		case 3: | ||||
| 			codecs[i].codec_name = "GSM"; | ||||
| 			codecs[i].rate = 8000; | ||||
| 			codecs[i].channels = 1; | ||||
| 			break; | ||||
| 		case 8: | ||||
| 			codecs[i].codec_name = "PCMA"; | ||||
| 			codecs[i].rate = 8000; | ||||
| 			codecs[i].channels = 1; | ||||
| 			break; | ||||
| 		case 18: | ||||
| 			codecs[i].codec_name = "G729"; | ||||
| 			codecs[i].rate = 8000; | ||||
| 			codecs[i].channels = 1; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, char *audio_name) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < used; ++i) { | ||||
| 		char audio_codec[64]; | ||||
| 		int rate = -1; | ||||
| 		int channels = -1; | ||||
| 		if (codecs[i].payload_type != payload) | ||||
| 			continue; | ||||
| 		if (sscanf(audio_name, "%63[^/]/%d/%d", | ||||
| 				audio_codec, &rate, &channels) < 1) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n", audio_name); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		codecs[i].map_line = talloc_strdup(ctx, audio_name); | ||||
| 		codecs[i].codec_name = talloc_strdup(ctx, audio_codec); | ||||
| 		codecs[i].rate = rate; | ||||
| 		codecs[i].channels = channels; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, audio_name); | ||||
| } | ||||
|  | ||||
| int is_codec_compatible(struct mgcp_endpoint *endp, struct sdp_rtp_map *codec) | ||||
| { | ||||
| 	char *bts_codec; | ||||
| 	char audio_codec[64]; | ||||
|  | ||||
| 	if (!codec->codec_name) | ||||
| 		return 0; | ||||
|  | ||||
| 	/* | ||||
| 	 * GSM, GSM/8000 and GSM/8000/1 should all be compatible.. let's go | ||||
| 	 * by name first. | ||||
| 	 */ | ||||
| 	bts_codec = endp->tcfg->audio_name; | ||||
| 	if (sscanf(bts_codec, "%63[^/]/%*d/%*d", audio_codec) < 1) | ||||
| 		return 0; | ||||
|  | ||||
| 	return strcasecmp(audio_codec, codec->codec_name) == 0; | ||||
| } | ||||
|  | ||||
| int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) | ||||
| { | ||||
| 	struct sdp_rtp_map codecs[10]; | ||||
| 	int codecs_used = 0; | ||||
| 	char *line; | ||||
| 	int maxptime = -1; | ||||
| 	int i; | ||||
| 	int codecs_assigned = 0; | ||||
| 	void *tmp_ctx = talloc_new(NULL); | ||||
|  | ||||
| 	memset(&codecs, 0, sizeof(codecs)); | ||||
|  | ||||
| 	for_each_line(line, p->save) { | ||||
| 		switch (line[0]) { | ||||
| 		case 'o': | ||||
| 		case 's': | ||||
| 		case 't': | ||||
| 		case 'v': | ||||
| 			/* skip these SDP attributes */ | ||||
| 			break; | ||||
| 		case 'a': { | ||||
| 			int payload; | ||||
| 			int ptime, ptime2 = 0; | ||||
| 			char audio_name[64]; | ||||
|  | ||||
|  | ||||
| 			if (sscanf(line, "a=rtpmap:%d %63s", | ||||
| 				   &payload, audio_name) == 2) { | ||||
| 				codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name); | ||||
| 			} else if (sscanf(line, "a=ptime:%d-%d", | ||||
| 					  &ptime, &ptime2) >= 1) { | ||||
| 				if (ptime2 > 0 && ptime2 != ptime) | ||||
| 					rtp->packet_duration_ms = 0; | ||||
| 				else | ||||
| 					rtp->packet_duration_ms = ptime; | ||||
| 			} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) { | ||||
| 				maxptime = ptime2; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		case 'm': { | ||||
| 			int port, rc; | ||||
|  | ||||
| 			rc = sscanf(line, "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d", | ||||
| 					&port, | ||||
| 					&codecs[0].payload_type, | ||||
| 					&codecs[1].payload_type, | ||||
| 					&codecs[2].payload_type, | ||||
| 					&codecs[3].payload_type, | ||||
| 					&codecs[4].payload_type, | ||||
| 					&codecs[5].payload_type, | ||||
| 					&codecs[6].payload_type, | ||||
| 					&codecs[7].payload_type, | ||||
| 					&codecs[8].payload_type, | ||||
| 					&codecs[9].payload_type); | ||||
| 			if (rc >= 2) { | ||||
| 				rtp->rtp_port = htons(port); | ||||
| 				rtp->rtcp_port = htons(port + 1); | ||||
| 				codecs_used = rc - 1; | ||||
| 				codecs_initialize(tmp_ctx, codecs, codecs_used); | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		case 'c': { | ||||
| 			char ipv4[16]; | ||||
|  | ||||
| 			if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) { | ||||
| 				inet_aton(ipv4, &rtp->addr); | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		default: | ||||
| 			if (p->endp) | ||||
| 				LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 				     "Unhandled SDP option: '%c'/%d on 0x%x\n", | ||||
| 				     line[0], line[0], ENDPOINT_NUMBER(p->endp)); | ||||
| 			else | ||||
| 				LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 				     "Unhandled SDP option: '%c'/%d\n", | ||||
| 				     line[0], line[0]); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Now select the primary and alt_codec */ | ||||
| 	for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) { | ||||
| 		struct mgcp_rtp_codec *codec = codecs_assigned == 0 ? | ||||
| 					&rtp->codec : &rtp->alt_codec; | ||||
|  | ||||
| 		if (endp->tcfg->no_audio_transcoding && | ||||
| 			!is_codec_compatible(endp, &codecs[i])) { | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n", | ||||
| 				codecs[i].codec_name); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		mgcp_set_audio_info(p->cfg, codec, | ||||
| 					codecs[i].payload_type, | ||||
| 					codecs[i].map_line); | ||||
| 		codecs_assigned += 1; | ||||
| 	} | ||||
|  | ||||
| 	if (codecs_assigned > 0) { | ||||
| 		/* TODO/XXX: Store this per codec and derive it on use */ | ||||
| 		if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den > | ||||
| 				rtp->codec.frame_duration_num * 1500) { | ||||
| 			/* more than 1 frame */ | ||||
| 			rtp->packet_duration_ms = 0; | ||||
| 		} | ||||
|  | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 		     "Got media info via SDP: port %d, payload %d (%s), " | ||||
| 		     "duration %d, addr %s\n", | ||||
| 		     ntohs(rtp->rtp_port), rtp->codec.payload_type, | ||||
| 		     rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown", | ||||
| 		     rtp->packet_duration_ms, inet_ntoa(rtp->addr)); | ||||
| 	} | ||||
|  | ||||
| 	talloc_free(tmp_ctx); | ||||
| 	return codecs_assigned > 0; | ||||
| } | ||||
|  | ||||
| @@ -1,611 +0,0 @@ | ||||
| /* | ||||
|  * (C) 2014 by On-Waves | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
|  | ||||
|  | ||||
| #include "g711common.h" | ||||
|  | ||||
| #include <osmocom/legacy_mgcp/mgcp.h> | ||||
| #include <osmocom/legacy_mgcp/mgcp_internal.h> | ||||
| #include <osmocom/legacy_mgcp/mgcp_transcode.h> | ||||
|  | ||||
| #include <osmocom/core/talloc.h> | ||||
| #include <osmocom/netif/rtp.h> | ||||
|  | ||||
| int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst) | ||||
| { | ||||
| 	struct mgcp_process_rtp_state *state = state_; | ||||
| 	if (dst) | ||||
| 		return (nsamples >= 0 ? | ||||
| 			nsamples / state->dst_samples_per_frame : | ||||
| 			1) * state->dst_frame_size; | ||||
| 	else | ||||
| 		return (nsamples >= 0 ? | ||||
| 			nsamples / state->src_samples_per_frame : | ||||
| 			1) * state->src_frame_size; | ||||
| } | ||||
|  | ||||
| static enum audio_format get_audio_format(const struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	if (codec->subtype_name) { | ||||
| 		if (!strcasecmp("GSM", codec->subtype_name)) | ||||
| 			return AF_GSM; | ||||
| 		if (!strcasecmp("PCMA", codec->subtype_name)) | ||||
| 			return AF_PCMA; | ||||
| 		if (!strcasecmp("PCMU", codec->subtype_name)) | ||||
| 			return AF_PCMU; | ||||
| #ifdef HAVE_BCG729 | ||||
| 		if (!strcasecmp("G729", codec->subtype_name)) | ||||
| 			return AF_G729; | ||||
| #endif | ||||
| 		if (!strcasecmp("L16", codec->subtype_name)) | ||||
| 			return AF_L16; | ||||
| 	} | ||||
|  | ||||
| 	switch (codec->payload_type) { | ||||
| 	case 0 /* PCMU */: | ||||
| 		return AF_PCMU; | ||||
| 	case 3 /* GSM */: | ||||
| 		return AF_GSM; | ||||
| 	case 8 /* PCMA */: | ||||
| 		return AF_PCMA; | ||||
| #ifdef HAVE_BCG729 | ||||
| 	case 18 /* G.729 */: | ||||
| 		return AF_G729; | ||||
| #endif | ||||
| 	case 11 /* L16 */: | ||||
| 		return AF_L16; | ||||
| 	default: | ||||
| 		return AF_INVALID; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void l16_encode(short *sample, unsigned char *buf, size_t n) | ||||
| { | ||||
| 	for (; n > 0; --n, ++sample, buf += 2) { | ||||
| 		buf[0] = sample[0] >> 8; | ||||
| 		buf[1] = sample[0] & 0xff; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void l16_decode(unsigned char *buf, short *sample, size_t n) | ||||
| { | ||||
| 	for (; n > 0; --n, ++sample, buf += 2) | ||||
| 		sample[0] = ((short)buf[0] << 8) | buf[1]; | ||||
| } | ||||
|  | ||||
| static void alaw_encode(short *sample, unsigned char *buf, size_t n) | ||||
| { | ||||
| 	for (; n > 0; --n) | ||||
| 		*(buf++) = s16_to_alaw(*(sample++)); | ||||
| } | ||||
|  | ||||
| static void alaw_decode(unsigned char *buf, short *sample, size_t n) | ||||
| { | ||||
| 	for (; n > 0; --n) | ||||
| 		*(sample++) = alaw_to_s16(*(buf++)); | ||||
| } | ||||
|  | ||||
| static void ulaw_encode(short *sample, unsigned char *buf, size_t n) | ||||
| { | ||||
| 	for (; n > 0; --n) | ||||
| 		*(buf++) = s16_to_ulaw(*(sample++)); | ||||
| } | ||||
|  | ||||
| static void ulaw_decode(unsigned char *buf, short *sample, size_t n) | ||||
| { | ||||
| 	for (; n > 0; --n) | ||||
| 		*(sample++) = ulaw_to_s16(*(buf++)); | ||||
| } | ||||
|  | ||||
| static int processing_state_destructor(struct mgcp_process_rtp_state *state) | ||||
| { | ||||
| 	switch (state->src_fmt) { | ||||
| 	case AF_GSM: | ||||
| 		if (state->src.gsm_handle) | ||||
| 			gsm_destroy(state->src.gsm_handle); | ||||
| 		break; | ||||
| #ifdef HAVE_BCG729 | ||||
| 	case AF_G729: | ||||
| 		if (state->src.g729_dec) | ||||
| 			closeBcg729DecoderChannel(state->src.g729_dec); | ||||
| 		break; | ||||
| #endif | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 	switch (state->dst_fmt) { | ||||
| 	case AF_GSM: | ||||
| 		if (state->dst.gsm_handle) | ||||
| 			gsm_destroy(state->dst.gsm_handle); | ||||
| 		break; | ||||
| #ifdef HAVE_BCG729 | ||||
| 	case AF_G729: | ||||
| 		if (state->dst.g729_enc) | ||||
| 			closeBcg729EncoderChannel(state->dst.g729_enc); | ||||
| 		break; | ||||
| #endif | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int mgcp_transcoding_setup(struct mgcp_endpoint *endp, | ||||
| 			   struct mgcp_rtp_end *dst_end, | ||||
| 			   struct mgcp_rtp_end *src_end) | ||||
| { | ||||
| 	struct mgcp_process_rtp_state *state; | ||||
| 	enum audio_format src_fmt, dst_fmt; | ||||
| 	const struct mgcp_rtp_codec *dst_codec = &dst_end->codec; | ||||
|  | ||||
| 	/* cleanup first */ | ||||
| 	if (dst_end->rtp_process_data) { | ||||
| 		talloc_free(dst_end->rtp_process_data); | ||||
| 		dst_end->rtp_process_data = NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!src_end) | ||||
| 		return 0; | ||||
|  | ||||
| 	const struct mgcp_rtp_codec *src_codec = &src_end->codec; | ||||
|  | ||||
| 	if (endp->tcfg->no_audio_transcoding) { | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 			"Transcoding disabled on endpoint 0x%x\n", | ||||
| 			ENDPOINT_NUMBER(endp)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	src_fmt = get_audio_format(src_codec); | ||||
| 	dst_fmt = get_audio_format(dst_codec); | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, | ||||
| 	     "Checking transcoding: %s (%d) -> %s (%d)\n", | ||||
| 	     src_codec->subtype_name, src_codec->payload_type, | ||||
| 	     dst_codec->subtype_name, dst_codec->payload_type); | ||||
|  | ||||
| 	if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) { | ||||
| 		if (!src_codec->subtype_name || !dst_codec->subtype_name) | ||||
| 			/* Not enough info, do nothing */ | ||||
| 			return 0; | ||||
|  | ||||
| 		if (strcasecmp(src_codec->subtype_name, dst_codec->subtype_name) == 0) | ||||
| 			/* Nothing to do */ | ||||
| 			return 0; | ||||
|  | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Cannot transcode: %s codec not supported (%s -> %s).\n", | ||||
| 		     src_fmt != AF_INVALID ? "destination" : "source", | ||||
| 		     src_codec->audio_name, dst_codec->audio_name); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	if (src_codec->rate && dst_codec->rate && src_codec->rate != dst_codec->rate) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Cannot transcode: rate conversion (%d -> %d) not supported.\n", | ||||
| 		     src_codec->rate, dst_codec->rate); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state); | ||||
| 	talloc_set_destructor(state, processing_state_destructor); | ||||
| 	dst_end->rtp_process_data = state; | ||||
|  | ||||
| 	state->src_fmt = src_fmt; | ||||
|  | ||||
| 	switch (state->src_fmt) { | ||||
| 	case AF_L16: | ||||
| 	case AF_S16: | ||||
| 		state->src_frame_size = 80 * sizeof(short); | ||||
| 		state->src_samples_per_frame = 80; | ||||
| 		break; | ||||
| 	case AF_GSM: | ||||
| 		state->src_frame_size = sizeof(gsm_frame); | ||||
| 		state->src_samples_per_frame = 160; | ||||
| 		state->src.gsm_handle = gsm_create(); | ||||
| 		if (!state->src.gsm_handle) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Failed to initialize GSM decoder.\n"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 		break; | ||||
| #ifdef HAVE_BCG729 | ||||
| 	case AF_G729: | ||||
| 		state->src_frame_size = 10; | ||||
| 		state->src_samples_per_frame = 80; | ||||
| 		state->src.g729_dec = initBcg729DecoderChannel(); | ||||
| 		if (!state->src.g729_dec) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Failed to initialize G.729 decoder.\n"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 		break; | ||||
| #endif | ||||
| 	case AF_PCMU: | ||||
| 	case AF_PCMA: | ||||
| 		state->src_frame_size = 80; | ||||
| 		state->src_samples_per_frame = 80; | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	state->dst_fmt = dst_fmt; | ||||
|  | ||||
| 	switch (state->dst_fmt) { | ||||
| 	case AF_L16: | ||||
| 	case AF_S16: | ||||
| 		state->dst_frame_size = 80*sizeof(short); | ||||
| 		state->dst_samples_per_frame = 80; | ||||
| 		break; | ||||
| 	case AF_GSM: | ||||
| 		state->dst_frame_size = sizeof(gsm_frame); | ||||
| 		state->dst_samples_per_frame = 160; | ||||
| 		state->dst.gsm_handle = gsm_create(); | ||||
| 		if (!state->dst.gsm_handle) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Failed to initialize GSM encoder.\n"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 		break; | ||||
| #ifdef HAVE_BCG729 | ||||
| 	case AF_G729: | ||||
| 		state->dst_frame_size = 10; | ||||
| 		state->dst_samples_per_frame = 80; | ||||
| 		state->dst.g729_enc = initBcg729EncoderChannel(); | ||||
| 		if (!state->dst.g729_enc) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Failed to initialize G.729 decoder.\n"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 		break; | ||||
| #endif | ||||
| 	case AF_PCMU: | ||||
| 	case AF_PCMA: | ||||
| 		state->dst_frame_size = 80; | ||||
| 		state->dst_samples_per_frame = 80; | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	if (dst_end->force_output_ptime) | ||||
| 		state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end); | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_INFO, | ||||
| 	     "Initialized RTP processing on: 0x%x " | ||||
| 	     "conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n", | ||||
| 	     ENDPOINT_NUMBER(endp), | ||||
| 	     src_fmt, src_codec->payload_type, src_codec->rate, src_end->fmtp_extra, | ||||
| 	     dst_fmt, dst_codec->payload_type, dst_codec->rate, dst_end->fmtp_extra); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, | ||||
| 					  int *payload_type, | ||||
| 					  const char**audio_name, | ||||
| 					  const char**fmtp_extra) | ||||
| { | ||||
| 	struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data; | ||||
| 	struct mgcp_rtp_codec *net_codec = &endp->net_end.codec; | ||||
| 	struct mgcp_rtp_codec *bts_codec = &endp->bts_end.codec; | ||||
|  | ||||
| 	if (!state || net_codec->payload_type < 0) { | ||||
| 		*payload_type = bts_codec->payload_type; | ||||
| 		*audio_name = bts_codec->audio_name; | ||||
| 		*fmtp_extra = endp->bts_end.fmtp_extra; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	*payload_type = net_codec->payload_type; | ||||
| 	*audio_name = net_codec->audio_name; | ||||
| 	*fmtp_extra = endp->net_end.fmtp_extra; | ||||
| } | ||||
|  | ||||
| static int decode_audio(struct mgcp_process_rtp_state *state, | ||||
| 			uint8_t **src, size_t *nbytes) | ||||
| { | ||||
| 	while (*nbytes >= state->src_frame_size) { | ||||
| 		if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Sample buffer too small: %zu > %zu.\n", | ||||
| 			     state->sample_cnt + state->src_samples_per_frame, | ||||
| 			     ARRAY_SIZE(state->samples)); | ||||
| 			return -ENOSPC; | ||||
| 		} | ||||
| 		switch (state->src_fmt) { | ||||
| 		case AF_GSM: | ||||
| 			if (gsm_decode(state->src.gsm_handle, | ||||
| 				       (gsm_byte *)*src, state->samples + state->sample_cnt) < 0) { | ||||
| 				LOGP(DLMGCP, LOGL_ERROR, | ||||
| 				     "Failed to decode GSM.\n"); | ||||
| 				return -EINVAL; | ||||
| 			} | ||||
| 			break; | ||||
| #ifdef HAVE_BCG729 | ||||
| 		case AF_G729: | ||||
| 			bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt); | ||||
| 			break; | ||||
| #endif | ||||
| 		case AF_PCMU: | ||||
| 			ulaw_decode(*src, state->samples + state->sample_cnt, | ||||
| 				    state->src_samples_per_frame); | ||||
| 			break; | ||||
| 		case AF_PCMA: | ||||
| 			alaw_decode(*src, state->samples + state->sample_cnt, | ||||
| 				    state->src_samples_per_frame); | ||||
| 			break; | ||||
| 		case AF_S16: | ||||
| 			memmove(state->samples + state->sample_cnt, *src, | ||||
| 				state->src_frame_size); | ||||
| 			break; | ||||
| 		case AF_L16: | ||||
| 			l16_decode(*src, state->samples + state->sample_cnt, | ||||
| 				   state->src_samples_per_frame); | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		*src        += state->src_frame_size; | ||||
| 		*nbytes     -= state->src_frame_size; | ||||
| 		state->sample_cnt += state->src_samples_per_frame; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int encode_audio(struct mgcp_process_rtp_state *state, | ||||
| 			uint8_t *dst, size_t buf_size, size_t max_samples) | ||||
| { | ||||
| 	int nbytes = 0; | ||||
| 	size_t nsamples = 0; | ||||
| 	/* Encode samples into dst */ | ||||
| 	while (nsamples + state->dst_samples_per_frame <= max_samples) { | ||||
| 		if (nbytes + state->dst_frame_size > buf_size) { | ||||
| 			if (nbytes > 0) | ||||
| 				break; | ||||
|  | ||||
| 			/* Not even one frame fits into the buffer */ | ||||
| 			LOGP(DLMGCP, LOGL_INFO, | ||||
| 			     "Encoding (RTP) buffer too small: %zu > %zu.\n", | ||||
| 			     nbytes + state->dst_frame_size, buf_size); | ||||
| 			return -ENOSPC; | ||||
| 		} | ||||
| 		switch (state->dst_fmt) { | ||||
| 		case AF_GSM: | ||||
| 			gsm_encode(state->dst.gsm_handle, | ||||
| 				   state->samples + state->sample_offs, dst); | ||||
| 			break; | ||||
| #ifdef HAVE_BCG729 | ||||
| 		case AF_G729: | ||||
| 			bcg729Encoder(state->dst.g729_enc, | ||||
| 				      state->samples + state->sample_offs, dst); | ||||
| 			break; | ||||
| #endif | ||||
| 		case AF_PCMU: | ||||
| 			ulaw_encode(state->samples + state->sample_offs, dst, | ||||
| 				    state->src_samples_per_frame); | ||||
| 			break; | ||||
| 		case AF_PCMA: | ||||
| 			alaw_encode(state->samples + state->sample_offs, dst, | ||||
| 				    state->src_samples_per_frame); | ||||
| 			break; | ||||
| 		case AF_S16: | ||||
| 			memmove(dst, state->samples + state->sample_offs, | ||||
| 				state->dst_frame_size); | ||||
| 			break; | ||||
| 		case AF_L16: | ||||
| 			l16_encode(state->samples + state->sample_offs, dst, | ||||
| 				   state->src_samples_per_frame); | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		dst        += state->dst_frame_size; | ||||
| 		nbytes     += state->dst_frame_size; | ||||
| 		state->sample_offs += state->dst_samples_per_frame; | ||||
| 		nsamples   += state->dst_samples_per_frame; | ||||
| 	} | ||||
| 	state->sample_cnt -= nsamples; | ||||
| 	return nbytes; | ||||
| } | ||||
|  | ||||
| static struct mgcp_rtp_end *source_for_dest(struct mgcp_endpoint *endp, | ||||
| 					struct mgcp_rtp_end *dst_end) | ||||
| { | ||||
| 	if (&endp->bts_end == dst_end) | ||||
| 		return &endp->net_end; | ||||
| 	else if (&endp->net_end == dst_end) | ||||
| 		return &endp->bts_end; | ||||
| 	OSMO_ASSERT(0); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * With some modems we get offered multiple codecs | ||||
|  * and we have selected one of them. It might not | ||||
|  * be the right one and we need to detect this with | ||||
|  * the first audio packets. One difficulty is that | ||||
|  * we patch the rtp payload type in place, so we | ||||
|  * need to discuss this. | ||||
|  */ | ||||
| struct mgcp_process_rtp_state *check_transcode_state( | ||||
| 				struct mgcp_endpoint *endp, | ||||
| 				struct mgcp_rtp_end *dst_end, | ||||
| 				struct rtp_hdr *rtp_hdr) | ||||
| { | ||||
| 	struct mgcp_rtp_end *src_end; | ||||
|  | ||||
| 	/* Only deal with messages from net to bts */ | ||||
| 	if (&endp->bts_end != dst_end) | ||||
| 		goto done; | ||||
|  | ||||
| 	src_end = source_for_dest(endp, dst_end); | ||||
|  | ||||
| 	/* Already patched */ | ||||
| 	if (rtp_hdr->payload_type == dst_end->codec.payload_type) | ||||
| 		goto done; | ||||
| 	/* The payload we expect */ | ||||
| 	if (rtp_hdr->payload_type == src_end->codec.payload_type) | ||||
| 		goto done; | ||||
| 	/* The matching alternate payload type? Then switch */ | ||||
| 	if (rtp_hdr->payload_type == src_end->alt_codec.payload_type) { | ||||
| 		struct mgcp_config *cfg = endp->cfg; | ||||
| 		struct mgcp_rtp_codec tmp_codec = src_end->alt_codec; | ||||
| 		src_end->alt_codec = src_end->codec; | ||||
| 		src_end->codec = tmp_codec; | ||||
| 		cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end); | ||||
| 		cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end); | ||||
| 	} | ||||
|  | ||||
| done: | ||||
| 	return dst_end->rtp_process_data; | ||||
| } | ||||
|  | ||||
| int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, | ||||
| 				struct mgcp_rtp_end *dst_end, | ||||
| 			     char *data, int *len, int buf_size) | ||||
| { | ||||
| 	struct mgcp_process_rtp_state *state; | ||||
| 	const size_t rtp_hdr_size = sizeof(struct rtp_hdr); | ||||
| 	struct rtp_hdr *rtp_hdr = (struct rtp_hdr *) data; | ||||
| 	char *payload_data = (char *) &rtp_hdr->data[0]; | ||||
| 	int payload_len = *len - rtp_hdr_size; | ||||
| 	uint8_t *src = (uint8_t *)payload_data; | ||||
| 	uint8_t *dst = (uint8_t *)payload_data; | ||||
| 	size_t nbytes = payload_len; | ||||
| 	size_t nsamples; | ||||
| 	size_t max_samples; | ||||
| 	uint32_t ts_no; | ||||
| 	int rc; | ||||
|  | ||||
| 	state = check_transcode_state(endp, dst_end, rtp_hdr); | ||||
| 	if (!state) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (state->src_fmt == state->dst_fmt) { | ||||
| 		if (!state->dst_packet_duration) | ||||
| 			return 0; | ||||
|  | ||||
| 		/* TODO: repackage without transcoding */ | ||||
| 	} | ||||
|  | ||||
| 	/* If the remaining samples do not fit into a fixed ptime, | ||||
| 	 * a) discard them, if the next packet is much later | ||||
| 	 * b) add silence and * send it, if the current packet is not | ||||
| 	 *    yet too late | ||||
| 	 * c) append the sample data, if the timestamp matches exactly | ||||
| 	 */ | ||||
|  | ||||
| 	/* TODO: check payload type (-> G.711 comfort noise) */ | ||||
|  | ||||
| 	if (payload_len > 0) { | ||||
| 		ts_no = ntohl(rtp_hdr->timestamp); | ||||
| 		if (!state->is_running) { | ||||
| 			state->next_seq = ntohs(rtp_hdr->sequence); | ||||
| 			state->next_time = ts_no; | ||||
| 			state->is_running = 1; | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 		if (state->sample_cnt > 0) { | ||||
| 			int32_t delta = ts_no - state->next_time; | ||||
| 			/* TODO: check sequence? reordering? packet loss? */ | ||||
|  | ||||
| 			if (delta > state->sample_cnt) { | ||||
| 				/* There is a time gap between the last packet | ||||
| 				 * and the current one. Just discard the | ||||
| 				 * partial data that is left in the buffer. | ||||
| 				 * TODO: This can be improved by adding silence | ||||
| 				 * instead if the delta is small enough. | ||||
| 				 */ | ||||
| 				LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 					"0x%x dropping sample buffer due delta=%d sample_cnt=%zu\n", | ||||
| 					ENDPOINT_NUMBER(endp), delta, state->sample_cnt); | ||||
| 				state->sample_cnt = 0; | ||||
| 				state->next_time = ts_no; | ||||
| 			} else if (delta < 0) { | ||||
| 				LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 				     "RTP time jumps backwards, delta = %d, " | ||||
| 				     "discarding buffered samples\n", | ||||
| 				     delta); | ||||
| 				state->sample_cnt = 0; | ||||
| 				state->sample_offs = 0; | ||||
| 				return -EAGAIN; | ||||
| 			} | ||||
|  | ||||
| 			/* Make sure the samples start without offset */ | ||||
| 			if (state->sample_offs && state->sample_cnt) | ||||
| 				memmove(&state->samples[0], | ||||
| 					&state->samples[state->sample_offs], | ||||
| 					state->sample_cnt * | ||||
| 					sizeof(state->samples[0])); | ||||
| 		} | ||||
|  | ||||
| 		state->sample_offs = 0; | ||||
|  | ||||
| 		/* Append decoded audio to samples */ | ||||
| 		decode_audio(state, &src, &nbytes); | ||||
|  | ||||
| 		if (nbytes > 0) | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 			     "Skipped audio frame in RTP packet: %zu octets\n", | ||||
| 			     nbytes); | ||||
| 	} else | ||||
| 		ts_no = state->next_time; | ||||
|  | ||||
| 	if (state->sample_cnt < state->dst_packet_duration) | ||||
| 		return -EAGAIN; | ||||
|  | ||||
| 	max_samples = | ||||
| 		state->dst_packet_duration ? | ||||
| 		state->dst_packet_duration : state->sample_cnt; | ||||
|  | ||||
| 	nsamples = state->sample_cnt; | ||||
|  | ||||
| 	rc = encode_audio(state, dst, buf_size, max_samples); | ||||
| 	/* | ||||
| 	 * There were no samples to encode? | ||||
| 	 * TODO: how does this work for comfort noise? | ||||
| 	 */ | ||||
| 	if (rc == 0) | ||||
| 		return -ENOMSG; | ||||
| 	/* Any other error during the encoding */ | ||||
| 	if (rc < 0) | ||||
| 		return rc; | ||||
|  | ||||
| 	nsamples -= state->sample_cnt; | ||||
|  | ||||
| 	*len = rtp_hdr_size + rc; | ||||
| 	rtp_hdr->sequence = htons(state->next_seq); | ||||
| 	rtp_hdr->timestamp = htonl(ts_no); | ||||
|  | ||||
| 	state->next_seq += 1; | ||||
| 	state->next_time = ts_no + nsamples; | ||||
|  | ||||
| 	/* | ||||
| 	 * XXX: At this point we should always have consumed | ||||
| 	 * samples. So doing OSMO_ASSERT(nsamples > 0) and returning | ||||
| 	 * rtp_hdr_size should be fine. | ||||
| 	 */ | ||||
| 	return nsamples ? rtp_hdr_size : 0; | ||||
| } | ||||
| @@ -1,6 +1,7 @@ | ||||
| AM_CPPFLAGS = \ | ||||
| 	$(all_includes) \ | ||||
| 	-I$(top_srcdir)/include \ | ||||
| 	-I$(top_builddir)/include \ | ||||
| 	-I$(top_builddir) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| @@ -8,21 +9,18 @@ AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOVTY_CFLAGS) \ | ||||
| 	$(LIBOSMONETIF_CFLAGS) \ | ||||
| 	$(COVERAGE_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(COVERAGE_LDFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| # This is _NOT_ the library release version, it's an API version. | ||||
| # Please read Chapter 6 "Library interface versions" of the libtool | ||||
| # documentation before making any modification | ||||
| MGCP_CLIENT_LIBVERSION=1:0:1 | ||||
| # 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=6:0:0 | ||||
|  | ||||
| lib_LTLIBRARIES = \ | ||||
| 	libosmo-mgcp-client.la \ | ||||
| @@ -31,7 +29,8 @@ lib_LTLIBRARIES = \ | ||||
| libosmo_mgcp_client_la_SOURCES = \ | ||||
| 	mgcp_client.c \ | ||||
| 	mgcp_client_vty.c \ | ||||
| 	../libosmo-legacy-mgcp/mgcp_common.c \ | ||||
| 	mgcp_client_fsm.c \ | ||||
| 	mgcp_client_endpoint_fsm.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION) | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										968
									
								
								src/libosmo-mgcp-client/mgcp_client_endpoint_fsm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										968
									
								
								src/libosmo-mgcp-client/mgcp_client_endpoint_fsm.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,968 @@ | ||||
| /* FSM to manage multiple connections of an MGW endpoint | ||||
|  * | ||||
|  * (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Neels Hofmeyr <neels@hofmeyr.de> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <sys/socket.h> | ||||
| #include <netinet/in.h> | ||||
| #include <arpa/inet.h> | ||||
|  | ||||
| #include <osmocom/core/fsm.h> | ||||
| #include <osmocom/core/byteswap.h> | ||||
| #include <osmocom/core/tdef.h> | ||||
|  | ||||
| #include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h> | ||||
|  | ||||
| #define LOG_CI(ci, level, fmt, args...) do { \ | ||||
| 	if (!ci || !ci->ep) \ | ||||
| 		LOGP(DLGLOBAL, level, "(unknown MGW endpoint) " fmt, ## args); \ | ||||
| 	else \ | ||||
| 		LOG_MGCPC_EP(ci->ep, level, "CI[%d] %s%s%s: " fmt, \ | ||||
| 			(int)(ci - ci->ep->ci), \ | ||||
| 			ci->label ? : "-", \ | ||||
| 			ci->mgcp_ci_str[0] ? " CI=" : "", \ | ||||
| 			ci->mgcp_ci_str[0] ? ci->mgcp_ci_str : "", \ | ||||
| 			## args); \ | ||||
| 	} while(0) | ||||
|  | ||||
| #define LOG_CI_VERB(ci, level, fmt, args...) do { \ | ||||
| 	if (ci->verb_info.addr[0]) \ | ||||
| 		LOG_CI(ci, level, "%s %s:%u: " fmt, \ | ||||
| 			osmo_mgcp_verb_name(ci->verb), ci->verb_info.addr, ci->verb_info.port, \ | ||||
| 			## args); \ | ||||
| 	else \ | ||||
| 		LOG_CI(ci, level, "%s: " fmt, \ | ||||
| 			osmo_mgcp_verb_name(ci->verb), \ | ||||
| 			## args); \ | ||||
| 	} while(0) | ||||
|  | ||||
| enum osmo_mgcpc_ep_fsm_state { | ||||
| 	OSMO_MGCPC_EP_ST_UNUSED = 0, | ||||
| 	OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE, | ||||
| 	OSMO_MGCPC_EP_ST_IN_USE, | ||||
| }; | ||||
|  | ||||
| enum osmo_mgcpc_ep_fsm_event { | ||||
| 	_OSMO_MGCPC_EP_EV_LAST = 0, | ||||
| 	/* and MGW response events are allocated dynamically */ | ||||
| }; | ||||
|  | ||||
| #define FIRST_CI_EVENT (_OSMO_MGCPC_EP_EV_LAST + (_OSMO_MGCPC_EP_EV_LAST & 1)) /* rounded up to even nr */ | ||||
| #define USABLE_CI ((32 - FIRST_CI_EVENT)/2) | ||||
| #define EV_TO_CI_IDX(event) ((event - FIRST_CI_EVENT) / 2) | ||||
|  | ||||
| #define CI_EV_SUCCESS(ci) (FIRST_CI_EVENT + (((ci) - ci->ep->ci) * 2)) | ||||
| #define CI_EV_FAILURE(ci) (CI_EV_SUCCESS(ci) + 1) | ||||
|  | ||||
| static struct osmo_fsm osmo_mgcpc_ep_fsm; | ||||
|  | ||||
| struct fsm_notify { | ||||
| 	struct llist_head entry; | ||||
| 	struct osmo_fsm_inst *fi; | ||||
| 	uint32_t success; | ||||
| 	uint32_t failure; | ||||
| 	void *data; | ||||
| }; | ||||
|  | ||||
| /*! One connection on an endpoint, corresponding to a connection identifier (CI) as returned by the MGW. | ||||
|  * An endpoint has a fixed number of slots of these, which may or may not be in use. | ||||
|  */ | ||||
| struct osmo_mgcpc_ep_ci { | ||||
| 	struct osmo_mgcpc_ep *ep; | ||||
|  | ||||
| 	bool occupied; | ||||
| 	char label[64]; | ||||
| 	struct osmo_fsm_inst *mgcp_client_fi; | ||||
|  | ||||
| 	bool pending; | ||||
| 	bool sent; | ||||
| 	enum mgcp_verb verb; | ||||
| 	struct mgcp_conn_peer verb_info; | ||||
| 	struct fsm_notify notify; | ||||
|  | ||||
| 	bool got_port_info; | ||||
| 	struct mgcp_conn_peer rtp_info; | ||||
| 	char mgcp_ci_str[MGCP_CONN_ID_LENGTH]; | ||||
| }; | ||||
|  | ||||
| /*! An MGW endpoint with N connections, like "rtpbridge/23@mgw". */ | ||||
| struct osmo_mgcpc_ep { | ||||
| 	/*! MGCP client connection to the MGW. */ | ||||
| 	struct mgcp_client *mgcp_client; | ||||
| 	struct osmo_fsm_inst *fi; | ||||
|  | ||||
| 	/*! Endpoint string; at first this might be a wildcard, and upon the first CRCX OK response, this will reflect | ||||
| 	 * the endpoint name returned by the MGW. */ | ||||
| 	char endpoint[MGCP_ENDPOINT_MAXLEN]; | ||||
|  | ||||
| 	/*! Timeout definitions used for this endpoint, see osmo_mgcpc_ep_fsm_timeouts. */ | ||||
| 	const struct osmo_tdef *T_defs; | ||||
|  | ||||
| 	/*! True as soon as the first CRCX OK is received. The endpoint name may be determined by the first CRCX | ||||
| 	 * response, so to dispatch any other messages, the FSM instance *must* wait for the first CRCX OK to arrive | ||||
| 	 * first. Once the endpoint name is pinpointed, any amount of operations may be dispatched concurrently. */ | ||||
| 	bool first_crcx_complete; | ||||
|  | ||||
| 	/*! Endpoint connection slots. Note that each connection has its own set of FSM event numbers to signal success | ||||
| 	 * and failure, depending on its index within this array. See CI_EV_SUCCESS and CI_EV_FAILURE. */ | ||||
| 	struct osmo_mgcpc_ep_ci ci[USABLE_CI]; | ||||
|  | ||||
| 	/*! Internal use: if a function keeps an fsm_notify for later dispatch while already clearing or re-using the | ||||
| 	 * ci[], the fsm_notify should be kept here to also get canceled by osmo_mgcpc_ep_cancel_notify(). */ | ||||
| 	struct llist_head background_notify; | ||||
| }; | ||||
|  | ||||
| const struct value_string osmo_mgcp_verb_names[] = { | ||||
| 	{ MGCP_VERB_CRCX, "CRCX" }, | ||||
| 	{ MGCP_VERB_MDCX, "MDCX" }, | ||||
| 	{ MGCP_VERB_DLCX, "DLCX" }, | ||||
| 	{ MGCP_VERB_AUEP, "AUEP" }, | ||||
| 	{ MGCP_VERB_RSIP, "RSIP" }, | ||||
| 	{} | ||||
| }; | ||||
|  | ||||
| static void osmo_mgcpc_ep_count(struct osmo_mgcpc_ep *ep, int *occupied, int *pending_not_sent, | ||||
| 				int *waiting_for_response); | ||||
|  | ||||
| static struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_check_ci(struct osmo_mgcpc_ep_ci *ci) | ||||
| { | ||||
| 	if (!ci) | ||||
| 		return NULL; | ||||
| 	if (!ci->ep) | ||||
| 		return NULL; | ||||
| 	if (ci < ci->ep->ci || ci >= &ci->ep->ci[USABLE_CI]) | ||||
| 		return NULL; | ||||
| 	return ci; | ||||
| } | ||||
|  | ||||
| static struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_for_event(struct osmo_mgcpc_ep *ep, uint32_t event) | ||||
| { | ||||
| 	int idx; | ||||
| 	if (event < FIRST_CI_EVENT) | ||||
| 		return NULL; | ||||
| 	idx = EV_TO_CI_IDX(event); | ||||
| 	if (idx >= sizeof(ep->ci)) | ||||
| 		return NULL; | ||||
| 	return osmo_mgcpc_ep_check_ci(&ep->ci[idx]); | ||||
| } | ||||
|  | ||||
| const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep) | ||||
| { | ||||
| 	if (!ep) | ||||
| 		return "NULL"; | ||||
| 	if (ep->endpoint[0]) | ||||
| 		return ep->endpoint; | ||||
| 	return osmo_fsm_inst_name(ep->fi); | ||||
| } | ||||
|  | ||||
| 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 | ||||
| 	 * this buffer is too small. */ | ||||
| 	static char buf[1024]; | ||||
|  | ||||
| 	if (!info) | ||||
| 		return "NULL"; | ||||
|  | ||||
| 	if (info->endpoint[0] | ||||
| 	    && info->addr[0]) | ||||
| 		snprintf(buf, sizeof(buf), "%s:%s:%u", | ||||
| 			 info->endpoint, info->addr, info->port); | ||||
| 	else if (info->endpoint[0]) | ||||
| 		snprintf(buf, sizeof(buf), "%s", info->endpoint); | ||||
| 	else if (info->addr[0]) | ||||
| 		snprintf(buf, sizeof(buf), "%s:%u", info->addr, info->port); | ||||
| 	else | ||||
| 		return "empty"; | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci) | ||||
| { | ||||
| 	const struct mgcp_conn_peer *rtp_info; | ||||
|  | ||||
| 	if (!ci) | ||||
| 		return "NULL"; | ||||
|  | ||||
| 	rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci); | ||||
|  | ||||
| 	if (rtp_info) | ||||
| 		return mgcp_conn_peer_name(rtp_info); | ||||
| 	return osmo_mgcpc_ep_name(ci->ep); | ||||
| } | ||||
|  | ||||
| const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci) | ||||
| { | ||||
| 	if (!ci || !ci->mgcp_ci_str[0]) | ||||
| 		return NULL; | ||||
| 	return ci->mgcp_ci_str; | ||||
| } | ||||
|  | ||||
| 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() | ||||
| { | ||||
| 	int i; | ||||
| 	for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) { | ||||
| 		if (i < _OSMO_MGCPC_EP_EV_LAST) | ||||
| 			continue; | ||||
| 		if (i < FIRST_CI_EVENT || EV_TO_CI_IDX(i) > USABLE_CI) { | ||||
| 			osmo_mgcpc_ep_fsm_event_names[i] = (struct value_string){i, "Unused"}; | ||||
| 			continue; | ||||
| 		} | ||||
| 		snprintf(osmo_mgcpc_ep_fsm_event_name_bufs[i], sizeof(osmo_mgcpc_ep_fsm_event_name_bufs[i]), | ||||
| 			 "MGW Response for CI #%d", EV_TO_CI_IDX(i)); | ||||
| 		osmo_mgcpc_ep_fsm_event_names[i] = (struct value_string){i, osmo_mgcpc_ep_fsm_event_name_bufs[i]}; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* T_defs is used to obtain an (Osmocom specific) T2427001: timeout for an MGCP response (note, 2427 corresponds to the | ||||
|  * default MGCP port in osmo-mgw). */ | ||||
| static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init() | ||||
| { | ||||
| 	OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0); | ||||
| 	fill_event_names(); | ||||
| } | ||||
|  | ||||
| struct osmo_mgcpc_ep *osmo_mgcpc_ep_fi_mgwep(struct osmo_fsm_inst *fi) | ||||
| { | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	OSMO_ASSERT(fi->fsm == &osmo_mgcpc_ep_fsm); | ||||
| 	OSMO_ASSERT(fi->priv); | ||||
| 	return fi->priv; | ||||
| } | ||||
|  | ||||
| /*! Allocate an osmo_mgcpc_ep FSM. | ||||
|  * MGCP messages to set up the endpoint will be sent on the given mgcp_client, as soon as the first | ||||
|  * osmo_mgcpc_ep_ci_request() is invoked. | ||||
|  * | ||||
|  * IMPORTANT: To avoid use-after-free problems, using this FSM requires use of deferred FSM deallocation using | ||||
|  * osmo_fsm_set_dealloc_ctx(), e.g. using osmo_select_main_ctx(OTC_SELECT) with osmo_select_main_ctx() as main loop. | ||||
|  * | ||||
|  * A typical sequence of events would be: | ||||
|  * | ||||
|  *    ep = osmo_mgcpc_ep_alloc(..., mgcp_client_rtpbridge_wildcard(client)); | ||||
|  *    ci_to_ran = osmo_mgcpc_ep_ci_add(ep); | ||||
|  *    osmo_mgcpc_ep_ci_request(ci_to_ran, MGCP_VERB_CRCX, verb_info, | ||||
|  *                             my_call_fsm, MY_EVENT_MGCP_OK, MY_EVENT_MGCP_FAIL); | ||||
|  *    ci_to_cn = osmo_mgcpc_ep_ci_add(ep); | ||||
|  *    osmo_mgcpc_ep_ci_request(ci_to_cn, MGCP_VERB_CRCX, verb_info, | ||||
|  *                             my_call_fsm, MY_EVENT_MGCP_OK, MY_EVENT_MGCP_FAIL); | ||||
|  *    ... | ||||
|  *    osmo_mgcpc_ep_ci_request(ci_to_ran, MGCP_VERB_MDCX, ...); | ||||
|  *    ... | ||||
|  *    osmo_mgcpc_ep_clear(ep); | ||||
|  *    ep = NULL; | ||||
|  * | ||||
|  * \param parent  Parent FSM. | ||||
|  * \param parent_term_event  Event to dispatch to the parent on termination of this FSM instance. | ||||
|  * \param mgcp_client  Connection to the MGW. | ||||
|  * \param T_defs  Timeout definitions to be used for FSM states, see osmo_mgcpc_ep_fsm_timeouts. | ||||
|  * \param fsm_id  FSM instance ID. | ||||
|  * \param endpoint_str_fmt  The endpoint string format to send to the MGW upon the first CRCX. | ||||
|  *                          See mgcp_client_rtpbridge_wildcard() for "rtpbridge" endpoints. | ||||
|  */ | ||||
| struct osmo_mgcpc_ep *osmo_mgcpc_ep_alloc(struct osmo_fsm_inst *parent, uint32_t parent_term_event, | ||||
| 					  struct mgcp_client *mgcp_client, | ||||
| 					  const struct osmo_tdef *T_defs, | ||||
| 					  const char *fsm_id, | ||||
| 					  const char *endpoint_str_fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| 	struct osmo_fsm_inst *fi; | ||||
| 	struct osmo_mgcpc_ep *ep; | ||||
| 	int rc; | ||||
|  | ||||
| 	if (!mgcp_client) | ||||
| 		return NULL; | ||||
|  | ||||
| 	fi = osmo_fsm_inst_alloc_child(&osmo_mgcpc_ep_fsm, parent, parent_term_event); | ||||
| 	OSMO_ASSERT(fi); | ||||
|  | ||||
| 	osmo_fsm_inst_update_id(fi, fsm_id); | ||||
|  | ||||
| 	ep = talloc_zero(fi, struct osmo_mgcpc_ep); | ||||
| 	OSMO_ASSERT(ep); | ||||
|  | ||||
| 	*ep = (struct osmo_mgcpc_ep){ | ||||
| 		.mgcp_client = mgcp_client, | ||||
| 		.fi = fi, | ||||
| 		.T_defs = T_defs, | ||||
| 	}; | ||||
| 	INIT_LLIST_HEAD(&ep->background_notify); | ||||
| 	fi->priv = ep; | ||||
|  | ||||
| 	va_start(ap, endpoint_str_fmt); | ||||
| 	rc = vsnprintf(ep->endpoint, sizeof(ep->endpoint), endpoint_str_fmt ? : "", ap); | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	if (rc <= 0 || rc >= sizeof(ep->endpoint)) { | ||||
| 		LOG_MGCPC_EP(ep, LOGL_ERROR, "Endpoint name too long or too short: %s\n", | ||||
| 			  ep->endpoint); | ||||
| 		osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_ERROR, 0); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	return ep; | ||||
| } | ||||
|  | ||||
| /*! Add a connection to an endpoint. | ||||
|  * Allocate a connection identifier slot in the osmo_mgcpc_ep instance, do not yet dispatch a CRCX. | ||||
|  * The CRCX is dispatched only upon the first osmo_mgcpc_ep_ci_request(). | ||||
|  * \param ep  Parent endpoint instance. | ||||
|  * \param label_fmt  Label for logging. | ||||
|  */ | ||||
| struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_add(struct osmo_mgcpc_ep *ep, | ||||
| 					      const char *label_fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| 	int i; | ||||
| 	struct osmo_mgcpc_ep_ci *ci; | ||||
|  | ||||
| 	for (i = 0; i < USABLE_CI; i++) { | ||||
| 		ci = &ep->ci[i]; | ||||
|  | ||||
| 		if (ci->occupied || ci->mgcp_client_fi) | ||||
| 			continue; | ||||
|  | ||||
| 		*ci = (struct osmo_mgcpc_ep_ci){ | ||||
| 			.ep = ep, | ||||
| 			.occupied = true, | ||||
| 		}; | ||||
| 		if (label_fmt) { | ||||
| 			va_start(ap, label_fmt); | ||||
| 			vsnprintf(ci->label, sizeof(ci->label), label_fmt, ap); | ||||
| 			va_end(ap); | ||||
| 		} | ||||
| 		return ci; | ||||
| 	} | ||||
|  | ||||
| 	LOG_MGCPC_EP(ep, LOGL_ERROR, | ||||
| 		  "Cannot allocate another endpoint, all " | ||||
| 		  OSMO_STRINGIFY_VAL(USABLE_CI) " are in use\n"); | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| static bool osmo_mgcpc_ep_fsm_check_state_chg_after_response(struct osmo_fsm_inst *fi); | ||||
|  | ||||
| static void on_failure(struct osmo_mgcpc_ep_ci *ci) | ||||
| { | ||||
| 	struct osmo_mgcpc_ep *ep = ci->ep; | ||||
| 	struct fsm_notify notify; | ||||
| 	int i; | ||||
|  | ||||
| 	if (!ci->occupied) | ||||
| 		return; | ||||
|  | ||||
| 	/* When dispatching an event for this CI, the user may decide to trigger the next request for this conn right | ||||
| 	 * away. So we must be ready with a cleared *ci. Store the notify separately and clear before dispatching. */ | ||||
| 	notify = ci->notify; | ||||
| 	/* Register the planned notification in ep->background_notify so we also catch any osmo_mgcpc_ep_cancel_notify() | ||||
| 	 * that might be triggered between clearing the ci and actually dispatching the event. */ | ||||
| 	llist_add(¬ify.entry, &ep->background_notify); | ||||
|  | ||||
| 	*ci = (struct osmo_mgcpc_ep_ci){ | ||||
| 		.ep = ci->ep, | ||||
| 	}; | ||||
|  | ||||
| 	/* An MGCP failure typically means the endpoint becomes unusable, cancel all pending request (except DLCX). | ||||
| 	 * Particularly, if two CRCX were scheduled and the first fails, we must no longer dispatch the second CRCX. */ | ||||
| 	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) { | ||||
| 		struct osmo_mgcpc_ep_ci *other_ci = &ep->ci[i]; | ||||
| 		if (other_ci == ci) | ||||
| 			continue; | ||||
| 		if (!other_ci->occupied) | ||||
| 			continue; | ||||
| 		if (!other_ci->pending) | ||||
| 			continue; | ||||
| 		if (other_ci->sent) | ||||
| 			continue; | ||||
| 		if (other_ci->verb == MGCP_VERB_DLCX) | ||||
| 			continue; | ||||
| 		/* Just clear the pending request, don't fire more events than below. */ | ||||
| 		other_ci->pending = false; | ||||
| 	} | ||||
|  | ||||
| 	/* If this check has terminated the FSM instance, don't fire any more events to prevent use-after-free problems. | ||||
| 	 * The endpoint FSM does dispatch a term event to its parent, and everything should be cleaned like that. */ | ||||
| 	if (!osmo_mgcpc_ep_fsm_check_state_chg_after_response(ep->fi)) { | ||||
| 		/* The ep has deallocated, no need to llist_del(¬ify.entry) here. */ | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (notify.fi) | ||||
| 		osmo_fsm_inst_dispatch(notify.fi, notify.failure, notify.data); | ||||
|  | ||||
| 	llist_del(¬ify.entry); | ||||
| } | ||||
|  | ||||
| static int update_endpoint_name(struct osmo_mgcpc_ep_ci *ci, const char *new_endpoint_name) | ||||
| { | ||||
| 	struct osmo_mgcpc_ep *ep = ci->ep; | ||||
| 	int rc; | ||||
| 	int i; | ||||
|  | ||||
| 	if (!strcmp(ep->endpoint, new_endpoint_name)) { | ||||
| 		/* Same endpoint name, nothing to do. */ | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* The endpoint name should only change on the very first CRCX response. */ | ||||
| 	if (ep->first_crcx_complete) { | ||||
| 		LOG_CI(ci, LOGL_ERROR, "Reponse returned mismatching endpoint name." | ||||
| 		       " This is endpoint %s, instead received %s\n", | ||||
| 		       ep->endpoint, new_endpoint_name); | ||||
| 		on_failure(ci); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/* This is the first CRCX response, update endpoint name. */ | ||||
| 	rc = OSMO_STRLCPY_ARRAY(ep->endpoint, new_endpoint_name); | ||||
| 	if (rc <= 0 || rc >= sizeof(ep->endpoint)) { | ||||
| 		LOG_CI(ci, LOGL_ERROR, "Unable to copy endpoint name %s\n", osmo_quote_str(new_endpoint_name, -1)); | ||||
| 		osmo_mgcpc_ep_ci_dlcx(ci); | ||||
| 		on_failure(ci); | ||||
| 		return -ENOSPC; | ||||
| 	} | ||||
|  | ||||
| 	/* Make sure already pending requests use this updated endpoint name. */ | ||||
| 	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) { | ||||
| 		struct osmo_mgcpc_ep_ci *other_ci = &ep->ci[i]; | ||||
| 		if (!other_ci->occupied) | ||||
| 			continue; | ||||
| 		if (!other_ci->pending) | ||||
| 			continue; | ||||
| 		if (other_ci->sent) | ||||
| 			continue; | ||||
| 		OSMO_STRLCPY_ARRAY(other_ci->verb_info.endpoint, ep->endpoint); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data) | ||||
| { | ||||
| 	struct mgcp_conn_peer *rtp_info; | ||||
|  | ||||
| 	if (!ci->occupied) | ||||
| 		return; | ||||
|  | ||||
| 	ci->pending = false; | ||||
|  | ||||
| 	switch (ci->verb) { | ||||
| 	case MGCP_VERB_CRCX: | ||||
| 		/* If we sent a wildcarded endpoint name on CRCX, we need to store the resulting endpoint | ||||
| 		 * name here. Also, we receive the MGW's RTP port information. */ | ||||
| 		rtp_info = data; | ||||
| 		OSMO_ASSERT(rtp_info); | ||||
| 		ci->got_port_info = true; | ||||
| 		ci->rtp_info = *rtp_info; | ||||
| 		osmo_strlcpy(ci->mgcp_ci_str, mgcp_conn_get_ci(ci->mgcp_client_fi), | ||||
| 			sizeof(ci->mgcp_ci_str)); | ||||
| 		if (rtp_info->endpoint[0]) { | ||||
| 			/* On errors, this instance might already be deallocated. Make sure to not access anything after | ||||
| 			 * error. */ | ||||
| 			if (update_endpoint_name(ci, rtp_info->endpoint)) | ||||
| 				return; | ||||
| 		} | ||||
| 		ci->ep->first_crcx_complete = true; | ||||
| 		break; | ||||
|  | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	LOG_CI(ci, LOGL_DEBUG, "received successful response to %s: RTP=%s%s\n", | ||||
| 	       osmo_mgcp_verb_name(ci->verb), | ||||
| 	       mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL), | ||||
| 	       ci->notify.fi ? "" : " (not sending a notification)"); | ||||
|  | ||||
| 	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 RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */ | ||||
| const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci) | ||||
| { | ||||
| 	ci = osmo_mgcpc_ep_check_ci((struct osmo_mgcpc_ep_ci*)ci); | ||||
| 	if (!ci) | ||||
| 		return NULL; | ||||
| 	if (!ci->got_port_info) | ||||
| 		return NULL; | ||||
| 	return &ci->rtp_info; | ||||
| } | ||||
|  | ||||
| /*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */ | ||||
| bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest) | ||||
| { | ||||
| 	const struct mgcp_conn_peer *rtp_info; | ||||
| 	struct sockaddr_in *sin; | ||||
|  | ||||
| 	rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci); | ||||
| 	if (!rtp_info) | ||||
| 		return false; | ||||
|  | ||||
|         sin = (struct sockaddr_in *)dest; | ||||
|  | ||||
|         sin->sin_family = AF_INET; | ||||
|         sin->sin_addr.s_addr = inet_addr(rtp_info->addr); | ||||
|         sin->sin_port = osmo_ntohs(rtp_info->port); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(const struct osmo_mgcpc_ep_ci *ci, uint8_t* cid) | ||||
| { | ||||
| 	const struct mgcp_conn_peer *rtp_info; | ||||
|  | ||||
| 	rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci); | ||||
| 	if (!rtp_info) | ||||
| 		return false; | ||||
|  | ||||
| 	if (!rtp_info->x_osmo_osmux_use) | ||||
| 		return false; | ||||
|  | ||||
| 	*cid = rtp_info->x_osmo_osmux_cid; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| static const struct osmo_tdef_state_timeout osmo_mgcpc_ep_fsm_timeouts[32] = { | ||||
| 	[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = { .T=2427001 }, | ||||
| }; | ||||
|  | ||||
| /* Transition to a state, using the T timer defined in assignment_fsm_timeouts. | ||||
|  * The actual timeout value is in turn obtained from osmo_mgcpc_ep.T_defs. | ||||
|  * Assumes local variable fi exists. */ | ||||
| #define osmo_mgcpc_ep_fsm_state_chg(state) \ | ||||
| 	osmo_tdef_fsm_inst_state_chg(fi, state, osmo_mgcpc_ep_fsm_timeouts, \ | ||||
| 				     ((struct osmo_mgcpc_ep*)fi->priv)->T_defs, 5) | ||||
|  | ||||
| /*! Dispatch an actual CRCX/MDCX/DLCX message for this connection. | ||||
|  * | ||||
|  * If the 'notify' instance deallocates before it received a notification of event_success or event_failure, | ||||
|  * osmo_mgcpc_ep_ci_cancel_notify() or osmo_mgcpc_ep_cancel_notify() must be called. It is not harmful to cancel | ||||
|  * notification after an event has been received. | ||||
|  * | ||||
|  * \param ci  Connection identifier as obtained from osmo_mgcpc_ep_ci_add(). | ||||
|  * \param verb  MGCP operation to dispatch. | ||||
|  * \param verb_info  Parameters for the MGCP operation. | ||||
|  * \param notify  Peer FSM instance to notify of completed/failed operation. | ||||
|  * \param event_success  Which event to dispatch to 'notify' upon OK response. | ||||
|  * \param event_failure  Which event to dispatch to 'notify' upon failure response. | ||||
|  * \param notify_data  Data pointer to pass to the event dispatch for both success and failure. | ||||
|  */ | ||||
| void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci, | ||||
| 			      enum mgcp_verb verb, const struct mgcp_conn_peer *verb_info, | ||||
| 			      struct osmo_fsm_inst *notify, | ||||
| 			      uint32_t event_success, uint32_t event_failure, | ||||
| 			      void *notify_data) | ||||
| { | ||||
| 	struct osmo_mgcpc_ep *ep; | ||||
| 	struct osmo_fsm_inst *fi; | ||||
| 	struct osmo_mgcpc_ep_ci cleared_ci; | ||||
| 	ci = osmo_mgcpc_ep_check_ci(ci); | ||||
|  | ||||
| 	if (!ci) { | ||||
| 		LOGP(DLGLOBAL, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n"); | ||||
| 		goto dispatch_error; | ||||
| 	} | ||||
| 	if (!verb_info && verb != MGCP_VERB_DLCX) { | ||||
| 		LOG_CI(ci, LOGL_ERROR, "Invalid MGW endpoint request: missing verb details for %s\n", | ||||
| 		       osmo_mgcp_verb_name(verb)); | ||||
| 		goto dispatch_error; | ||||
| 	} | ||||
| 	if ((verb < 0) || (verb > MGCP_VERB_RSIP)) { | ||||
| 		LOG_CI(ci, LOGL_ERROR, "Invalid MGW endpoint request: unknown verb: %s\n", | ||||
| 		       osmo_mgcp_verb_name(verb)); | ||||
| 		goto dispatch_error; | ||||
| 	} | ||||
|  | ||||
| 	ep = ci->ep; | ||||
| 	fi = ep->fi; | ||||
|  | ||||
| 	/* Clear volatile state by explicitly keeping those that should remain. Because we can't assign | ||||
| 	 * the char[] directly, dance through cleared_ci and copy back. */ | ||||
| 	cleared_ci = (struct osmo_mgcpc_ep_ci){ | ||||
| 		.ep = ep, | ||||
| 		.mgcp_client_fi = ci->mgcp_client_fi, | ||||
| 		.got_port_info = ci->got_port_info, | ||||
| 		.rtp_info = ci->rtp_info, | ||||
|  | ||||
| 		.occupied = true, | ||||
| 		/* .pending = true follows below */ | ||||
| 		.verb = verb, | ||||
| 		.notify = { | ||||
| 			.fi = notify, | ||||
| 			.success = event_success, | ||||
| 			.failure = event_failure, | ||||
| 			.data = notify_data, | ||||
| 		} | ||||
| 	}; | ||||
| 	osmo_strlcpy(cleared_ci.label, ci->label, sizeof(cleared_ci.label)); | ||||
| 	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; | ||||
|  | ||||
| 	if (ep->endpoint[0]) { | ||||
| 		if (ci->verb_info.endpoint[0] && strcmp(ci->verb_info.endpoint, ep->endpoint)) | ||||
| 			LOG_CI(ci, LOGL_ERROR, | ||||
| 			       "Warning: Requested %s on endpoint %s, but this CI is on endpoint %s." | ||||
| 			       " Using the proper endpoint instead.\n", | ||||
| 			       osmo_mgcp_verb_name(verb), ci->verb_info.endpoint, ep->endpoint); | ||||
| 		osmo_strlcpy(ci->verb_info.endpoint, ep->endpoint, sizeof(ci->verb_info.endpoint)); | ||||
| 	} | ||||
|  | ||||
| 	switch (ci->verb) { | ||||
| 	case MGCP_VERB_CRCX: | ||||
| 		if (ci->mgcp_client_fi) { | ||||
| 			LOG_CI(ci, LOGL_ERROR, "CRCX can be called only once per MGW endpoint CI\n"); | ||||
| 			on_failure(ci); | ||||
| 			return; | ||||
| 		} | ||||
| 		break; | ||||
|  | ||||
| 	case MGCP_VERB_MDCX: | ||||
| 		if (!ci->mgcp_client_fi) { | ||||
| 			LOG_CI_VERB(ci, LOGL_ERROR, "The first verb on an unused MGW endpoint CI must be CRCX, not %s\n", | ||||
| 				    osmo_mgcp_verb_name(ci->verb)); | ||||
| 			on_failure(ci); | ||||
| 			return; | ||||
| 		} | ||||
| 		break; | ||||
|  | ||||
| 	case MGCP_VERB_DLCX: | ||||
| 		if (!ci->mgcp_client_fi) { | ||||
| 			LOG_CI_VERB(ci, LOGL_DEBUG, "Ignoring DLCX on unused MGW endpoint CI\n"); | ||||
| 			return; | ||||
| 		} | ||||
| 		break; | ||||
|  | ||||
| 	default: | ||||
| 		LOG_CI(ci, LOGL_ERROR, "This verb is not supported: %s\n", osmo_mgcp_verb_name(ci->verb)); | ||||
| 		on_failure(ci); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	ci->pending = true; | ||||
|  | ||||
| 	LOG_CI_VERB(ci, LOGL_DEBUG, "Scheduling\n"); | ||||
|  | ||||
| 	if (ep->fi->state != OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE) | ||||
| 		osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE); | ||||
|  | ||||
| 	return; | ||||
| dispatch_error: | ||||
| 	if (notify) | ||||
| 		osmo_fsm_inst_dispatch(notify, event_failure, notify_data); | ||||
| } | ||||
|  | ||||
| /*! No longer notify for any state changes for any conns of this endpoint. | ||||
|  * Useful if the notify instance passed to osmo_mgcpc_ep_ci_request() is about to deallocate. | ||||
|  * \param ep  The endpoint FSM instance. | ||||
|  * \param notify  Which target to cancel notification for, if NULL cancel all notifications. */ | ||||
| void osmo_mgcpc_ep_cancel_notify(struct osmo_mgcpc_ep *ep, struct osmo_fsm_inst *notify) | ||||
| { | ||||
| 	struct fsm_notify *n; | ||||
| 	int i; | ||||
| 	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) { | ||||
| 		struct osmo_mgcpc_ep_ci *ci = &ep->ci[i]; | ||||
| 		if (!notify || ci->notify.fi == notify) | ||||
| 			ci->notify.fi = NULL; | ||||
| 	} | ||||
| 	llist_for_each_entry(n, &ep->background_notify, entry) { | ||||
| 		if (!notify || n->fi == notify) | ||||
| 			n->fi = NULL; | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| /* Return the osmo_mgcpc_ep that this conn belongs to. */ | ||||
| struct osmo_mgcpc_ep *osmo_mgcpc_ep_ci_ep(struct osmo_mgcpc_ep_ci *conn) | ||||
| { | ||||
| 	if (!conn) | ||||
| 		return NULL; | ||||
| 	return conn->ep; | ||||
| } | ||||
|  | ||||
| static int send_verb(struct osmo_mgcpc_ep_ci *ci) | ||||
| { | ||||
| 	int rc; | ||||
| 	struct osmo_mgcpc_ep *ep = ci->ep; | ||||
| 	struct fsm_notify notify; | ||||
|  | ||||
| 	if (!ci->occupied || !ci->pending || ci->sent) | ||||
| 		return 0; | ||||
|  | ||||
| 	switch (ci->verb) { | ||||
|  | ||||
| 	case MGCP_VERB_CRCX: | ||||
| 		OSMO_ASSERT(!ci->mgcp_client_fi); | ||||
| 		LOG_CI_VERB(ci, LOGL_DEBUG, "Sending\n"); | ||||
| 		ci->mgcp_client_fi = mgcp_conn_create(ep->mgcp_client, ep->fi, | ||||
| 						      CI_EV_FAILURE(ci), CI_EV_SUCCESS(ci), | ||||
| 						      &ci->verb_info); | ||||
| 		ci->sent = true; | ||||
| 		if (!ci->mgcp_client_fi){ | ||||
| 			LOG_CI_VERB(ci, LOGL_ERROR, "Cannot send\n"); | ||||
| 			on_failure(ci); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 		osmo_fsm_inst_update_id(ci->mgcp_client_fi, ci->label); | ||||
| 		break; | ||||
|  | ||||
| 	case MGCP_VERB_MDCX: | ||||
| 		OSMO_ASSERT(ci->mgcp_client_fi); | ||||
| 		LOG_CI_VERB(ci, LOGL_DEBUG, "Sending\n"); | ||||
| 		rc = mgcp_conn_modify(ci->mgcp_client_fi, CI_EV_SUCCESS(ci), &ci->verb_info); | ||||
| 		ci->sent = true; | ||||
| 		if (rc) { | ||||
| 			LOG_CI_VERB(ci, LOGL_ERROR, "Cannot send (rc=%d %s)\n", rc, strerror(-rc)); | ||||
| 			on_failure(ci); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 		break; | ||||
|  | ||||
| 	case MGCP_VERB_DLCX: | ||||
| 		LOG_CI(ci, LOGL_DEBUG, "Sending MGCP: %s %s\n", | ||||
| 		       osmo_mgcp_verb_name(ci->verb), ci->mgcp_ci_str); | ||||
| 		/* The way this is designed, we actually need to forget all about the ci right away. */ | ||||
| 		mgcp_conn_delete(ci->mgcp_client_fi); | ||||
| 		notify = ci->notify; | ||||
| 		*ci = (struct osmo_mgcpc_ep_ci){ | ||||
| 			.ep = ep, | ||||
| 		}; | ||||
| 		/* When dispatching an event for this CI, the user may decide to trigger the next request for this conn | ||||
| 		 * right away. So we must be ready with a cleared *ci. */ | ||||
| 		if (notify.fi) | ||||
| 			osmo_fsm_inst_dispatch(notify.fi, notify.success, notify.data); | ||||
| 		break; | ||||
|  | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /*! DLCX all connections, terminate the endpoint FSM and free. */ | ||||
| void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep) | ||||
| { | ||||
| 	if (!ep) | ||||
| 		return; | ||||
| 	osmo_mgcpc_ep_cancel_notify(ep, NULL); | ||||
| 	osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_REGULAR, 0); | ||||
| } | ||||
|  | ||||
| static void osmo_mgcpc_ep_count(struct osmo_mgcpc_ep *ep, int *occupied, int *pending_not_sent, | ||||
| 				int *waiting_for_response) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	if (occupied) | ||||
| 		*occupied = 0; | ||||
|  | ||||
| 	if (pending_not_sent) | ||||
| 		*pending_not_sent = 0; | ||||
|  | ||||
| 	if (waiting_for_response) | ||||
| 		*waiting_for_response = 0; | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) { | ||||
| 		struct osmo_mgcpc_ep_ci *ci = &ep->ci[i]; | ||||
| 		if (ci->occupied) { | ||||
| 			if (occupied) | ||||
| 				(*occupied)++; | ||||
| 		} else | ||||
| 			continue; | ||||
|  | ||||
| 		if (ci->pending) | ||||
| 			LOG_CI_VERB(ci, LOGL_DEBUG, "%s\n", | ||||
| 				    ci->sent ? "waiting for response" : "waiting to be sent"); | ||||
| 		else | ||||
| 			LOG_CI_VERB(ci, LOGL_DEBUG, "done (%s)\n", mgcp_conn_peer_name(osmo_mgcpc_ep_ci_get_rtp_info(ci))); | ||||
|  | ||||
| 		if (ci->pending && ci->sent) | ||||
| 			if (waiting_for_response) | ||||
| 				(*waiting_for_response)++; | ||||
| 		if (ci->pending && !ci->sent) | ||||
| 			if (pending_not_sent) | ||||
| 				(*pending_not_sent)++; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static bool osmo_mgcpc_ep_fsm_check_state_chg_after_response(struct osmo_fsm_inst *fi) | ||||
| { | ||||
| 	int waiting_for_response; | ||||
| 	int occupied; | ||||
| 	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi); | ||||
|  | ||||
| 	osmo_mgcpc_ep_count(ep, &occupied, NULL, &waiting_for_response); | ||||
| 	LOG_MGCPC_EP(ep, LOGL_DEBUG, "CI in use: %d, waiting for response: %d\n", occupied, waiting_for_response); | ||||
|  | ||||
| 	if (!occupied)  { | ||||
| 		/* All CI have been released. The endpoint no longer exists. Notify the parent FSM, by | ||||
| 		 * terminating. */ | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, 0); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if (!waiting_for_response) { | ||||
| 		if (fi->state != OSMO_MGCPC_EP_ST_IN_USE) | ||||
| 			osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_IN_USE); | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| static void osmo_mgcpc_ep_fsm_wait_mgw_response_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) | ||||
| { | ||||
| 	static int re_enter = 0; | ||||
| 	int rc; | ||||
| 	int count = 0; | ||||
| 	int i; | ||||
| 	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi); | ||||
|  | ||||
| 	re_enter++; | ||||
| 	OSMO_ASSERT(re_enter < 10); | ||||
|  | ||||
| 	/* The first CRCX gives us the endpoint name in the CRCX response. So we must wait for the first CRCX endpoint | ||||
| 	 * response to come in before sending any other MGCP requests -- otherwise we might end up creating new | ||||
| 	 * endpoints instead of acting on the same. This FSM always sends out N requests and waits for all of them to | ||||
| 	 * complete before sending out new requests. Hence we're safe when the very first time at most one request is | ||||
| 	 * sent (which needs to be a CRCX). */ | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) { | ||||
| 		struct osmo_mgcpc_ep_ci *ci = &ep->ci[i]; | ||||
|  | ||||
| 		/* Make sure that only CRCX get dispatched if no CRCX were sent yet. */ | ||||
| 		if (!ep->first_crcx_complete) { | ||||
| 			if (ci->occupied && ci->verb != MGCP_VERB_CRCX) | ||||
| 				continue; | ||||
| 		} | ||||
|  | ||||
| 		rc = send_verb(&ep->ci[i]); | ||||
| 		/* Need to be careful not to access the instance after failure. Event chains may already have | ||||
| 		 * deallocated this memory. */ | ||||
| 		if (rc < 0) | ||||
| 			return; | ||||
| 		if (!rc) | ||||
| 			continue; | ||||
| 		count++; | ||||
| 		/* Make sure that we wait for the first CRCX response before dispatching more requests. */ | ||||
| 		if (!ep->first_crcx_complete) | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	LOG_MGCPC_EP(ep, LOGL_DEBUG, "Sent messages: %d\n", count); | ||||
| 	if (ep->first_crcx_complete) | ||||
| 		osmo_mgcpc_ep_fsm_check_state_chg_after_response(fi); | ||||
| 	re_enter--; | ||||
| } | ||||
|  | ||||
| static void osmo_mgcpc_ep_fsm_handle_ci_events(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||
| { | ||||
| 	struct osmo_mgcpc_ep_ci *ci; | ||||
| 	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi); | ||||
| 	ci = osmo_mgcpc_ep_ci_for_event(ep, event); | ||||
| 	if (ci) { | ||||
| 		if (event == CI_EV_SUCCESS(ci)) | ||||
| 			on_success(ci, data); | ||||
| 		else | ||||
| 			on_failure(ci); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void osmo_mgcpc_ep_fsm_in_use_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) | ||||
| { | ||||
| 	int pending_not_sent; | ||||
| 	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi); | ||||
|  | ||||
| 	osmo_mgcpc_ep_count(ep, NULL, &pending_not_sent, NULL); | ||||
| 	if (pending_not_sent) | ||||
| 		osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE); | ||||
| } | ||||
|  | ||||
| #define S(x)	(1 << (x)) | ||||
|  | ||||
| static const struct osmo_fsm_state osmo_mgcpc_ep_fsm_states[] = { | ||||
| 	[OSMO_MGCPC_EP_ST_UNUSED] = { | ||||
| 		.name = "UNUSED", | ||||
| 		.in_event_mask = 0, | ||||
| 		.out_state_mask = 0 | ||||
| 			| S(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE) | ||||
| 			, | ||||
| 	}, | ||||
| 	[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = { | ||||
| 		.name = "WAIT_MGW_RESPONSE", | ||||
| 		.onenter = osmo_mgcpc_ep_fsm_wait_mgw_response_onenter, | ||||
| 		.action = osmo_mgcpc_ep_fsm_handle_ci_events, | ||||
| 		.in_event_mask = 0xffffffff, | ||||
| 		.out_state_mask = 0 | ||||
| 			| S(OSMO_MGCPC_EP_ST_IN_USE) | ||||
| 			, | ||||
| 	}, | ||||
| 	[OSMO_MGCPC_EP_ST_IN_USE] = { | ||||
| 		.name = "IN_USE", | ||||
| 		.onenter = osmo_mgcpc_ep_fsm_in_use_onenter, | ||||
| 		.action = osmo_mgcpc_ep_fsm_handle_ci_events, | ||||
| 		.in_event_mask = 0xffffffff, /* mgcp_client_fsm may send parent term anytime */ | ||||
| 		.out_state_mask = 0 | ||||
| 			| S(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE) | ||||
| 			, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi) | ||||
| { | ||||
| 	int i; | ||||
| 	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi); | ||||
|  | ||||
| 	switch (fi->T) { | ||||
| 	default: | ||||
| 		for (i = 0; i < ARRAY_SIZE(ep->ci); i++) { | ||||
| 			struct osmo_mgcpc_ep_ci *ci = &ep->ci[i]; | ||||
| 			if (!ci->occupied) | ||||
| 				continue; | ||||
| 			if (!(ci->pending && ci->sent)) | ||||
| 				continue; | ||||
| 			on_failure(ci); | ||||
| 		} | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct osmo_fsm osmo_mgcpc_ep_fsm = { | ||||
| 	.name = "mgw-endp", | ||||
| 	.states = osmo_mgcpc_ep_fsm_states, | ||||
| 	.num_states = ARRAY_SIZE(osmo_mgcpc_ep_fsm_states), | ||||
| 	.log_subsys = DLMGCP, | ||||
| 	.event_names = osmo_mgcpc_ep_fsm_event_names, | ||||
| 	.timer_cb = osmo_mgcpc_ep_fsm_timer_cb, | ||||
| 	/* The FSM termination will automatically trigger any mgcp_client_fsm instances to DLCX. */ | ||||
| }; | ||||
							
								
								
									
										748
									
								
								src/libosmo-mgcp-client/mgcp_client_fsm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										748
									
								
								src/libosmo-mgcp-client/mgcp_client_fsm.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,748 @@ | ||||
| /* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Philipp Maier | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <osmocom/mgcp_client/mgcp_client.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_fsm.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/fsm.h> | ||||
| #include <osmocom/core/byteswap.h> | ||||
| #include <arpa/inet.h> | ||||
| #include <osmocom/core/logging.h> | ||||
|  | ||||
| /* Context information, this is attached to the priv pointer of the FSM and | ||||
|  * is also handed back when dispatcheing events to the parent FSM. This is | ||||
|  * purly intened and not meant to be accessible for the API user */ | ||||
| struct mgcp_ctx { | ||||
| 	/* MGCP client instance that is used to interact with the MGW */ | ||||
| 	struct mgcp_client *mgcp; | ||||
|  | ||||
| 	/* The ID of the last pending transaction. This is used internally | ||||
| 	 * to cancel the transaction in case of an error */ | ||||
| 	mgcp_trans_id_t mgw_pending_trans; | ||||
|  | ||||
| 	/* Flag to mark that there is a pending transaction */ | ||||
| 	bool mgw_trans_pending; | ||||
|  | ||||
| 	/* Connection ID which has been assigned by he MGW */ | ||||
| 	char conn_id[MGCP_CONN_ID_MAXLEN]; | ||||
|  | ||||
| 	/* Local RTP connection info, the MGW will send outgoing traffic to the | ||||
| 	 * ip/port specified here. The Address does not have to be choosen right | ||||
| 	 * on the creation of a connection. It can always be modified later by | ||||
| 	 * the user. */ | ||||
| 	struct mgcp_conn_peer conn_peer_local; | ||||
|  | ||||
| 	/* Remote RTP connection info, the ip/port specified here is the address | ||||
| 	 * where the MGW expects the RTP data to be sent. This address is | ||||
| 	 * defined by soly by the MGW and can not be influenced by the user. */ | ||||
| 	struct mgcp_conn_peer conn_peer_remote; | ||||
|  | ||||
| 	/* The terminate flag is a way to handle cornercase sitations that | ||||
| 	 * might occur when the user runs into an error situation and sends | ||||
| 	 * a DLCX command while the FSM is waiting for a response. In this | ||||
| 	 * case the DLCX command is not executed immediately. Instead the | ||||
| 	 * terminate flag is set. When the response to from the previous | ||||
| 	 * operation is received, we know that there is a DLCX event is | ||||
| 	 * pending. The FSM then generates the EV_DLCX by itsself before | ||||
| 	 * it enters ST_READY to cause the immediate execution of the | ||||
| 	 * DLCX procedure. (If normal operations are executed too fast, | ||||
| 	 * the API functions will return an error. In general, the user | ||||
| 	 * should synchronize using the callback events) */ | ||||
| 	bool terminate; | ||||
|  | ||||
| 	/* Event that is sent when the current operation is completed (except | ||||
| 	 * for DLCX, there the specified parent_term_evt is sent instead) */ | ||||
| 	uint32_t parent_evt; | ||||
| }; | ||||
|  | ||||
| #define S(x)	(1 << (x)) | ||||
|  | ||||
| #define MGCP_MGW_TIMEOUT 4	/* in seconds */ | ||||
| #define MGCP_MGW_TIMEOUT_TIMER_NR 1 | ||||
|  | ||||
| enum fsm_mgcp_client_states { | ||||
| 	ST_CRCX, | ||||
| 	ST_CRCX_RESP, | ||||
| 	ST_READY, | ||||
| 	ST_MDCX_RESP, | ||||
| 	ST_DLCX_RESP, | ||||
| }; | ||||
|  | ||||
| enum fsm_mgcp_client_evt { | ||||
| 	EV_CRCX, | ||||
| 	EV_CRCX_RESP, | ||||
| 	EV_MDCX, | ||||
| 	EV_MDCX_RESP, | ||||
| 	EV_DLCX, | ||||
| 	EV_DLCX_RESP, | ||||
| }; | ||||
|  | ||||
| static const struct value_string fsm_mgcp_client_evt_names[] = { | ||||
| 	OSMO_VALUE_STRING(EV_CRCX), | ||||
| 	OSMO_VALUE_STRING(EV_CRCX_RESP), | ||||
| 	OSMO_VALUE_STRING(EV_MDCX), | ||||
| 	OSMO_VALUE_STRING(EV_MDCX_RESP), | ||||
| 	OSMO_VALUE_STRING(EV_DLCX), | ||||
| 	OSMO_VALUE_STRING(EV_DLCX_RESP), | ||||
| 	{0, NULL} | ||||
| }; | ||||
|  | ||||
| static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info) | ||||
| { | ||||
| 	*mgcp_msg = (struct mgcp_msg) { | ||||
| 		.verb = MGCP_VERB_CRCX, | ||||
| 		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | ||||
| 			     | MGCP_MSG_PRESENCE_CONN_MODE), | ||||
| 		.call_id = 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)); | ||||
|  | ||||
| 	if (info->x_osmo_ign) { | ||||
| 		mgcp_msg->x_osmo_ign = info->x_osmo_ign; | ||||
| 		mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_IGN; | ||||
| 	} | ||||
|  | ||||
| 	if (info->x_osmo_osmux_use) { | ||||
| 		mgcp_msg->x_osmo_osmux_cid = info->x_osmo_osmux_cid; | ||||
| 		mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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; | ||||
| 	mgcp_msg->audio_ip = info->addr; | ||||
| 	mgcp_msg->audio_port = info->port; | ||||
| 	mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND; | ||||
| } | ||||
|  | ||||
| static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer) | ||||
| { | ||||
| 	enum mgcp_connection_mode conn_mode = peer->conn_mode; | ||||
| 	if (conn_mode != MGCP_CONN_NONE) | ||||
| 		mgcp_msg->conn_mode = conn_mode; | ||||
| } | ||||
|  | ||||
| static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx) | ||||
| { | ||||
| 	struct mgcp_msg mgcp_msg; | ||||
|  | ||||
| 	mgcp_msg = (struct mgcp_msg) { | ||||
| 		.verb = MGCP_VERB_MDCX, | ||||
| 		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID | | ||||
| 			     MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT), | ||||
| 		.call_id =  mgcp_ctx->conn_peer_remote.call_id, | ||||
| 		.conn_id = mgcp_ctx->conn_id, | ||||
| 		.conn_mode = MGCP_CONN_RECV_SEND, | ||||
| 		.audio_ip = mgcp_ctx->conn_peer_local.addr, | ||||
| 		.audio_port = mgcp_ctx->conn_peer_local.port, | ||||
| 		.ptime = mgcp_ctx->conn_peer_local.ptime, | ||||
| 		.codecs_len = mgcp_ctx->conn_peer_local.codecs_len, | ||||
| 		.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)); | ||||
|  | ||||
| 	set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local); | ||||
|  | ||||
| 	if (mgcp_ctx->conn_peer_local.x_osmo_osmux_use) { | ||||
| 		mgcp_msg.x_osmo_osmux_cid = mgcp_ctx->conn_peer_local.x_osmo_osmux_cid; | ||||
| 		mgcp_msg.presence |= MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID; | ||||
| 	} | ||||
|  | ||||
| 	/* Note: We take the endpoint and the call_id from the remote | ||||
| 	 * connection info, because we can be confident that the | ||||
| 	 * information there is valid. For the local info, we explicitly | ||||
| 	 * allow endpoint and call_id to be optional */ | ||||
| 	return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); | ||||
| } | ||||
|  | ||||
| struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx) | ||||
| { | ||||
| 	struct mgcp_msg mgcp_msg; | ||||
|  | ||||
| 	mgcp_msg = (struct mgcp_msg) { | ||||
| 		.verb = MGCP_VERB_DLCX, | ||||
| 		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID), | ||||
| 		.call_id = mgcp_ctx->conn_peer_remote.call_id, | ||||
| 		.conn_id = mgcp_ctx->conn_id, | ||||
| 	}; | ||||
| 	osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN); | ||||
|  | ||||
| 	return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); | ||||
| } | ||||
|  | ||||
| static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv); | ||||
|  | ||||
| static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = data; | ||||
| 	struct mgcp_client *mgcp; | ||||
| 	struct mgcp_msg mgcp_msg; | ||||
| 	struct msgb *msg; | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
| 	mgcp = mgcp_ctx->mgcp; | ||||
| 	OSMO_ASSERT(mgcp); | ||||
|  | ||||
| 	switch (event) { | ||||
| 	case EV_CRCX: | ||||
| 		LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: creating connection on MGW endpoint:%s...\n", | ||||
| 			 mgcp_ctx->conn_peer_local.endpoint); | ||||
|  | ||||
| 		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); | ||||
|  | ||||
| 		mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); | ||||
| 		mgcp_ctx->mgw_trans_pending = true; | ||||
| 		rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi); | ||||
| 		if (rc < 0) { | ||||
| 			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		osmo_fsm_inst_state_chg(fi, ST_CRCX_RESP, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); | ||||
| 		break; | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Return the CI that the MGW allocated during CRCX response. This is purely informational for logging | ||||
|  * and identity tracking; the mgcp_conn_*() functions take care of using the right CI internally. */ | ||||
| const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||
| 	return mgcp_ctx->conn_id; | ||||
| } | ||||
|  | ||||
| static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv) | ||||
| { | ||||
| 	struct osmo_fsm_inst *fi = priv; | ||||
| 	struct mgcp_ctx *mgcp_ctx; | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	mgcp_ctx = fi->priv; | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	mgcp_ctx->mgw_trans_pending = false; | ||||
|  | ||||
| 	if (r->head.response_code != 200) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, | ||||
| 			 "MGW/CRCX: response yields error: %d %s\n", r->head.response_code, r->head.comment); | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	osmo_strlcpy(mgcp_ctx->conn_id, r->head.conn_id, sizeof(mgcp_ctx->conn_id)); | ||||
| 	LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with CI: %s\n", mgcp_ctx->conn_id); | ||||
|  | ||||
| 	rc = mgcp_response_parse_params(r); | ||||
| 	if (rc) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: Cannot parse CRCX response\n"); | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
| 	LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port); | ||||
| 	if (r->head.x_osmo_osmux_use) { | ||||
| 		LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded using Osmux %u\n", r->head.x_osmo_osmux_cid); | ||||
| 		mgcp_ctx->conn_peer_remote.x_osmo_osmux_use = true; | ||||
| 		mgcp_ctx->conn_peer_remote.x_osmo_osmux_cid = r->head.x_osmo_osmux_cid; | ||||
| 	} | ||||
|  | ||||
| 	osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr)); | ||||
| 	mgcp_ctx->conn_peer_remote.port = r->audio_port; | ||||
|  | ||||
| 	if (strlen(r->head.endpoint) > 0) { | ||||
| 		/* If we get an endpoint identifier back from the MGW, take it */ | ||||
| 		osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, r->head.endpoint, | ||||
| 			     sizeof(mgcp_ctx->conn_peer_remote.endpoint)); | ||||
| 	} else if (strstr(mgcp_ctx->conn_peer_local.endpoint, "*") == NULL) { | ||||
| 		/* If we do not get an endpoint identifier back and the | ||||
| 		 * identifier we used to create the connection is not a | ||||
| 		 * wildcarded one, we take the local endpoint identifier | ||||
| 		 * instead */ | ||||
| 		osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, mgcp_ctx->conn_peer_local.endpoint, | ||||
| 			     sizeof(mgcp_ctx->conn_peer_local.endpoint)); | ||||
| 	} else { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: CRCX yielded not suitable endpoint identifier\n"); | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	mgcp_ctx->conn_peer_remote.call_id = mgcp_ctx->conn_peer_local.call_id; | ||||
|  | ||||
| 	osmo_fsm_inst_dispatch(fi, EV_CRCX_RESP, mgcp_ctx); | ||||
| } | ||||
|  | ||||
| static void fsm_crcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = data; | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	switch (event) { | ||||
| 	case EV_CRCX_RESP: | ||||
| 		osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0); | ||||
| 		if (mgcp_ctx->terminate) { | ||||
| 			/* Trigger immediate DLCX if DLCX was requested while the FSM was | ||||
| 			 * busy with the previous operation */ | ||||
| 			LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: FSM was busy while DLCX was requested, executing now...\n"); | ||||
| 			osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx); | ||||
| 		} else | ||||
| 			osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote); | ||||
| 		break; | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv); | ||||
| static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv); | ||||
|  | ||||
| static void fsm_ready_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = data; | ||||
| 	struct msgb *msg; | ||||
| 	struct mgcp_client *mgcp; | ||||
| 	uint32_t new_state; | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
| 	mgcp = mgcp_ctx->mgcp; | ||||
| 	OSMO_ASSERT(mgcp); | ||||
|  | ||||
| 	switch (event) { | ||||
| 	case EV_MDCX: | ||||
| 		msg = make_mdcx_msg(mgcp_ctx); | ||||
| 		OSMO_ASSERT(msg); | ||||
| 		rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_resp_cb, fi); | ||||
| 		new_state = ST_MDCX_RESP; | ||||
| 		break; | ||||
| 	case EV_DLCX: | ||||
| 		msg = make_dlcx_msg(mgcp_ctx); | ||||
| 		OSMO_ASSERT(msg); | ||||
| 		rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_resp_cb, fi); | ||||
| 		new_state = ST_DLCX_RESP; | ||||
| 		break; | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); | ||||
| 	mgcp_ctx->mgw_trans_pending = true; | ||||
|  | ||||
| 	if (rc < 0) { | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	osmo_fsm_inst_state_chg(fi, new_state, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); | ||||
| } | ||||
|  | ||||
| static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv) | ||||
| { | ||||
| 	struct osmo_fsm_inst *fi = priv; | ||||
| 	struct mgcp_ctx *mgcp_ctx; | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	mgcp_ctx = fi->priv; | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	mgcp_ctx->mgw_trans_pending = false; | ||||
|  | ||||
| 	if (r->head.response_code != 200) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: response yields error: %d %s\n", r->head.response_code, | ||||
| 			 r->head.comment); | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	rc = mgcp_response_parse_params(r); | ||||
| 	if (rc) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: Cannot parse MDCX response\n"); | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
| 	LOGPFSML(fi, LOGL_DEBUG, "MGW/MDCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port); | ||||
|  | ||||
| 	if (r->head.x_osmo_osmux_use) { | ||||
| 		LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded using Osmux %u\n", r->head.x_osmo_osmux_cid); | ||||
| 		mgcp_ctx->conn_peer_remote.x_osmo_osmux_use = true; | ||||
| 		mgcp_ctx->conn_peer_remote.x_osmo_osmux_cid = r->head.x_osmo_osmux_cid; | ||||
| 	} | ||||
|  | ||||
| 	osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr)); | ||||
| 	mgcp_ctx->conn_peer_remote.port = r->audio_port; | ||||
|  | ||||
| 	osmo_fsm_inst_dispatch(fi, EV_MDCX_RESP, mgcp_ctx); | ||||
| } | ||||
|  | ||||
| static void fsm_mdcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = data; | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	switch (event) { | ||||
| 	case EV_MDCX_RESP: | ||||
| 		osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0); | ||||
| 		if (mgcp_ctx->terminate) { | ||||
| 			/* Trigger immediate DLCX if DLCX was requested while the FSM was | ||||
| 			 * busy with the previous operation */ | ||||
| 			LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: FSM was busy while DLCX was requested, executing now...\n"); | ||||
| 			osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx); | ||||
| 		} else | ||||
| 			osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote); | ||||
| 		break; | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv) | ||||
| { | ||||
| 	struct osmo_fsm_inst *fi = priv; | ||||
| 	struct mgcp_ctx *mgcp_ctx; | ||||
|  | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	mgcp_ctx = fi->priv; | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	mgcp_ctx->mgw_trans_pending = false; | ||||
|  | ||||
| 	if (r->head.response_code != 250) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, | ||||
| 			 "MGW/DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment); | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	osmo_fsm_inst_dispatch(fi, EV_DLCX_RESP, mgcp_ctx); | ||||
| } | ||||
|  | ||||
| static void fsm_dlcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = data; | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	switch (event) { | ||||
| 	case EV_DLCX_RESP: | ||||
| 		/* Rub out the connection identifier, since the connection | ||||
| 		 * is no longer present and we will use the connection id | ||||
| 		 * to know in error cases if the connection is still present | ||||
| 		 * or not */ | ||||
| 		memset(mgcp_ctx->conn_id, 0, sizeof(mgcp_ctx->conn_id)); | ||||
|  | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); | ||||
| 		break; | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int fsm_timeout_cb(struct osmo_fsm_inst *fi) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||
| 	struct mgcp_client *mgcp; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
| 	mgcp = mgcp_ctx->mgcp; | ||||
| 	OSMO_ASSERT(mgcp); | ||||
|  | ||||
| 	if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) { | ||||
| 		/* Note: We were unable to communicate with the MGW, | ||||
| 		 * unfortunately there is no meaningful action we can take | ||||
| 		 * now other than giving up. */ | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); | ||||
| 	} else { | ||||
| 		/* Note: Ther must not be any unsolicited timers | ||||
| 		 * in this FSM. If so, we have serious problem. */ | ||||
| 		OSMO_ASSERT(false); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||
| 	struct mgcp_client *mgcp; | ||||
| 	struct msgb *msg; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
| 	mgcp = mgcp_ctx->mgcp; | ||||
| 	OSMO_ASSERT(mgcp); | ||||
|  | ||||
| 	/* If there is still a transaction pending, cancel it now. */ | ||||
| 	if (mgcp_ctx->mgw_trans_pending) | ||||
| 		mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans); | ||||
|  | ||||
| 	/* Should the FSM be terminated while there are still open connections | ||||
| 	 * on the MGW, we send an unconditional DLCX to terminate the | ||||
| 	 * connection. This is not the normal case. The user should always use | ||||
| 	 * mgcp_conn_delete() to instruct the FSM to perform a graceful exit */ | ||||
| 	if (strlen(mgcp_ctx->conn_id)) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, | ||||
| 			 "MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n"); | ||||
| 		msg = make_dlcx_msg(mgcp_ctx); | ||||
| 		if (!msg) | ||||
| 			LOGPFSML(fi, LOGL_ERROR, "MGW/DLCX: Error composing DLCX message\n"); | ||||
| 		else | ||||
| 			mgcp_client_tx(mgcp, msg, NULL, NULL); | ||||
| 	} | ||||
|  | ||||
| 	talloc_free(mgcp_ctx); | ||||
| } | ||||
|  | ||||
| static struct osmo_fsm_state fsm_mgcp_client_states[] = { | ||||
|  | ||||
| 	/* Initial CRCX state. This state is immediately entered and executed | ||||
| 	 * when the FSM is started. The rationale is that we first have to | ||||
| 	 * create a connectin before we can execute other operations on that | ||||
| 	 * connection. */ | ||||
| 	[ST_CRCX] = { | ||||
| 		     .in_event_mask = S(EV_CRCX), | ||||
| 		     .out_state_mask = S(ST_CRCX_RESP), | ||||
| 		     .name = OSMO_STRINGIFY(ST_CRCX), | ||||
| 		     .action = fsm_crcx_cb, | ||||
| 		     }, | ||||
|  | ||||
| 	/* Wait for the response to a CRCX operation, check and process the | ||||
| 	 * results, change to ST_READY afterwards. */ | ||||
| 	[ST_CRCX_RESP] = { | ||||
| 			  .in_event_mask = S(EV_CRCX_RESP), | ||||
| 			  .out_state_mask = S(ST_READY), | ||||
| 			  .name = OSMO_STRINGIFY(ST_CRCX_RESP), | ||||
| 			  .action = fsm_crcx_resp_cb, | ||||
| 			  }, | ||||
|  | ||||
| 	/* In this idle state we wait for further operations (e.g. MDCX) that | ||||
| 	 * can be executed by the user using the API. There is no timeout in | ||||
| 	 * this state. The connection lives on until the user decides to | ||||
| 	 * terminate it (DLCX). */ | ||||
| 	[ST_READY] = { | ||||
| 		      .in_event_mask = S(EV_MDCX) | S(EV_DLCX), | ||||
| 		      .out_state_mask = S(ST_MDCX_RESP) | S(ST_DLCX_RESP), | ||||
| 		      .name = OSMO_STRINGIFY(ST_READY), | ||||
| 		      .action = fsm_ready_cb, | ||||
| 		      }, | ||||
|  | ||||
| 	/* Wait for the response of a MDCX operation, check and process the | ||||
| 	 * results, change to ST_READY afterwards. */ | ||||
| 	[ST_MDCX_RESP] = { | ||||
| 			  .in_event_mask = S(EV_MDCX_RESP), | ||||
| 			  .out_state_mask = S(ST_READY), | ||||
| 			  .name = OSMO_STRINGIFY(ST_MDCX_RESP), | ||||
| 			  .action = fsm_mdcx_resp_cb, | ||||
| 			  }, | ||||
|  | ||||
| 	/* Wait for the response of a DLCX operation and terminate the FSM | ||||
| 	 * normally. */ | ||||
| 	[ST_DLCX_RESP] = { | ||||
| 			  .in_event_mask = S(EV_DLCX_RESP), | ||||
| 			  .out_state_mask = 0, | ||||
| 			  .name = OSMO_STRINGIFY(ST_DLCX_RESP), | ||||
| 			  .action = fsm_dlcx_resp_cb, | ||||
| 			  }, | ||||
| }; | ||||
|  | ||||
| static struct osmo_fsm fsm_mgcp_client = { | ||||
| 	.name = "MGCP_CONN", | ||||
| 	.states = fsm_mgcp_client_states, | ||||
| 	.num_states = ARRAY_SIZE(fsm_mgcp_client_states), | ||||
| 	.timer_cb = fsm_timeout_cb, | ||||
| 	.cleanup = fsm_cleanup_cb, | ||||
| 	.event_names = fsm_mgcp_client_evt_names, | ||||
| }; | ||||
|  | ||||
| /*! allocate FSM, and create a new connection on the MGW. | ||||
|  *  \param[in] mgcp MGCP client descriptor. | ||||
|  *  \param[in] parent_fi Parent FSM instance. | ||||
|  *  \param[in] parent_term_evt Event to be sent to parent when terminating. | ||||
|  *  \param[in] parent_evt Event to be sent to parent when operation is done. | ||||
|  *  \param[in] conn_peer Connection parameters (ip, port...). | ||||
|  *  \returns newly-allocated, initialized and registered FSM instance, NULL on error. */ | ||||
| struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, | ||||
| 				       uint32_t parent_term_evt, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx; | ||||
| 	struct osmo_fsm_inst *fi; | ||||
| 	struct in_addr ip_test; | ||||
|  | ||||
| 	OSMO_ASSERT(parent_fi); | ||||
| 	OSMO_ASSERT(mgcp); | ||||
| 	OSMO_ASSERT(conn_peer); | ||||
|  | ||||
| 	/* Check if IP/Port information in conn info makes sense */ | ||||
| 	if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0) | ||||
| 		return NULL; | ||||
|  | ||||
| 	/* Allocate and configure a new fsm instance */ | ||||
| 	fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt); | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	mgcp_ctx = talloc_zero(fi, struct mgcp_ctx); | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
| 	mgcp_ctx->mgcp = mgcp; | ||||
| 	mgcp_ctx->parent_evt = parent_evt; | ||||
|  | ||||
| 	memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local)); | ||||
| 	fi->priv = mgcp_ctx; | ||||
|  | ||||
| 	/* start state machine */ | ||||
| 	OSMO_ASSERT(fi->state == ST_CRCX); | ||||
| 	osmo_fsm_inst_dispatch(fi, EV_CRCX, mgcp_ctx); | ||||
|  | ||||
| 	return fi; | ||||
| } | ||||
|  | ||||
| /*! modify an existing connection on the MGW. | ||||
|  *  \param[in] fi FSM instance. | ||||
|  *  \param[in] parent_evt Event to be sent to parent when operation is done. | ||||
|  *  \param[in] conn_peer New connection information (ip, port...). | ||||
|  *  \returns 0 on success, -EINVAL on error. */ | ||||
| int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer) | ||||
| { | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||
| 	struct in_addr ip_test; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
| 	OSMO_ASSERT(conn_peer); | ||||
|  | ||||
| 	/* The user must not issue an MDCX before the CRCX has completed, | ||||
| 	 * if this happens, it means that the parent FSM has overhead the | ||||
| 	 * parent_evt (mandatory!) and executed the MDCX without even | ||||
| 	 * waiting for the results. Another reason could be that the | ||||
| 	 * parent FSM got messed up */ | ||||
| 	OSMO_ASSERT(fi->state != ST_CRCX_RESP); | ||||
|  | ||||
| 	/* If the user tries to issue an MDCX while an DLCX operation is | ||||
| 	 * pending, there must be a serious problem with the paren FSM. | ||||
| 	 * Eeither the parent_term_evt (mandatory!) has been overheard, | ||||
| 	 * or the parant FSM got messed so badly that it still assumes | ||||
| 	 * a live connection although it as killed it. */ | ||||
| 	OSMO_ASSERT(fi->state != ST_DLCX_RESP); | ||||
|  | ||||
| 	/* Check if IP/Port parameters make sense */ | ||||
| 	if (conn_peer->port == 0) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	if (inet_aton(conn_peer->addr, &ip_test) == 0) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/*! The user may supply an endpoint identifier in conn_peer. The | ||||
| 	 *  identifier is then checked. This check is optional. Later steps do | ||||
| 	 *  not depend on the endpoint identifier supplied here because it is | ||||
| 	 *  already implicitly known from the CRCX phase. */ | ||||
| 	if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint)) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, endpoint mismatches: requested %s, should be %s\n", | ||||
| 			 conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/*! Note: The call-id is implicitly known from the previous CRCX and | ||||
| 	 *  will not be checked even when it is set in conn_peer. */ | ||||
|  | ||||
| 	mgcp_ctx->parent_evt = parent_evt; | ||||
| 	memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local)); | ||||
| 	osmo_fsm_inst_dispatch(fi, EV_MDCX, mgcp_ctx); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! delete existing connection on the MGW, destroy FSM afterwards. | ||||
|  *  \param[in] fi FSM instance. */ | ||||
| void mgcp_conn_delete(struct osmo_fsm_inst *fi) | ||||
| { | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	if (fi->proc.terminating) | ||||
| 		return; | ||||
|  | ||||
| 	/* Unlink FSM from parent */ | ||||
| 	osmo_fsm_inst_unlink_parent(fi, NULL); | ||||
|  | ||||
| 	/* An error situation where the parent FSM must be killed immediately | ||||
| 	 * may lead into a situation where the DLCX can not be executed right | ||||
| 	 * at that moment because the FSM is still busy with another operation. | ||||
| 	 * In those cases we postpone the DLCX so that the FSM and the | ||||
| 	 * connections on the MGW get cleaned up gracefully. */ | ||||
| 	if (fi->state != ST_READY) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "MGW: operation still pending, DLCX will be postponed.\n"); | ||||
| 		mgcp_ctx->terminate = true; | ||||
| 		return; | ||||
| 	} | ||||
| 	osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx); | ||||
| } | ||||
|  | ||||
| const char *osmo_mgcpc_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 | ||||
| 	 * this buffer is too small. */ | ||||
| 	static char buf[1024]; | ||||
|  | ||||
| 	if (!info) | ||||
| 		return "NULL"; | ||||
|  | ||||
| 	if (info->endpoint[0] | ||||
| 	    && info->addr[0]) | ||||
| 		snprintf(buf, sizeof(buf), "%s:%s:%u", | ||||
| 			 info->endpoint, info->addr, info->port); | ||||
| 	else if (info->endpoint[0]) | ||||
| 		snprintf(buf, sizeof(buf), "%s", info->endpoint); | ||||
| 	else if (info->addr[0]) | ||||
| 		snprintf(buf, sizeof(buf), "%s:%u", info->addr, info->port); | ||||
| 	else | ||||
| 		return "empty"; | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| static __attribute__((constructor)) void osmo_mgcp_client_fsm_init() | ||||
| { | ||||
| 	OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0); | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| /* MGCPGW client interface to quagga VTY */ | ||||
| /* MGCP client interface to quagga VTY */ | ||||
| /* (C) 2016 by sysmocom s.m.f.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> | ||||
| @@ -6,16 +6,16 @@ | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| @@ -25,32 +25,37 @@ | ||||
| #include <talloc.h> | ||||
|  | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/vty/misc.h> | ||||
| #include <osmocom/core/utils.h> | ||||
|  | ||||
| #include <osmocom/legacy_mgcp/vty.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client.h> | ||||
|  | ||||
| #define MGCPGW_STR "MGCP gateway configuration for RTP streams\n" | ||||
| #define MGW_STR MGCP_CLIENT_MGW_STR | ||||
|  | ||||
| void *global_mgcp_client_ctx = NULL; | ||||
| struct mgcp_client_conf *global_mgcp_client_conf = NULL; | ||||
|  | ||||
| DEFUN(cfg_mgcpgw_local_ip, cfg_mgcpgw_local_ip_cmd, | ||||
|       "mgcpgw local-ip A.B.C.D", | ||||
|       MGCPGW_STR "local bind to connect to MGCP gateway with\n" | ||||
| DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd, | ||||
|       "mgw local-ip A.B.C.D", | ||||
|       MGW_STR "local bind to connect to MGW from\n" | ||||
|       "local bind IP address\n") | ||||
| { | ||||
| 	if (!global_mgcp_client_conf) | ||||
| 		return CMD_ERR_NOTHING_TODO; | ||||
| 	OSMO_ASSERT(global_mgcp_client_ctx); | ||||
| 	global_mgcp_client_conf->local_addr = | ||||
| 		talloc_strdup(global_mgcp_client_ctx, argv[0]); | ||||
| 	osmo_talloc_replace_string(global_mgcp_client_ctx, | ||||
| 				   (char**)&global_mgcp_client_conf->local_addr, | ||||
| 				   argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd, | ||||
| 		 "mgcpgw local-ip A.B.C.D", | ||||
| 		 MGW_STR "local bind to connect to MGCP gateway with\n" | ||||
| 		 "local bind IP address\n") | ||||
|  | ||||
| DEFUN(cfg_mgcpgw_local_port, cfg_mgcpgw_local_port_cmd, | ||||
|       "mgcpgw local-port <0-65535>", | ||||
|       MGCPGW_STR "local bind to connect to MGCP gateway with\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 bind port\n") | ||||
| { | ||||
| 	if (!global_mgcp_client_conf) | ||||
| @@ -58,61 +63,93 @@ DEFUN(cfg_mgcpgw_local_port, cfg_mgcpgw_local_port_cmd, | ||||
| 	global_mgcp_client_conf->local_port = atoi(argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| 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") | ||||
|  | ||||
| DEFUN(cfg_mgcpgw_remote_ip, cfg_mgcpgw_remote_ip_cmd, | ||||
|       "mgcpgw remote-ip A.B.C.D", | ||||
|       MGCPGW_STR "remote bind to connect to MGCP gateway with\n" | ||||
|       "remote bind IP address\n") | ||||
| DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd, | ||||
|       "mgw remote-ip A.B.C.D", | ||||
|       MGW_STR "remote IP address to reach the MGW at\n" | ||||
|       "remote IP address\n") | ||||
| { | ||||
| 	if (!global_mgcp_client_conf) | ||||
| 		return CMD_ERR_NOTHING_TODO; | ||||
| 	OSMO_ASSERT(global_mgcp_client_ctx); | ||||
| 	global_mgcp_client_conf->remote_addr = | ||||
| 		talloc_strdup(global_mgcp_client_ctx, argv[0]); | ||||
| 	osmo_talloc_replace_string(global_mgcp_client_ctx, | ||||
| 				   (char**)&global_mgcp_client_conf->remote_addr, | ||||
| 				   argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd, | ||||
| 		 "mgcpgw remote-ip A.B.C.D", | ||||
| 		 MGW_STR "remote bind to connect to MGCP gateway with\n" | ||||
| 		 "remote bind IP address\n") | ||||
|  | ||||
| DEFUN(cfg_mgcpgw_remote_port, cfg_mgcpgw_remote_port_cmd, | ||||
|       "mgcpgw remote-port <0-65535>", | ||||
|       MGCPGW_STR "remote bind to connect to MGCP gateway with\n" | ||||
|       "remote bind port\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\n") | ||||
| { | ||||
| 	if (!global_mgcp_client_conf) | ||||
| 		return CMD_ERR_NOTHING_TODO; | ||||
| 	global_mgcp_client_conf->remote_port = atoi(argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| 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") | ||||
|  | ||||
| DEFUN(cfg_mgcpgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd, | ||||
|       "mgcpgw endpoint-range <1-65534> <1-65534>", | ||||
|       MGCPGW_STR "usable range of endpoint identifiers\n" | ||||
|       "set first useable endpoint identifier\n" | ||||
|       "set the last useable endpoint identifier\n") | ||||
| DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_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") | ||||
| { | ||||
| 	uint16_t first_endpoint = atoi(argv[0]); | ||||
| 	uint16_t last_endpoint = atoi(argv[1]); | ||||
|  | ||||
| 	if (last_endpoint < first_endpoint) { | ||||
| 		vty_out(vty, "last endpoint must be greater than first endpoint!%s", | ||||
| 			VTY_NEWLINE); | ||||
| 		return CMD_SUCCESS; | ||||
| 	} | ||||
|  | ||||
| 	global_mgcp_client_conf->first_endpoint = first_endpoint; | ||||
| 	global_mgcp_client_conf->last_endpoint = last_endpoint; | ||||
| 	vty_out(vty, "Please do not use legacy config 'mgw endpoint-range'" | ||||
| 		" (the range can no longer be defined by the MGCP client)%s", | ||||
| 		VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd, | ||||
|       "mgcpgw endpoint-range <1-65534> <1-65534>", | ||||
|       MGW_STR "usable range of endpoint identifiers\n" | ||||
|       "set first useable endpoint identifier\n" | ||||
|       "set the last useable endpoint identifier\n") | ||||
|  | ||||
| #define BTS_START_STR "First UDP port allocated for the BTS side\n" | ||||
| #define UDP_PORT_STR "UDP Port number\n" | ||||
| DEFUN(cfg_mgcp_rtp_bts_base_port, | ||||
|       cfg_mgcp_rtp_bts_base_port_cmd, | ||||
| DEFUN_DEPRECATED(cfg_mgw_rtp_bts_base_port, | ||||
|       cfg_mgw_rtp_bts_base_port_cmd, | ||||
|       "mgw bts-base <0-65534>", | ||||
|       MGW_STR | ||||
|       "DEPRECATED: there is no explicit BTS side in current osmo-mgw\n" "-\n") | ||||
| { | ||||
| 	vty_out(vty, "Please do not use legacy config 'mgw bts-base'" | ||||
| 		" (there is no explicit BTS side in an MGW anymore)%s", | ||||
| 		VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port, | ||||
|       cfg_mgcpgw_rtp_bts_base_port_cmd, | ||||
|       "mgcpgw bts-base <0-65534>", | ||||
|       MGCPGW_STR | ||||
|       MGW_STR | ||||
|       BTS_START_STR | ||||
|       UDP_PORT_STR) | ||||
|  | ||||
| DEFUN(cfg_mgw_endpoint_domain_name, | ||||
|       cfg_mgw_endpoint_domain_name_cmd, | ||||
|       "mgw endpoint-domain NAME", | ||||
|       MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n" | ||||
|       "Domain name, should be alphanumeric.\n") | ||||
| { | ||||
| 	global_mgcp_client_conf->bts_base = atoi(argv[0]); | ||||
| 	if (osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0], | ||||
| 			 sizeof(global_mgcp_client_conf->endpoint_domain_name)) | ||||
| 	    >= sizeof(global_mgcp_client_conf->endpoint_domain_name)) { | ||||
| 		vty_out(vty, "%% Error: 'mgw endpoint-domain' name too long, max length is %zu: '%s'%s", | ||||
| 			sizeof(global_mgcp_client_conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -120,40 +157,28 @@ int mgcp_client_config_write(struct vty *vty, const char *indent) | ||||
| { | ||||
| 	const char *addr; | ||||
| 	int port; | ||||
| 	uint16_t first_endpoint; | ||||
| 	uint16_t last_endpoint; | ||||
| 	uint16_t bts_base; | ||||
|  | ||||
| 	addr = global_mgcp_client_conf->local_addr; | ||||
| 	if (addr) | ||||
| 		vty_out(vty, "%smgcpgw local-ip %s%s", indent, addr, | ||||
| 		vty_out(vty, "%smgw local-ip %s%s", indent, addr, | ||||
| 			VTY_NEWLINE); | ||||
| 	port = global_mgcp_client_conf->local_port; | ||||
| 	if (port >= 0) | ||||
| 		vty_out(vty, "%smgcpgw local-port %u%s", indent, | ||||
| 		vty_out(vty, "%smgw local-port %u%s", indent, | ||||
| 			(uint16_t)port, VTY_NEWLINE); | ||||
|  | ||||
| 	addr = global_mgcp_client_conf->remote_addr; | ||||
| 	if (addr) | ||||
| 		vty_out(vty, "%smgcpgw remote-ip %s%s", indent, addr, | ||||
| 		vty_out(vty, "%smgw remote-ip %s%s", indent, addr, | ||||
| 			VTY_NEWLINE); | ||||
| 	port = global_mgcp_client_conf->remote_port; | ||||
| 	if (port >= 0) | ||||
| 		vty_out(vty, "%smgcpgw remote-port %u%s", indent, | ||||
| 		vty_out(vty, "%smgw remote-port %u%s", indent, | ||||
| 			(uint16_t)port, VTY_NEWLINE); | ||||
|  | ||||
| 	first_endpoint = global_mgcp_client_conf->first_endpoint; | ||||
| 	last_endpoint = global_mgcp_client_conf->last_endpoint; | ||||
| 	if (last_endpoint != 0) { | ||||
| 		vty_out(vty, "%smgcpgw endpoint-range %u %u%s", indent, | ||||
| 			first_endpoint, last_endpoint, VTY_NEWLINE); | ||||
| 	} | ||||
|  | ||||
| 	bts_base = global_mgcp_client_conf->bts_base; | ||||
| 	if (bts_base) { | ||||
| 		vty_out(vty, "%smgcpgw bts-base %u%s", indent, | ||||
| 			bts_base, VTY_NEWLINE); | ||||
| 	} | ||||
| 	if (global_mgcp_client_conf->endpoint_domain_name[0]) | ||||
| 		vty_out(vty, "%smgw endpoint-domain %s%s", indent, | ||||
| 			global_mgcp_client_conf->endpoint_domain_name, VTY_NEWLINE); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| @@ -163,10 +188,21 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c | ||||
| 	global_mgcp_client_ctx = talloc_ctx; | ||||
| 	global_mgcp_client_conf = conf; | ||||
|  | ||||
| 	install_element(node, &cfg_mgw_local_ip_cmd); | ||||
| 	install_element(node, &cfg_mgw_local_port_cmd); | ||||
| 	install_element(node, &cfg_mgw_remote_ip_cmd); | ||||
| 	install_element(node, &cfg_mgw_remote_port_cmd); | ||||
| 	install_element(node, &cfg_mgw_endpoint_range_cmd); | ||||
| 	install_element(node, &cfg_mgw_rtp_bts_base_port_cmd); | ||||
| 	install_element(node, &cfg_mgw_endpoint_domain_name_cmd); | ||||
|  | ||||
| 	/* deprecated 'mgcpgw' commands */ | ||||
| 	install_element(node, &cfg_mgcpgw_local_ip_cmd); | ||||
| 	install_element(node, &cfg_mgcpgw_local_port_cmd); | ||||
| 	install_element(node, &cfg_mgcpgw_remote_ip_cmd); | ||||
| 	install_element(node, &cfg_mgcpgw_remote_port_cmd); | ||||
| 	install_element(node, &cfg_mgcpgw_endpoint_range_cmd); | ||||
| 	install_element(node, &cfg_mgcp_rtp_bts_base_port_cmd); | ||||
| 	install_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd); | ||||
|  | ||||
| 	osmo_fsm_vty_add_cmds(); | ||||
| } | ||||
|   | ||||
							
								
								
									
										43
									
								
								src/libosmo-mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/libosmo-mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| AM_CPPFLAGS = \ | ||||
| 	$(all_includes) \ | ||||
| 	-I$(top_srcdir)/include \ | ||||
| 	-I$(top_builddir) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOGSM_CFLAGS) \ | ||||
| 	$(LIBOSMOVTY_CFLAGS) \ | ||||
| 	$(LIBOSMONETIF_CFLAGS) \ | ||||
| 	$(COVERAGE_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	$(COVERAGE_LDFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| noinst_LIBRARIES = \ | ||||
| 	libosmo-mgcp.a \ | ||||
| 	$(NULL) | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	g711common.h \ | ||||
| 	$(NULL) | ||||
|  | ||||
| libosmo_mgcp_a_SOURCES = \ | ||||
| 	mgcp_protocol.c \ | ||||
| 	mgcp_network.c \ | ||||
| 	mgcp_vty.c \ | ||||
| 	mgcp_osmux.c \ | ||||
| 	mgcp_sdp.c \ | ||||
| 	mgcp_codec.c \ | ||||
| 	mgcp_msg.c \ | ||||
| 	mgcp_conn.c \ | ||||
| 	mgcp_stat.c \ | ||||
| 	mgcp_endp.c \ | ||||
| 	$(NULL) | ||||
							
								
								
									
										453
									
								
								src/libosmo-mgcp/mgcp_codec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										453
									
								
								src/libosmo-mgcp/mgcp_codec.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,453 @@ | ||||
| /* | ||||
|  * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2014 by On-Waves | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| /* Helper function to dump codec information of a specified codec to a printable | ||||
|  * string, used by dump_codec_summary() */ | ||||
| static char *dump_codec(struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	static char str[256]; | ||||
| 	char *pt_str; | ||||
|  | ||||
| 	if (codec->payload_type > 76) | ||||
| 		pt_str = "DYNAMIC"; | ||||
| 	else if (codec->payload_type > 72) | ||||
| 		pt_str = "RESERVED <!>"; | ||||
| 	else if (codec->payload_type != PTYPE_UNDEFINED) | ||||
| 		pt_str = codec->subtype_name; | ||||
| 	else | ||||
| 		pt_str = "INVALID <!>"; | ||||
|  | ||||
| 	snprintf(str, sizeof(str), "(pt:%i=%s, audio:%s subt=%s, rate=%u, ch=%i, t=%u/%u)", codec->payload_type, pt_str, | ||||
| 		 codec->audio_name, codec->subtype_name, codec->rate, codec->channels, codec->frame_duration_num, | ||||
| 		 codec->frame_duration_den); | ||||
| 	return str; | ||||
| } | ||||
|  | ||||
| /*! Dump a summary of all negotiated codecs to debug log | ||||
|  *  \param[in] conn related rtp-connection. */ | ||||
| void mgcp_codec_summary(struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	struct mgcp_rtp_end *rtp; | ||||
| 	unsigned int i; | ||||
| 	struct mgcp_rtp_codec *codec; | ||||
| 	struct mgcp_endpoint *endp; | ||||
|  | ||||
| 	rtp = &conn->end; | ||||
| 	endp = conn->conn->endp; | ||||
|  | ||||
| 	if (rtp->codecs_assigned == 0) { | ||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, "conn:%s no codecs available\n", | ||||
| 			 mgcp_conn_dump(conn->conn)); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* Store parsed codec information */ | ||||
| 	for (i = 0; i < rtp->codecs_assigned; i++) { | ||||
| 		codec = &rtp->codecs[i]; | ||||
|  | ||||
| 		LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s codecs[%u]:%s", | ||||
| 			 mgcp_conn_dump(conn->conn), i, dump_codec(codec)); | ||||
|  | ||||
| 		if (codec == rtp->codec) | ||||
| 			LOGPC(DLMGCP, LOGL_DEBUG, " [selected]"); | ||||
|  | ||||
| 		LOGPC(DLMGCP, LOGL_DEBUG, "\n"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Initalize or reset codec information with default data. */ | ||||
| static void codec_init(struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	*codec = (struct mgcp_rtp_codec){ | ||||
| 		.payload_type = -1, | ||||
| 		.frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM, | ||||
| 		.frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN, | ||||
| 		.rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE, | ||||
| 		.channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS, | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| static void codec_free(struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	if (codec->subtype_name) | ||||
| 		talloc_free(codec->subtype_name); | ||||
| 	if (codec->audio_name) | ||||
| 		talloc_free(codec->audio_name); | ||||
| 	*codec = (struct mgcp_rtp_codec){}; | ||||
| } | ||||
|  | ||||
| /*! Initalize or reset codec information with default data. | ||||
|  *  \param[out] conn related rtp-connection. */ | ||||
| void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	int i; | ||||
| 	for (i = 0; i < conn->end.codecs_assigned; i++) | ||||
| 		codec_free(&conn->end.codecs[i]); | ||||
| 	conn->end.codecs_assigned = 0; | ||||
| 	conn->end.codec = NULL; | ||||
| } | ||||
|  | ||||
| /*! Add codec configuration depending on payload type and/or codec name. This | ||||
|  *  function uses the input parameters to extrapolate the full codec information. | ||||
|  *  \param[out] codec configuration (caller provided memory). | ||||
|  *  \param[out] conn related rtp-connection. | ||||
|  *  \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined). | ||||
|  *  \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1"). | ||||
|  *  \param[in] param optional codec parameters (set to NULL when unused). | ||||
|  *  \returns 0 on success, -EINVAL on failure. */ | ||||
| int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param) | ||||
| { | ||||
| 	int rate; | ||||
| 	int channels; | ||||
| 	char audio_codec[64]; | ||||
| 	struct mgcp_rtp_codec *codec; | ||||
| 	unsigned int pt_offset = conn->end.codecs_assigned; | ||||
| 	void *ctx = conn->conn; | ||||
|  | ||||
| 	/* The amount of codecs we can store is limited, make sure we do not | ||||
| 	 * overrun this limit. */ | ||||
| 	if (conn->end.codecs_assigned >= MGCP_MAX_CODECS) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	/* First unused entry */ | ||||
| 	codec = &conn->end.codecs[conn->end.codecs_assigned]; | ||||
|  | ||||
| 	/* Initalize the codec struct with some default data to begin with */ | ||||
| 	codec_init(codec); | ||||
|  | ||||
| 	if (payload_type != PTYPE_UNDEFINED) { | ||||
| 		/* Make sure we do not get any reserved or undefined type numbers */ | ||||
| 		/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */ | ||||
| 		if ((payload_type == 1 || payload_type == 2 || payload_type == 19) | ||||
| 		    || (payload_type >= 72 && payload_type <= 76) | ||||
| 		    || (payload_type >= 127)) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, "Cannot add codec, payload type number %d is reserved\n", | ||||
| 			     payload_type); | ||||
| 			goto error; | ||||
| 		} | ||||
|  | ||||
| 		codec->payload_type = payload_type; | ||||
| 	} | ||||
|  | ||||
| 	/* When no audio name is given, we are forced to use the payload | ||||
| 	 * type to generate the audio name. This is only possible for | ||||
| 	 * non dynamic payload types, which are statically defined */ | ||||
| 	if (!audio_name) { | ||||
| 		switch (payload_type) { | ||||
| 		case 0: | ||||
| 			audio_name = talloc_strdup(ctx, "PCMU/8000/1"); | ||||
| 			break; | ||||
| 		case 3: | ||||
| 			audio_name = talloc_strdup(ctx, "GSM/8000/1"); | ||||
| 			break; | ||||
| 		case 8: | ||||
| 			audio_name = talloc_strdup(ctx, "PCMA/8000/1"); | ||||
| 			break; | ||||
| 		case 18: | ||||
| 			audio_name = talloc_strdup(ctx, "G729/8000/1"); | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* The given payload type is not known to us, or it | ||||
| 			 * it is a dynamic payload type for which we do not | ||||
| 			 * know the audio name. We must give up here */ | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, "No audio codec name given, and payload type %d unknown\n", | ||||
| 			     payload_type); | ||||
| 			goto error; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Now we extract the codec subtype name, rate and channels. The latter | ||||
| 	 * two are optional. If they are not present we use the safe defaults | ||||
| 	 * above. */ | ||||
| 	if (strlen(audio_name) >= sizeof(audio_codec)) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(audio_name, -1)); | ||||
| 		goto error; | ||||
| 	} | ||||
| 	channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS; | ||||
| 	rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; | ||||
| 	if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(audio_name, -1)); | ||||
| 		goto error; | ||||
| 	} | ||||
|  | ||||
| 	/* Note: We only accept configurations with one audio channel! */ | ||||
| 	if (channels != 1) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot handle audio codec with more than one channel: %s\n", | ||||
| 		     osmo_quote_str(audio_name, -1)); | ||||
| 		goto error; | ||||
| 	} | ||||
|  | ||||
| 	codec->rate = rate; | ||||
| 	codec->channels = channels; | ||||
| 	codec->subtype_name = talloc_strdup(ctx, audio_codec); | ||||
| 	codec->audio_name = talloc_strdup(ctx, audio_name); | ||||
| 	codec->payload_type = payload_type; | ||||
|  | ||||
| 	if (!strcmp(audio_codec, "G729")) { | ||||
| 		codec->frame_duration_num = 10; | ||||
| 		codec->frame_duration_den = 1000; | ||||
| 	} else { | ||||
| 		codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; | ||||
| 		codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; | ||||
| 	} | ||||
|  | ||||
| 	/* Derive the payload type if it is unknown */ | ||||
| 	if (codec->payload_type == PTYPE_UNDEFINED) { | ||||
| 		/* TODO: This is semi dead code, see OS#4150 */ | ||||
|  | ||||
| 		/* For the known codecs from the static range we restore | ||||
| 		 * the IANA or 3GPP assigned payload type number */ | ||||
| 		if (codec->rate == 8000 && codec->channels == 1) { | ||||
| 			/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */ | ||||
| 			if (!strcmp(codec->subtype_name, "GSM")) | ||||
| 				codec->payload_type = 3; | ||||
| 			else if (!strcmp(codec->subtype_name, "PCMA")) | ||||
| 				codec->payload_type = 8; | ||||
| 			else if (!strcmp(codec->subtype_name, "PCMU")) | ||||
| 				codec->payload_type = 0; | ||||
| 			else if (!strcmp(codec->subtype_name, "G729")) | ||||
| 				codec->payload_type = 18; | ||||
|  | ||||
| 			/* See also: 3GPP TS 48.103, chapter 5.4.2.2 RTP Payload | ||||
| 			 * Note: These are not fixed payload types as the IANA | ||||
| 			 * defined once, they still remain dymanic payload | ||||
| 			 * types, but with a payload type number preference. */ | ||||
| 			else if (!strcmp(codec->subtype_name, "GSM-EFR")) | ||||
| 				codec->payload_type = 110; | ||||
| 			else if (!strcmp(codec->subtype_name, "GSM-HR-08")) | ||||
| 				codec->payload_type = 111; | ||||
| 			else if (!strcmp(codec->subtype_name, "AMR")) | ||||
| 				codec->payload_type = 112; | ||||
| 			else if (!strcmp(codec->subtype_name, "AMR-WB")) | ||||
| 				codec->payload_type = 113; | ||||
| 		} | ||||
|  | ||||
| 		/* If we could not determine a payload type we assume that | ||||
| 		 * we are dealing with a codec from the dynamic range. We | ||||
| 		 * choose a fixed identifier from 96-109. (Note: normally, | ||||
| 		 * the dynamic payload type rante is from 96-127, but from | ||||
| 		 * 110 onwards 3gpp defines prefered codec types, which are | ||||
| 		 * also fixed, see above)  */ | ||||
| 		if (codec->payload_type < 0) { | ||||
| 			/* FIXME: pt_offset is completely unrelated and useless here, any of those numbers may already | ||||
| 			 * have been added to the codecs. Instead, there should be an iterator checking for an actually | ||||
| 			 * unused dynamic payload type number. */ | ||||
| 			codec->payload_type = 96 + pt_offset; | ||||
| 			if (codec->payload_type > 109) { | ||||
| 				LOGP(DLMGCP, LOGL_ERROR, "Ran out of payload type numbers to assign dynamically\n"); | ||||
| 				goto error; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Copy over optional codec parameters */ | ||||
| 	if (param) { | ||||
| 		codec->param = *param; | ||||
| 		codec->param_present = true; | ||||
| 	} else | ||||
| 		codec->param_present = false; | ||||
|  | ||||
| 	conn->end.codecs_assigned++; | ||||
| 	return 0; | ||||
| error: | ||||
| 	/* Make sure we leave a clean codec entry on error. */ | ||||
| 	codec_free(codec); | ||||
| 	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) | ||||
| { | ||||
| 	char codec_name[64]; | ||||
|  | ||||
| 	/* A codec name must be set, if not, this might mean that the codec | ||||
| 	 * (payload type) that was assigned is unknown to us so we must stop | ||||
| 	 * here. */ | ||||
| 	if (!codec->subtype_name) | ||||
| 		return false; | ||||
|  | ||||
| 	/* We now extract the codec_name (letters before the /, e.g. "GSM" | ||||
| 	 * from the audio name that is stored in the trunk configuration. | ||||
| 	 * We do not compare to the full audio_name because we expect that | ||||
| 	 * "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the | ||||
| 	 * audio name of the codec is set to "GSM" */ | ||||
| 	if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1) | ||||
| 		return false; | ||||
|  | ||||
| 	/* Finally we check if the subtype_name we have generated from the | ||||
| 	 * audio_name in the trunc struct patches the codec_name of the | ||||
| 	 * given codec */ | ||||
| 	if (strcasecmp(codec_name, codec->subtype_name) == 0) | ||||
| 		return true; | ||||
|  | ||||
| 	/* FIXME: It is questinable that the method to pick a compatible | ||||
| 	 * codec can work properly. Since this useses tcfg->audio_name, as | ||||
| 	 * a reference, which is set to "AMR/8000" permanently. | ||||
| 	 * tcfg->audio_name must be updated by the first connection that | ||||
| 	 * has been made on an endpoint, so that the second connection | ||||
| 	 * can make a meaningful decision here */ | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| /*! Decide for one suitable codec | ||||
|  *  \param[in] conn related rtp-connection. | ||||
|  *  \returns 0 on success, -EINVAL on failure. */ | ||||
| int mgcp_codec_decide(struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	struct mgcp_rtp_end *rtp; | ||||
| 	unsigned int i; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	bool codec_assigned = false; | ||||
|  | ||||
| 	endp = conn->conn->endp; | ||||
| 	rtp = &conn->end; | ||||
|  | ||||
| 	/* This function works on the results the SDP/LCO parser has extracted | ||||
| 	 * from the MGCP message. The goal is to select a suitable codec for | ||||
| 	 * the given connection. When transcoding is available, the first codec | ||||
| 	 * from the codec list is taken without further checking. When | ||||
| 	 * transcoding is not available, then the choice must be made more | ||||
| 	 * carefully. Each codec in the list is checked until one is found that | ||||
| 	 * is rated compatible. The rating is done by the helper function | ||||
| 	 * is_codec_compatible(), which does the actual checking. */ | ||||
| 	for (i = 0; i < rtp->codecs_assigned; i++) { | ||||
| 		/* When no transcoding is available, avoid codecs that would | ||||
| 		 * require transcoding. */ | ||||
| 		if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) { | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n", | ||||
| 			     rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		rtp->codec = &rtp->codecs[i]; | ||||
| 		codec_assigned = true; | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	/* FIXME: To the reviewes: This is problematic. I do not get why we | ||||
| 	 * need to reset the packet_duration_ms depending on the codec | ||||
| 	 * selection. I thought it were all 20ms? Is this to address some | ||||
| 	 * cornercase. (This piece of code was in the code path before, | ||||
| 	 * together with the note: "TODO/XXX: Store this per codec and derive | ||||
| 	 * it on use" */ | ||||
| 	if (codec_assigned) { | ||||
| 		if (rtp->maximum_packet_time >= 0 | ||||
| 		    && rtp->maximum_packet_time * rtp->codec->frame_duration_den > | ||||
| 		    rtp->codec->frame_duration_num * 1500) | ||||
| 			rtp->packet_duration_ms = 0; | ||||
|  | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return -EINVAL; | ||||
| } | ||||
|  | ||||
| /* 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": | ||||
|  * | ||||
|  *    octet-align: Permissible values are 0 and 1.  If 1, octet-aligned | ||||
|  *                 operation SHALL be used.  If 0 or if not present, | ||||
|  *                 bandwidth-efficient operation is employed. | ||||
|  * | ||||
|  * https://tools.ietf.org/html/rfc4867 | ||||
|  */ | ||||
| static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	if (!codec->param_present) | ||||
| 		return false; | ||||
| 	if (!codec->param.amr_octet_aligned_present) | ||||
| 		return false; | ||||
| 	return codec->param.amr_octet_aligned; | ||||
| } | ||||
|  | ||||
| /* Compare two codecs, all parameters must match up, except for the payload type | ||||
|  * number. */ | ||||
| static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b) | ||||
| { | ||||
| 	if (codec_a->rate != codec_b->rate) | ||||
| 		return false; | ||||
| 	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; | ||||
| 	if (strcmp(codec_a->subtype_name, codec_b->subtype_name)) | ||||
| 		return false; | ||||
| 	if (!strcmp(codec_a->subtype_name, "AMR")) { | ||||
| 		if (amr_is_octet_aligned(codec_a) != 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) | ||||
| { | ||||
| 	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; | ||||
| 	unsigned int i; | ||||
| 	unsigned int codecs_assigned; | ||||
|  | ||||
| 	rtp_src = &conn_src->end; | ||||
| 	rtp_dst = &conn_dst->end; | ||||
|  | ||||
| 	/* Find the codec information that is used on the source side */ | ||||
| 	codecs_assigned = rtp_src->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]; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!codec_src) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	/* Use the codec infrmation from the source and try to find the | ||||
| 	 * equivalent of it on the destination side */ | ||||
| 	codecs_assigned = rtp_dst->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]; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!codec_dst) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	return codec_dst->payload_type; | ||||
| } | ||||
							
								
								
									
										397
									
								
								src/libosmo-mgcp/mgcp_conn.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										397
									
								
								src/libosmo-mgcp/mgcp_conn.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,397 @@ | ||||
| /* Message connection list handling */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Philipp Maier | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/mgcp/mgcp_common.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
| #include <osmocom/mgcp/mgcp_sdp.h> | ||||
| #include <osmocom/mgcp/mgcp_codec.h> | ||||
| #include <osmocom/gsm/gsm_utils.h> | ||||
| #include <osmocom/core/rate_ctr.h> | ||||
| #include <osmocom/core/timer.h> | ||||
| #include <ctype.h> | ||||
|  | ||||
| const static struct rate_ctr_group_desc rate_ctr_group_desc = { | ||||
| 	.group_name_prefix = "conn_rtp", | ||||
| 	.group_description = "rtp connection statistics", | ||||
| 	.class_id = 1, | ||||
| 	.num_ctr = ARRAY_SIZE(mgcp_conn_rate_ctr_desc), | ||||
| 	.ctr_desc = mgcp_conn_rate_ctr_desc | ||||
| }; | ||||
|  | ||||
|  | ||||
| /* Allocate a new connection identifier. According to RFC3435, they must | ||||
|  * be unique only within the scope of the endpoint. (Caller must provide | ||||
|  * memory for id) */ | ||||
| static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id) | ||||
| { | ||||
| #define MGCP_CONN_ID_GEN_LEN 8 | ||||
| 	int i; | ||||
| 	int k; | ||||
| 	int rc; | ||||
| 	uint8_t id_bin[MGCP_CONN_ID_GEN_LEN / 2]; | ||||
| 	char *id_hex; | ||||
|  | ||||
| 	/* Generate a connection id that is unique for the current endpoint. | ||||
| 	 * Technically a counter would be sufficient, but in order to | ||||
| 	 * be able to find a specific connection in large logfiles and to | ||||
| 	 * prevent unintentional connections we assign the connection | ||||
| 	 * identifiers randomly from a reasonable large number space */ | ||||
| 	for (i = 0; i < 32; i++) { | ||||
| 		rc = osmo_get_rand_id(id_bin, sizeof(id_bin)); | ||||
| 		if (rc < 0) | ||||
| 			return rc; | ||||
|  | ||||
| 		id_hex = osmo_hexdump_nospc(id_bin, sizeof(id_bin)); | ||||
| 		for (k = 0; k < strlen(id_hex); k++) | ||||
| 			id_hex[k] = toupper(id_hex[k]); | ||||
|  | ||||
| 		/* ensure that the generated conn_id is unique | ||||
| 		 * for this endpoint */ | ||||
| 		if (!mgcp_conn_get_rtp(endp, id_hex)) { | ||||
| 			osmo_strlcpy(id, id_hex, MGCP_CONN_ID_MAXLEN); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGPENDP(endp, DLMGCP, LOGL_ERROR, "unable to generate a unique connectionIdentifier\n"); | ||||
|  | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /* Initialize rtp connection struct with default values */ | ||||
| static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn) | ||||
| { | ||||
| 	struct mgcp_rtp_end *end = &conn_rtp->end; | ||||
| 	/* FIXME: Each new rate counter group requires an unique index. At the | ||||
| 	 * moment we generate this index using this counter, but perhaps there | ||||
| 	 * is a more concious way to assign the indexes. */ | ||||
| 	static unsigned int rate_ctr_index = 0; | ||||
|  | ||||
| 	conn_rtp->type = MGCP_RTP_DEFAULT; | ||||
| 	conn_rtp->osmux.cid_allocated = false; | ||||
| 	conn_rtp->osmux.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; | ||||
|  | ||||
| 	/* Set default values */ | ||||
| 	end->frames_per_packet = 0;	/* unknown */ | ||||
| 	end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS; | ||||
| 	end->output_enabled = 0; | ||||
| 	end->maximum_packet_time = -1; | ||||
|  | ||||
| 	conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index); | ||||
| 	if (!conn_rtp->rate_ctr_group) | ||||
| 		return -1; | ||||
|  | ||||
| 	conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR]; | ||||
| 	conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR]; | ||||
| 	rate_ctr_index++; | ||||
|  | ||||
| 	/* Make sure codec table is reset */ | ||||
| 	mgcp_codec_reset_all(conn_rtp); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Cleanup rtp connection struct */ | ||||
| static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp) | ||||
| { | ||||
| 	if (mgcp_conn_rtp_is_osmux(conn_rtp)) | ||||
| 		conn_osmux_disable(conn_rtp); | ||||
| 	mgcp_free_rtp_port(&conn_rtp->end); | ||||
| 	rate_ctr_group_free(conn_rtp->rate_ctr_group); | ||||
| 	mgcp_codec_reset_all(conn_rtp); | ||||
| } | ||||
|  | ||||
| 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); | ||||
| } | ||||
|  | ||||
| void mgcp_conn_watchdog_kick(struct mgcp_conn *conn) | ||||
| { | ||||
| 	int timeout = conn->endp->cfg->conn_timeout; | ||||
| 	if (!timeout) | ||||
| 		return; | ||||
|  | ||||
| 	LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "watchdog kicked\n"); | ||||
| 	osmo_timer_schedule(&conn->watchdog, timeout, 0); | ||||
| } | ||||
|  | ||||
| /*! allocate a new connection list entry. | ||||
|  *  \param[in] ctx talloc context | ||||
|  *  \param[in] endp associated endpoint | ||||
|  *  \param[in] id identification number of the connection | ||||
|  *  \param[in] type connection type (e.g. MGCP_CONN_TYPE_RTP) | ||||
|  *  \returns pointer to allocated connection, NULL on error */ | ||||
| struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, | ||||
| 				  enum mgcp_conn_type type, char *name) | ||||
| { | ||||
| 	struct mgcp_conn *conn; | ||||
| 	int rc; | ||||
|  | ||||
| 	/* Do not allow more then two connections */ | ||||
| 	if (llist_count(&endp->conns) >= endp->type->max_conns) | ||||
| 		return NULL; | ||||
|  | ||||
| 	/* Create new connection and add it to the list */ | ||||
| 	conn = talloc_zero(ctx, struct mgcp_conn); | ||||
| 	if (!conn) | ||||
| 		return NULL; | ||||
| 	conn->endp = endp; | ||||
| 	conn->type = type; | ||||
| 	conn->mode = MGCP_CONN_NONE; | ||||
| 	conn->mode_orig = MGCP_CONN_NONE; | ||||
| 	osmo_strlcpy(conn->name, name, sizeof(conn->name)); | ||||
| 	rc = mgcp_alloc_id(endp, conn->id); | ||||
| 	if (rc < 0) { | ||||
| 		talloc_free(conn); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	switch (type) { | ||||
| 	case MGCP_CONN_TYPE_RTP: | ||||
| 		if (mgcp_rtp_conn_init(&conn->u.rtp, conn) < 0) { | ||||
| 			talloc_free(conn); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		/* NOTE: This should never be called with an | ||||
| 		 * invalid type, its up to the programmer | ||||
| 		 * to ensure propery types */ | ||||
| 		OSMO_ASSERT(false); | ||||
| 	} | ||||
|  | ||||
| 	/* Initialize watchdog */ | ||||
| 	osmo_timer_setup(&conn->watchdog, mgcp_conn_watchdog_cb, conn); | ||||
| 	mgcp_conn_watchdog_kick(conn); | ||||
| 	llist_add(&conn->entry, &endp->conns); | ||||
|  | ||||
| 	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_trunk_config *trunk, struct mgcp_conn_rtp *conn_rtp) | ||||
| { | ||||
| 	struct rate_ctr_group *all_stats = trunk->all_rtp_conn_stats; | ||||
| 	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group; | ||||
| 	int i; | ||||
|  | ||||
| 	if (all_stats == NULL || conn_stats == NULL) | ||||
| 		return; | ||||
|  | ||||
| 	/* Compared to per-connection RTP statistics, aggregated RTP statistics | ||||
| 	 * contain one additional rate couter item (RTP_NUM_CONNECTIONS). | ||||
| 	 * All other counters in both counter groups correspond to each other. */ | ||||
| 	OSMO_ASSERT(conn_stats->desc->num_ctr + 1 == all_stats->desc->num_ctr); | ||||
|  | ||||
| 	for (i = 0; i < conn_stats->desc->num_ctr; i++) | ||||
| 		rate_ctr_add(&all_stats->ctr[i], conn_stats->ctr[i].current); | ||||
|  | ||||
| 	rate_ctr_inc(&all_stats->ctr[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) | ||||
| { | ||||
| 	struct mgcp_conn *conn; | ||||
|  | ||||
| 	conn = mgcp_conn_get(endp, id); | ||||
| 	if (!conn) | ||||
| 		return; | ||||
|  | ||||
| 	/* Run endpoint cleanup action. By this we inform the endpoint about | ||||
| 	 * the removal of the connection and allow it to clean up its inner | ||||
| 	 * state accordingly */ | ||||
| 	if (endp->type->cleanup_cb) | ||||
| 		endp->type->cleanup_cb(endp, conn); | ||||
|  | ||||
| 	switch (conn->type) { | ||||
| 	case MGCP_CONN_TYPE_RTP: | ||||
| 		aggregate_rtp_conn_stats(endp->tcfg, &conn->u.rtp); | ||||
| 		mgcp_rtp_conn_cleanup(&conn->u.rtp); | ||||
| 		break; | ||||
| 	default: | ||||
| 		/* NOTE: This should never be called with an | ||||
| 		 * invalid type, its up to the programmer | ||||
| 		 * to ensure propery types */ | ||||
| 		OSMO_ASSERT(false); | ||||
| 	} | ||||
|  | ||||
| 	osmo_timer_del(&conn->watchdog); | ||||
| 	llist_del(&conn->entry); | ||||
| 	talloc_free(conn); | ||||
| } | ||||
|  | ||||
| /*! free oldest connection in the list. | ||||
|  *  \param[in] endp associated endpoint */ | ||||
| void mgcp_conn_free_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); | ||||
| 	if (!conn) | ||||
| 		return; | ||||
|  | ||||
| 	mgcp_conn_free(endp, conn->id); | ||||
| } | ||||
|  | ||||
| /*! free all connections at once. | ||||
|  *  \param[in] endp associated endpoint */ | ||||
| void mgcp_conn_free_all(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	struct mgcp_conn *conn; | ||||
| 	struct mgcp_conn *conn_tmp; | ||||
|  | ||||
| 	/* Drop all items in the list */ | ||||
| 	llist_for_each_entry_safe(conn, conn_tmp, &endp->conns, entry) { | ||||
| 		mgcp_conn_free(endp, conn->id); | ||||
| 	} | ||||
|  | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| /*! dump basic connection information to human readable string. | ||||
|  *  \param[in] conn to dump | ||||
|  *  \returns human readable string */ | ||||
| char *mgcp_conn_dump(struct mgcp_conn *conn) | ||||
| { | ||||
| 	static char str[sizeof(conn->name)+sizeof(conn->id)+256]; | ||||
|  | ||||
| 	if (!conn) { | ||||
| 		snprintf(str, sizeof(str), "(null connection)"); | ||||
| 		return str; | ||||
| 	} | ||||
|  | ||||
| 	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->name, | ||||
| 			 conn->id, | ||||
| 			 inet_ntoa(conn->u.rtp.end.addr), | ||||
| 			 ntohs(conn->u.rtp.end.rtp_port), | ||||
| 			 ntohs(conn->u.rtp.end.rtcp_port)); | ||||
| 		break; | ||||
|  | ||||
| 	default: | ||||
| 		/* Should not happen, we should be able to dump | ||||
| 		 * every possible connection type. */ | ||||
| 		snprintf(str, sizeof(str), "(unknown connection type)"); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	return str; | ||||
| } | ||||
|  | ||||
| /*! find destination connection on a specific endpoint. | ||||
|  *  \param[in] conn to search a destination for | ||||
|  *  \returns destination connection, NULL on failure */ | ||||
| struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn) | ||||
| { | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	struct mgcp_conn *partner_conn; | ||||
| 	endp = conn->endp; | ||||
|  | ||||
| 	/*! NOTE: This simply works by grabbing the first connection that is | ||||
| 	 *  not the supplied connection, which is suitable for endpoints that | ||||
| 	 *  do not serve more than two connections. */ | ||||
|  | ||||
| 	llist_for_each_entry(partner_conn, &endp->conns, entry) { | ||||
| 		if (conn != partner_conn) { | ||||
| 			return partner_conn; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
							
								
								
									
										55
									
								
								src/libosmo-mgcp/mgcp_endp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/libosmo-mgcp/mgcp_endp.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| /* Endpoint types */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Philipp Maier | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
|  | ||||
| /* Endpoint typeset definition */ | ||||
| const struct mgcp_endpoint_typeset ep_typeset = { | ||||
| 	/* Specify endpoint properties for RTP endpoint */ | ||||
| 	.rtp.max_conns = 2, | ||||
| 	.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb, | ||||
| 	.rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb | ||||
| }; | ||||
|  | ||||
| /*! 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); | ||||
|  | ||||
| 	/* Reset endpoint parameters and states */ | ||||
| 	talloc_free(endp->callid); | ||||
| 	endp->callid = NULL; | ||||
| 	talloc_free(endp->local_options.string); | ||||
| 	endp->local_options.string = NULL; | ||||
| 	talloc_free(endp->local_options.codec); | ||||
| 	endp->local_options.codec = NULL; | ||||
| 	endp->wildcarded_req = false; | ||||
| } | ||||
							
								
								
									
										514
									
								
								src/libosmo-mgcp/mgcp_msg.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										514
									
								
								src/libosmo-mgcp/mgcp_msg.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,514 @@ | ||||
| /* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ | ||||
| /* Message parser/generator utilities */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2012 by On-Waves | ||||
|  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * 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 <limits.h> | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/mgcp/mgcp_common.h> | ||||
| #include <osmocom/mgcp/mgcp_msg.h> | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
|  | ||||
| /*! Display an mgcp message on the log output. | ||||
|  *  \param[in] message mgcp message string | ||||
|  *  \param[in] len message mgcp message string length | ||||
|  *  \param[in] preamble string to display in logtext in front of each line */ | ||||
| void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble) | ||||
| { | ||||
| 	unsigned char line[80]; | ||||
| 	unsigned char *ptr; | ||||
| 	unsigned int consumed = 0; | ||||
| 	unsigned int consumed_line = 0; | ||||
| 	unsigned int line_count = 0; | ||||
|  | ||||
| 	if (!log_check_level(DLMGCP, LOGL_DEBUG)) | ||||
| 		return; | ||||
|  | ||||
| 	while (1) { | ||||
| 		memset(line, 0, sizeof(line)); | ||||
| 		ptr = line; | ||||
| 		consumed_line = 0; | ||||
| 		do { | ||||
| 			if (*message != '\n' && *message != '\r') { | ||||
| 				*ptr = *message; | ||||
| 				ptr++; | ||||
| 			} | ||||
| 			message++; | ||||
| 			consumed++; | ||||
| 			consumed_line++; | ||||
| 		} while (*message != '\n' && consumed < len | ||||
| 			 && consumed_line < sizeof(line)); | ||||
|  | ||||
| 		if (strlen((const char *)line)) { | ||||
| 			LOGP(DLMGCP, LOGL_DEBUG, "%s: line #%02u: %s\n", | ||||
| 			     preamble, line_count, line); | ||||
| 			line_count++; | ||||
| 		} | ||||
|  | ||||
| 		if (consumed >= len) | ||||
| 			return; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /*! 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) | ||||
| { | ||||
| 	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 (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 für RTP connections */ | ||||
| 	if (conn->type == MGCP_CONN_TYPE_RTP) { | ||||
| 		conn->u.rtp.end.output_enabled = | ||||
| 		    conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0; | ||||
| 	} | ||||
|  | ||||
| 	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 %d\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; | ||||
| } | ||||
|  | ||||
| /* We have a null terminated string with the endpoint name here. We only | ||||
|  * support two kinds. Simple ones as seen on the BSC level and the ones | ||||
|  * seen on the trunk side. (helper function for find_endpoint()) */ | ||||
| static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg, | ||||
| 					      const char *mgcp) | ||||
| { | ||||
| 	char *rest = NULL; | ||||
| 	struct mgcp_trunk_config *tcfg; | ||||
| 	int trunk, endp; | ||||
| 	struct mgcp_endpoint *endp_ptr; | ||||
|  | ||||
| 	trunk = strtoul(mgcp + 6, &rest, 10); | ||||
| 	if (rest == NULL || rest[0] != '/' || trunk < 1) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	endp = strtoul(rest + 1, &rest, 10); | ||||
| 	if (rest == NULL || rest[0] != '@') { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* signalling is on timeslot 1 */ | ||||
| 	if (endp == 1) | ||||
| 		return NULL; | ||||
|  | ||||
| 	tcfg = mgcp_trunk_num(cfg, trunk); | ||||
| 	if (!tcfg) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n", | ||||
| 		     trunk); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (!tcfg->endpoints) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Endpoints of trunk %d not allocated.\n", trunk); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (endp < 1 || endp >= tcfg->number_endpoints) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n", | ||||
| 		     mgcp); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	endp_ptr = &tcfg->endpoints[endp]; | ||||
| 	endp_ptr->wildcarded_req = false; | ||||
| 	return endp_ptr; | ||||
| } | ||||
|  | ||||
| /* Find an endpoint that is not in use. Do this by going through the endpoint | ||||
|  * array, check the callid. A callid nullpointer indicates that the endpoint | ||||
|  * is free */ | ||||
| static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints, | ||||
| 						unsigned int number_endpoints) | ||||
| { | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	for (i = 0; i < number_endpoints; i++) { | ||||
| 		if (endpoints[i].callid == NULL) { | ||||
| 			endp = &endpoints[i]; | ||||
| 			LOGPENDP(endp, DLMGCP, LOGL_DEBUG, | ||||
| 			     "found free endpoint\n"); | ||||
| 			endp->wildcarded_req = true; | ||||
| 			return endp; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n"); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /* Check if the domain name, which is supplied with the endpoint name | ||||
|  * matches the configuration. */ | ||||
| static int check_domain_name(struct mgcp_config *cfg, const char *mgcp) | ||||
| { | ||||
| 	char *domain_to_check; | ||||
|  | ||||
| 	domain_to_check = strstr(mgcp, "@"); | ||||
| 	if (!domain_to_check) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	/* Accept any domain if configured as "*" */ | ||||
| 	if (!strcmp(cfg->domain, "*")) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (strcmp(domain_to_check+1, cfg->domain) != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Search the endpoint pool for the endpoint that had been selected via the | ||||
|  * MGCP message (helper function for mgcp_analyze_header()) */ | ||||
| static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, | ||||
| 					   const char *mgcp, | ||||
| 					   int *cause) | ||||
| { | ||||
| 	char *endptr = NULL; | ||||
| 	unsigned int gw = INT_MAX; | ||||
| 	const char *endpoint_number_str; | ||||
| 	struct mgcp_endpoint *endp; | ||||
|  | ||||
| 	*cause = 0; | ||||
|  | ||||
| 	/* Check if the domainname in the request is correct */ | ||||
| 	if (check_domain_name(cfg, mgcp)) { | ||||
| 		*cause = -500; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if the E1 trunk is requested */ | ||||
| 	if (strncmp(mgcp, "ds/e1", 5) == 0) { | ||||
| 		endp = find_e1_endpoint(cfg, mgcp); | ||||
| 		if (!endp) | ||||
| 			*cause = -500; | ||||
| 		return endp; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if the virtual trunk is addressed (new, correct way with prefix) */ | ||||
| 	if (strncmp | ||||
| 	    (mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, | ||||
| 	     strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) { | ||||
| 		endpoint_number_str = | ||||
| 		    mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK); | ||||
| 		if (endpoint_number_str[0] == '*') { | ||||
| 			endp = find_free_endpoint(cfg->trunk.endpoints, | ||||
| 						  cfg->trunk.number_endpoints); | ||||
| 			if (!endp) | ||||
| 				*cause = -403; | ||||
| 			return endp; | ||||
| 		} | ||||
| 		gw = strtoul(endpoint_number_str, &endptr, 16); | ||||
| 		if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') { | ||||
| 			endp = &cfg->trunk.endpoints[gw]; | ||||
| 			endp->wildcarded_req = false; | ||||
| 			return endp; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Deprecated method without prefix */ | ||||
| 	LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 	     "Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n", | ||||
| 	     MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp); | ||||
| 	gw = strtoul(mgcp, &endptr, 16); | ||||
| 	if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') { | ||||
| 		endp = &cfg->trunk.endpoints[gw]; | ||||
| 		endp->wildcarded_req = false; | ||||
| 		return endp; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp); | ||||
| 	*cause = -500; | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /*! Analyze and parse the the hader of an MGCP messeage string. | ||||
|  *  \param[out] pdata caller provided memory to store the parsing results | ||||
|  *  \param[in] data mgcp message string | ||||
|  *  \returns when the status line was complete and transaction_id and | ||||
|  *  endp out parameters are set, -1 on error */ | ||||
| int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data) | ||||
| { | ||||
| 	int i = 0; | ||||
| 	char *elem, *save = NULL; | ||||
| 	int cause; | ||||
|  | ||||
| 	/*! This function will parse the header part of the received | ||||
| 	 *  MGCP message. The parsing results are stored in pdata. | ||||
| 	 *  The function will also automatically search the pool with | ||||
| 	 *  available endpoints in order to find an endpoint that matches | ||||
| 	 *  the endpoint string in in the header */ | ||||
|  | ||||
| 	OSMO_ASSERT(data); | ||||
| 	pdata->trans = "000000"; | ||||
|  | ||||
| 	for (elem = strtok_r(data, " ", &save); elem; | ||||
| 	     elem = strtok_r(NULL, " ", &save)) { | ||||
| 		switch (i) { | ||||
| 		case 0: | ||||
| 			pdata->trans = elem; | ||||
| 			break; | ||||
| 		case 1: | ||||
| 			pdata->endp = find_endpoint(pdata->cfg, elem, &cause); | ||||
| 			if (!pdata->endp) { | ||||
| 				LOGP(DLMGCP, LOGL_ERROR, | ||||
| 				     "Unable to find Endpoint `%s'\n", elem); | ||||
| 				OSMO_ASSERT(cause < 0); | ||||
| 				return cause; | ||||
| 			} | ||||
| 			break; | ||||
| 		case 2: | ||||
| 			if (strcasecmp("MGCP", elem)) { | ||||
| 				LOGP(DLMGCP, LOGL_ERROR, | ||||
| 				     "MGCP header parsing error\n"); | ||||
| 				return -510; | ||||
| 			} | ||||
| 			break; | ||||
| 		case 3: | ||||
| 			if (strcmp("1.0", elem)) { | ||||
| 				LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' " | ||||
| 				     "not supported\n", elem); | ||||
| 				return -528; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		i++; | ||||
| 	} | ||||
|  | ||||
| 	if (i != 4) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n"); | ||||
| 		pdata->trans = "000000"; | ||||
| 		pdata->endp = NULL; | ||||
| 		return -510; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! Extract OSMUX CID from an MGCP parameter line (string). | ||||
|  *  \param[in] line single parameter line from the MGCP message | ||||
|  *  \returns OSMUX CID, -1 wildcard, -2 on error */ | ||||
| int mgcp_parse_osmux_cid(const char *line) | ||||
| { | ||||
| 	int osmux_cid; | ||||
|  | ||||
|  | ||||
| 	if (strcasecmp(line + 2, "Osmux: *") == 0) { | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n", | ||||
| 		     line); | ||||
| 		return -2; | ||||
| 	} | ||||
|  | ||||
| 	if (osmux_cid > OSMUX_CID_MAX) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n", | ||||
| 		     osmux_cid, OSMUX_CID_MAX); | ||||
| 		return -2; | ||||
| 	} | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid); | ||||
|  | ||||
| 	return osmux_cid; | ||||
| } | ||||
|  | ||||
| /*! Check MGCP parameter line (string) for plausibility. | ||||
|  *  \param[in] endp pointer to endpoint (only used for log output) | ||||
|  *  \param[in] line single parameter line from the MGCP message | ||||
|  *  \returns 1 when line seems plausible, 0 on error */ | ||||
| int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line) | ||||
| { | ||||
| 	const size_t line_len = strlen(line); | ||||
| 	if (line[0] != '\0' && line_len < 2) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Wrong MGCP option format: '%s' on 0x%x\n", | ||||
| 		     line, ENDPOINT_NUMBER(endp)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* FIXME: A couple more checks wouldn't hurt... */ | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /*! Check if the specified callid seems plausible. | ||||
|   * \param[in] endp pointer to endpoint | ||||
|   * \param{in] callid to verify | ||||
|   * \returns 1 when callid seems plausible, 0 on error */ | ||||
| int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid) | ||||
| { | ||||
| 	/*! This function compares the supplied callid with the called that is | ||||
| 	 *  stored in the endpoint structure. */ | ||||
|  | ||||
| 	if (!endp) | ||||
| 		return -1; | ||||
|  | ||||
| 	/* Accept any CallID for "X-Osmo-IGN: C" */ | ||||
| 	if (endp->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!callid) | ||||
| 		return -1; | ||||
| 	if (!endp->callid) | ||||
| 		return -1; | ||||
|  | ||||
| 	if (strcmp(endp->callid, callid) != 0) { | ||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||
| 			 "CallIDs mismatch: '%s' != '%s'\n", | ||||
| 			 endp->callid, callid); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! Check if the specified connection id seems plausible. | ||||
|   * \param[in] endp pointer to endpoint | ||||
|   * \param{in] connection id to verify | ||||
|   * \returns 0 when connection id is valid and exists, an RFC3435 error code on error. | ||||
|   */ | ||||
| int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id) | ||||
| { | ||||
| 	/* For invalid conn_ids, return 510 "The transaction could not be executed, because some | ||||
| 	 * unspecified protocol error was detected." */ | ||||
|  | ||||
| 	/* Check for null identifiers */ | ||||
| 	if (!conn_id) { | ||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||
| 			 "invalid ConnectionIdentifier (missing)\n"); | ||||
| 		return 510; | ||||
| 	} | ||||
|  | ||||
| 	/* Check for empty connection identifiers */ | ||||
| 	if (strlen(conn_id) == 0) { | ||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||
| 			 "invalid ConnectionIdentifier (empty)\n"); | ||||
| 		return 510; | ||||
| 	} | ||||
|  | ||||
| 	/* Check for over long connection identifiers */ | ||||
| 	if (strlen(conn_id) > (MGCP_CONN_ID_MAXLEN-1)) { | ||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||
| 			"invalid ConnectionIdentifier (too long: %zu > %d) 0x%s\n", | ||||
| 			 strlen(conn_id), MGCP_CONN_ID_MAXLEN-1, conn_id); | ||||
| 		return 510; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if connection exists */ | ||||
| 	if (mgcp_conn_get(endp, conn_id)) | ||||
| 		return 0; | ||||
|  | ||||
| 	LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||
| 	     "no connection found under ConnectionIdentifier 0x%s\n", conn_id); | ||||
|  | ||||
| 	/* When the conn_id was not found, return error code 515 "The transaction refers to an incorrect | ||||
| 	 * connection-id (may have been already deleted)." */ | ||||
| 	return 515; | ||||
| } | ||||
|  | ||||
| /*! Extract individual lines from MCGP message. | ||||
|   * \param[in] str MGCP message string, consisting of multiple lines | ||||
|   * \param{in] saveptr pointer to next line in str | ||||
|   * \returns line, NULL when done */ | ||||
| char *mgcp_strline(char *str, char **saveptr) | ||||
| { | ||||
| 	char *result; | ||||
|  | ||||
| 	/*! The function must be called with *str set to the input string | ||||
| 	 *  for the first line. After that saveptr will be initalized. | ||||
| 	 *  all consecutive lines are extracted by calling the function | ||||
| 	 *  with str set to NULL. When done, the function will return NULL | ||||
| 	 *  to indicate that all lines have been parsed. */ | ||||
|  | ||||
| 	if (str) | ||||
| 		*saveptr = str; | ||||
|  | ||||
| 	result = *saveptr; | ||||
|  | ||||
| 	if (*saveptr != NULL) { | ||||
| 		*saveptr = strpbrk(*saveptr, "\r\n"); | ||||
|  | ||||
| 		if (*saveptr != NULL) { | ||||
| 			char *eos = *saveptr; | ||||
|  | ||||
| 			if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n') | ||||
| 				(*saveptr)++; | ||||
| 			(*saveptr)++; | ||||
| 			if ((*saveptr)[0] == '\0') | ||||
| 				*saveptr = NULL; | ||||
|  | ||||
| 			*eos = '\0'; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
							
								
								
									
										1533
									
								
								src/libosmo-mgcp/mgcp_network.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1533
									
								
								src/libosmo-mgcp/mgcp_network.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										654
									
								
								src/libosmo-mgcp/mgcp_osmux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										654
									
								
								src/libosmo-mgcp/mgcp_osmux.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,654 @@ | ||||
| /* | ||||
|  * (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org> | ||||
|  * (C) 2012-2013 by On Waves ehf <http://www.on-waves.com> | ||||
|  * All rights not specifically granted under this license are 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. | ||||
|  */ | ||||
|  | ||||
| #include <stdio.h> /* for printf */ | ||||
| #include <string.h> /* for memcpy */ | ||||
| #include <stdlib.h> /* for abs */ | ||||
| #include <inttypes.h> /* for PRIu64 */ | ||||
| #include <netinet/in.h> | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/core/socket.h> | ||||
| #include <osmocom/core/talloc.h> | ||||
|  | ||||
| #include <osmocom/netif/osmux.h> | ||||
| #include <osmocom/netif/rtp.h> | ||||
| #include <osmocom/netif/amr.h> | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp.h> | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/mgcp/osmux.h> | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
|  | ||||
| static struct osmo_fd osmux_fd; | ||||
|  | ||||
| static LLIST_HEAD(osmux_handle_list); | ||||
|  | ||||
| struct osmux_handle { | ||||
| 	struct llist_head head; | ||||
| 	struct osmux_in_handle *in; | ||||
| 	struct in_addr rem_addr; | ||||
| 	int rem_port; /* network byte order */ | ||||
| 	int refcnt; | ||||
| }; | ||||
|  | ||||
| static void *osmux; | ||||
|  | ||||
| /* Deliver OSMUX batch to the remote end */ | ||||
| static void osmux_deliver_cb(struct msgb *batch_msg, void *data) | ||||
| { | ||||
| 	struct osmux_handle *handle = data; | ||||
| 	struct sockaddr_in out = { | ||||
| 		.sin_family = AF_INET, | ||||
| 		.sin_port = handle->rem_port, | ||||
| 	}; | ||||
|  | ||||
| 	memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr)); | ||||
| 	sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0, | ||||
| 		(struct sockaddr *)&out, sizeof(out)); | ||||
| 	msgb_free(batch_msg); | ||||
| } | ||||
|  | ||||
| /* Lookup existing OSMUX handle for specified destination address. */ | ||||
| static struct osmux_handle * | ||||
| osmux_handle_find_get(struct in_addr *addr, int rem_port) | ||||
| { | ||||
| 	struct osmux_handle *h; | ||||
|  | ||||
| 	llist_for_each_entry(h, &osmux_handle_list, head) { | ||||
| 		if (memcmp(&h->rem_addr, addr, sizeof(struct in_addr)) == 0 && | ||||
| 		    h->rem_port == rem_port) { | ||||
| 			LOGP(DLMGCP, LOGL_DEBUG, "using existing OSMUX handle " | ||||
| 						"for addr=%s:%d\n", | ||||
| 				inet_ntoa(*addr), ntohs(rem_port)); | ||||
| 			h->refcnt++; | ||||
| 			return h; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /* Put down no longer needed OSMUX handle */ | ||||
| static void osmux_handle_put(struct osmux_in_handle *in) | ||||
| { | ||||
| 	struct osmux_handle *h; | ||||
|  | ||||
| 	llist_for_each_entry(h, &osmux_handle_list, head) { | ||||
| 		if (h->in == in) { | ||||
| 			if (--h->refcnt == 0) { | ||||
| 				LOGP(DLMGCP, LOGL_INFO, | ||||
| 				     "Releasing unused osmux handle for %s:%d\n", | ||||
| 				     inet_ntoa(h->rem_addr), | ||||
| 				     ntohs(h->rem_port)); | ||||
| 				LOGP(DLMGCP, LOGL_INFO, "Stats: " | ||||
| 				     "input RTP msgs: %u bytes: %"PRIu64" " | ||||
| 				     "output osmux msgs: %u bytes: %"PRIu64"\n", | ||||
| 				     in->stats.input_rtp_msgs, | ||||
| 				     in->stats.input_rtp_bytes, | ||||
| 				     in->stats.output_osmux_msgs, | ||||
| 				     in->stats.output_osmux_bytes); | ||||
| 				llist_del(&h->head); | ||||
| 				osmux_xfrm_input_fini(h->in); | ||||
| 				talloc_free(h); | ||||
| 			} | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in); | ||||
| } | ||||
|  | ||||
| /* Allocate free OSMUX handle */ | ||||
| static struct osmux_handle * | ||||
| osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) | ||||
| { | ||||
| 	struct osmux_handle *h; | ||||
|  | ||||
| 	h = talloc_zero(osmux, struct osmux_handle); | ||||
| 	if (!h) | ||||
| 		return NULL; | ||||
| 	h->rem_addr = *addr; | ||||
| 	h->rem_port = rem_port; | ||||
| 	h->refcnt++; | ||||
|  | ||||
| 	h->in = talloc_zero(h, struct osmux_in_handle); | ||||
| 	if (!h->in) { | ||||
| 		talloc_free(h); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* sequence number to start OSMUX message from */ | ||||
| 	h->in->osmux_seq = 0; | ||||
|  | ||||
| 	h->in->batch_factor = cfg->osmux_batch; | ||||
|  | ||||
| 	/* If batch size is zero, the library defaults to 1470 bytes. */ | ||||
| 	h->in->batch_size = cfg->osmux_batch_size; | ||||
| 	h->in->deliver = osmux_deliver_cb; | ||||
| 	osmux_xfrm_input_init(h->in); | ||||
| 	h->in->data = h; | ||||
|  | ||||
| 	llist_add(&h->head, &osmux_handle_list); | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "created new OSMUX handle for addr=%s:%d\n", | ||||
| 		inet_ntoa(*addr), ntohs(rem_port)); | ||||
|  | ||||
| 	return h; | ||||
| } | ||||
|  | ||||
| /* Lookup existing handle for a specified address, if the handle can not be | ||||
|  * found, the function will automatically allocate one */ | ||||
| static struct osmux_in_handle * | ||||
| osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) | ||||
| { | ||||
| 	struct osmux_handle *h; | ||||
|  | ||||
| 	h = osmux_handle_find_get(addr, rem_port); | ||||
| 	if (h != NULL) | ||||
| 		return h->in; | ||||
|  | ||||
| 	h = osmux_handle_alloc(cfg, addr, rem_port); | ||||
| 	if (h == NULL) | ||||
| 		return NULL; | ||||
|  | ||||
| 	return h->in; | ||||
| } | ||||
|  | ||||
| /*! send RTP packet through OSMUX connection. | ||||
|  *  \param[in] buf rtp data | ||||
|  *  \param[in] buf_len length of rtp data | ||||
|  *  \param[in] conn associated RTP connection | ||||
|  *  \returns 0 on success, -1 on ERROR */ | ||||
| int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct msgb *msg; | ||||
|  | ||||
| 	msg = msgb_alloc(4096, "RTP"); | ||||
| 	if (!msg) | ||||
| 		return -1; | ||||
|  | ||||
| 	memcpy(msg->data, buf, buf_len); | ||||
| 	msgb_put(msg, buf_len); | ||||
|  | ||||
| 	if (conn->osmux.state != OSMUX_STATE_ENABLED) { | ||||
| 		LOGPCONN(conn->conn, DLMGCP, LOGL_INFO, "forwarding RTP to Osmux conn not yet enabled, dropping (cid=%d)\n", | ||||
| 		conn->osmux.cid); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.cid)) > 0) { | ||||
| 		/* batch full, build and deliver it */ | ||||
| 		osmux_xfrm_input_deliver(conn->osmux.in); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Lookup the endpoint that corresponds to the specified address (port) */ | ||||
| static struct mgcp_conn_rtp* | ||||
| osmux_conn_lookup(struct mgcp_config *cfg, uint8_t cid, | ||||
| 		struct in_addr *from_addr) | ||||
| { | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	struct mgcp_conn *conn = NULL; | ||||
| 	struct mgcp_conn_rtp * conn_rtp; | ||||
| 	int i; | ||||
|  | ||||
| 	for (i=0; i<cfg->trunk.number_endpoints; i++) { | ||||
|  | ||||
| 		endp = &cfg->trunk.endpoints[i]; | ||||
|  | ||||
| 		llist_for_each_entry(conn, &endp->conns, entry) { | ||||
| 			if (conn->type != MGCP_CONN_TYPE_RTP) | ||||
| 				continue; | ||||
|  | ||||
| 			conn_rtp = &conn->u.rtp; | ||||
| 			if (!mgcp_conn_rtp_is_osmux(conn_rtp)) | ||||
| 				continue; | ||||
|  | ||||
| 			if (conn_rtp->osmux.cid == cid) | ||||
| 				return conn_rtp; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Cannot find osmux conn with cid=%d\n", cid); | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /* FIXME: this is declared and used in mgcp_network.c, but documentation of mgcp_dispatch_rtp_bridge_cb() states another enum is to be used */ | ||||
| enum { | ||||
| 	MGCP_PROTO_RTP, | ||||
| 	MGCP_PROTO_RTCP, | ||||
| }; | ||||
|  | ||||
| static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data) | ||||
| { | ||||
| 	struct mgcp_conn_rtp *conn = data; | ||||
| 	struct mgcp_endpoint *endp = conn->conn->endp; | ||||
| 	struct sockaddr_in addr = { | ||||
| 		.sin_addr = conn->end.addr, | ||||
| 		.sin_port = conn->end.rtp_port, | ||||
| 	}; /* FIXME: not set/used in cb */ | ||||
|  | ||||
|  | ||||
| 	endp->type->dispatch_rtp_cb(MGCP_PROTO_RTP, &addr, (char *)msg->data, msg->len, conn->conn); | ||||
| 	msgb_free(msg); | ||||
| } | ||||
|  | ||||
| static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr) | ||||
| { | ||||
| 	struct msgb *msg; | ||||
| 	socklen_t slen = sizeof(*addr); | ||||
| 	int ret; | ||||
|  | ||||
| 	msg = msgb_alloc(4096, "OSMUX"); | ||||
| 	if (!msg) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot allocate message\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0, | ||||
| 			(struct sockaddr *)addr, &slen); | ||||
| 	if (ret <= 0) { | ||||
| 		msgb_free(msg); | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot receive message\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	msgb_put(msg, ret); | ||||
|  | ||||
| 	return msg; | ||||
| } | ||||
|  | ||||
| /* Updates endp osmux state and returns 0 if it can process messages, -1 otherwise */ | ||||
| static int endp_osmux_state_check(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn, | ||||
| 				  bool sending) | ||||
| { | ||||
| 	switch(conn->osmux.state) { | ||||
| 	case OSMUX_STATE_ACTIVATING: | ||||
| 	if (osmux_enable_conn(endp, conn, &conn->end.addr, conn->end.rtp_port) < 0) { | ||||
| 			LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, | ||||
| 				 "Could not enable osmux for conn on %s: %s\n", | ||||
| 				 sending ? "sent" : "received", | ||||
| 				 mgcp_conn_dump(conn->conn)); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, | ||||
| 			 "Osmux %s CID %u towards %s:%u is now enabled\n", | ||||
| 			 sending ? "sent" : "received", | ||||
| 			 conn->osmux.cid, inet_ntoa(conn->end.addr), | ||||
| 			 ntohs(conn->end.rtp_port)); | ||||
| 		return 0; | ||||
| 	case OSMUX_STATE_ENABLED: | ||||
| 		return 0; | ||||
| 	default: | ||||
| 		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, | ||||
| 			 "Osmux %s in conn %s without full negotiation, state %d\n", | ||||
| 			 sending ? "sent" : "received", | ||||
| 			 mgcp_conn_dump(conn->conn), conn->osmux.state); | ||||
| 		return -1; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int osmux_legacy_dummy_parse_cid(struct sockaddr_in *addr, struct msgb *msg, | ||||
| 					uint8_t *osmux_cid) | ||||
| { | ||||
| 	if (msg->len < 1 + sizeof(*osmux_cid)) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Discarding truncated Osmux dummy load: %s\n", osmo_hexdump(msg->data, msg->len)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* extract the osmux CID from the dummy message */ | ||||
| 	memcpy(osmux_cid, &msg->data[1], sizeof(*osmux_cid)); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* This is called from the bsc-nat */ | ||||
| static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr, | ||||
| 			      struct msgb *msg) | ||||
| { | ||||
| 	uint8_t osmux_cid; | ||||
| 	struct mgcp_conn_rtp *conn; | ||||
|  | ||||
| 	if (osmux_legacy_dummy_parse_cid(addr, msg, &osmux_cid) < 0) | ||||
| 		goto out; | ||||
|  | ||||
| 	conn = osmux_conn_lookup(cfg, osmux_cid, &addr->sin_addr); | ||||
| 	if (!conn) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Cannot find conn for Osmux CID %d\n", osmux_cid); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	endp_osmux_state_check(conn->conn->endp, conn, false); | ||||
| 	/* Only needed to punch hole in firewall, it can be dropped */ | ||||
| out: | ||||
| 	msgb_free(msg); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| #define osmux_chunk_length(msg, rem) (rem - msg->len); | ||||
|  | ||||
| static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| { | ||||
| 	struct msgb *msg; | ||||
| 	struct osmux_hdr *osmuxh; | ||||
| 	struct sockaddr_in addr; | ||||
| 	struct mgcp_config *cfg = ofd->data; | ||||
| 	uint32_t rem; | ||||
| 	struct mgcp_conn_rtp *conn_src; | ||||
|  | ||||
| 	msg = osmux_recv(ofd, &addr); | ||||
| 	if (!msg) | ||||
| 		return -1; | ||||
|  | ||||
| 	if (!cfg->osmux) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "bsc-nat wants to use Osmux but bsc did not request it\n"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	/* not any further processing dummy messages */ | ||||
| 	if (msg->data[0] == MGCP_DUMMY_LOAD) | ||||
| 		return osmux_handle_dummy(cfg, &addr, msg); | ||||
|  | ||||
| 	rem = msg->len; | ||||
| 	while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) { | ||||
|  | ||||
| 		conn_src = osmux_conn_lookup(cfg, osmuxh->circuit_id, | ||||
| 					     &addr.sin_addr); | ||||
| 		if (!conn_src) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Cannot find a src conn for circuit_id=%d\n", | ||||
| 			     osmuxh->circuit_id); | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		/*conn_dst = mgcp_find_dst_conn(conn_src->conn); | ||||
| 		if (!conn_dst) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Cannot find a dst conn for circuit_id=%d\n", | ||||
| 			     osmuxh->circuit_id); | ||||
| 			goto out; | ||||
| 		}*/ | ||||
|  | ||||
| 		if (endp_osmux_state_check(conn_src->conn->endp, conn_src, false) == 0) { | ||||
| 			conn_src->osmux.stats.octets += osmux_chunk_length(msg, rem); | ||||
| 			conn_src->osmux.stats.chunks++; | ||||
| 			osmux_xfrm_output_sched(&conn_src->osmux.out, osmuxh); | ||||
| 		} | ||||
| 		rem = msg->len; | ||||
| 	} | ||||
| out: | ||||
| 	msgb_free(msg); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int osmux_init(int role, struct mgcp_config *cfg) | ||||
| { | ||||
| 	int ret; | ||||
|  | ||||
| 	osmux_fd.cb = osmux_read_fd_cb; | ||||
| 	osmux_fd.data = cfg; | ||||
|  | ||||
| 	ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port); | ||||
| 	if (ret < 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket to %s:%u\n", | ||||
| 		     cfg->osmux_addr, cfg->osmux_port); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp); | ||||
| 	osmux_fd.when |= BSC_FD_READ; | ||||
|  | ||||
| 	ret = osmo_fd_register(&osmux_fd); | ||||
| 	if (ret < 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket %s\n", | ||||
| 		     osmo_sock_get_name2(osmux_fd.fd)); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	cfg->osmux_init = 1; | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_INFO, "OSMUX socket listening on %s\n", | ||||
| 		 osmo_sock_get_name2(osmux_fd.fd)); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! enable OSXMUX circuit for a specified connection. | ||||
|  *  \param[in] endp mgcp endpoint (configuration) | ||||
|  *  \param[in] conn connection to disable | ||||
|  *  \param[in] addr IP address of remote OSMUX endpoint | ||||
|  *  \param[in] port portnumber of the remote OSMUX endpoint (in network byte order) | ||||
|  *  \returns 0 on success, -1 on ERROR */ | ||||
| int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn, | ||||
| 		      struct in_addr *addr, uint16_t port) | ||||
| { | ||||
| 	/*! If osmux is enabled, initialize the output handler. This handler is | ||||
| 	 *  used to reconstruct the RTP flow from osmux. The RTP SSRC is | ||||
| 	 *  allocated based on the circuit ID (conn_net->osmux.cid), which is unique | ||||
| 	 *  in the local scope to the BSC/BSC-NAT. We use it to divide the RTP | ||||
| 	 *  SSRC space (2^32) by the OSMUX_CID_MAX + 1 possible circuit IDs, then randomly | ||||
| 	 *  select one value from that window. Thus, we have no chance to have | ||||
| 	 *  overlapping RTP SSRC traveling to the BTSes behind the BSC, | ||||
| 	 *  similarly, for flows traveling to the MSC. | ||||
| 	 */ | ||||
| 	struct in_addr addr_unset = {}; | ||||
| 	static const uint32_t rtp_ssrc_winlen = UINT32_MAX / (OSMUX_CID_MAX + 1); | ||||
| 	uint16_t osmux_dummy = endp->cfg->osmux_dummy; | ||||
|  | ||||
| 	/* Check if osmux is enabled for the specified connection */ | ||||
| 	if (conn->osmux.state != OSMUX_STATE_ACTIVATING) { | ||||
| 		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, | ||||
| 			 "conn:%s didn't negotiate Osmux, state %d\n", | ||||
| 			 mgcp_conn_dump(conn->conn), conn->osmux.state); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* Wait until we have the connection information from MDCX */ | ||||
| 	if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0) { | ||||
| 		LOGPCONN(conn->conn, DLMGCP, LOGL_INFO, | ||||
| 			"Osmux remote address/port still unknown\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port); | ||||
| 	if (!conn->osmux.in) { | ||||
| 		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, | ||||
| 			"Cannot allocate input osmux handle for conn:%s\n", | ||||
| 			mgcp_conn_dump(conn->conn)); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy) < 0) { | ||||
| 		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, | ||||
| 			"Cannot open osmux circuit %u for conn:%s\n", | ||||
| 		     conn->osmux.cid, mgcp_conn_dump(conn->conn)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	osmux_xfrm_output_init2(&conn->osmux.out, | ||||
| 			       (conn->osmux.cid * rtp_ssrc_winlen) + | ||||
| 			        (random() % rtp_ssrc_winlen), | ||||
| 			       conn->end.codec->payload_type); | ||||
|  | ||||
| 	osmux_xfrm_output_set_tx_cb(&conn->osmux.out, | ||||
| 				    scheduled_from_osmux_tx_rtp_cb, conn); | ||||
|  | ||||
| 	conn->osmux.state = OSMUX_STATE_ENABLED; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! disable OSXMUX circuit for a specified connection. | ||||
|  *  \param[in] conn connection to disable */ | ||||
| void conn_osmux_disable(struct mgcp_conn_rtp *conn) | ||||
| { | ||||
|  | ||||
| 	OSMO_ASSERT(conn->osmux.state != OSMUX_STATE_DISABLED); | ||||
|  | ||||
| 	LOGPCONN(conn->conn, DLMGCP, LOGL_INFO, | ||||
| 		"Releasing connection using Osmux CID %u\n", conn->osmux.cid); | ||||
|  | ||||
| 	if (conn->osmux.state == OSMUX_STATE_ENABLED) { | ||||
| 		/* We are closing, we don't need pending RTP packets to be transmitted */ | ||||
| 		osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL); | ||||
| 		osmux_xfrm_output_flush(&conn->osmux.out); | ||||
|  | ||||
| 		osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid); | ||||
| 		conn->osmux.state = OSMUX_STATE_DISABLED; | ||||
| 		conn_osmux_release_cid(conn); | ||||
| 		osmux_handle_put(conn->osmux.in); | ||||
| 	} | ||||
| 	conn_osmux_release_cid(conn); | ||||
| } | ||||
|  | ||||
| /*! relase OSXMUX cid, that had been allocated to this connection. | ||||
|  *  \param[in] conn connection with OSMUX cid to release */ | ||||
| void conn_osmux_release_cid(struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	if (conn->osmux.cid_allocated) | ||||
| 		osmux_cid_pool_put(conn->osmux.cid); | ||||
| 	conn->osmux.cid = 0; | ||||
| 	conn->osmux.cid_allocated = false; | ||||
| } | ||||
|  | ||||
| /*! allocate OSXMUX cid to connection. | ||||
|  *  \param[in] conn connection for which we allocate the OSMUX cid | ||||
|  * \param[in] osmux_cid OSMUX cid to allocate. -1 Means take next available one. | ||||
|  * \returns Allocated OSMUX cid, -1 on error (no free  cids avail, or selected one is already taken). | ||||
|  */ | ||||
| int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid) | ||||
| { | ||||
| 	if (osmux_cid != -1 && osmux_cid_pool_allocated((uint8_t) osmux_cid)) { | ||||
| 		LOGPCONN(conn->conn, DLMGCP, LOGL_INFO, | ||||
| 			 "Osmux CID %d already allocated!\n", | ||||
| 			 osmux_cid); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (osmux_cid == -1) { | ||||
| 		osmux_cid = osmux_cid_pool_get_next(); | ||||
| 		if (osmux_cid == -1) { | ||||
| 			LOGPCONN(conn->conn, DLMGCP, LOGL_INFO, | ||||
| 				 "no available Osmux CID to allocate!\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} else | ||||
| 		osmux_cid_pool_get(osmux_cid); | ||||
|  | ||||
| 	conn->osmux.cid = (uint8_t) osmux_cid; | ||||
| 	conn->osmux.cid_allocated = true; | ||||
| 	conn->type = MGCP_OSMUX_BSC; | ||||
| 	return osmux_cid; | ||||
| } | ||||
|  | ||||
| /*! send RTP dummy packet to OSMUX connection port. | ||||
|  *  \param[in] endp mcgp endpoint that holds the RTP connection | ||||
|  *  \param[in] conn associated RTP connection | ||||
|  *  \returns bytes sent, -1 on error */ | ||||
| int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	struct osmux_hdr *osmuxh; | ||||
| 	int buf_len; | ||||
| 	struct in_addr addr_unset = {}; | ||||
|  | ||||
| 	/*! The dummy packet will not be sent via the actual OSMUX connection, | ||||
| 	 *  instead it is sent out of band to port where the remote OSMUX | ||||
| 	 *  multplexer is listening. The goal is to ensure that the connection | ||||
| 	 *  is kept open */ | ||||
|  | ||||
| 	/*! We don't need to send the dummy load for osmux so often as another | ||||
| 	 *  endpoint may have already punched the hole in the firewall. This | ||||
| 	 *  approach is simple though. */ | ||||
|  | ||||
| 	/* Wait until we have the connection information from MDCX */ | ||||
| 	if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (endp_osmux_state_check(endp, conn, true) < 0) | ||||
| 		return 0; | ||||
|  | ||||
| 	buf_len = sizeof(struct osmux_hdr) + osmo_amr_bytes(AMR_FT_0); | ||||
| 	osmuxh = (struct osmux_hdr *) alloca(buf_len); | ||||
| 	memset(osmuxh, 0, buf_len); | ||||
| 	osmuxh->ft = OSMUX_FT_DUMMY; | ||||
| 	osmuxh->amr_ft = AMR_FT_0; | ||||
| 	osmuxh->circuit_id = conn->osmux.cid; | ||||
|  | ||||
| 	LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, | ||||
| 		 "sending OSMUX dummy load to %s:%u CID %u\n", | ||||
| 		 inet_ntoa(conn->end.addr), ntohs(conn->end.rtp_port), conn->osmux.cid); | ||||
|  | ||||
| 	return mgcp_udp_send(osmux_fd.fd, &conn->end.addr, | ||||
| 			     conn->end.rtp_port, (char*)osmuxh, buf_len); | ||||
| } | ||||
|  | ||||
| /* bsc-nat allocates/releases the Osmux circuit ID. +7 to round up to 8 bit boundary. */ | ||||
| static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1 + 7) / 8]; | ||||
|  | ||||
| /*! count the number of taken OSMUX cids. | ||||
|  *  \returns number of OSMUX cids in use */ | ||||
| int osmux_cid_pool_count_used(void) | ||||
| { | ||||
| 	int i, j, used = 0; | ||||
|  | ||||
| 	for (i = 0; i < sizeof(osmux_cid_bitmap); i++) { | ||||
| 		for (j = 0; j < 8; j++) { | ||||
| 			if (osmux_cid_bitmap[i] & (1 << j)) | ||||
| 				used += 1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return used; | ||||
| } | ||||
|  | ||||
| /*! take a free OSMUX cid. | ||||
|  *  \returns OSMUX cid */ | ||||
| int osmux_cid_pool_get_next(void) | ||||
| { | ||||
| 	int i, j; | ||||
|  | ||||
| 	for (i = 0; i < sizeof(osmux_cid_bitmap); i++) { | ||||
| 		for (j = 0; j < 8; j++) { | ||||
| 			if (osmux_cid_bitmap[i] & (1 << j)) | ||||
| 				continue; | ||||
|  | ||||
| 			osmux_cid_bitmap[i] |= (1 << j); | ||||
| 			LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 			     "Allocating Osmux CID %u from pool\n", (i * 8) + j); | ||||
| 			return (i * 8) + j; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n"); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /*! take a specific OSMUX cid. | ||||
|  *  \param[in] osmux_cid OSMUX cid */ | ||||
| void osmux_cid_pool_get(uint8_t osmux_cid) | ||||
| { | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Allocating Osmux CID %u from pool\n", osmux_cid); | ||||
| 	osmux_cid_bitmap[osmux_cid / 8] |= (1 << (osmux_cid % 8)); | ||||
| } | ||||
|  | ||||
| /*! put back a no longer used OSMUX cid. | ||||
|  *  \param[in] osmux_cid OSMUX cid */ | ||||
| void osmux_cid_pool_put(uint8_t osmux_cid) | ||||
| { | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid); | ||||
| 	osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8)); | ||||
| } | ||||
|  | ||||
| /*! check if OSMUX cid is already taken */ | ||||
| bool osmux_cid_pool_allocated(uint8_t osmux_cid) | ||||
| { | ||||
| 	return !!(osmux_cid_bitmap[osmux_cid / 8] & (1 << (osmux_cid % 8))); | ||||
| } | ||||
							
								
								
									
										1778
									
								
								src/libosmo-mgcp/mgcp_protocol.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1778
									
								
								src/libosmo-mgcp/mgcp_protocol.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										588
									
								
								src/libosmo-mgcp/mgcp_sdp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										588
									
								
								src/libosmo-mgcp/mgcp_sdp.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,588 @@ | ||||
| /* | ||||
|  * Some SDP file parsing... | ||||
|  * | ||||
|  * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2014 by On-Waves | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/mgcp/mgcp.h> | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/mgcp/mgcp_msg.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
| #include <osmocom/mgcp/mgcp_codec.h> | ||||
| #include <osmocom/mgcp/mgcp_sdp.h> | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <stdlib.h> | ||||
| #include <limits.h> | ||||
|  | ||||
| /* Two structs to store intermediate parsing results. The function | ||||
|  * mgcp_parse_sdp_data() is using the following two structs as temporary | ||||
|  * storage for parsing the SDP codec information. */ | ||||
| struct sdp_rtp_map { | ||||
| 	/* the type */ | ||||
| 	int payload_type; | ||||
| 	/* null, static or later dynamic codec name */ | ||||
| 	char *codec_name; | ||||
| 	/* A pointer to the original line for later parsing */ | ||||
| 	char *map_line; | ||||
|  | ||||
| 	int rate; | ||||
| 	int channels; | ||||
| }; | ||||
| struct sdp_fmtp_param { | ||||
| 	int payload_type; | ||||
| 	struct mgcp_codec_param param; | ||||
| }; | ||||
|  | ||||
|  | ||||
| /* Helper function to extrapolate missing codec parameters in a codec mao from | ||||
|  * an already filled in payload_type, called from: mgcp_parse_sdp_data() */ | ||||
| static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < used; ++i) { | ||||
| 		switch (codecs[i].payload_type) { | ||||
| 		case 0: | ||||
| 			codecs[i].codec_name = "PCMU"; | ||||
| 			codecs[i].rate = 8000; | ||||
| 			codecs[i].channels = 1; | ||||
| 			break; | ||||
| 		case 3: | ||||
| 			codecs[i].codec_name = "GSM"; | ||||
| 			codecs[i].rate = 8000; | ||||
| 			codecs[i].channels = 1; | ||||
| 			break; | ||||
| 		case 8: | ||||
| 			codecs[i].codec_name = "PCMA"; | ||||
| 			codecs[i].rate = 8000; | ||||
| 			codecs[i].channels = 1; | ||||
| 			break; | ||||
| 		case 18: | ||||
| 			codecs[i].codec_name = "G729"; | ||||
| 			codecs[i].rate = 8000; | ||||
| 			codecs[i].channels = 1; | ||||
| 			break; | ||||
| 		default: | ||||
| 			codecs[i].codec_name = NULL; | ||||
| 			codecs[i].rate = 0; | ||||
| 			codecs[i].channels = 0; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Helper function to update codec map information with additional data from | ||||
|  * SDP, called from: mgcp_parse_sdp_data() */ | ||||
| static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, | ||||
| 			  int payload, const char *audio_name) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < used; ++i) { | ||||
| 		char audio_codec[64]; | ||||
| 		int rate = -1; | ||||
| 		int channels = -1; | ||||
|  | ||||
| 		/* Note: We can only update payload codecs that already exist | ||||
| 		 * in our codec list. If we get an unexpected payload type, | ||||
| 		 * we just drop it */ | ||||
| 		if (codecs[i].payload_type != payload) | ||||
| 			continue; | ||||
|  | ||||
| 		if (sscanf(audio_name, "%63[^/]/%d/%d", | ||||
| 			   audio_codec, &rate, &channels) < 1) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n", | ||||
| 			     audio_name); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		codecs[i].map_line = talloc_strdup(ctx, audio_name); | ||||
| 		codecs[i].codec_name = talloc_strdup(ctx, audio_codec); | ||||
| 		codecs[i].rate = rate; | ||||
| 		codecs[i].channels = channels; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, | ||||
| 	     audio_name); | ||||
| } | ||||
|  | ||||
| /* Extract payload types from SDP, also check for duplicates */ | ||||
| static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs, | ||||
| 		       unsigned int codecs_len, char *sdp) | ||||
| { | ||||
| 	char *str; | ||||
| 	char *str_ptr; | ||||
| 	char *pt_str; | ||||
| 	char *pt_end; | ||||
| 	unsigned long int pt; | ||||
| 	unsigned int count = 0; | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	str = talloc_zero_size(ctx, strlen(sdp) + 1); | ||||
| 	str_ptr = str; | ||||
| 	strcpy(str_ptr, sdp); | ||||
|  | ||||
| 	str_ptr = strstr(str_ptr, "RTP/AVP "); | ||||
| 	if (!str_ptr) | ||||
| 		goto exit; | ||||
|  | ||||
| 	pt_str = strtok(str_ptr, " "); | ||||
| 	if (!pt_str) | ||||
| 		goto exit; | ||||
|  | ||||
| 	while (1) { | ||||
| 		/* Do not allow excessive payload types */ | ||||
| 		if (count > codecs_len) | ||||
| 			goto error; | ||||
|  | ||||
| 		pt_str = strtok(NULL, " "); | ||||
| 		if (!pt_str) | ||||
| 			break; | ||||
|  | ||||
| 		errno = 0; | ||||
| 		pt = strtoul(pt_str, &pt_end, 0); | ||||
| 		if ((errno == ERANGE && pt == ULONG_MAX) || (errno && !pt) || | ||||
| 		    pt_str == pt_end) | ||||
| 			goto error; | ||||
|  | ||||
| 		if (pt >> 7) /* PT is 7 bit field, higher values not allowed */ | ||||
| 			goto error; | ||||
|  | ||||
| 		/* Do not allow duplicate payload types */ | ||||
| 		for (i = 0; i < count; i++) | ||||
| 			if (codecs[i].payload_type == pt) | ||||
| 				goto error; | ||||
|  | ||||
| 		codecs[count].payload_type = pt; | ||||
| 		count++; | ||||
| 	} | ||||
|  | ||||
| exit: | ||||
| 	talloc_free(str); | ||||
| 	return count; | ||||
| error: | ||||
| 	talloc_free(str); | ||||
| 	return -EINVAL; | ||||
| } | ||||
|  | ||||
| /* Extract fmtp parameters from SDP, called from: mgcp_parse_sdp_data() */ | ||||
| static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp) | ||||
| { | ||||
| 	char *str; | ||||
| 	char *str_ptr; | ||||
| 	char *param_str; | ||||
| 	unsigned int pt; | ||||
| 	unsigned int count = 0; | ||||
| 	char delimiter; | ||||
| 	unsigned int amr_octet_aligned; | ||||
|  | ||||
| 	memset(fmtp_param, 0, sizeof(*fmtp_param)); | ||||
|  | ||||
| 	str = talloc_zero_size(ctx, strlen(sdp) + 1); | ||||
| 	str_ptr = str; | ||||
| 	strcpy(str_ptr, sdp); | ||||
|  | ||||
| 	/* Check if the input string begins with an fmtp token */ | ||||
| 	str_ptr = strstr(str_ptr, "fmtp:"); | ||||
| 	if (!str_ptr) | ||||
| 		goto exit; | ||||
| 	str_ptr += 5; | ||||
|  | ||||
| 	/* Extract payload type */ | ||||
| 	if (sscanf(str_ptr, "%u ", &pt) != 1) | ||||
| 		goto error; | ||||
| 	fmtp_param->payload_type = pt; | ||||
|  | ||||
| 	/* Advance pointer to the beginning of the parameter section and | ||||
| 	 * tokenize string */ | ||||
| 	str_ptr = strstr(str_ptr, " "); | ||||
| 	if (!str_ptr) | ||||
| 		goto error; | ||||
| 	str_ptr++; | ||||
|  | ||||
| 	param_str = strtok(str_ptr, " "); | ||||
| 	if (!param_str) | ||||
| 		goto exit; | ||||
|  | ||||
| 	while (1) { | ||||
| 		/* Make sure that we don't get trapped in an endless loop */ | ||||
| 		if (count > 256) | ||||
| 			goto error; | ||||
|  | ||||
| 		/* Chop off delimiters ';' at the end */ | ||||
| 		delimiter = str_ptr[strlen(str_ptr) - 1]; | ||||
| 		if (delimiter == ';' || delimiter == ',') | ||||
| 			str_ptr[strlen(str_ptr) - 1] = '\0'; | ||||
|  | ||||
| 		/* AMR octet aligned parameter */ | ||||
| 		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, " "); | ||||
| 		if (!param_str) | ||||
| 			break; | ||||
| 		count++; | ||||
| 	} | ||||
|  | ||||
| exit: | ||||
| 	talloc_free(str); | ||||
| 	return 0; | ||||
| error: | ||||
| 	talloc_free(str); | ||||
| 	return -EINVAL; | ||||
| } | ||||
|  | ||||
| /* Pick optional fmtp parameters by payload type, if there are no fmtp | ||||
|  * parameters, a nullpointer is returned */ | ||||
| static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len) | ||||
| { | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	for (i = 0; i < fmtp_params_len; i++) { | ||||
| 		if (fmtp_params[i].payload_type == pt) | ||||
| 			return &fmtp_params[i].param; | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /*! Analyze SDP input string. | ||||
|  *  \param[in] endp trunk endpoint. | ||||
|  *  \param[out] conn associated rtp connection. | ||||
|  *  \param[out] caller provided memory to store the parsing results. | ||||
|  * | ||||
|  *  Note: In conn (conn->end) the function returns the packet duration, | ||||
|  *  rtp port, rtcp port and the codec information. | ||||
|  *  \returns 0 on success, -1 on failure. */ | ||||
| int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp, | ||||
| 			struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p) | ||||
| { | ||||
| 	struct sdp_rtp_map codecs[MGCP_MAX_CODECS]; | ||||
| 	unsigned int codecs_used = 0; | ||||
| 	struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS]; | ||||
| 	unsigned int fmtp_used = 0; | ||||
| 	struct mgcp_codec_param *codec_param; | ||||
| 	char *line; | ||||
| 	unsigned int i; | ||||
| 	void *tmp_ctx = talloc_new(NULL); | ||||
| 	struct mgcp_rtp_end *rtp; | ||||
|  | ||||
| 	int payload; | ||||
| 	int ptime, ptime2 = 0; | ||||
| 	char audio_name[64]; | ||||
| 	int port, rc; | ||||
| 	char ipv4[16]; | ||||
|  | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	OSMO_ASSERT(conn); | ||||
| 	OSMO_ASSERT(p); | ||||
|  | ||||
| 	rtp = &conn->end; | ||||
| 	memset(&codecs, 0, sizeof(codecs)); | ||||
|  | ||||
| 	for_each_line(line, p->save) { | ||||
| 		switch (line[0]) { | ||||
| 		case 'o': | ||||
| 		case 's': | ||||
| 		case 't': | ||||
| 		case 'v': | ||||
| 			/* skip these SDP attributes */ | ||||
| 			break; | ||||
| 		case 'a': | ||||
| 			if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) { | ||||
| 				codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name); | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) { | ||||
| 				if (ptime2 > 0 && ptime2 != ptime) | ||||
| 					rtp->packet_duration_ms = 0; | ||||
| 				else | ||||
| 					rtp->packet_duration_ms = ptime; | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) { | ||||
| 				rtp->maximum_packet_time = ptime2; | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			if (strncmp("a=fmtp:", line, 6) == 0) { | ||||
| 				rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line); | ||||
| 				if (rc >= 0) | ||||
| 					fmtp_used++; | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			break; | ||||
| 		case 'm': | ||||
| 			rc = sscanf(line, "m=audio %d RTP/AVP", &port); | ||||
| 			if (rc == 1) { | ||||
| 				rtp->rtp_port = htons(port); | ||||
| 				rtp->rtcp_port = htons(port + 1); | ||||
| 			} | ||||
|  | ||||
| 			rc = pt_from_sdp(conn->conn, codecs, | ||||
| 					 ARRAY_SIZE(codecs), line); | ||||
| 			if (rc > 0) | ||||
| 				codecs_used = rc; | ||||
| 			break; | ||||
| 		case 'c': | ||||
|  | ||||
| 			if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) { | ||||
| 				inet_aton(ipv4, &rtp->addr); | ||||
| 			} | ||||
| 			break; | ||||
| 		default: | ||||
| 			if (p->endp) | ||||
| 				LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 				     "Unhandled SDP option: '%c'/%d on 0x%x\n", | ||||
| 				     line[0], line[0], | ||||
| 				     ENDPOINT_NUMBER(p->endp)); | ||||
| 			else | ||||
| 				LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 				     "Unhandled SDP option: '%c'/%d\n", | ||||
| 				     line[0], line[0]); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS); | ||||
|  | ||||
| 	/* So far we have only set the payload type in the codec struct. Now we | ||||
| 	 * fill up the remaining fields of the codec description with some default | ||||
| 	 * information */ | ||||
| 	codecs_initialize(tmp_ctx, codecs, codecs_used); | ||||
|  | ||||
| 	/* Store parsed codec information */ | ||||
| 	for (i = 0; i < codecs_used; i++) { | ||||
| 		codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used); | ||||
| 		rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param); | ||||
| 		if (rc < 0) | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp)); | ||||
| 	} | ||||
|  | ||||
| 	talloc_free(tmp_ctx); | ||||
|  | ||||
| 	LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE, | ||||
| 	     "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:", | ||||
| 	     ntohs(rtp->rtp_port), inet_ntoa(rtp->addr), | ||||
| 	     rtp->packet_duration_ms); | ||||
| 	if (codecs_used == 0) | ||||
| 		LOGPC(DLMGCP, LOGL_NOTICE, "none"); | ||||
| 	for (i = 0; i < codecs_used; i++) { | ||||
| 		LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s", | ||||
| 		      rtp->codecs[i].payload_type, | ||||
| 		      rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown"); | ||||
| 		LOGPC(DLMGCP, LOGL_NOTICE, " "); | ||||
| 	} | ||||
| 	LOGPC(DLMGCP, LOGL_NOTICE, "\n"); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Add rtpmap string to the sdp payload, but only when the payload type falls | ||||
|  * into the dynamic payload type range */ | ||||
| static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name) | ||||
| { | ||||
| 	int rc; | ||||
|  | ||||
| 	if (payload_type >= 96 && payload_type <= 127) { | ||||
| 		if (!audio_name) | ||||
| 			return -EINVAL; | ||||
| 		rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name); | ||||
| 		if (rc < 0) | ||||
| 			return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Add audio strings to sdp payload */ | ||||
| static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port) | ||||
| { | ||||
| 	int rc; | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port); | ||||
| 	if (rc < 0) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	for (i = 0; i < payload_types_len; i++) { | ||||
| 		rc = msgb_printf(sdp, " %d", payload_types[i]); | ||||
| 		if (rc < 0) | ||||
| 			return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	rc = msgb_printf(sdp, "\r\n"); | ||||
| 	if (rc < 0) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* 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) | ||||
| { | ||||
| 	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); | ||||
|  | ||||
| 		/* Add amr octet align parameter */ | ||||
| 		if (fmtp_params[i].param.amr_octet_aligned_present) { | ||||
| 			if (fmtp_params[i].param.amr_octet_aligned) | ||||
| 				rc = msgb_printf(sdp, " octet-align=1"); | ||||
| 			else | ||||
| 				rc = msgb_printf(sdp, " octet-align=0"); | ||||
| 			if (rc < 0) | ||||
| 				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", fmtp_params[i].payload_type); | ||||
| 		if (rc < 0) | ||||
| 			return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! Generate SDP response string. | ||||
|  *  \param[in] endp trunk endpoint. | ||||
|  *  \param[in] conn associated rtp connection. | ||||
|  *  \param[out] sdp msg buffer to append resulting SDP string data. | ||||
|  *  \param[in] addr IPV4 address string (e.g. 192.168.100.1). | ||||
|  *  \returns 0 on success, -1 on failure. */ | ||||
| int mgcp_write_response_sdp(const struct mgcp_endpoint *endp, | ||||
| 			    const struct mgcp_conn_rtp *conn, struct msgb *sdp, | ||||
| 			    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; | ||||
| 	int rc; | ||||
| 	int payload_types[1]; | ||||
| 	int local_port; | ||||
| 	struct sdp_fmtp_param fmtp_params[1]; | ||||
|         unsigned int fmtp_params_len = 0; | ||||
|  | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	OSMO_ASSERT(conn); | ||||
| 	OSMO_ASSERT(sdp); | ||||
| 	OSMO_ASSERT(addr); | ||||
|  | ||||
| 	/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */ | ||||
| 	endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp, | ||||
| 					      &codec, &fmtp_extra, | ||||
| 					      (struct mgcp_conn_rtp *)conn); | ||||
|  | ||||
| 	audio_name = codec->audio_name; | ||||
| 	payload_type = codec->payload_type; | ||||
|  | ||||
| 	rc = msgb_printf(sdp, | ||||
| 			 "v=0\r\n" | ||||
| 			 "o=- %s 23 IN IP4 %s\r\n" | ||||
| 			 "s=-\r\n" | ||||
| 			 "c=IN IP4 %s\r\n" | ||||
| 			 "t=0 0\r\n", conn->conn->id, addr, addr); | ||||
|  | ||||
| 	if (rc < 0) | ||||
| 		goto buffer_too_small; | ||||
|  | ||||
| 	if (payload_type >= 0) { | ||||
|  | ||||
| 		payload_types[0] = payload_type; | ||||
| 		if (mgcp_conn_rtp_is_osmux(conn)) | ||||
| 			local_port = endp->cfg->osmux_port; | ||||
| 		else | ||||
| 			local_port = conn->end.local_port; | ||||
| 		rc = add_audio(sdp, payload_types, 1, local_port); | ||||
| 		if (rc < 0) | ||||
| 			goto buffer_too_small; | ||||
|  | ||||
| 		if (endp->tcfg->audio_send_name) { | ||||
| 			rc = add_rtpmap(sdp, payload_type, audio_name); | ||||
| 			if (rc < 0) | ||||
| 				goto buffer_too_small; | ||||
| 		} | ||||
|  | ||||
| 		if (codec->param_present) { | ||||
| 			fmtp_param.payload_type = payload_type; | ||||
| 			fmtp_param.param = codec->param; | ||||
| 			fmtp_params[0] = fmtp_param; | ||||
| 			fmtp_params_len = 1; | ||||
| 		} | ||||
| 		rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra); | ||||
| 		if (rc < 0) | ||||
| 			goto buffer_too_small; | ||||
| 	} | ||||
| 	if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) { | ||||
| 		rc = msgb_printf(sdp, "a=ptime:%u\r\n", | ||||
| 				 conn->end.packet_duration_ms); | ||||
| 		if (rc < 0) | ||||
| 			goto buffer_too_small; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
|  | ||||
| buffer_too_small: | ||||
| 	LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n"); | ||||
| 	return -1; | ||||
| } | ||||
							
								
								
									
										138
									
								
								src/libosmo-mgcp/mgcp_stat.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/libosmo-mgcp/mgcp_stat.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | ||||
| /* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ | ||||
| /* The statistics generator */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2012 by On-Waves | ||||
|  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp_stat.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
| #include <limits.h> | ||||
| #include <inttypes.h> | ||||
|  | ||||
| /* Helper function for mgcp_format_stats_rtp() to calculate packet loss */ | ||||
| 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 = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]; | ||||
|  | ||||
| 	*expected = state->stats.cycles + state->stats.max_seq; | ||||
| 	*expected = *expected - state->stats.base_seq + 1; | ||||
|  | ||||
| 	if (!state->stats.initialized) { | ||||
| 		*expected = 0; | ||||
| 		*loss = 0; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Make sure the sign is correct and use the biggest | ||||
| 	 * positive/negative number that fits. | ||||
| 	 */ | ||||
| 	*loss = *expected - packets_rx->current; | ||||
| 	if (*expected < packets_rx->current) { | ||||
| 		if (*loss > 0) | ||||
| 			*loss = INT_MIN; | ||||
| 	} else { | ||||
| 		if (*loss < 0) | ||||
| 			*loss = INT_MAX; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Helper function for mgcp_format_stats_rtp() to calculate jitter */ | ||||
| uint32_t calc_jitter(struct mgcp_rtp_state *state) | ||||
| { | ||||
| 	if (!state->stats.initialized) | ||||
| 		return 0; | ||||
| 	return state->stats.jitter >> 4; | ||||
| } | ||||
|  | ||||
| /* Generate statistics for an RTP connection */ | ||||
| static void mgcp_format_stats_rtp(char *str, size_t str_len, | ||||
| 				  struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	uint32_t expected, jitter; | ||||
| 	int ploss; | ||||
| 	int nchars; | ||||
|  | ||||
| 	struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]; | ||||
| 	struct rate_ctr *octets_rx = &conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR]; | ||||
| 	struct rate_ctr *packets_tx = &conn->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]; | ||||
| 	struct rate_ctr *octets_tx = &conn->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR]; | ||||
|  | ||||
| 	calc_loss(conn, &expected, &ploss); | ||||
| 	jitter = calc_jitter(&conn->state); | ||||
|  | ||||
| 	nchars = snprintf(str, str_len, | ||||
| 			  "\r\nP: PS=%" PRIu64 ", OS=%" PRIu64 ", PR=%" PRIu64 ", OR=%" PRIu64 ", PL=%d, JI=%u", | ||||
| 			  packets_tx->current, octets_tx->current, | ||||
| 			  packets_rx->current, octets_rx->current, | ||||
| 			  ploss, jitter); | ||||
| 	if (nchars < 0 || nchars >= str_len) | ||||
| 		goto truncate; | ||||
|  | ||||
| 	str += nchars; | ||||
| 	str_len -= nchars; | ||||
|  | ||||
| 	if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) { | ||||
| 		/* Error Counter */ | ||||
| 		nchars = snprintf(str, str_len, | ||||
| 				  "\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64, | ||||
| 				  conn->state.in_stream.err_ts_ctr->current, | ||||
| 				  conn->state.out_stream.err_ts_ctr->current); | ||||
| 		if (nchars < 0 || nchars >= str_len) | ||||
| 			goto truncate; | ||||
|  | ||||
| 		str += nchars; | ||||
| 		str_len -= nchars; | ||||
|  | ||||
| 		if (conn->osmux.state == OSMUX_STATE_ENABLED) { | ||||
| 			snprintf(str, str_len, | ||||
| 				 "\r\nX-Osmux-ST: CR=%u, BR=%u", | ||||
| 				 conn->osmux.stats.chunks, conn->osmux.stats.octets); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| truncate: | ||||
| 	str[str_len - 1] = '\0'; | ||||
| } | ||||
|  | ||||
| /*! format statistics into an mgcp parameter string. | ||||
|  *  \param[out] str resulting string | ||||
|  *  \param[in] str_len length of the string buffer | ||||
|  *  \param[in] conn connection to evaluate */ | ||||
| void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn) | ||||
| { | ||||
| 	memset(str, 0, str_len); | ||||
| 	if (!conn) | ||||
| 		return; | ||||
|  | ||||
| 	/* NOTE: At the moment we only support generating statistics for | ||||
| 	 * RTP connections. However, in the future we may also want to | ||||
| 	 * generate statistics for other connection types as well. Lets | ||||
| 	 * keep this option open: */ | ||||
| 	switch (conn->type) { | ||||
| 	case MGCP_CONN_TYPE_RTP: | ||||
| 		mgcp_format_stats_rtp(str, str_len, &conn->u.rtp); | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -8,24 +8,23 @@ AM_CFLAGS = \ | ||||
| 	-Wall \
 | ||||
| 	$(LIBOSMOCORE_CFLAGS) \
 | ||||
| 	$(LIBOSMOVTY_CFLAGS) \
 | ||||
| 	$(LIBOSMOGSM_CFLAGS) \
 | ||||
| 	$(LIBOSMONETIF_CFLAGS) \
 | ||||
| 	$(LIBBCG729_CFLAGS) \
 | ||||
| 	$(COVERAGE_CFLAGS) \
 | ||||
| 	$(NULL) | ||||
| 
 | ||||
| bin_PROGRAMS = \
 | ||||
| 	osmo-bsc_mgcp \
 | ||||
| 	osmo-mgw \
 | ||||
| 	$(NULL) | ||||
| 
 | ||||
| osmo_bsc_mgcp_SOURCES = \
 | ||||
| 	mgcp_main.c \
 | ||||
| osmo_mgw_SOURCES = \
 | ||||
| 	mgw_main.c \
 | ||||
| 	$(NULL) | ||||
| 
 | ||||
| osmo_bsc_mgcp_LDADD = \
 | ||||
| 	$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
 | ||||
| osmo_mgw_LDADD = \
 | ||||
| 	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
 | ||||
| 	$(LIBOSMOCORE_LIBS) \
 | ||||
| 	$(LIBOSMONETIF_LIBS) \
 | ||||
| 	$(LIBOSMOVTY_LIBS) \
 | ||||
| 	$(LIBBCG729_LIBS) \
 | ||||
| 	$(LIBRARY_GSM) \
 | ||||
| 	$(LIBOSMOGSM_LIBS) \
 | ||||
| 	$(LIBOSMONETIF_LIBS) \
 | ||||
| 	$(NULL) | ||||
| @@ -4,6 +4,7 @@ | ||||
| /*
 | ||||
|  * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2011 by On-Waves | ||||
|  * (C) 2017 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
| @@ -32,9 +33,11 @@ | ||||
| 
 | ||||
| #include <sys/socket.h> | ||||
| 
 | ||||
| #include <osmocom/legacy_mgcp/mgcp.h> | ||||
| #include <osmocom/legacy_mgcp/mgcp_internal.h> | ||||
| #include <osmocom/legacy_mgcp/vty.h> | ||||
| #include <osmocom/mgcp/mgcp.h> | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/mgcp/vty.h> | ||||
| #include <osmocom/mgcp/debug.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
| 
 | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/core/msgb.h> | ||||
| @@ -43,38 +46,37 @@ | ||||
| #include <osmocom/core/stats.h> | ||||
| #include <osmocom/core/rate_ctr.h> | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/core/socket.h> | ||||
| 
 | ||||
| #include <osmocom/vty/telnet_interface.h> | ||||
| #include <osmocom/vty/logging.h> | ||||
| #include <osmocom/vty/ports.h> | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/vty/stats.h> | ||||
| #include <osmocom/vty/misc.h> | ||||
| 
 | ||||
| #include "../../bscconfig.h" | ||||
| 
 | ||||
| #ifdef BUILD_MGCP_TRANSCODING | ||||
| #include <osmocom/legacy_mgcp/mgcp_transcode.h> | ||||
| #endif | ||||
| 
 | ||||
| #define _GNU_SOURCE | ||||
| #include <getopt.h> | ||||
| 
 | ||||
| #warning "Make use of the rtp proxy code" | ||||
| /* FIXME: Make use of the rtp proxy code */ | ||||
| 
 | ||||
| static struct mgcp_config *cfg; | ||||
| static struct mgcp_trunk_config *reset_trunk; | ||||
| static int reset_endpoints = 0; | ||||
| static int daemonize = 0; | ||||
| 
 | ||||
| const char *openbsc_copyright = | ||||
| const char *osmomgw_copyright = | ||||
| 	"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n" | ||||
| 	"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" | ||||
| 	"Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n" | ||||
| 	"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n" | ||||
| 	"Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n" | ||||
| 	"Philipp Maier\r\n\r\n" | ||||
| 	"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n" | ||||
| 	"This is free software: you are free to change and redistribute it.\r\n" | ||||
| 	"There is NO WARRANTY, to the extent permitted by law.\r\n"; | ||||
| 
 | ||||
| static char *config_file = "mgcp.cfg"; | ||||
| static char *config_file = "osmo-mgw.cfg"; | ||||
| 
 | ||||
| /* used by msgb and mgcp */ | ||||
| void *tall_bsc_ctx = NULL; | ||||
| @@ -102,7 +104,7 @@ static void handle_options(int argc, char **argv) | ||||
| 			{0, 0, 0, 0}, | ||||
| 		}; | ||||
| 
 | ||||
| 		c = getopt_long(argc, argv, "hc:VD", long_options, &option_index); | ||||
| 		c = getopt_long(argc, argv, "hc:sVD", long_options, &option_index); | ||||
| 
 | ||||
| 		if (c == -1) | ||||
| 			break; | ||||
| @@ -130,12 +132,20 @@ static void handle_options(int argc, char **argv) | ||||
| 			break; | ||||
| 		}; | ||||
| 	} | ||||
| 	if (argc > optind) { | ||||
| 		fprintf(stderr, "Unsupported positional arguments on command line\n"); | ||||
| 		exit(2); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* simply remember this */ | ||||
| /* Callback function to be called when the RSIP ("Reset in Progress") mgcp
 | ||||
|  * command is received */ | ||||
| static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg) | ||||
| { | ||||
| 	/* Set flag so that, when read_call_agent() is called next time
 | ||||
| 	 * the reset can progress */ | ||||
| 	reset_endpoints = 1; | ||||
| 
 | ||||
| 	reset_trunk = tcfg; | ||||
| 
 | ||||
| 	return 0; | ||||
| @@ -173,15 +183,19 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what) | ||||
| 		msgb_free(resp); | ||||
| 	} | ||||
| 
 | ||||
| 	/* reset endpoints */ | ||||
| 	if (reset_endpoints) { | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 		     "Asked to reset endpoints: %d/%d\n", | ||||
| 		     reset_trunk->trunk_nr, reset_trunk->trunk_type); | ||||
| 
 | ||||
| 		/* reset flag */ | ||||
| 		reset_endpoints = 0; | ||||
| 
 | ||||
| 		/* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */ | ||||
| 		/* Walk over all endpoints and trigger a release, this will release all
 | ||||
| 		 * endpoints, possible open connections are forcefully dropped */ | ||||
| 		for (i = 1; i < reset_trunk->number_endpoints; ++i) | ||||
| 			mgcp_release_endp(&reset_trunk->endpoints[i]); | ||||
| 			mgcp_endp_release(&reset_trunk->endpoints[i]); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| @@ -189,38 +203,38 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what) | ||||
| 
 | ||||
| int mgcp_vty_is_config_node(struct vty *vty, int node) | ||||
| { | ||||
|         switch (node) { | ||||
|         case CONFIG_NODE: | ||||
|                 return 0; | ||||
| 	switch (node) { | ||||
| 	case CONFIG_NODE: | ||||
| 		return 0; | ||||
| 
 | ||||
|         default: | ||||
|                 return 1; | ||||
|         } | ||||
| 	default: | ||||
| 		return 1; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int mgcp_vty_go_parent(struct vty *vty) | ||||
| { | ||||
|         switch (vty->node) { | ||||
|         case TRUNK_NODE: | ||||
|                 vty->node = MGCP_NODE; | ||||
|                 vty->index = NULL; | ||||
|                 break; | ||||
|         case MGCP_NODE: | ||||
|         default: | ||||
|                 if (mgcp_vty_is_config_node(vty, vty->node)) | ||||
|                         vty->node = CONFIG_NODE; | ||||
|                 else | ||||
|                         vty->node = ENABLE_NODE; | ||||
| 	switch (vty->node) { | ||||
| 	case TRUNK_NODE: | ||||
| 		vty->node = MGCP_NODE; | ||||
| 		vty->index = NULL; | ||||
| 		break; | ||||
| 	case MGCP_NODE: | ||||
| 	default: | ||||
| 		if (mgcp_vty_is_config_node(vty, vty->node)) | ||||
| 			vty->node = CONFIG_NODE; | ||||
| 		else | ||||
| 			vty->node = ENABLE_NODE; | ||||
| 
 | ||||
|                 vty->index = NULL; | ||||
|         } | ||||
| 		vty->index = NULL; | ||||
| 	} | ||||
| 
 | ||||
|         return vty->node; | ||||
| 	return vty->node; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static struct vty_app_info vty_info = { | ||||
| 	.name 		= "OpenBSC MGCP", | ||||
| 	.name 		= "OsmoMGW", | ||||
| 	.version	= PACKAGE_VERSION, | ||||
| 	.go_parent_cb	= mgcp_vty_go_parent, | ||||
| 	.is_config_node	= mgcp_vty_is_config_node, | ||||
| @@ -228,40 +242,41 @@ static struct vty_app_info vty_info = { | ||||
| 
 | ||||
| static const struct log_info_cat log_categories[] = { | ||||
| 	/* DLMGCP is provided by the MGCP library */ | ||||
| 	[DRTP] = { | ||||
| 		  .name = "DRTP", | ||||
| 		  .description = "RTP stream handling", | ||||
| 		  .color = "\033[1;30m", | ||||
| 		  .enabled = 1,.loglevel = LOGL_NOTICE, | ||||
| 		  }, | ||||
| }; | ||||
| 
 | ||||
| const struct log_info log_info = { | ||||
|         .cat = log_categories, | ||||
|         .num_cat = ARRAY_SIZE(log_categories), | ||||
| 	.cat = log_categories, | ||||
| 	.num_cat = ARRAY_SIZE(log_categories), | ||||
| }; | ||||
| 
 | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	struct sockaddr_in addr; | ||||
| 	int on = 1, rc; | ||||
| 	unsigned int flags; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent"); | ||||
| 	vty_info.tall_ctx = tall_bsc_ctx; | ||||
| 
 | ||||
| 	msgb_talloc_ctx_init(tall_bsc_ctx, 0); | ||||
| 
 | ||||
| 	osmo_init_ignore_signals(); | ||||
| 	osmo_init_logging(&log_info); | ||||
| 	osmo_init_logging2(tall_bsc_ctx, &log_info); | ||||
| 
 | ||||
| 	cfg = mgcp_config_alloc(); | ||||
| 	if (!cfg) | ||||
| 		return -1; | ||||
| 
 | ||||
| #ifdef BUILD_MGCP_TRANSCODING | ||||
| 	cfg->setup_rtp_processing_cb = &mgcp_transcoding_setup; | ||||
| 	cfg->rtp_processing_cb = &mgcp_transcoding_process_rtp; | ||||
| 	cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format; | ||||
| #endif | ||||
| 
 | ||||
| 	cfg->trunk.force_realloc = 1; | ||||
| 
 | ||||
| 	vty_info.copyright = openbsc_copyright; | ||||
| 	vty_info.copyright = osmomgw_copyright; | ||||
| 	vty_init(&vty_info); | ||||
| 	logging_vty_add_cmds(NULL); | ||||
| 	osmo_stats_vty_add_cmds(&log_info); | ||||
| 	logging_vty_add_cmds(); | ||||
| 	osmo_talloc_vty_add_cmds(); | ||||
| 	osmo_stats_vty_add_cmds(); | ||||
| 	mgcp_vty_init(); | ||||
| 
 | ||||
| 	handle_options(argc, argv); | ||||
| @@ -275,61 +290,37 @@ int main(int argc, char **argv) | ||||
| 
 | ||||
| 	/* start telnet after reading config for vty_get_bind_addr() */ | ||||
| 	rc = telnet_init_dynif(tall_bsc_ctx, NULL, | ||||
| 			       vty_get_bind_addr(), OSMO_VTY_PORT_BSC_MGCP); | ||||
| 			       vty_get_bind_addr(), OSMO_VTY_PORT_MGW); | ||||
| 	if (rc < 0) | ||||
| 		return rc; | ||||
| 
 | ||||
| 	/* set some callbacks */ | ||||
| 	/* Set the reset callback function. This functions is called when the
 | ||||
| 	 * mgcp-command "RSIP" (Reset in Progress) is received */ | ||||
| 	cfg->reset_cb = mgcp_rsip_cb; | ||||
| 
 | ||||
|         /* we need to bind a socket */ | ||||
|         if (rc == 0) { | ||||
| 		cfg->gw_fd.bfd.when = BSC_FD_READ; | ||||
| 		cfg->gw_fd.bfd.cb = read_call_agent; | ||||
| 		cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); | ||||
| 		if (cfg->gw_fd.bfd.fd < 0) { | ||||
| 			perror("Gateway failed to listen"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	/* we need to bind a socket */ | ||||
| 	flags = OSMO_SOCK_F_BIND; | ||||
| 	if (cfg->call_agent_addr) | ||||
| 		flags |= OSMO_SOCK_F_CONNECT; | ||||
| 
 | ||||
| 		setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); | ||||
| 
 | ||||
| 		memset(&addr, 0, sizeof(addr)); | ||||
| 		addr.sin_family = AF_INET; | ||||
| 		addr.sin_port = htons(cfg->source_port); | ||||
| 		inet_aton(cfg->source_addr, &addr.sin_addr); | ||||
| 
 | ||||
| 		if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | ||||
| 			perror("Gateway failed to bind"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg"); | ||||
| 		if (!cfg->gw_fd.bfd.data) { | ||||
| 			fprintf(stderr, "Gateway memory error.\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		if (cfg->call_agent_addr) { | ||||
| 			addr.sin_port = htons(2727); | ||||
| 			inet_aton(cfg->call_agent_addr, &addr.sin_addr); | ||||
| 			if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | ||||
| 				LOGP(DLMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n", | ||||
| 				     cfg->call_agent_addr, errno); | ||||
| 				close(cfg->gw_fd.bfd.fd); | ||||
| 				cfg->gw_fd.bfd.fd = -1; | ||||
| 				return -1; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) { | ||||
| 			LOGP(DLMGCP, LOGL_FATAL, "Failed to register the fd\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP.\n"); | ||||
| 	rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, | ||||
| 				cfg->source_addr, cfg->source_port, | ||||
| 				cfg->call_agent_addr, cfg->call_agent_addr ? 2727 : 0, flags); | ||||
| 	if (rc < 0) { | ||||
| 		perror("Gateway failed to bind"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	cfg->gw_fd.bfd.cb = read_call_agent; | ||||
| 	cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg"); | ||||
| 	if (!cfg->gw_fd.bfd.data) { | ||||
| 		fprintf(stderr, "Gateway memory error.\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP, listen on %s:%u\n", | ||||
| 	     cfg->source_addr, cfg->source_port); | ||||
| 
 | ||||
| 	/* initialisation */ | ||||
| 	srand(time(NULL)); | ||||
| 
 | ||||
| @@ -1,6 +1,6 @@ | ||||
| SUBDIRS = \ | ||||
| 	legacy_mgcp \ | ||||
| 	mgcp_client \ | ||||
| 	mgcp \ | ||||
| 	$(NULL) | ||||
|  | ||||
| # The `:;' works around a Bash 3.2 bug when the output is not writeable. | ||||
| @@ -25,7 +25,6 @@ EXTRA_DIST = \ | ||||
| 	testsuite.at \ | ||||
| 	$(srcdir)/package.m4 \ | ||||
| 	$(TESTSUITE) \ | ||||
| 	vty_test_runner.py \ | ||||
| 	$(NULL) | ||||
|  | ||||
| TESTSUITE = $(srcdir)/testsuite | ||||
| @@ -38,7 +37,6 @@ if ENABLE_EXT_TESTS | ||||
| python-tests: $(BUILT_SOURCES) | ||||
| 	osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v | ||||
| 	osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v | ||||
| 	$(PYTHON) $(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v | ||||
| else | ||||
| python-tests: $(BUILT_SOURCES) | ||||
| 	echo "Not running python-based tests (determined at configure-time)" | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| enable_nat_test='@osmo_ac_build_nat@' | ||||
| enable_smpp_test='@osmo_ac_build_smpp@' | ||||
| enable_bsc_test='@osmo_ac_build_bsc@' | ||||
| enable_mgcp_transcoding_test='@osmo_ac_mgcp_transcoding@' | ||||
| enable_sgsn_test='@found_libgtp_and_libcares@' | ||||
| enable_oap_test='@found_libgtp_and_libcares@' | ||||
| enable_gtphub_test='@found_libgtp_and_libcares@' | ||||
|   | ||||
| @@ -1,61 +0,0 @@ | ||||
| AM_CPPFLAGS = \ | ||||
| 	$(all_includes) \ | ||||
| 	-I$(top_srcdir)/include \ | ||||
| 	-I$(top_srcdir) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	-ggdb3 \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMONETIF_CFLAGS) \ | ||||
| 	$(LIBBCG729_CFLAGS) \ | ||||
| 	$(COVERAGE_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	$(COVERAGE_LDFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	mgcp_test.ok \ | ||||
| 	mgcp_transcoding_test.ok \ | ||||
| 	$(NULL) | ||||
|  | ||||
| noinst_PROGRAMS = \ | ||||
| 	mgcp_test \ | ||||
| 	$(NULL) | ||||
| if BUILD_MGCP_TRANSCODING | ||||
| noinst_PROGRAMS += \ | ||||
| 	mgcp_transcoding_test \ | ||||
| 	$(NULL) | ||||
| endif | ||||
|  | ||||
| mgcp_test_SOURCES = \ | ||||
| 	mgcp_test.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| mgcp_test_LDADD = \ | ||||
| 	$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(LIBRARY_DL) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	$(LIBRARY_GSM) \ | ||||
| 	-lm  \ | ||||
| 	$(NULL) | ||||
|  | ||||
| mgcp_transcoding_test_SOURCES = \ | ||||
| 	mgcp_transcoding_test.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| mgcp_transcoding_test_LDADD = \ | ||||
| 	$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(LIBBCG729_LIBS) \ | ||||
| 	$(LIBRARY_DL) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	$(LIBRARY_GSM) \ | ||||
| 	-lm \ | ||||
| 	$(NULL) | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,480 +0,0 @@ | ||||
| line: 'one CR' | ||||
| line: 'two CR' | ||||
| line: '' | ||||
| line: 'one CRLF' | ||||
| line: 'two CRLF' | ||||
| line: '' | ||||
| line: 'one LF' | ||||
| line: 'two LF' | ||||
| line: '' | ||||
| line: 'mixed (4 lines)' | ||||
| line: '' | ||||
| line: '' | ||||
| line: '' | ||||
| Testing AUEP1 | ||||
| Testing AUEP2 | ||||
| Testing MDCX1 | ||||
| Testing MDCX2 | ||||
| Testing CRCX | ||||
| Dummy packets: 1 | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-20 | ||||
| Connection mode: 1: RECV | ||||
| Testing MDCX3 | ||||
| Dummy packets: 1 | ||||
| Packet duration not set | ||||
| Requested packetization period not set | ||||
| Connection mode not set | ||||
| Testing MDCX4 | ||||
| Dummy packets: 1 | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-20 | ||||
| Connection mode: 3: SEND RECV | ||||
| Testing MDCX4_PT1 | ||||
| Dummy packets: 1 | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-40 | ||||
| Connection mode: 3: SEND RECV | ||||
| Testing MDCX4_PT2 | ||||
| Dummy packets: 1 | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-20 | ||||
| Connection mode: 3: SEND RECV | ||||
| Testing MDCX4_PT3 | ||||
| Dummy packets: 1 | ||||
| Detected packet duration: 40 | ||||
| Requested packetization period not set | ||||
| Connection mode: 3: SEND RECV | ||||
| Testing MDCX4_SO | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-20 | ||||
| Connection mode: 2: SEND | ||||
| Testing MDCX4_RO | ||||
| Dummy packets: 1 | ||||
| Packet duration not set | ||||
| Requested packetetization period: 20-20 | ||||
| Connection mode: 1: RECV | ||||
| Testing DLCX | ||||
| Detected packet duration: 20 | ||||
| Requested packetization period not set | ||||
| Connection mode: 0: NONE | ||||
| Testing CRCX_ZYN | ||||
| Dummy packets: 1 | ||||
| Packet duration not set | ||||
| Requested packetization period not set | ||||
| Connection mode: 1: RECV | ||||
| Testing EMPTY | ||||
| Testing SHORT1 | ||||
| Testing SHORT2 | ||||
| Testing SHORT3 | ||||
| Testing SHORT4 | ||||
| Testing RQNT1 | ||||
| Testing RQNT2 | ||||
| Testing DLCX | ||||
| Detected packet duration: 20 | ||||
| Requested packetization period not set | ||||
| Connection mode: 0: NONE | ||||
| Testing CRCX | ||||
| Dummy packets: 1 | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-20 | ||||
| Connection mode: 1: RECV | ||||
| Testing MDCX3 | ||||
| Dummy packets: 1 | ||||
| Packet duration not set | ||||
| Requested packetization period not set | ||||
| Connection mode not set | ||||
| Testing DLCX | ||||
| Detected packet duration: 20 | ||||
| Requested packetization period not set | ||||
| Connection mode: 0: NONE | ||||
| Testing CRCX | ||||
| Re-transmitting CRCX | ||||
| Testing RQNT1 | ||||
| Re-transmitting RQNT1 | ||||
| Testing RQNT2 | ||||
| Re-transmitting RQNT2 | ||||
| Testing MDCX3 | ||||
| Re-transmitting MDCX3 | ||||
| Testing DLCX | ||||
| Re-transmitting DLCX | ||||
| Testing packet loss calculation. | ||||
| Testing stat parsing | ||||
| Parsing result: 0 | ||||
| Parsing result: 0 | ||||
| Testing packet error detection, patch SSRC. | ||||
| Output SSRC changed to 11223344 | ||||
| In TS: 0, dTS: 0, Seq: 0 | ||||
| Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = 0 | ||||
| In TS: 160, dTS: 160, Seq: 1 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = 0 | ||||
| In TS: 320, dTS: 160, Seq: 2 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = 0 | ||||
| In TS: 320, dTS: 160, Seq: 3 | ||||
| Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 10, Transit = 160 | ||||
| In TS: 480, dTS: 160, Seq: 4 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 9, Transit = 160 | ||||
| In TS: 640, dTS: 160, Seq: 5 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 8, Transit = 160 | ||||
| In TS: 960, dTS: 320, Seq: 6 | ||||
| Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 18, Transit = 0 | ||||
| In TS: 1120, dTS: 160, Seq: 7 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 17, Transit = 0 | ||||
| In TS: 1280, dTS: 160, Seq: 8 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 16, Transit = 0 | ||||
| In TS: 1400, dTS: 120, Seq: 9 | ||||
| Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 | ||||
| Stats: Jitter = 17, Transit = 40 | ||||
| In TS: 1560, dTS: 160, Seq: 10 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 16, Transit = 40 | ||||
| In TS: 1720, dTS: 160, Seq: 11 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 15, Transit = 40 | ||||
| In TS: 34688, dTS: 0, Seq: 12 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -32768 | ||||
| In TS: 34848, dTS: 160, Seq: 13 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -32768 | ||||
| In TS: 35008, dTS: 160, Seq: 14 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -32768 | ||||
| In TS: 35128, dTS: 120, Seq: 15 | ||||
| Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 | ||||
| Stats: Jitter = 2, Transit = -32728 | ||||
| In TS: 35288, dTS: 160, Seq: 16 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 2, Transit = -32728 | ||||
| In TS: 35448, dTS: 160, Seq: 17 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 2, Transit = -32728 | ||||
| In TS: 35768, dTS: 160, Seq: 19 | ||||
| Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 12, Transit = -32888 | ||||
| In TS: 35928, dTS: 160, Seq: 20 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 11, Transit = -32888 | ||||
| In TS: 36088, dTS: 160, Seq: 21 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 20, Transit = -33048 | ||||
| In TS: 36088, dTS: 160, Seq: 21 | ||||
| Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 29, Transit = -32888 | ||||
| In TS: 36248, dTS: 160, Seq: 22 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 27, Transit = -32888 | ||||
| In TS: 36408, dTS: 160, Seq: 23 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 25, Transit = -32888 | ||||
| In TS: 36568, dTS: 160, Seq: 23 | ||||
| Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1 | ||||
| Stats: Jitter = 24, Transit = -32888 | ||||
| In TS: 36728, dTS: 160, Seq: 24 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 22, Transit = -32888 | ||||
| In TS: 36888, dTS: 160, Seq: 25 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 21, Transit = -32888 | ||||
| In TS: 160000, dTS: 0, Seq: 1000 | ||||
| Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160160, dTS: 160, Seq: 1001 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160320, dTS: 160, Seq: 1002 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| Testing packet error detection. | ||||
| Output SSRC changed to 11223344 | ||||
| In TS: 0, dTS: 0, Seq: 0 | ||||
| Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = 0 | ||||
| In TS: 160, dTS: 160, Seq: 1 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = 0 | ||||
| In TS: 320, dTS: 160, Seq: 2 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = 0 | ||||
| In TS: 320, dTS: 160, Seq: 3 | ||||
| Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 10, Transit = 160 | ||||
| In TS: 480, dTS: 160, Seq: 4 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 9, Transit = 160 | ||||
| In TS: 640, dTS: 160, Seq: 5 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 8, Transit = 160 | ||||
| In TS: 960, dTS: 320, Seq: 6 | ||||
| Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 18, Transit = 0 | ||||
| In TS: 1120, dTS: 160, Seq: 7 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 17, Transit = 0 | ||||
| In TS: 1280, dTS: 160, Seq: 8 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 16, Transit = 0 | ||||
| In TS: 1400, dTS: 120, Seq: 9 | ||||
| Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 | ||||
| Stats: Jitter = 17, Transit = 40 | ||||
| In TS: 1560, dTS: 160, Seq: 10 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 16, Transit = 40 | ||||
| In TS: 1720, dTS: 160, Seq: 11 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 15, Transit = 40 | ||||
| Output SSRC changed to 10203040 | ||||
| In TS: 34688, dTS: 0, Seq: 12 | ||||
| Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -32768 | ||||
| In TS: 34848, dTS: 160, Seq: 13 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -32768 | ||||
| In TS: 35008, dTS: 160, Seq: 14 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -32768 | ||||
| In TS: 35128, dTS: 120, Seq: 15 | ||||
| Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 | ||||
| Stats: Jitter = 2, Transit = -32728 | ||||
| In TS: 35288, dTS: 160, Seq: 16 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 2, Transit = -32728 | ||||
| In TS: 35448, dTS: 160, Seq: 17 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 2, Transit = -32728 | ||||
| In TS: 35768, dTS: 160, Seq: 19 | ||||
| Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 12, Transit = -32888 | ||||
| In TS: 35928, dTS: 160, Seq: 20 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 11, Transit = -32888 | ||||
| In TS: 36088, dTS: 160, Seq: 21 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 20, Transit = -33048 | ||||
| In TS: 36088, dTS: 160, Seq: 21 | ||||
| Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 29, Transit = -32888 | ||||
| In TS: 36248, dTS: 160, Seq: 22 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 27, Transit = -32888 | ||||
| In TS: 36408, dTS: 160, Seq: 23 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 25, Transit = -32888 | ||||
| In TS: 36568, dTS: 160, Seq: 23 | ||||
| Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1 | ||||
| Stats: Jitter = 24, Transit = -32888 | ||||
| In TS: 36728, dTS: 160, Seq: 24 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 22, Transit = -32888 | ||||
| In TS: 36888, dTS: 160, Seq: 25 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 21, Transit = -32888 | ||||
| Output SSRC changed to 50607080 | ||||
| In TS: 160000, dTS: 0, Seq: 1000 | ||||
| Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160160, dTS: 160, Seq: 1001 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160320, dTS: 160, Seq: 1002 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| Testing packet error detection, patch timestamps. | ||||
| Output SSRC changed to 11223344 | ||||
| In TS: 0, dTS: 0, Seq: 0 | ||||
| Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = 0 | ||||
| In TS: 160, dTS: 160, Seq: 1 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = 0 | ||||
| In TS: 320, dTS: 160, Seq: 2 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = 0 | ||||
| In TS: 320, dTS: 160, Seq: 3 | ||||
| Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 10, Transit = 160 | ||||
| In TS: 480, dTS: 160, Seq: 4 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 9, Transit = 160 | ||||
| In TS: 640, dTS: 160, Seq: 5 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 8, Transit = 160 | ||||
| In TS: 960, dTS: 320, Seq: 6 | ||||
| Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 18, Transit = 0 | ||||
| In TS: 1120, dTS: 160, Seq: 7 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 17, Transit = 0 | ||||
| In TS: 1280, dTS: 160, Seq: 8 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 16, Transit = 0 | ||||
| In TS: 1400, dTS: 120, Seq: 9 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 | ||||
| Stats: Jitter = 17, Transit = 40 | ||||
| In TS: 1560, dTS: 160, Seq: 10 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 16, Transit = 40 | ||||
| In TS: 1720, dTS: 160, Seq: 11 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 15, Transit = 40 | ||||
| Output SSRC changed to 10203040 | ||||
| In TS: 34688, dTS: 0, Seq: 12 | ||||
| Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -32768 | ||||
| In TS: 34848, dTS: 160, Seq: 13 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -32768 | ||||
| In TS: 35008, dTS: 160, Seq: 14 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -32768 | ||||
| In TS: 35128, dTS: 120, Seq: 15 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 | ||||
| Stats: Jitter = 2, Transit = -32728 | ||||
| In TS: 35288, dTS: 160, Seq: 16 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 2, Transit = -32728 | ||||
| In TS: 35448, dTS: 160, Seq: 17 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 2, Transit = -32728 | ||||
| In TS: 35768, dTS: 160, Seq: 19 | ||||
| Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 12, Transit = -32888 | ||||
| In TS: 35928, dTS: 160, Seq: 20 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 11, Transit = -32888 | ||||
| In TS: 36088, dTS: 160, Seq: 21 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 20, Transit = -33048 | ||||
| In TS: 36088, dTS: 160, Seq: 21 | ||||
| Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 29, Transit = -32888 | ||||
| In TS: 36248, dTS: 160, Seq: 22 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 27, Transit = -32888 | ||||
| In TS: 36408, dTS: 160, Seq: 23 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 25, Transit = -32888 | ||||
| In TS: 36568, dTS: 160, Seq: 23 | ||||
| Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1 | ||||
| Stats: Jitter = 24, Transit = -32888 | ||||
| In TS: 36728, dTS: 160, Seq: 24 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 22, Transit = -32888 | ||||
| In TS: 36888, dTS: 160, Seq: 25 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 21, Transit = -32888 | ||||
| Output SSRC changed to 50607080 | ||||
| In TS: 160000, dTS: 0, Seq: 1000 | ||||
| Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160160, dTS: 160, Seq: 1001 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160320, dTS: 160, Seq: 1002 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| Testing packet error detection, patch SSRC, patch timestamps. | ||||
| Output SSRC changed to 11223344 | ||||
| In TS: 0, dTS: 0, Seq: 0 | ||||
| Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = 0 | ||||
| In TS: 160, dTS: 160, Seq: 1 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = 0 | ||||
| In TS: 320, dTS: 160, Seq: 2 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = 0 | ||||
| In TS: 320, dTS: 160, Seq: 3 | ||||
| Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 10, Transit = 160 | ||||
| In TS: 480, dTS: 160, Seq: 4 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 9, Transit = 160 | ||||
| In TS: 640, dTS: 160, Seq: 5 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 8, Transit = 160 | ||||
| In TS: 960, dTS: 320, Seq: 6 | ||||
| Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 18, Transit = 0 | ||||
| In TS: 1120, dTS: 160, Seq: 7 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 17, Transit = 0 | ||||
| In TS: 1280, dTS: 160, Seq: 8 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 16, Transit = 0 | ||||
| In TS: 1400, dTS: 120, Seq: 9 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 | ||||
| Stats: Jitter = 17, Transit = 40 | ||||
| In TS: 1560, dTS: 160, Seq: 10 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 16, Transit = 40 | ||||
| In TS: 1720, dTS: 160, Seq: 11 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 15, Transit = 40 | ||||
| In TS: 34688, dTS: 0, Seq: 12 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -32768 | ||||
| In TS: 34848, dTS: 160, Seq: 13 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -32768 | ||||
| In TS: 35008, dTS: 160, Seq: 14 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -32768 | ||||
| In TS: 35128, dTS: 120, Seq: 15 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 | ||||
| Stats: Jitter = 2, Transit = -32728 | ||||
| In TS: 35288, dTS: 160, Seq: 16 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 2, Transit = -32728 | ||||
| In TS: 35448, dTS: 160, Seq: 17 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 2, Transit = -32728 | ||||
| In TS: 35768, dTS: 160, Seq: 19 | ||||
| Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 12, Transit = -32888 | ||||
| In TS: 35928, dTS: 160, Seq: 20 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 11, Transit = -32888 | ||||
| In TS: 36088, dTS: 160, Seq: 21 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 20, Transit = -33048 | ||||
| In TS: 36088, dTS: 160, Seq: 21 | ||||
| Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 29, Transit = -32888 | ||||
| In TS: 36248, dTS: 160, Seq: 22 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 27, Transit = -32888 | ||||
| In TS: 36408, dTS: 160, Seq: 23 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 25, Transit = -32888 | ||||
| In TS: 36568, dTS: 160, Seq: 23 | ||||
| Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1 | ||||
| Stats: Jitter = 24, Transit = -32888 | ||||
| In TS: 36728, dTS: 160, Seq: 24 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 22, Transit = -32888 | ||||
| In TS: 36888, dTS: 160, Seq: 25 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 21, Transit = -32888 | ||||
| In TS: 160000, dTS: 0, Seq: 1000 | ||||
| Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160160, dTS: 160, Seq: 1001 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160320, dTS: 160, Seq: 1002 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| Testing multiple payload types | ||||
| Testing no sequence flow on initial packet | ||||
| Testing no rtpmap name | ||||
| Done | ||||
| @@ -1,661 +0,0 @@ | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <err.h> | ||||
| #include <stdint.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #include <osmocom/core/talloc.h> | ||||
| #include <osmocom/core/application.h> | ||||
|  | ||||
| #include <osmocom/netif/rtp.h> | ||||
|  | ||||
| #include <osmocom/legacy_mgcp/mgcp.h> | ||||
| #include <osmocom/legacy_mgcp/mgcp_internal.h> | ||||
|  | ||||
| #include "bscconfig.h" | ||||
| #ifndef BUILD_MGCP_TRANSCODING | ||||
| #error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)" | ||||
| #endif | ||||
|  | ||||
| #include <osmocom/legacy_mgcp/mgcp_transcode.h> | ||||
|  | ||||
| uint8_t *audio_frame_l16[] = { | ||||
| }; | ||||
|  | ||||
| struct rtp_packets { | ||||
| 	float t; | ||||
| 	int len; | ||||
| 	char *data; | ||||
| }; | ||||
|  | ||||
| struct rtp_packets audio_packets_l16[] = { | ||||
| 	/* RTP: SeqNo=1, TS=160 */ | ||||
| 	{0.020000, 332, | ||||
| 		"\x80\x0B\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| struct rtp_packets audio_packets_gsm[] = { | ||||
| 	/* RTP: SeqNo=1, TS=160 */ | ||||
| 	{0.020000, 45, | ||||
| 		"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" | ||||
| 		"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B" | ||||
| 		"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A" | ||||
| 		"\xDE" | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| struct rtp_packets audio_packets_gsm_invalid_size[] = { | ||||
| 	/* RTP: SeqNo=1, TS=160 */ | ||||
| 	{0.020000, 41, | ||||
| 		"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" | ||||
| 		"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B" | ||||
| 		"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A" | ||||
| 		"\xDE" | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| struct rtp_packets audio_packets_gsm_invalid_data[] = { | ||||
| 	/* RTP: SeqNo=1, TS=160 */ | ||||
| 	{0.020000, 45, | ||||
| 		"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" | ||||
| 		"\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE" | ||||
| 		"\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE" | ||||
| 		"\xEE" | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| struct rtp_packets audio_packets_gsm_invalid_ptype[] = { | ||||
| 	/* RTP: SeqNo=1, TS=160 */ | ||||
| 	{0.020000, 45, | ||||
| 		"\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" | ||||
| 		"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B" | ||||
| 		"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A" | ||||
| 		"\xDE" | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| struct rtp_packets audio_packets_g729[] = { | ||||
| 	/* RTP: SeqNo=1, TS=160 */ | ||||
| 	{0.020000, 32, | ||||
| 		"\x80\x12\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" | ||||
| 		"\xAF\xC2\x81\x40\x00\xFA\xCE\xA4\x21\x7C\xC5\xC3\x4F\xA5\x98\xF5" | ||||
| 		"\xB2\x95\xC4\xAD" | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| struct rtp_packets audio_packets_pcma[] = { | ||||
| 	/* RTP: SeqNo=1, TS=160 */ | ||||
| 	{0.020000, 172, | ||||
| 		"\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" | ||||
| 		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" | ||||
| 		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" | ||||
| 		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" | ||||
| 		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" | ||||
| 		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" | ||||
| 		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" | ||||
| 		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" | ||||
| 		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" | ||||
| 		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" | ||||
| 		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" | ||||
| 	}, | ||||
| 	/* RTP: SeqNo=26527, TS=232640 */ | ||||
| 	{0.020000, 92, | ||||
| 		"\x80\x08\x67\x9f\x00\x03\x8c\xc0\x04\xaa\x67\x9f\xd5\xd5\xd5\xd5" | ||||
| 		"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5" | ||||
| 		"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5" | ||||
| 		"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5" | ||||
| 		"\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\xd5\xd5\x55\x55\xd5\xd5\x55\x55" | ||||
| 		"\xd5\xd5\xd5\x55\x55\xd5\xd5\xd5\x55\x55\xd5\xd5" | ||||
| 	}, | ||||
| 	/* RTP: SeqNo=26528, TS=232720 */ | ||||
| 	{0.020000, 92, | ||||
| 		"\x80\x08\x67\xa0\x00\x03\x8d\x10\x04\xaa\x67\x9f\x55\xd5\xd5\x55" | ||||
| 		"\xd5\x55\xd5\xd5\xd5\x55\xd5\x55\xd5\xd5\x55\xd5\x55\xd5\x55\xd5" | ||||
| 		"\x55\x55\xd5\x55\xd5\xd5\x55\x55\x55\x55\x55\xd5\xd5\x55\xd5\xd5" | ||||
| 		"\xd5\x55\xd5\xd5\xd5\x55\x54\x55\xd5\xd5\x55\xd5\xd5\xd5\xd5\x55" | ||||
| 		"\x54\x55\xd5\x55\xd5\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd4" | ||||
| 		"\xd5\x54\x55\xd5\xd4\xd5\x54\xd5\x55\xd5\xd5\xd5" | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
| static int audio_name_to_type(const char *name) | ||||
| { | ||||
| 	if (!strcasecmp(name, "gsm")) | ||||
| 		return 3; | ||||
| #ifdef HAVE_BCG729 | ||||
| 	else if (!strcasecmp(name, "g729")) | ||||
| 		return 18; | ||||
| #endif | ||||
| 	else if (!strcasecmp(name, "pcma")) | ||||
| 		return 8; | ||||
| 	else if (!strcasecmp(name, "l16")) | ||||
| 		return 11; | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst); | ||||
|  | ||||
| static int given_configured_endpoint(int in_samples, int out_samples, | ||||
| 				const char *srcfmt, const char *dstfmt, | ||||
| 				void **out_ctx, struct mgcp_endpoint **out_endp) | ||||
| { | ||||
| 	int rc; | ||||
| 	struct mgcp_rtp_end *dst_end; | ||||
| 	struct mgcp_rtp_end *src_end; | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct mgcp_trunk_config *tcfg; | ||||
| 	struct mgcp_endpoint *endp; | ||||
|  | ||||
| 	cfg = mgcp_config_alloc(); | ||||
| 	tcfg = talloc_zero(cfg, struct mgcp_trunk_config); | ||||
| 	endp = talloc_zero(tcfg, struct mgcp_endpoint); | ||||
|  | ||||
| 	cfg->setup_rtp_processing_cb = mgcp_transcoding_setup; | ||||
| 	cfg->rtp_processing_cb = mgcp_transcoding_process_rtp; | ||||
| 	cfg->get_net_downlink_format_cb = mgcp_transcoding_net_downlink_format; | ||||
|  | ||||
| 	tcfg->endpoints = endp; | ||||
| 	tcfg->number_endpoints = 1; | ||||
| 	tcfg->cfg = cfg; | ||||
| 	endp->tcfg = tcfg; | ||||
| 	endp->cfg = cfg; | ||||
| 	mgcp_initialize_endp(endp); | ||||
|  | ||||
| 	dst_end = &endp->bts_end; | ||||
| 	dst_end->codec.payload_type = audio_name_to_type(dstfmt); | ||||
|  | ||||
| 	src_end = &endp->net_end; | ||||
| 	src_end->codec.payload_type = audio_name_to_type(srcfmt); | ||||
|  | ||||
| 	if (out_samples) { | ||||
| 		dst_end->codec.frame_duration_den = dst_end->codec.rate; | ||||
| 		dst_end->codec.frame_duration_num = out_samples; | ||||
| 		dst_end->frames_per_packet = 1; | ||||
| 		dst_end->force_output_ptime = 1; | ||||
| 	} | ||||
|  | ||||
| 	rc = mgcp_transcoding_setup(endp, dst_end, src_end); | ||||
| 	if (rc < 0) { | ||||
| 		printf("setup failed: %s", strerror(-rc)); | ||||
| 		abort(); | ||||
| 	} | ||||
|  | ||||
| 	*out_ctx = cfg; | ||||
| 	*out_endp = endp; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int transcode_test(const char *srcfmt, const char *dstfmt, | ||||
| 			  uint8_t *src_pkts, size_t src_pkt_size) | ||||
| { | ||||
| 	char buf[4096] = {0x80, 0}; | ||||
| 	void *ctx; | ||||
|  | ||||
| 	struct mgcp_rtp_end *dst_end; | ||||
| 	struct mgcp_process_rtp_state *state; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	int in_size; | ||||
| 	const int in_samples = 160; | ||||
| 	int len, cont; | ||||
|  | ||||
| 	printf("== Transcoding test ==\n"); | ||||
| 	printf("converting %s -> %s\n", srcfmt, dstfmt); | ||||
|  | ||||
| 	given_configured_endpoint(in_samples, 0, srcfmt, dstfmt, &ctx, &endp); | ||||
|  | ||||
| 	dst_end = &endp->bts_end; | ||||
| 	state = dst_end->rtp_process_data; | ||||
| 	OSMO_ASSERT(state != NULL); | ||||
|  | ||||
| 	in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0); | ||||
| 	OSMO_ASSERT(sizeof(buf) >= in_size + 12); | ||||
|  | ||||
| 	memcpy(buf, src_pkts, src_pkt_size); | ||||
|  | ||||
| 	len = src_pkt_size; | ||||
|  | ||||
| 	cont = mgcp_transcoding_process_rtp(endp, dst_end, | ||||
| 					    buf, &len, sizeof(buf)); | ||||
| 	if (cont < 0) { | ||||
| 		printf("Nothing encoded due: %s\n", strerror(-cont)); | ||||
| 		talloc_free(ctx); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (len < 24) { | ||||
| 		printf("encoded: %s\n", osmo_hexdump((unsigned char *)buf, len)); | ||||
| 	} else { | ||||
| 		const char *str = osmo_hexdump((unsigned char *)buf, len); | ||||
| 		int i = 0; | ||||
| 		const int prefix = 4; | ||||
| 		const int cutlen = 48; | ||||
| 		int nchars = 0; | ||||
|  | ||||
| 		printf("encoded:\n"); | ||||
| 		do { | ||||
| 			nchars = printf("%*s%-.*s", prefix, "", cutlen, str + i); | ||||
| 			i += nchars - prefix; | ||||
| 			printf("\n"); | ||||
| 		} while (nchars - prefix >= cutlen); | ||||
| 	} | ||||
| 	printf("counted: %d\n", cont); | ||||
| 	talloc_free(ctx); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void test_rtp_seq_state(void) | ||||
| { | ||||
| 	char buf[4096]; | ||||
| 	int len; | ||||
| 	int cont; | ||||
| 	void *ctx; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	struct mgcp_process_rtp_state *state; | ||||
| 	struct rtp_hdr *hdr; | ||||
| 	uint32_t ts_no; | ||||
| 	uint16_t seq_no; | ||||
|  | ||||
| 	given_configured_endpoint(160, 0, "pcma", "l16", &ctx, &endp); | ||||
| 	state = endp->bts_end.rtp_process_data; | ||||
| 	OSMO_ASSERT(!state->is_running); | ||||
| 	OSMO_ASSERT(state->next_seq == 0); | ||||
| 	OSMO_ASSERT(state->next_time == 0); | ||||
|  | ||||
| 	/* initialize packet */ | ||||
| 	len = audio_packets_pcma[0].len; | ||||
| 	memcpy(buf, audio_packets_pcma[0].data, len); | ||||
| 	cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len); | ||||
| 	OSMO_ASSERT(cont >= 0); | ||||
| 	OSMO_ASSERT(state->is_running); | ||||
| 	OSMO_ASSERT(state->next_seq == 2); | ||||
| 	OSMO_ASSERT(state->next_time == 240); | ||||
|  | ||||
| 	/* verify that the right timestamp was written */ | ||||
| 	OSMO_ASSERT(len == audio_packets_pcma[0].len); | ||||
| 	hdr = (struct rtp_hdr *) &buf[0]; | ||||
|  | ||||
| 	memcpy(&ts_no, &hdr->timestamp, sizeof(ts_no)); | ||||
| 	OSMO_ASSERT(htonl(ts_no) == 160); | ||||
| 	memcpy(&seq_no, &hdr->sequence, sizeof(seq_no)); | ||||
| 	OSMO_ASSERT(htons(seq_no) == 1); | ||||
| 	/* Check the right sequence number is written */ | ||||
| 	state->next_seq = 1234; | ||||
| 	len = audio_packets_pcma[0].len; | ||||
| 	memcpy(buf, audio_packets_pcma[0].data, len); | ||||
| 	cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len); | ||||
| 	OSMO_ASSERT(cont >= 0); | ||||
| 	OSMO_ASSERT(len == audio_packets_pcma[0].len); | ||||
| 	hdr = (struct rtp_hdr *) &buf[0]; | ||||
|  | ||||
| 	memcpy(&seq_no, &hdr->sequence, sizeof(seq_no)); | ||||
| 	OSMO_ASSERT(htons(seq_no) == 1234); | ||||
|  | ||||
| 	talloc_free(ctx); | ||||
| } | ||||
|  | ||||
| static void test_transcode_result(void) | ||||
| { | ||||
| 	char buf[4096]; | ||||
| 	int len, res; | ||||
| 	void *ctx; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	struct mgcp_process_rtp_state *state; | ||||
|  | ||||
| 	{ | ||||
| 		/* from GSM to PCMA and same ptime */ | ||||
| 		given_configured_endpoint(160, 0, "gsm", "pcma", &ctx, &endp); | ||||
| 		state = endp->bts_end.rtp_process_data; | ||||
|  | ||||
| 		/* result */ | ||||
| 		len = audio_packets_gsm[0].len; | ||||
| 		memcpy(buf, audio_packets_gsm[0].data, len); | ||||
| 		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); | ||||
| 		OSMO_ASSERT(res == sizeof(struct rtp_hdr)); | ||||
| 		OSMO_ASSERT(state->sample_cnt == 0); | ||||
|  | ||||
| 		len = res; | ||||
| 		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); | ||||
| 		OSMO_ASSERT(res == -ENOMSG); | ||||
|  | ||||
| 		talloc_free(ctx); | ||||
| 	} | ||||
|  | ||||
| 	{ | ||||
| 		/* from GSM to PCMA and same ptime */ | ||||
| 		given_configured_endpoint(160, 160, "gsm", "pcma", &ctx, &endp); | ||||
| 		state = endp->bts_end.rtp_process_data; | ||||
|  | ||||
| 		/* result */ | ||||
| 		len = audio_packets_gsm[0].len; | ||||
| 		memcpy(buf, audio_packets_gsm[0].data, len); | ||||
| 		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); | ||||
| 		OSMO_ASSERT(res == sizeof(struct rtp_hdr)); | ||||
| 		OSMO_ASSERT(state->sample_cnt == 0); | ||||
|  | ||||
| 		len = res; | ||||
| 		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); | ||||
| 		OSMO_ASSERT(res == -EAGAIN); | ||||
|  | ||||
| 		talloc_free(ctx); | ||||
| 	} | ||||
|  | ||||
| 	{ | ||||
| 		/* from PCMA to GSM and wrong different ptime */ | ||||
| 		given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp); | ||||
| 		state = endp->bts_end.rtp_process_data; | ||||
|  | ||||
| 		/* Add the first sample */ | ||||
| 		len = audio_packets_pcma[1].len; | ||||
| 		memcpy(buf, audio_packets_pcma[1].data, len); | ||||
| 		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); | ||||
| 		OSMO_ASSERT(state->sample_cnt == 80); | ||||
| 		OSMO_ASSERT(state->next_time == 232640); | ||||
| 		OSMO_ASSERT(res < 0); | ||||
|  | ||||
| 		/* Add the second sample and it should be consumable */ | ||||
| 		len = audio_packets_pcma[2].len; | ||||
| 		memcpy(buf, audio_packets_pcma[2].data, len); | ||||
| 		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); | ||||
| 		OSMO_ASSERT(state->sample_cnt == 0); | ||||
| 		OSMO_ASSERT(state->next_time == 232640 + 80 + 160); | ||||
| 		OSMO_ASSERT(res == sizeof(struct rtp_hdr)); | ||||
|  | ||||
| 		talloc_free(ctx); | ||||
| 	} | ||||
|  | ||||
| 	{ | ||||
| 		/* from PCMA to GSM with a big time jump */ | ||||
| 		struct rtp_hdr *hdr; | ||||
| 		uint32_t ts; | ||||
|  | ||||
| 		given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp); | ||||
| 		state = endp->bts_end.rtp_process_data; | ||||
|  | ||||
| 		/* Add the first sample */ | ||||
| 		len = audio_packets_pcma[1].len; | ||||
| 		memcpy(buf, audio_packets_pcma[1].data, len); | ||||
| 		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); | ||||
| 		OSMO_ASSERT(state->sample_cnt == 80); | ||||
| 		OSMO_ASSERT(state->next_time == 232640); | ||||
| 		OSMO_ASSERT(state->next_seq == 26527); | ||||
| 		OSMO_ASSERT(res < 0); | ||||
|  | ||||
| 		/* Add a skip to the packet to force a 'resync' */ | ||||
| 		len = audio_packets_pcma[2].len; | ||||
| 		memcpy(buf, audio_packets_pcma[2].data, len); | ||||
| 		hdr = (struct rtp_hdr *) &buf[0]; | ||||
| 		/* jump the time and add alignment error */ | ||||
| 		ts = ntohl(hdr->timestamp) + 123 * 80 + 2; | ||||
| 		hdr->timestamp = htonl(ts); | ||||
| 		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); | ||||
| 		OSMO_ASSERT(res < 0); | ||||
| 		OSMO_ASSERT(state->sample_cnt == 80); | ||||
| 		OSMO_ASSERT(state->next_time == ts); | ||||
| 		OSMO_ASSERT(state->next_seq == 26527); | ||||
| 		/* TODO: this can create alignment errors */ | ||||
|  | ||||
|  | ||||
| 		/* Now attempt to consume 160 samples */ | ||||
| 		len = audio_packets_pcma[2].len; | ||||
| 		memcpy(buf, audio_packets_pcma[2].data, len); | ||||
| 		hdr = (struct rtp_hdr *) &buf[0]; | ||||
| 		ts += 80; | ||||
| 		hdr->timestamp = htonl(ts); | ||||
| 		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); | ||||
| 		OSMO_ASSERT(res == 12); | ||||
| 		OSMO_ASSERT(state->sample_cnt == 0); | ||||
| 		OSMO_ASSERT(state->next_time == ts + 160); | ||||
| 		OSMO_ASSERT(state->next_seq == 26528); | ||||
|  | ||||
| 		talloc_free(ctx); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void test_transcode_change(void) | ||||
| { | ||||
| 	char buf[4096] = {0x80, 0}; | ||||
| 	void *ctx; | ||||
|  | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	struct mgcp_process_rtp_state *state; | ||||
| 	struct rtp_hdr *hdr; | ||||
|  | ||||
| 	int len, res; | ||||
|  | ||||
| 	{ | ||||
| 		/* from GSM to PCMA and same ptime */ | ||||
| 		printf("Testing Initial L16->GSM, PCMA->GSM\n"); | ||||
| 		given_configured_endpoint(160, 0, "l16", "gsm", &ctx, &endp); | ||||
| 		endp->net_end.alt_codec = endp->net_end.codec; | ||||
| 		endp->net_end.alt_codec.payload_type = audio_name_to_type("pcma"); | ||||
| 		state = endp->bts_end.rtp_process_data; | ||||
|  | ||||
| 		/* initial transcoding work */ | ||||
| 		OSMO_ASSERT(state->src_fmt == AF_L16); | ||||
| 		OSMO_ASSERT(state->dst_fmt == AF_GSM); | ||||
| 		OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 8); | ||||
| 		OSMO_ASSERT(endp->net_end.codec.payload_type == 11); | ||||
|  | ||||
| 		/* result */ | ||||
| 		len = audio_packets_pcma[0].len; | ||||
| 		memcpy(buf, audio_packets_pcma[0].data, len); | ||||
| 		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); | ||||
| 		state = endp->bts_end.rtp_process_data; | ||||
| 		OSMO_ASSERT(res == sizeof(struct rtp_hdr)); | ||||
| 		OSMO_ASSERT(state->sample_cnt == 0); | ||||
| 		OSMO_ASSERT(state->src_fmt == AF_PCMA); | ||||
| 		OSMO_ASSERT(state->dst_fmt == AF_GSM); | ||||
| 		OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 11); | ||||
| 		OSMO_ASSERT(endp->net_end.codec.payload_type == 8); | ||||
|  | ||||
| 		len = res; | ||||
| 		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); | ||||
| 		OSMO_ASSERT(res == -ENOMSG); | ||||
| 		OSMO_ASSERT(state == endp->bts_end.rtp_process_data); | ||||
|  | ||||
|  | ||||
| 		/* now check that comfort noise doesn't change anything */ | ||||
| 		len = audio_packets_pcma[1].len; | ||||
| 		memcpy(buf, audio_packets_pcma[1].data, len); | ||||
| 		hdr = (struct rtp_hdr *) buf; | ||||
| 		hdr->payload_type = 12; | ||||
| 		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); | ||||
| 		OSMO_ASSERT(state == endp->bts_end.rtp_process_data); | ||||
| 		OSMO_ASSERT(state->sample_cnt == 80); | ||||
| 		OSMO_ASSERT(state->src_fmt == AF_PCMA); | ||||
| 		OSMO_ASSERT(state->dst_fmt == AF_GSM); | ||||
| 		OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 11); | ||||
| 		OSMO_ASSERT(endp->net_end.codec.payload_type == 8); | ||||
|  | ||||
| 		talloc_free(ctx); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int test_repacking(int in_samples, int out_samples, int no_transcode) | ||||
| { | ||||
| 	char buf[4096] = {0x80, 0}; | ||||
| 	int cc; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	void *ctx; | ||||
|  | ||||
| 	struct mgcp_process_rtp_state *state; | ||||
| 	int in_cnt; | ||||
| 	int out_size; | ||||
| 	int in_size; | ||||
| 	uint32_t ts = 0; | ||||
| 	uint16_t seq = 0; | ||||
| 	const char *srcfmt = "pcma"; | ||||
| 	const char *dstfmt = no_transcode ? "pcma" : "l16"; | ||||
|  | ||||
| 	printf("== Transcoding test ==\n"); | ||||
| 	printf("converting %s -> %s\n", srcfmt, dstfmt); | ||||
|  | ||||
| 	given_configured_endpoint(in_samples, out_samples, srcfmt, dstfmt, &ctx, &endp); | ||||
|  | ||||
| 	state = endp->bts_end.rtp_process_data; | ||||
| 	OSMO_ASSERT(state != NULL); | ||||
|  | ||||
| 	in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0); | ||||
| 	OSMO_ASSERT(sizeof(buf) >= in_size + 12); | ||||
|  | ||||
| 	out_size = mgcp_transcoding_get_frame_size(state, -1, 1); | ||||
| 	OSMO_ASSERT(sizeof(buf) >= out_size + 12); | ||||
|  | ||||
| 	buf[1] = endp->net_end.codec.payload_type; | ||||
| 	*(uint16_t*)(buf+2) = htons(1); | ||||
| 	*(uint32_t*)(buf+4) = htonl(0); | ||||
| 	*(uint32_t*)(buf+8) = htonl(0xaabbccdd); | ||||
|  | ||||
| 	for (in_cnt = 0; in_cnt < 16; in_cnt++) { | ||||
| 		int cont; | ||||
| 		int len; | ||||
|  | ||||
| 		/* fake PCMA data */ | ||||
| 		printf("generating %d %s input samples\n", in_samples, srcfmt); | ||||
| 		for (cc = 0; cc < in_samples; cc++) | ||||
| 			buf[12+cc] = cc; | ||||
|  | ||||
| 		*(uint16_t*)(buf+2) = htonl(seq); | ||||
| 		*(uint32_t*)(buf+4) = htonl(ts); | ||||
|  | ||||
| 		seq += 1; | ||||
| 		ts += in_samples; | ||||
|  | ||||
| 		cc += 12; /* include RTP header */ | ||||
|  | ||||
| 		len = cc; | ||||
|  | ||||
| 		do { | ||||
| 			cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, | ||||
| 							    buf, &len, sizeof(buf)); | ||||
| 			if (cont == -EAGAIN) { | ||||
| 				fprintf(stderr, "Got EAGAIN\n"); | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			if (cont < 0) { | ||||
| 				printf("processing failed: %s", strerror(-cont)); | ||||
| 				abort(); | ||||
| 			} | ||||
|  | ||||
| 			len -= 12; /* ignore RTP header */ | ||||
|  | ||||
| 			printf("got %d %s output frames (%d octets) count=%d\n", | ||||
| 			       len / out_size, dstfmt, len, cont); | ||||
|  | ||||
| 			len = cont; | ||||
| 		} while (len > 0); | ||||
| 	} | ||||
|  | ||||
| 	talloc_free(ctx); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| 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(int argc, char **argv) | ||||
| { | ||||
| 	int rc; | ||||
| 	osmo_init_logging(&log_info); | ||||
|  | ||||
| 	printf("=== Transcoding Good Cases ===\n"); | ||||
|  | ||||
| 	transcode_test("l16", "l16", | ||||
| 		       (uint8_t *)audio_packets_l16[0].data, | ||||
| 		       audio_packets_l16[0].len); | ||||
| 	transcode_test("l16", "gsm", | ||||
| 		       (uint8_t *)audio_packets_l16[0].data, | ||||
| 		       audio_packets_l16[0].len); | ||||
| 	transcode_test("l16", "pcma", | ||||
| 		       (uint8_t *)audio_packets_l16[0].data, | ||||
| 		       audio_packets_l16[0].len); | ||||
| 	transcode_test("gsm", "l16", | ||||
| 		       (uint8_t *)audio_packets_gsm[0].data, | ||||
| 		       audio_packets_gsm[0].len); | ||||
| 	transcode_test("gsm", "gsm", | ||||
| 		       (uint8_t *)audio_packets_gsm[0].data, | ||||
| 		       audio_packets_gsm[0].len); | ||||
| 	transcode_test("gsm", "pcma", | ||||
| 		       (uint8_t *)audio_packets_gsm[0].data, | ||||
| 		       audio_packets_gsm[0].len); | ||||
| 	transcode_test("pcma", "l16", | ||||
| 		       (uint8_t *)audio_packets_pcma[0].data, | ||||
| 		       audio_packets_pcma[0].len); | ||||
| 	transcode_test("pcma", "gsm", | ||||
| 		       (uint8_t *)audio_packets_pcma[0].data, | ||||
| 		       audio_packets_pcma[0].len); | ||||
| 	transcode_test("pcma", "pcma", | ||||
| 		       (uint8_t *)audio_packets_pcma[0].data, | ||||
| 		       audio_packets_pcma[0].len); | ||||
|  | ||||
| 	printf("=== Transcoding Bad Cases ===\n"); | ||||
|  | ||||
| 	printf("Invalid size:\n"); | ||||
| 	rc = transcode_test("gsm", "pcma", | ||||
| 		       (uint8_t *)audio_packets_gsm_invalid_size[0].data, | ||||
| 		       audio_packets_gsm_invalid_size[0].len); | ||||
| 	OSMO_ASSERT(rc < 0); | ||||
|  | ||||
| 	printf("Invalid data:\n"); | ||||
| 	rc = transcode_test("gsm", "pcma", | ||||
| 		       (uint8_t *)audio_packets_gsm_invalid_data[0].data, | ||||
| 		       audio_packets_gsm_invalid_data[0].len); | ||||
| 	OSMO_ASSERT(rc < 0); | ||||
|  | ||||
| 	printf("Invalid payload type:\n"); | ||||
| 	rc = transcode_test("gsm", "pcma", | ||||
| 		       (uint8_t *)audio_packets_gsm_invalid_ptype[0].data, | ||||
| 		       audio_packets_gsm_invalid_ptype[0].len); | ||||
| 	OSMO_ASSERT(rc == 0); | ||||
|  | ||||
| 	printf("=== Repacking ===\n"); | ||||
|  | ||||
| 	test_repacking(160, 160, 0); | ||||
| 	test_repacking(160, 160, 1); | ||||
| 	test_repacking(160, 80, 0); | ||||
| 	test_repacking(160, 80, 1); | ||||
| 	test_repacking(160, 320, 0); | ||||
| 	test_repacking(160, 320, 1); | ||||
| 	test_repacking(160, 240, 0); | ||||
| 	test_repacking(160, 240, 1); | ||||
| 	test_repacking(160, 100, 0); | ||||
| 	test_repacking(160, 100, 1); | ||||
| 	test_rtp_seq_state(); | ||||
| 	test_transcode_result(); | ||||
| 	test_transcode_change(); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -1,539 +0,0 @@ | ||||
| === Transcoding Good Cases === | ||||
| == Transcoding test == | ||||
| converting l16 -> l16 | ||||
| encoded: | ||||
|     80 0b 00 01 00 00 00 a0 11 22 33 44 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13  | ||||
|     5a 9e 40 13 00 00 bf ed a5 62 bf ed  | ||||
| counted: 0 | ||||
| == Transcoding test == | ||||
| converting l16 -> gsm | ||||
| encoded: | ||||
|     80 0b 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9  | ||||
|     62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc  | ||||
|     69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de  | ||||
| counted: 12 | ||||
| == Transcoding test == | ||||
| converting l16 -> pcma | ||||
| encoded: | ||||
|     80 0b 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25  | ||||
| counted: 12 | ||||
| == Transcoding test == | ||||
| converting gsm -> l16 | ||||
| encoded: | ||||
|     80 03 00 01 00 00 00 a0 11 22 33 44 00 00 54 00  | ||||
|     59 f0 34 20 c4 c8 b9 f8 e2 18 f1 e8 f2 28 f0 e0  | ||||
|     46 08 4f 80 2c a0 a9 c8 80 00 c0 58 3f 80 63 c0  | ||||
|     24 b8 fa b8 f6 88 0b a0 c8 70 a8 b0 c8 c0 3b a8  | ||||
|     66 a0 2e 38 d1 f8 98 98 aa 18 e8 30 26 a0 37 40  | ||||
|     37 e8 17 00 ee 50 b7 80 b1 88 de 28 18 40 45 b0  | ||||
|     4f 48 21 d8 df 78 ae 68 ab 98 d6 b8 18 18 48 90  | ||||
|     4e 70 27 40 e8 10 b5 b0 ac 80 d4 60 14 50 48 48  | ||||
|     50 10 2a 00 ec 08 ba 00 af 58 d1 c0 10 60 45 c8  | ||||
|     54 10 30 78 f1 a8 bb 18 ad 48 ce 30 0a e8 3f 30  | ||||
|     4f 10 32 18 f6 18 bf 20 ac 30 cd 80 0b d0 43 d8  | ||||
|     55 e0 34 a0 f5 78 bc 98 ad 98 cd c8 0a 80 40 58  | ||||
|     51 c0 35 40 f9 60 c1 68 ac c8 cb 38 08 00 40 98  | ||||
|     51 e0 34 d8 fa 28 c2 f0 ae 40 c7 70 02 d0 3c a8  | ||||
|     54 78 38 a0 fc 68 c2 08 ad 50 c7 78 01 60 39 c0  | ||||
|     51 38 3a e8 00 e8 c6 38 ab d8 c4 00 fe 08 39 18  | ||||
|     50 30 39 50 01 d8 ca 70 b1 80 c4 c8 fc 58 36 40  | ||||
|     51 d8 3b 08 02 80 c8 58 b0 60 c5 a8 fb d0 33 e8  | ||||
|     4e 80 3c e0 06 10 cb 90 ae 48 c2 60 f9 58 34 08  | ||||
|     4d a0 3a a8 06 48 cf 80 b4 60 c3 e8 f7 90 30 18  | ||||
|     4d a0 3b 98 07 90 cf 18 b4 68 c4 88  | ||||
| counted: 12 | ||||
| == Transcoding test == | ||||
| converting gsm -> gsm | ||||
| encoded: | ||||
|     80 03 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9  | ||||
|     62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc  | ||||
|     69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de  | ||||
| counted: 0 | ||||
| == Transcoding test == | ||||
| converting gsm -> pcma | ||||
| encoded: | ||||
|     80 03 00 01 00 00 00 a0 11 22 33 44 d5 a0 a3 bf  | ||||
|     38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60  | ||||
|     17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82  | ||||
|     04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6  | ||||
|     02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd  | ||||
|     19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf  | ||||
|     10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf  | ||||
|     62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8  | ||||
|     db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8  | ||||
|     f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8  | ||||
|     ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38  | ||||
| counted: 12 | ||||
| == Transcoding test == | ||||
| converting pcma -> l16 | ||||
| encoded: | ||||
|     80 08 00 01 00 00 00 a0 11 22 33 44 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00  | ||||
|     5a 00 42 00 00 08 be 00 a6 00 be 00  | ||||
| counted: 12 | ||||
| == Transcoding test == | ||||
| converting pcma -> gsm | ||||
| encoded: | ||||
|     80 08 00 01 00 00 00 a0 11 22 33 44 d4 b9 f4 5d  | ||||
|     d9 50 5a e1 a0 cd 76 ea 52 0e 87 53 ad d4 ea a2  | ||||
|     0a 63 ca e9 60 79 e2 2a 25 d2 c0 f3 39  | ||||
| counted: 12 | ||||
| == Transcoding test == | ||||
| converting pcma -> pcma | ||||
| encoded: | ||||
|     80 08 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5  | ||||
|     d5 25 23 25 d5 a5 a3 a5 d5 25 23 25  | ||||
| counted: 0 | ||||
| === Transcoding Bad Cases === | ||||
| Invalid size: | ||||
| == Transcoding test == | ||||
| converting gsm -> pcma | ||||
| Nothing encoded due: No message of desired type | ||||
| Invalid data: | ||||
| == Transcoding test == | ||||
| converting gsm -> pcma | ||||
| Nothing encoded due: No message of desired type | ||||
| Invalid payload type: | ||||
| == Transcoding test == | ||||
| converting gsm -> pcma | ||||
| encoded: | ||||
|     80 08 00 01 00 00 00 a0 11 22 33 44 d5 a0 a3 bf  | ||||
|     38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60  | ||||
|     17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82  | ||||
|     04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6  | ||||
|     02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd  | ||||
|     19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf  | ||||
|     10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf  | ||||
|     62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8  | ||||
|     db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8  | ||||
|     f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8  | ||||
|     ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38  | ||||
| counted: 12 | ||||
| === Repacking === | ||||
| == Transcoding test == | ||||
| converting pcma -> l16 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 l16 output frames (320 octets) count=12 | ||||
| == Transcoding test == | ||||
| converting pcma -> pcma | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 2 pcma output frames (160 octets) count=12 | ||||
| == Transcoding test == | ||||
| converting pcma -> l16 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| == Transcoding test == | ||||
| converting pcma -> pcma | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| == Transcoding test == | ||||
| converting pcma -> l16 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 l16 output frames (640 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 l16 output frames (640 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 l16 output frames (640 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 l16 output frames (640 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 l16 output frames (640 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 l16 output frames (640 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 l16 output frames (640 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 l16 output frames (640 octets) count=12 | ||||
| == Transcoding test == | ||||
| converting pcma -> pcma | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 pcma output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 pcma output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 pcma output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 pcma output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 pcma output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 pcma output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 pcma output frames (320 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 4 pcma output frames (320 octets) count=12 | ||||
| == Transcoding test == | ||||
| converting pcma -> l16 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 l16 output frames (480 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 l16 output frames (480 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 l16 output frames (480 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 l16 output frames (480 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 l16 output frames (480 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 l16 output frames (480 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 l16 output frames (480 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 l16 output frames (480 octets) count=12 | ||||
| == Transcoding test == | ||||
| converting pcma -> pcma | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 pcma output frames (240 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 pcma output frames (240 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 pcma output frames (240 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 pcma output frames (240 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 pcma output frames (240 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 pcma output frames (240 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 pcma output frames (240 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| generating 160 pcma input samples | ||||
| got 3 pcma output frames (240 octets) count=12 | ||||
| == Transcoding test == | ||||
| converting pcma -> l16 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| got 1 l16 output frames (160 octets) count=12 | ||||
| == Transcoding test == | ||||
| converting pcma -> pcma | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| generating 160 pcma input samples | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| got 1 pcma output frames (80 octets) count=12 | ||||
| Testing Initial L16->GSM, PCMA->GSM | ||||
							
								
								
									
										41
									
								
								tests/mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								tests/mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| AM_CPPFLAGS = \ | ||||
| 	$(all_includes) \ | ||||
| 	-I$(top_srcdir)/include \ | ||||
| 	-I$(top_srcdir) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	-ggdb3 \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOVTY_CFLAGS) \ | ||||
| 	$(LIBOSMOGSM_CFLAGS) \ | ||||
| 	$(LIBOSMONETIF_CFLAGS) \ | ||||
| 	$(COVERAGE_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	$(COVERAGE_LDFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	mgcp_test.ok \ | ||||
| 	$(NULL) | ||||
|  | ||||
| noinst_PROGRAMS = \ | ||||
| 	mgcp_test \ | ||||
| 	$(NULL) | ||||
|  | ||||
| mgcp_test_SOURCES = \ | ||||
| 	mgcp_test.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| mgcp_test_LDADD = \ | ||||
| 	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(LIBRARY_DL) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	-lm  \ | ||||
| 	$(NULL) | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user