mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-10-31 12:03:50 +00:00 
			
		
		
		
	Compare commits
	
		
			166 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 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 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -38,6 +38,7 @@ missing | ||||
| stamp-h1 | ||||
| libtool | ||||
| ltmain.sh | ||||
| m4/*.m4 | ||||
|  | ||||
| # git-version-gen magic | ||||
| .tarball-version | ||||
|   | ||||
| @@ -20,7 +20,6 @@ pkgconfigdir = $(libdir)/pkgconfig | ||||
| pkgconfig_DATA = \ | ||||
| 	libosmo-legacy-mgcp.pc \ | ||||
| 	libosmo-mgcp-client.pc \ | ||||
| 	libosmo-mgcp.pc \ | ||||
| 	$(NULL) | ||||
|  | ||||
| BUILT_SOURCES = $(top_srcdir)/.version | ||||
|   | ||||
| @@ -23,4 +23,4 @@ | ||||
| # 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 | ||||
| #library		what		description / commit summary line | ||||
|   | ||||
							
								
								
									
										41
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -39,9 +39,40 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""]) | ||||
| AC_SUBST(LIBRARY_DL) | ||||
|  | ||||
|  | ||||
| PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.10.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.10.0) | ||||
| PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.1.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.12.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.12.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.12.0) | ||||
| PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.3.0) | ||||
|  | ||||
| AC_ARG_ENABLE(sanitize, | ||||
| 	[AS_HELP_STRING( | ||||
| 		[--enable-sanitize], | ||||
| 		[Compile with address sanitizer enabled], | ||||
| 	)], | ||||
| 	[sanitize=$enableval], [sanitize="no"]) | ||||
| if test x"$sanitize" = x"yes" | ||||
| then | ||||
| 	CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined" | ||||
| 	CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" | ||||
| fi | ||||
|  | ||||
| AC_ARG_ENABLE(werror, | ||||
| 	[AS_HELP_STRING( | ||||
| 		[--enable-werror], | ||||
| 		[Turn all compiler warnings into errors, with exceptions: | ||||
| 		 a) deprecation (allow upstream to mark deprecation without breaking builds); | ||||
| 		 b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds) | ||||
| 		] | ||||
| 	)], | ||||
| 	[werror=$enableval], [werror="no"]) | ||||
| if test x"$werror" = x"yes" | ||||
| then | ||||
| 	WERROR_FLAGS="-Werror" | ||||
| 	WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" | ||||
| 	WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" | ||||
| 	CFLAGS="$CFLAGS $WERROR_FLAGS" | ||||
| 	CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" | ||||
| fi | ||||
|  | ||||
| # 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.])], | ||||
| @@ -113,13 +144,15 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests]) | ||||
| AC_MSG_RESULT([$enable_ext_tests]) | ||||
| AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes") | ||||
|  | ||||
| AC_MSG_RESULT([CFLAGS="$CFLAGS"]) | ||||
| AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"]) | ||||
|  | ||||
| dnl Generate the output | ||||
| AM_CONFIG_HEADER(bscconfig.h) | ||||
|  | ||||
| AC_OUTPUT( | ||||
|     libosmo-legacy-mgcp.pc | ||||
|     libosmo-mgcp-client.pc | ||||
|     libosmo-mgcp.pc | ||||
|     include/Makefile | ||||
|     include/osmocom/Makefile | ||||
|     include/osmocom/legacy_mgcp/Makefile | ||||
|   | ||||
| @@ -14,9 +14,9 @@ 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]") | ||||
| @@ -37,7 +37,7 @@ set -x | ||||
|  | ||||
| cd "$base" | ||||
| autoreconf --install --force | ||||
| ./configure $MGCP --enable-vty-tests --enable-external-tests | ||||
| ./configure $MGCP --enable-vty-tests --enable-external-tests --enable-werror | ||||
| $MAKE $PARALLEL_MAKE | ||||
| LD_LIBRARY_PATH="$inst/lib" $MAKE check \ | ||||
|   || cat-testlogs.sh | ||||
| @@ -45,3 +45,5 @@ LD_LIBRARY_PATH="$inst/lib" \ | ||||
|   DISTCHECK_CONFIGURE_FLAGS="$MGCP --enable-vty-tests --enable-external-tests" \ | ||||
|   $MAKE distcheck \ | ||||
|   || cat-testlogs.sh | ||||
|  | ||||
| osmo-clean-workspace.sh | ||||
|   | ||||
							
								
								
									
										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 | ||||
							
								
								
									
										203
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										203
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,206 @@ | ||||
| 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 | ||||
|   | ||||
							
								
								
									
										21
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -16,25 +16,10 @@ Homepage: https://osmocom.org/projects/osmo-mgw | ||||
| Package: osmo-mgw | ||||
| Architecture: any | ||||
| Multi-Arch: foreign | ||||
| Depends: libosmo-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-mgcp0 | ||||
| Section: libs | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| Pre-Depends: ${misc:Pre-Depends} | ||||
| Depends: ${misc:Depends}, ${shlibs:Depends} | ||||
| Description: libosmo-mgcp: Osmocom's Media Gateway server library | ||||
|  | ||||
| Package: libosmo-mgcp-dev | ||||
| Section: libdevel | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| Depends: libosmo-mgcp0 (= ${binary:Version}), ${misc:Depends} | ||||
| Description: libosmo-mgcp: Osmocom's Media Gateway server library | ||||
|  | ||||
| Package: libosmo-mgcp-client1 | ||||
| Package: libosmo-mgcp-client3 | ||||
| Section: libs | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| @@ -46,7 +31,7 @@ Package: libosmo-mgcp-client-dev | ||||
| Section: libdevel | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| Depends: libosmo-mgcp-client1 (= ${binary:Version}), ${misc:Depends} | ||||
| Depends: libosmo-mgcp-client3 (= ${binary:Version}), ${misc:Depends} | ||||
| Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities | ||||
|  | ||||
| Package: osmo-bsc-mgcp | ||||
|   | ||||
							
								
								
									
										19
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							| @@ -21,6 +21,25 @@ License:   AGPL-3.0+ | ||||
|  You should have received a copy of the GNU Affero General Public License | ||||
|  along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| Files:     src/libosmo-mgcp-client/* include/osmocom/mgcp_client/* | ||||
| Copyright: 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de> | ||||
|            Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c) | ||||
|            2009 by Harald Welte <laforge@gnumonks.org> | ||||
|            2009-2011 by Holger Hans Peter Freyther | ||||
| License:   GPL-2.0+ | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2 of the License, or | ||||
|  (at your option) any later version. | ||||
|  . | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  . | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| Files:     src/libosmo-legacy-mgcp/g711common.h | ||||
| Copyright: 2000 Abramo Bagnara <abramo@alsa-project.org> | ||||
| License:   GPL-2.0+ | ||||
|   | ||||
							
								
								
									
										4
									
								
								debian/libosmo-mgcp-dev.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/libosmo-mgcp-dev.install
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +0,0 @@ | ||||
| usr/include/osmocom/mgcp | ||||
| usr/lib/*/libosmo-mgcp.so | ||||
| usr/lib/*/libosmo-mgcp.a | ||||
| usr/lib/*/pkgconfig/libosmo-mgcp.pc | ||||
							
								
								
									
										1
									
								
								debian/libosmo-mgcp0.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/libosmo-mgcp0.install
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | ||||
| usr/lib/*/libosmo-mgcp.so.* | ||||
							
								
								
									
										1
									
								
								debian/osmo-bsc-mgcp.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-bsc-mgcp.install
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,2 @@ | ||||
| usr/bin/osmo-bsc_mgcp | ||||
| usr/share/doc/osmo-mgw/examples/osmo-bsc_mgcp/mgcp.cfg | ||||
|   | ||||
							
								
								
									
										1
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,2 @@ | ||||
| usr/bin/osmo-mgw | ||||
| usr/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw.cfg | ||||
|   | ||||
							
								
								
									
										1
									
								
								debian/osmo-mgw.service
									
									
									
									
										vendored
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-mgw.service
									
									
									
									
										vendored
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../contrib/systemd/osmo-mgw.service | ||||
| @@ -2,12 +2,17 @@ | ||||
| ! 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 force-ptime 20 | ||||
|  sdp audio payload number 98 | ||||
|  sdp audio payload name AMR/8000 | ||||
|  number endpoints 31 | ||||
|  no rtcp-omit | ||||
|   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 | ||||
|   | ||||
| @@ -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]*) ;; | ||||
|   | ||||
| @@ -7,6 +7,7 @@ nobase_include_HEADERS = \ | ||||
| 	osmocom/legacy_mgcp/mgcp_internal.h \ | ||||
| 	osmocom/legacy_mgcp/osmux.h \ | ||||
| 	osmocom/mgcp_client/mgcp_client.h \ | ||||
| 	osmocom/mgcp_client/mgcp_client_fsm.h \ | ||||
| 	osmocom/mgcp_client/mgcp_common.h \ | ||||
| 	osmocom/mgcp/mgcp.h \ | ||||
| 	osmocom/mgcp/mgcp_common.h \ | ||||
|   | ||||
| @@ -243,6 +243,12 @@ struct mgcp_config { | ||||
| 	 * message. | ||||
| 	 */ | ||||
| 	uint16_t osmux_dummy; | ||||
|  | ||||
| 	/* Use a jitterbuffer on the bts-side receiver */ | ||||
| 	bool bts_use_jibuf; | ||||
| 	/* Minimum and maximum buffer size for the jitter buffer, in ms */ | ||||
| 	uint32_t bts_jitter_delay_min; | ||||
| 	uint32_t bts_jitter_delay_max; | ||||
| }; | ||||
|  | ||||
| /* config management */ | ||||
|   | ||||
| @@ -25,6 +25,7 @@ | ||||
| #include <string.h> | ||||
|  | ||||
| #include <osmocom/core/select.h> | ||||
| #include <osmocom/netif/jibuf.h> | ||||
|  | ||||
| #define CI_UNUSED 0 | ||||
|  | ||||
| @@ -198,6 +199,14 @@ struct mgcp_endpoint { | ||||
| 			uint32_t octets; | ||||
| 		} stats; | ||||
| 	} osmux; | ||||
|  | ||||
| 	/* Jitter buffer */ | ||||
| 	struct osmo_jibuf* bts_jb; | ||||
| 	/* Use a jitterbuffer on the bts-side receiver */ | ||||
| 	bool bts_use_jibuf; | ||||
| 	/* Minimum and maximum buffer size for the jitter buffer, in ms */ | ||||
| 	uint32_t bts_jitter_delay_min; | ||||
| 	uint32_t bts_jitter_delay_max; | ||||
| }; | ||||
|  | ||||
| #define for_each_line(line, save)			\ | ||||
| @@ -335,3 +344,8 @@ static inline const char *mgcp_bts_src_addr(struct mgcp_endpoint *endp) | ||||
| } | ||||
|  | ||||
| int mgcp_msg_terminate_nul(struct msgb *msg); | ||||
|  | ||||
| /** | ||||
|  * Internal jitter buffer related | ||||
|  */ | ||||
| void mgcp_dejitter_udp_send(struct msgb *msg, void *data); | ||||
|   | ||||
| @@ -1,31 +1,8 @@ | ||||
| #ifndef OPENBSC_VTY_H | ||||
| #define OPENBSC_VTY_H | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/vty/vty.h> | ||||
| #include <osmocom/vty/buffer.h> | ||||
| #include <osmocom/vty/command.h> | ||||
|  | ||||
| struct gsm_network; | ||||
| struct vty; | ||||
|  | ||||
| void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *); | ||||
|  | ||||
| struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base); | ||||
|  | ||||
| extern struct cmd_element cfg_description_cmd; | ||||
| extern struct cmd_element cfg_no_description_cmd; | ||||
|  | ||||
| enum mgcp_vty_node { | ||||
| 	MGCP_NODE = _LAST_OSMOVTY_NODE + 1, | ||||
| 	TRUNK_NODE, | ||||
| }; | ||||
|  | ||||
| struct log_info; | ||||
| int bsc_vty_init(struct gsm_network *network); | ||||
| int bsc_vty_init_extra(void); | ||||
|  | ||||
| void msc_vty_init(struct gsm_network *msc_network); | ||||
|  | ||||
| struct gsm_network *gsmnet_from_vty(struct vty *vty); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -3,5 +3,8 @@ noinst_HEADERS = \ | ||||
| 	mgcp_msg.h \ | ||||
| 	mgcp_conn.h \ | ||||
| 	mgcp_stat.h \ | ||||
| 	mgcp_ep.h \ | ||||
| 	mgcp_endp.h \ | ||||
| 	mgcp_sdp.h \ | ||||
| 	mgcp_codec.h \ | ||||
| 	debug.h \ | ||||
| 	$(NULL) | ||||
|   | ||||
| @@ -1,7 +1,4 @@ | ||||
| /* Endpoint types */ | ||||
| 
 | ||||
| /*
 | ||||
|  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
| /* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Philipp Maier | ||||
| @@ -21,12 +18,18 @@ | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <osmocom/mgcp/mgcp_ep.h> | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #pragma once | ||||
| 
 | ||||
| /* 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 | ||||
| #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; | ||||
| @@ -37,31 +37,6 @@ | ||||
| #define RTP_PORT_DEFAULT_RANGE_START 16002 | ||||
| #define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64 | ||||
|  | ||||
| /** | ||||
|  * 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); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Handling of MGCP Endpoints and the MGCP Config | ||||
|  */ | ||||
| @@ -99,11 +74,13 @@ 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); | ||||
| 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; | ||||
|  | ||||
| typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp, | ||||
| 				     struct mgcp_conn_rtp *conn_dst, | ||||
| 				     struct mgcp_conn_rtp *conn_src); | ||||
|  | ||||
| typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp, | ||||
| 				int *payload_type, | ||||
| 				const char**subtype_name, | ||||
| @@ -121,9 +98,25 @@ struct mgcp_port_range { | ||||
| 	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 | ||||
|  | ||||
| struct mgcp_trunk_config { | ||||
| 	struct llist_head entry; | ||||
| @@ -160,6 +153,7 @@ struct mgcp_trunk_config { | ||||
| 	int rtp_accept_all; | ||||
|  | ||||
| 	unsigned int number_endpoints; | ||||
| 	int vty_number_endpoints; | ||||
| 	struct mgcp_endpoint *endpoints; | ||||
| }; | ||||
|  | ||||
| @@ -220,6 +214,8 @@ struct mgcp_config { | ||||
| 	 * message. | ||||
| 	 */ | ||||
| 	uint16_t osmux_dummy; | ||||
| 	/* domain name of the media gateway */ | ||||
| 	char domain[255+1]; | ||||
| }; | ||||
|  | ||||
| /* config management */ | ||||
| @@ -228,7 +224,6 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, | ||||
| 		      enum mgcp_role role); | ||||
| int mgcp_vty_init(void); | ||||
| int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg); | ||||
| void mgcp_release_endp(struct mgcp_endpoint *endp); | ||||
| void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval); | ||||
|  | ||||
| /* | ||||
| @@ -236,22 +231,6 @@ 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); | ||||
|   | ||||
							
								
								
									
										6
									
								
								include/osmocom/mgcp/mgcp_codec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								include/osmocom/mgcp/mgcp_codec.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| #pragma once | ||||
|  | ||||
| void mgcp_codec_summary(struct mgcp_conn_rtp *conn); | ||||
| void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn); | ||||
| int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name); | ||||
| int mgcp_codec_decide(struct mgcp_conn_rtp *conn); | ||||
| @@ -68,4 +68,22 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Maximum length of the comment field */ | ||||
| #define MGCP_COMMENT_MAXLEN 256 | ||||
|  | ||||
| /* String length of Connection Identifiers | ||||
|  * (see also RFC3435 2.1.3.2 Names of Connections) */ | ||||
| #define MGCP_CONN_ID_LENGTH 32+1 | ||||
|  | ||||
| /* String length of Endpoint Identifiers. | ||||
| /  (see also RFC3435 section 3.2.1.3) */ | ||||
| #define MGCP_ENDPOINT_MAXLEN (255*2+1+1) | ||||
|  | ||||
| /* A prefix to denote the virtual trunk (RTP on both ends) */ | ||||
| #define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/" | ||||
|  | ||||
| /* Maximal number of payload types / codecs that can be negotiated via SDP at | ||||
|  * at once. */ | ||||
| #define MGCP_MAX_CODECS 10 | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -27,13 +27,23 @@ | ||||
| #include <osmocom/core/linuxlist.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 | ||||
| }; | ||||
|  | ||||
| struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, | ||||
| 				  uint32_t id, enum mgcp_conn_type type, | ||||
| 				  char *name); | ||||
| struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id); | ||||
| 				  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, | ||||
| 					uint32_t id); | ||||
| void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id); | ||||
| 					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); | ||||
|   | ||||
							
								
								
									
										99
									
								
								include/osmocom/mgcp/mgcp_endp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								include/osmocom/mgcp/mgcp_endp.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| /* Endpoint types */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Philipp Maier | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| struct sockaddr_in; | ||||
| struct mgcp_conn; | ||||
| struct mgcp_endpoint; | ||||
|  | ||||
| /* Callback type for RTP dispatcher functions | ||||
|    (e.g mgcp_dispatch_rtp_bridge_cb, see below) */ | ||||
| typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr, | ||||
| 				     char *buf, unsigned int buf_size, | ||||
| 				     struct mgcp_conn *conn); | ||||
|  | ||||
| /* Callback type for endpoint specific cleanup actions. This function | ||||
|  * is automatically executed when a connection is freed (see mgcp_conn_free() | ||||
|  * in mgcp_conn.c). Depending on the type of the endpoint there may be endpoint | ||||
|  * specific things to take care of once a connection has been removed. */ | ||||
| typedef void (*mgcp_cleanup_cp) (struct mgcp_endpoint *endp, | ||||
| 				 struct mgcp_conn *conn); | ||||
|  | ||||
| /*! MGCP endpoint properties */ | ||||
| struct mgcp_endpoint_type { | ||||
| 	/*! maximum number of connections */ | ||||
| 	int max_conns; | ||||
|  | ||||
| 	/*! callback that defines how to dispatch incoming RTP data */ | ||||
| 	mgcp_dispatch_rtp_cb dispatch_rtp_cb; | ||||
|  | ||||
| 	/*! callback that implements endpoint specific cleanup actions */ | ||||
| 	mgcp_cleanup_cp cleanup_cb; | ||||
| }; | ||||
|  | ||||
| /*! MGCP endpoint typeset */ | ||||
| struct mgcp_endpoint_typeset { | ||||
| 	struct mgcp_endpoint_type rtp; | ||||
| }; | ||||
|  | ||||
| /*! static MGCP endpoint typeset (pre-initalized, read-only) */ | ||||
| extern const struct mgcp_endpoint_typeset ep_typeset; | ||||
|  | ||||
| /*! MGCP endpoint model */ | ||||
| struct mgcp_endpoint { | ||||
|  | ||||
| 	/*! Call identifier string (as supplied by the call agant) */ | ||||
| 	char *callid; | ||||
|  | ||||
| 	/*! Local connection options (see mgcp_internal.h) */ | ||||
| 	struct mgcp_lco local_options; | ||||
|  | ||||
| 	/*! List with connections active on this endpoint */ | ||||
| 	struct llist_head conns; | ||||
|  | ||||
| 	/*! Backpointer to the MGW configuration */ | ||||
| 	struct mgcp_config *cfg; | ||||
|  | ||||
| 	/*! Backpointer to the Trunk specific configuration */ | ||||
| 	struct mgcp_trunk_config *tcfg; | ||||
|  | ||||
| 	/*! Endpoint properties (see above) */ | ||||
| 	const struct mgcp_endpoint_type *type; | ||||
|  | ||||
| 	/*! Last MGCP transmission (in case re-transmission is required) */ | ||||
| 	char *last_trans; | ||||
|  | ||||
| 	/*! Last MGCP response (in case re-transmission is required) */ | ||||
| 	char *last_response; | ||||
|  | ||||
| 	/*! Memorize if this endpoint was choosen by the MGW (wildcarded, true) | ||||
| 	 *   or if the user has choosen the particular endpoint explicitly. */ | ||||
| 	bool wildcarded_req; | ||||
| }; | ||||
|  | ||||
| /*! Extract endpoint number for a given endpoint */ | ||||
| #define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints)) | ||||
|  | ||||
| void mgcp_endp_release(struct mgcp_endpoint *endp); | ||||
|  | ||||
| @@ -1,50 +0,0 @@ | ||||
| /* Endpoint types */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Philipp Maier | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| struct sockaddr_in; | ||||
| struct mgcp_conn; | ||||
|  | ||||
| /* Callback type for RTP dispatcher functions | ||||
|    (e.g mgcp_dispatch_rtp_bridge_cb, see below) */ | ||||
| typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in * addr, | ||||
| 				     char *buf, unsigned int buf_size, | ||||
| 				     struct mgcp_conn * conn); | ||||
|  | ||||
| /*! MGCP endpoint properties */ | ||||
| struct mgcp_endpoint_type { | ||||
| 	/*!< maximum number of connections */ | ||||
| 	int max_conns; | ||||
|  | ||||
| 	/*!< callback that defines how to dispatch incoming RTP data */ | ||||
| 	mgcp_dispatch_rtp_cb dispatch_rtp_cb; | ||||
| }; | ||||
|  | ||||
| /*! MGCP endpoint typeset */ | ||||
| struct mgcp_endpoint_typeset { | ||||
| 	struct mgcp_endpoint_type rtp; | ||||
| }; | ||||
|  | ||||
| /*! static MGCP endpoint typeset (pre-initalized, read-only) */ | ||||
| extern const struct mgcp_endpoint_typeset ep_typeset; | ||||
| @@ -27,11 +27,15 @@ | ||||
| #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 | ||||
|  | ||||
| #define CONN_ID_BTS 0 | ||||
| #define CONN_ID_NET 1 | ||||
| /* 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, | ||||
| @@ -42,33 +46,43 @@ struct mgcp_rtp_stream_state { | ||||
| 	uint32_t ssrc; | ||||
| 	uint16_t last_seq; | ||||
| 	uint32_t last_timestamp; | ||||
| 	uint32_t err_ts_counter; | ||||
| 	struct rate_ctr *err_ts_ctr; | ||||
| 	int32_t last_tsdelta; | ||||
| 	uint32_t last_arrival_time; | ||||
| }; | ||||
|  | ||||
| struct mgcp_rtp_state { | ||||
| 	/* has this state structure been initialized? */ | ||||
| 	int initialized; | ||||
| 	int patch_ssrc; | ||||
|  | ||||
| 	uint32_t orig_ssrc; | ||||
| 	struct { | ||||
| 		/* are we patching the SSRC value? */ | ||||
| 		int patch_ssrc; | ||||
| 		/* original SSRC (to which we shall patch any different SSRC) */ | ||||
| 		uint32_t orig_ssrc; | ||||
| 		/* offset to apply on the sequence number */ | ||||
| 		int seq_offset; | ||||
| 		/* offset to apply on the timestamp number */ | ||||
| 		int32_t timestamp_offset; | ||||
| 	} patch; | ||||
|  | ||||
| 	int seq_offset; | ||||
|  | ||||
| 	int32_t  timestamp_offset; | ||||
| 	/* duration of a packet (FIXME: in which unit?) */ | ||||
| 	uint32_t packet_duration; | ||||
|  | ||||
| 	struct mgcp_rtp_stream_state in_stream; | ||||
| 	struct mgcp_rtp_stream_state out_stream; | ||||
|  | ||||
| 	/* jitter and packet loss calculation */ | ||||
| 	int stats_initialized; | ||||
| 	uint16_t stats_base_seq; | ||||
| 	uint16_t stats_max_seq; | ||||
| 	uint32_t stats_ssrc; | ||||
| 	uint32_t stats_jitter; | ||||
| 	int32_t stats_transit; | ||||
| 	int stats_cycles; | ||||
| 	struct { | ||||
| 		int initialized; | ||||
| 		uint16_t base_seq; | ||||
| 		uint16_t max_seq; | ||||
| 		uint32_t ssrc; | ||||
| 		uint32_t jitter; | ||||
| 		int32_t transit; | ||||
| 		int cycles; | ||||
| 	} stats; | ||||
|  | ||||
| 	bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */ | ||||
| }; | ||||
|  | ||||
| @@ -83,43 +97,50 @@ struct mgcp_rtp_codec { | ||||
| 	char *subtype_name; | ||||
| }; | ||||
|  | ||||
| /* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */ | ||||
| struct mgcp_rtp_end { | ||||
| 	/* statistics */ | ||||
| 	unsigned int packets_rx; | ||||
| 	unsigned int octets_rx; | ||||
| 	unsigned int packets_tx; | ||||
| 	unsigned int octets_tx; | ||||
| 	unsigned int dropped_packets; | ||||
| 	/* local IP address of the RTP socket */ | ||||
| 	struct in_addr addr; | ||||
|  | ||||
| 	/* in network byte order */ | ||||
| 	int rtp_port, rtcp_port; | ||||
|  | ||||
| 	/* audio codec information */ | ||||
| 	struct mgcp_rtp_codec codec; | ||||
| 	struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */ | ||||
| 	/* currently selected audio codec */ | ||||
| 	struct mgcp_rtp_codec *codec; | ||||
|  | ||||
| 	/* array with assigned audio codecs to choose from (SDP) */ | ||||
| 	struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS]; | ||||
|  | ||||
| 	/* number of assigned audio codecs (SDP) */ | ||||
| 	unsigned int codecs_assigned; | ||||
|  | ||||
| 	/* per endpoint data */ | ||||
| 	int  frames_per_packet; | ||||
| 	uint32_t packet_duration_ms; | ||||
| 	int maximum_packet_time; /* -1: not set */ | ||||
| 	char *fmtp_extra; | ||||
| 	/* are we transmitting packets (1) or dropping (0) outbound packets */ | ||||
| 	int output_enabled; | ||||
| 	/* FIXME: This parameter can be set + printed, but is nowhere used! */ | ||||
| 	int force_output_ptime; | ||||
|  | ||||
| 	/* RTP patching */ | ||||
| 	int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ | ||||
| 	/* should we perform align_rtp_timestamp_offset() (1) or not (0) */ | ||||
| 	int force_aligned_timing; | ||||
| 	void *rtp_process_data; | ||||
|  | ||||
| 	/* Each end has a separate socket for RTP and RTCP */ | ||||
| 	struct osmo_fd rtp; | ||||
| 	struct osmo_fd rtcp; | ||||
|  | ||||
| 	/* local UDP port number of the RTP socket; RTCP is +1 */ | ||||
| 	int local_port; | ||||
| }; | ||||
|  | ||||
| struct mgcp_rtp_tap { | ||||
| 	/* is this tap active (1) or not (0) */ | ||||
| 	int enabled; | ||||
| 	/* IP/port to which we're forwarding the tapped data */ | ||||
| 	struct sockaddr_in forward; | ||||
| }; | ||||
|  | ||||
| @@ -155,7 +176,7 @@ struct mgcp_conn_rtp { | ||||
| 	/* Sequence bits */ | ||||
| 	struct mgcp_rtp_state state; | ||||
|  | ||||
| 	/* taps for the rtp connection */ | ||||
| 	/* taps for the rtp connection; one per direction */ | ||||
| 	struct mgcp_rtp_tap tap_in; | ||||
| 	struct mgcp_rtp_tap tap_out; | ||||
|  | ||||
| @@ -177,6 +198,8 @@ struct mgcp_conn_rtp { | ||||
| 			uint32_t octets; | ||||
| 		} stats; | ||||
| 	} osmux; | ||||
|  | ||||
| 	struct rate_ctr_group *rate_ctr_group; | ||||
| }; | ||||
|  | ||||
| /*! Connection type, specifies which member of the union "u" in mgcp_conn | ||||
| @@ -187,33 +210,33 @@ enum mgcp_conn_type { | ||||
|  | ||||
| /*! MGCP connection (untyped) */ | ||||
| struct mgcp_conn { | ||||
| 	/*!< list head */ | ||||
| 	/*! list head */ | ||||
| 	struct llist_head entry; | ||||
|  | ||||
| 	/*!< Backpointer to the endpoint where the conn belongs to */ | ||||
| 	/*! Backpointer to the endpoint where the conn belongs to */ | ||||
| 	struct mgcp_endpoint *endp; | ||||
|  | ||||
| 	/*!< type of the connection (union) */ | ||||
| 	/*! type of the connection (union) */ | ||||
| 	enum mgcp_conn_type type; | ||||
|  | ||||
| 	/*!< mode of the connection */ | ||||
| 	/*! mode of the connection */ | ||||
| 	enum mgcp_connection_mode mode; | ||||
|  | ||||
| 	/*!< copy of the mode to restore the original setting (VTY) */ | ||||
| 	/*! copy of the mode to restore the original setting (VTY) */ | ||||
| 	enum mgcp_connection_mode mode_orig; | ||||
|  | ||||
| 	/*!< connection id to identify the conntion */ | ||||
| 	uint32_t id; | ||||
| 	/*! connection id to identify the connection */ | ||||
| 	char id[MGCP_CONN_ID_LENGTH]; | ||||
|  | ||||
| 	/*!< human readable name (vty, logging) */ | ||||
| 	/*! human readable name (vty, logging) */ | ||||
| 	char name[256]; | ||||
|  | ||||
| 	/*!< union with connection description */ | ||||
| 	/*! union with connection description */ | ||||
| 	union { | ||||
| 		struct mgcp_conn_rtp rtp; | ||||
| 	} u; | ||||
|  | ||||
| 	/*!< pointer to optional private data */ | ||||
| 	/*! pointer to optional private data */ | ||||
| 	void *priv; | ||||
| }; | ||||
|  | ||||
| @@ -221,25 +244,9 @@ struct mgcp_conn { | ||||
|  | ||||
| struct mgcp_endpoint_type; | ||||
|  | ||||
| struct mgcp_endpoint { | ||||
| 	char *callid; | ||||
| 	struct mgcp_lco local_options; | ||||
|  | ||||
| 	struct llist_head conns; | ||||
|  | ||||
| 	/* backpointer */ | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct mgcp_trunk_config *tcfg; | ||||
|  | ||||
| 	const struct mgcp_endpoint_type *type; | ||||
|  | ||||
| 	/* fields for re-transmission */ | ||||
| 	char *last_trans; | ||||
| 	char *last_response; | ||||
| }; | ||||
|  | ||||
|  | ||||
| #define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints)) | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Internal structure while parsing a request | ||||
| @@ -249,7 +256,6 @@ struct mgcp_parse_data { | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	char *trans; | ||||
| 	char *save; | ||||
| 	int found; | ||||
| }; | ||||
|  | ||||
| int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, | ||||
| @@ -258,6 +264,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, | ||||
| int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn); | ||||
| int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf, | ||||
| 				unsigned int buf_size, struct mgcp_conn *conn); | ||||
| void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn); | ||||
| int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port, | ||||
| 			   struct mgcp_conn_rtp *conn); | ||||
| void mgcp_free_rtp_port(struct mgcp_rtp_end *end); | ||||
| @@ -271,6 +278,8 @@ static inline int endp_back_channel(int endpoint) | ||||
| struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index); | ||||
| struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index); | ||||
|  | ||||
| char *get_lco_identifier(const char *options); | ||||
| int check_local_cx_options(void *ctx, const char *options); | ||||
| void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, | ||||
| 			 struct mgcp_rtp_end *rtp); | ||||
| uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp, | ||||
| @@ -281,8 +290,8 @@ int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_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); | ||||
| 				      struct mgcp_conn_rtp *conn_dst, | ||||
| 				      struct mgcp_conn_rtp *conn_src); | ||||
|  | ||||
| void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, | ||||
| 					  int *payload_type, | ||||
| @@ -317,16 +326,5 @@ enum { | ||||
| #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); | ||||
|  | ||||
| /*! get the ip-address where the mgw application is bound on. | ||||
|  *  \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters | ||||
|  *  \returns pointer to a string that contains the source ip-address */ | ||||
| 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; | ||||
| } | ||||
| void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn); | ||||
|   | ||||
| @@ -43,7 +43,7 @@ int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line); | ||||
|  | ||||
| int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid); | ||||
|  | ||||
| int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *ci); | ||||
| int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id); | ||||
|  | ||||
| char *mgcp_strline(char *str, char **saveptr); | ||||
|  | ||||
| @@ -54,5 +54,3 @@ char *mgcp_strline(char *str, char **saveptr); | ||||
| #define for_each_non_empty_line(line, save)\ | ||||
| 	for (line = strtok_r(NULL, "\r\n", &save); line;\ | ||||
| 	     line = strtok_r(NULL, "\r\n", &save)) | ||||
|  | ||||
| int mgcp_parse_ci(uint32_t *conn_id, const char *ci); | ||||
|   | ||||
							
								
								
									
										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); | ||||
| @@ -30,8 +30,7 @@ | ||||
| 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_rtp_state *s, struct mgcp_rtp_end *, | ||||
| 			uint32_t *expected, int *loss); | ||||
| 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 *); | ||||
|   | ||||
| @@ -1,31 +1,8 @@ | ||||
| #ifndef OPENBSC_VTY_H | ||||
| #define OPENBSC_VTY_H | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/vty/vty.h> | ||||
| #include <osmocom/vty/buffer.h> | ||||
| #include <osmocom/vty/command.h> | ||||
|  | ||||
| struct gsm_network; | ||||
| struct vty; | ||||
|  | ||||
| void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *); | ||||
|  | ||||
| struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base); | ||||
|  | ||||
| extern struct cmd_element cfg_description_cmd; | ||||
| extern struct cmd_element cfg_no_description_cmd; | ||||
|  | ||||
| enum mgcp_vty_node { | ||||
| 	MGCP_NODE = _LAST_OSMOVTY_NODE + 1, | ||||
| 	TRUNK_NODE, | ||||
| }; | ||||
|  | ||||
| struct log_info; | ||||
| int bsc_vty_init(struct gsm_network *network); | ||||
| int bsc_vty_init_extra(void); | ||||
|  | ||||
| void msc_vty_init(struct gsm_network *msc_network); | ||||
|  | ||||
| struct gsm_network *gsmnet_from_vty(struct vty *vty); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <arpa/inet.h> | ||||
|  | ||||
| #include <osmocom/mgcp_client/mgcp_common.h> | ||||
|  | ||||
| @@ -25,16 +26,83 @@ struct mgcp_client_conf { | ||||
|  | ||||
| typedef unsigned int mgcp_trans_id_t; | ||||
|  | ||||
| /*! Enumeration of the codec types that mgcp_client is able to handle. */ | ||||
| enum mgcp_codecs { | ||||
| 	CODEC_PCMU_8000_1 = 0, | ||||
| 	CODEC_GSM_8000_1 = 3, | ||||
| 	CODEC_PCMA_8000_1 = 8, | ||||
| 	CODEC_G729_8000_1 = 18, | ||||
| 	CODEC_GSMEFR_8000_1 = 110, | ||||
| 	CODEC_GSMHR_8000_1 = 111,	 | ||||
| 	CODEC_AMR_8000_1 = 112, | ||||
| 	CODEC_AMRWB_16000_1 = 113, | ||||
| }; | ||||
| /* Note: when new codec types are added, the corresponding value strings | ||||
|  * in mgcp_client.c (codec_table) must be updated as well. Enumerations | ||||
|  * in enum mgcp_codecs must correspond to a valid payload type. However, | ||||
|  * this is an internal assumption that is made to avoid lookup tables. | ||||
|  * The API-User should not rely on this coincidence! */ | ||||
|  | ||||
| /*! Structure to build a payload type map to allow the defiition custom payload | ||||
|  *  types. */ | ||||
| struct ptmap { | ||||
| 	/*! codec for which a payload type number should be defined */ | ||||
| 	enum mgcp_codecs codec; | ||||
|  | ||||
| 	/*! payload type number (96-127) */ | ||||
| 	unsigned int pt; | ||||
| }; | ||||
|  | ||||
| struct mgcp_response_head { | ||||
|        int response_code; | ||||
|        mgcp_trans_id_t trans_id; | ||||
|        const char *comment; | ||||
| 	int response_code; | ||||
| 	mgcp_trans_id_t trans_id; | ||||
| 	char comment[MGCP_COMMENT_MAXLEN]; | ||||
| 	char conn_id[MGCP_CONN_ID_LENGTH]; | ||||
| 	char endpoint[MGCP_ENDPOINT_MAXLEN]; | ||||
| }; | ||||
|  | ||||
| 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 | ||||
|  | ||||
| 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; | ||||
| }; | ||||
|  | ||||
| void mgcp_client_conf_init(struct mgcp_client_conf *conf); | ||||
| @@ -60,22 +128,35 @@ 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); | ||||
| 			   enum mgcp_connection_mode mode) | ||||
| OSMO_DEPRECATED("Use mgcp_msg_gen() instead"); | ||||
|  | ||||
| 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); | ||||
| 			   uint16_t rtp_port, enum mgcp_connection_mode mode) | ||||
| OSMO_DEPRECATED("Use mgcp_msg_gen() instead"); | ||||
|  | ||||
| struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint, | ||||
| 			   unsigned int call_id); | ||||
| 			   unsigned int call_id) | ||||
| OSMO_DEPRECATED("Use mgcp_msg_gen() instead"); | ||||
|  | ||||
| struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg); | ||||
| mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg); | ||||
|  | ||||
| extern const struct value_string mgcp_client_connection_mode_strs[]; | ||||
| static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode) | ||||
| { | ||||
| 	return get_value_string(mgcp_client_connection_mode_strs, mode); | ||||
| } | ||||
|  | ||||
| enum mgcp_codecs map_str_to_codec(const char *str); | ||||
| unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len, | ||||
| 			     enum mgcp_codecs codec); | ||||
| enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len, | ||||
| 				 unsigned int pt); | ||||
|   | ||||
							
								
								
									
										44
									
								
								include/osmocom/mgcp_client/mgcp_client_fsm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								include/osmocom/mgcp_client/mgcp_client_fsm.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/mgcp_client/mgcp_common.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client.h> | ||||
| #include <osmocom/gsm/protocol/gsm_04_08.h> | ||||
|  | ||||
| /*! This struct organizes the connection infromation one connection side | ||||
|  *  (either remote or local). It is used to pass parameters (local) to the FSM | ||||
|  *  and get responses (remote) from the FSM as pointer attached to the FSM | ||||
|  *  event. | ||||
|  *  | ||||
|  *  When modifiying a connection, the endpoint and call_id members may be left | ||||
|  *  unpopulated. The call_id field is ignored in this case. If an endpoint | ||||
|  *  identifier is supplied it is checked against the internal state to make | ||||
|  *  sure it is correct. */ | ||||
| struct mgcp_conn_peer { | ||||
| 	/*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */ | ||||
| 	char addr[INET_ADDRSTRLEN]; | ||||
|  | ||||
| 	/*! RTP connection IP-Port (optional)  */ | ||||
| 	uint16_t port; | ||||
|  | ||||
| 	/*! RTP endpoint */ | ||||
| 	char endpoint[MGCP_ENDPOINT_MAXLEN]; | ||||
|  | ||||
| 	/*! CALL ID (unique per connection) */ | ||||
| 	unsigned int call_id; | ||||
|  | ||||
| 	/*! RTP packetization interval (optional) */ | ||||
| 	unsigned int ptime; | ||||
|  | ||||
| 	/*! RTP codec list (optional) */ | ||||
| 	enum mgcp_codecs codecs[MGCP_MAX_CODECS]; | ||||
|  | ||||
| 	/*! Number of codecs in RTP codec list (optional) */ | ||||
| 	unsigned int codecs_len; | ||||
| }; | ||||
|  | ||||
| struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt, | ||||
| 				       uint32_t parent_evt, struct mgcp_conn_peer *conn_peer); | ||||
| int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer); | ||||
| void mgcp_conn_delete(struct osmo_fsm_inst *fi); | ||||
|  | ||||
| const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi); | ||||
| @@ -1,10 +0,0 @@ | ||||
| prefix=@prefix@ | ||||
| exec_prefix=@exec_prefix@ | ||||
| libdir=@libdir@ | ||||
| includedir=@includedir@ | ||||
|  | ||||
| Name: Osmocom Media Gateway Control Protocol library | ||||
| Description: C Utility Library | ||||
| Version: @VERSION@ | ||||
| Libs: -L${libdir} -losmo-mgcp | ||||
| Cflags: -I${includedir}/ | ||||
| @@ -24,7 +24,7 @@ AM_LDFLAGS = \ | ||||
|  | ||||
| # This is not at all related to the release version, but a range of supported | ||||
| # API versions. Read TODO_RELEASE in the source tree's root! | ||||
| LEGACY_MGCP_LIBVERSION=0:0:0 | ||||
| LEGACY_MGCP_LIBVERSION=1:0:1 | ||||
|  | ||||
| lib_LTLIBRARIES = \ | ||||
| 	libosmo-legacy-mgcp.la \ | ||||
|   | ||||
| @@ -584,6 +584,36 @@ static int mgcp_send_transcoder(struct mgcp_rtp_end *end, | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| void mgcp_dejitter_udp_send(struct msgb *msg, void *data) | ||||
| { | ||||
| 	struct mgcp_rtp_end *rtp_end = (struct mgcp_rtp_end *) data; | ||||
|  | ||||
| 	int rc = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, | ||||
| 			   rtp_end->rtp_port, (char*) msg->data, msg->len); | ||||
| 	if (rc != msg->len) | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			"Failed to send data after jitter buffer: %d\n", rc); | ||||
| 	msgb_free(msg); | ||||
| } | ||||
|  | ||||
| static int enqueue_dejitter(struct osmo_jibuf *jb, struct mgcp_rtp_end *rtp_end, char *buf, int len) | ||||
| { | ||||
| 	struct msgb *msg; | ||||
| 	msg = msgb_alloc(len, "mgcp-jibuf"); | ||||
| 	if (!msg) | ||||
| 		return -1; | ||||
|  | ||||
| 	memcpy(msg->data, buf, len); | ||||
| 	msgb_put(msg, len); | ||||
|  | ||||
| 	if (osmo_jibuf_enqueue(jb, msg) < 0) { | ||||
| 		rtp_end->dropped_packets += 1; | ||||
| 		msgb_free(msg); | ||||
| 	} | ||||
|  | ||||
| 	return len; | ||||
| } | ||||
|  | ||||
| int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, | ||||
| 	      struct sockaddr_in *addr, char *buf, int rc) | ||||
| { | ||||
| @@ -591,6 +621,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, | ||||
| 	struct mgcp_rtp_end *rtp_end; | ||||
| 	struct mgcp_rtp_state *rtp_state; | ||||
| 	int tap_idx; | ||||
| 	struct osmo_jibuf *jb; | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 	     "endpoint %x dest %s tcfg->audio_loop %d endp->conn_mode %d (== loopback: %d)\n", | ||||
| @@ -612,10 +643,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, | ||||
| 		rtp_end = &endp->net_end; | ||||
| 		rtp_state = &endp->bts_state; | ||||
| 		tap_idx = MGCP_TAP_NET_OUT; | ||||
| 		jb = endp->bts_jb; | ||||
| 	} else { | ||||
| 		rtp_end = &endp->bts_end; | ||||
| 		rtp_state = &endp->net_state; | ||||
| 		tap_idx = MGCP_TAP_BTS_OUT; | ||||
| 		jb = NULL; | ||||
| 	} | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 	     "endpoint %x dest %s net_end %s %d %d bts_end %s %d %d rtp_end %s %d %d\n", | ||||
| @@ -680,9 +713,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, | ||||
| 				rtp_state->patched_first_rtp_payload = true; | ||||
| 			} | ||||
|  | ||||
| 			rc = mgcp_udp_send(rtp_end->rtp.fd, | ||||
| 					   &rtp_end->addr, | ||||
| 					   rtp_end->rtp_port, buf, len); | ||||
| 			if (jb) | ||||
| 				rc = enqueue_dejitter(jb, rtp_end, buf, len); | ||||
| 			else | ||||
| 				rc = mgcp_udp_send(rtp_end->rtp.fd, | ||||
| 						   &rtp_end->addr, | ||||
| 						   rtp_end->rtp_port, buf, len); | ||||
|  | ||||
| 			if (rc <= 0) | ||||
| 				return rc; | ||||
|   | ||||
| @@ -267,7 +267,6 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| { | ||||
| 	struct msgb *msg; | ||||
| 	struct osmux_hdr *osmuxh; | ||||
| 	struct llist_head list; | ||||
| 	struct sockaddr_in addr; | ||||
| 	struct mgcp_config *cfg = ofd->data; | ||||
| 	uint32_t rem; | ||||
| @@ -297,8 +296,7 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| 		endp->osmux.stats.chunks++; | ||||
| 		rem = msg->len; | ||||
|  | ||||
| 		osmux_xfrm_output(osmuxh, &endp->osmux.out, &list); | ||||
| 		osmux_tx_sched(&list, scheduled_tx_bts_cb, endp); | ||||
| 		osmux_xfrm_output_sched(&endp->osmux.out, osmuxh); | ||||
| 	} | ||||
| out: | ||||
| 	msgb_free(msg); | ||||
| @@ -359,7 +357,6 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| { | ||||
| 	struct msgb *msg; | ||||
| 	struct osmux_hdr *osmuxh; | ||||
| 	struct llist_head list; | ||||
| 	struct sockaddr_in addr; | ||||
| 	struct mgcp_config *cfg = ofd->data; | ||||
| 	uint32_t rem; | ||||
| @@ -389,8 +386,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| 		endp->osmux.stats.chunks++; | ||||
| 		rem = msg->len; | ||||
|  | ||||
| 		osmux_xfrm_output(osmuxh, &endp->osmux.out, &list); | ||||
| 		osmux_tx_sched(&list, scheduled_tx_net_cb, endp); | ||||
| 		osmux_xfrm_output_sched(&endp->osmux.out, osmuxh); | ||||
| 	} | ||||
| out: | ||||
| 	msgb_free(msg); | ||||
| @@ -470,9 +466,13 @@ int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint | ||||
| 	switch (endp->cfg->role) { | ||||
| 		case MGCP_BSC_NAT: | ||||
| 			endp->type = MGCP_OSMUX_BSC_NAT; | ||||
| 			osmux_xfrm_output_set_tx_cb(&endp->osmux.out, | ||||
| 							scheduled_tx_net_cb, endp); | ||||
| 			break; | ||||
| 		case MGCP_BSC: | ||||
| 			endp->type = MGCP_OSMUX_BSC; | ||||
| 			osmux_xfrm_output_set_tx_cb(&endp->osmux.out, | ||||
| 							scheduled_tx_bts_cb, endp); | ||||
| 			break; | ||||
| 	} | ||||
| 	endp->osmux.state = OSMUX_STATE_ENABLED; | ||||
| @@ -484,6 +484,11 @@ void osmux_disable_endpoint(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	LOGP(DLMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n", | ||||
| 	     ENDPOINT_NUMBER(endp), endp->osmux.cid); | ||||
|  | ||||
| 	/* We are closing, we don't need pending RTP packets to be transmitted */ | ||||
| 	osmux_xfrm_output_set_tx_cb(&endp->osmux.out, NULL, NULL); | ||||
| 	osmux_xfrm_output_flush(&endp->osmux.out); | ||||
|  | ||||
| 	osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid); | ||||
| 	endp->osmux.state = OSMUX_STATE_DISABLED; | ||||
| 	endp->osmux.cid = -1; | ||||
|   | ||||
| @@ -372,8 +372,8 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) | ||||
|  | ||||
| 	display_mgcp_message(msg->l2h, msgb_l2len(msg), "Received message"); | ||||
|  | ||||
|         /* attempt to treat it as a response */ | ||||
|         if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) { | ||||
| 	/* attempt to treat it as a response */ | ||||
| 	if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) { | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code); | ||||
| 		return NULL; | ||||
| 	} | ||||
| @@ -863,6 +863,11 @@ mgcp_header_done: | ||||
| 		goto error2; | ||||
| 	} | ||||
|  | ||||
| 	/* Apply Jiter buffer settings for this endpoint, they can be overriden by CRCX policy later */ | ||||
| 	endp->bts_use_jibuf = endp->cfg->bts_use_jibuf; | ||||
| 	endp->bts_jitter_delay_min = endp->cfg->bts_jitter_delay_min; | ||||
| 	endp->bts_jitter_delay_max = endp->cfg->bts_jitter_delay_max; | ||||
|  | ||||
| 	endp->allocated = 1; | ||||
|  | ||||
| 	/* set up RTP media parameters */ | ||||
| @@ -898,6 +903,13 @@ mgcp_header_done: | ||||
| 		case MGCP_POLICY_DEFER: | ||||
| 			/* stop processing */ | ||||
| 			create_transcoder(endp); | ||||
| 			/* Set up jitter buffer if required after policy has updated jibuf endp values */ | ||||
| 			if (endp->bts_use_jibuf) { | ||||
| 				endp->bts_jb = osmo_jibuf_alloc(tcfg->endpoints); | ||||
| 				osmo_jibuf_set_min_delay(endp->bts_jb, endp->bts_jitter_delay_min); | ||||
| 				osmo_jibuf_set_max_delay(endp->bts_jb, endp->bts_jitter_delay_max); | ||||
| 				osmo_jibuf_set_dequeue_cb(endp->bts_jb, mgcp_dejitter_udp_send, &endp->net_end); | ||||
| 			} | ||||
| 			return NULL; | ||||
| 			break; | ||||
| 		case MGCP_POLICY_CONT: | ||||
| @@ -906,6 +918,14 @@ mgcp_header_done: | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Set up jitter buffer if required after policy has updated jibuf endp values */ | ||||
| 	if (endp->bts_use_jibuf) { | ||||
| 		endp->bts_jb = osmo_jibuf_alloc(tcfg->endpoints); | ||||
| 		osmo_jibuf_set_min_delay(endp->bts_jb, endp->bts_jitter_delay_min); | ||||
| 		osmo_jibuf_set_max_delay(endp->bts_jb, endp->bts_jitter_delay_max); | ||||
| 		osmo_jibuf_set_dequeue_cb(endp->bts_jb, mgcp_dejitter_udp_send, &endp->net_end); | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n", | ||||
| 		ENDPOINT_NUMBER(endp), endp->ci, | ||||
| 		endp->net_end.local_port, endp->bts_end.local_port); | ||||
| @@ -1373,6 +1393,9 @@ int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg) | ||||
| void mgcp_release_endp(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp)); | ||||
| 	if (endp->bts_jb) | ||||
| 		osmo_jibuf_delete(endp->bts_jb); | ||||
| 	endp->bts_jb = NULL; | ||||
| 	endp->ci = CI_UNUSED; | ||||
| 	endp->allocated = 0; | ||||
|  | ||||
| @@ -1588,24 +1611,26 @@ void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size) | ||||
| 	msg += nchars; | ||||
| 	size -= nchars; | ||||
|  | ||||
| 	/* Error Counter */ | ||||
| 	nchars = snprintf(msg, size, | ||||
| 			  "\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u", | ||||
| 			  endp->net_state.in_stream.err_ts_counter, | ||||
| 			  endp->net_state.out_stream.err_ts_counter, | ||||
| 			  endp->bts_state.in_stream.err_ts_counter, | ||||
| 			  endp->bts_state.out_stream.err_ts_counter); | ||||
| 	if (nchars < 0 || nchars >= size) | ||||
| 		goto truncate; | ||||
| 	if (endp->cfg->osmux != OSMUX_USAGE_OFF) { | ||||
| 		/* Error Counter */ | ||||
| 		nchars = snprintf(msg, size, | ||||
| 				  "\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u", | ||||
| 				  endp->net_state.in_stream.err_ts_counter, | ||||
| 				  endp->net_state.out_stream.err_ts_counter, | ||||
| 				  endp->bts_state.in_stream.err_ts_counter, | ||||
| 				  endp->bts_state.out_stream.err_ts_counter); | ||||
| 		if (nchars < 0 || nchars >= size) | ||||
| 			goto truncate; | ||||
|  | ||||
| 	msg += nchars; | ||||
| 	size -= nchars; | ||||
| 		msg += nchars; | ||||
| 		size -= nchars; | ||||
|  | ||||
| 	if (endp->osmux.state == OSMUX_STATE_ENABLED) { | ||||
| 		snprintf(msg, size, | ||||
| 			 "\r\nX-Osmux-ST: CR=%u, BR=%u", | ||||
| 			 endp->osmux.stats.chunks, | ||||
| 			 endp->osmux.stats.octets); | ||||
| 		if (endp->osmux.state == OSMUX_STATE_ENABLED) { | ||||
| 			snprintf(msg, size, | ||||
| 				 "\r\nX-Osmux-ST: CR=%u, BR=%u", | ||||
| 				 endp->osmux.stats.chunks, | ||||
| 				 endp->osmux.stats.octets); | ||||
| 		} | ||||
| 	} | ||||
| truncate: | ||||
| 	msg[size - 1] = '\0'; | ||||
|   | ||||
| @@ -29,6 +29,7 @@ | ||||
| #include <osmocom/legacy_mgcp/vty.h> | ||||
|  | ||||
| #include <string.h> | ||||
| #include <inttypes.h> | ||||
|  | ||||
| #define RTCP_OMIT_STR "Drop RTCP packets in both directions\n" | ||||
| #define RTP_PATCH_STR "Modify RTP packet header in both directions\n" | ||||
| @@ -164,6 +165,13 @@ static int config_write_mgcp(struct vty *vty) | ||||
| 		vty_out(vty, "  osmux dummy %s%s", | ||||
| 			g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE); | ||||
| 	} | ||||
| 	if (g_cfg->bts_use_jibuf) | ||||
| 		vty_out(vty, "  bts-jitter-buffer%s", VTY_NEWLINE); | ||||
| 	if (g_cfg->bts_jitter_delay_min) | ||||
| 		vty_out(vty, "  bts-jitter-delay-min %"PRIu32"%s", g_cfg->bts_jitter_delay_min, VTY_NEWLINE); | ||||
| 	if (g_cfg->bts_jitter_delay_max) | ||||
| 		vty_out(vty, "  bts-jitter-delay-max %"PRIu32"%s", g_cfg->bts_jitter_delay_max, VTY_NEWLINE); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -241,6 +249,11 @@ DEFUN(show_mcgp, show_mgcp_cmd, | ||||
|  | ||||
| 	if (g_cfg->osmux) | ||||
| 		vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(), VTY_NEWLINE); | ||||
| 	vty_out(vty, "Jitter Buffer by default on Uplink : %s%s", | ||||
| 		g_cfg->bts_use_jibuf ? "on" : "off", VTY_NEWLINE); | ||||
| 	if (g_cfg->bts_use_jibuf) | ||||
| 		vty_out(vty, "Jitter Buffer delays: min=%"PRIu32" max=%"PRIu32"%s", | ||||
| 		g_cfg->bts_jitter_delay_min, g_cfg->bts_jitter_delay_max, VTY_NEWLINE); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| @@ -1344,6 +1357,63 @@ DEFUN(cfg_mgcp_osmux_dummy, | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| #define DEJITTER_STR "Uplink Jitter Buffer" | ||||
| DEFUN(cfg_mgcp_bts_use_jibuf, | ||||
|       cfg_mgcp_bts_use_jibuf_cmd, | ||||
|       "bts-jitter-buffer", | ||||
|       DEJITTER_STR "\n") | ||||
| { | ||||
| 	g_cfg->bts_use_jibuf = true; | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgcp_no_bts_use_jibuf, | ||||
|       cfg_mgcp_no_bts_use_jibuf_cmd, | ||||
|       "no bts-jitter-buffer", | ||||
|       NO_STR DEJITTER_STR "\n") | ||||
| { | ||||
| 	g_cfg->bts_use_jibuf = false; | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgcp_bts_jitter_delay_min, | ||||
|       cfg_mgcp_bts_jitter_delay_min_cmd, | ||||
|       "bts-jitter-buffer-delay-min <1-65535>", | ||||
|       DEJITTER_STR " Minimum Delay in ms\n" "Minimum Delay in ms\n") | ||||
| { | ||||
| 	g_cfg->bts_jitter_delay_min = atoi(argv[0]); | ||||
| 	if (!g_cfg->bts_jitter_delay_min) { | ||||
| 		vty_out(vty, "bts-jitter-buffer-delay-min cannot be zero.%s", VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	if (g_cfg->bts_jitter_delay_min && g_cfg->bts_jitter_delay_max && | ||||
| 	    g_cfg->bts_jitter_delay_min > g_cfg->bts_jitter_delay_max) { | ||||
| 		vty_out(vty, "bts-jitter-buffer-delay-min cannot be bigger than " \ | ||||
| 			"bts-jitter-buffer-delay-max.%s", VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgcp_bts_jitter_delay_max, | ||||
|       cfg_mgcp_bts_jitter_delay_max_cmd, | ||||
|       "bts-jitter-buffer-delay-max <1-65535>", | ||||
|       DEJITTER_STR " Maximum Delay in ms\n" "Maximum Delay in ms\n") | ||||
| { | ||||
| 	g_cfg->bts_jitter_delay_max = atoi(argv[0]); | ||||
| 	if (!g_cfg->bts_jitter_delay_max) { | ||||
| 		vty_out(vty, "bts-jitter-buffer-delay-max cannot be zero.%s", VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	if (g_cfg->bts_jitter_delay_min && g_cfg->bts_jitter_delay_max && | ||||
| 	    g_cfg->bts_jitter_delay_min > g_cfg->bts_jitter_delay_max) { | ||||
| 		vty_out(vty, "bts-jitter-buffer-delay-max cannot be smaller than " \ | ||||
| 			"bts-jitter-buffer-delay-min.%s", VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| int mgcp_vty_init(void) | ||||
| { | ||||
| 	install_element_ve(&show_mgcp_cmd); | ||||
| @@ -1356,7 +1426,6 @@ int mgcp_vty_init(void) | ||||
| 	install_element(CONFIG_NODE, &cfg_mgcp_cmd); | ||||
| 	install_node(&mgcp_node, config_write_mgcp); | ||||
|  | ||||
| 	vty_install_default(MGCP_NODE); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd); | ||||
| @@ -1412,11 +1481,14 @@ int mgcp_vty_init(void) | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_bts_use_jibuf_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_no_bts_use_jibuf_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_bts_jitter_delay_min_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_bts_jitter_delay_max_cmd); | ||||
|  | ||||
|  | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd); | ||||
| 	install_node(&trunk_node, config_write_trunk); | ||||
| 	vty_install_default(TRUNK_NODE); | ||||
| 	install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_cmd); | ||||
| 	install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_once_cmd); | ||||
| 	install_element(TRUNK_NODE, &cfg_trunk_no_rtp_keepalive_cmd); | ||||
|   | ||||
| @@ -20,7 +20,7 @@ AM_LDFLAGS = \ | ||||
|  | ||||
| # This is not at all related to the release version, but a range of supported | ||||
| # API versions. Read TODO_RELEASE in the source tree's root! | ||||
| MGCP_CLIENT_LIBVERSION=1:0:1 | ||||
| MGCP_CLIENT_LIBVERSION=4:0:1 | ||||
|  | ||||
| lib_LTLIBRARIES = \ | ||||
| 	libosmo-mgcp-client.la \ | ||||
| @@ -29,6 +29,7 @@ lib_LTLIBRARIES = \ | ||||
| libosmo_mgcp_client_la_SOURCES = \ | ||||
| 	mgcp_client.c \ | ||||
| 	mgcp_client_vty.c \ | ||||
| 	mgcp_client_fsm.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION) | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										694
									
								
								src/libosmo-mgcp-client/mgcp_client_fsm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										694
									
								
								src/libosmo-mgcp-client/mgcp_client_fsm.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,694 @@ | ||||
| /* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Philipp Maier | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <osmocom/mgcp_client/mgcp_client.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_fsm.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/fsm.h> | ||||
| #include <osmocom/core/byteswap.h> | ||||
| #include <arpa/inet.h> | ||||
| #include <osmocom/core/logging.h> | ||||
|  | ||||
| /* Context information, this is attached to the priv pointer of the FSM and | ||||
|  * is also handed back when dispatcheing events to the parent FSM. This is | ||||
|  * purly intened and not meant to be accessible for the API user */ | ||||
| struct mgcp_ctx { | ||||
| 	/* MGCP client instance that is used to interact with the MGW */ | ||||
| 	struct mgcp_client *mgcp; | ||||
|  | ||||
| 	/* The ID of the last pending transaction. This is used internally | ||||
| 	 * to cancel the transaction in case of an error */ | ||||
| 	mgcp_trans_id_t mgw_pending_trans; | ||||
|  | ||||
| 	/* Flag to mark that there is a pending transaction */ | ||||
| 	bool mgw_trans_pending; | ||||
|  | ||||
| 	/* Connection ID which has been assigned by he MGW */ | ||||
| 	char conn_id[MGCP_CONN_ID_LENGTH]; | ||||
|  | ||||
| 	/* Local RTP connection info, the MGW will send outgoing traffic to the | ||||
| 	 * ip/port specified here. The Address does not have to be choosen right | ||||
| 	 * on the creation of a connection. It can always be modified later by | ||||
| 	 * the user. */ | ||||
| 	struct mgcp_conn_peer conn_peer_local; | ||||
|  | ||||
| 	/* Remote RTP connection info, the ip/port specified here is the address | ||||
| 	 * where the MGW expects the RTP data to be sent. This address is | ||||
| 	 * defined by soly by the MGW and can not be influenced by the user. */ | ||||
| 	struct mgcp_conn_peer conn_peer_remote; | ||||
|  | ||||
| 	/* The terminate flag is a way to handle cornercase sitations that | ||||
| 	 * might occur when the user runs into an error situation and sends | ||||
| 	 * a DLCX command while the FSM is waiting for a response. In this | ||||
| 	 * case the DLCX command is not executed immediately. Instead the | ||||
| 	 * terminate flag is set. When the response to from the previous | ||||
| 	 * operation is received, we know that there is a DLCX event is | ||||
| 	 * pending. The FSM then generates the EV_DLCX by itsself before | ||||
| 	 * it enters ST_READY to cause the immediate execution of the | ||||
| 	 * DLCX procedure. (If normal operations are executed too fast, | ||||
| 	 * the API functions will return an error. In general, the user | ||||
| 	 * should synchronize using the callback events) */ | ||||
| 	bool terminate; | ||||
|  | ||||
| 	/* Event that is sent when the current operation is completed (except | ||||
| 	 * for DLCX, there the specified parent_term_evt is sent instead) */ | ||||
| 	uint32_t parent_evt; | ||||
| }; | ||||
|  | ||||
| #define S(x)	(1 << (x)) | ||||
|  | ||||
| #define MGCP_MGW_TIMEOUT 4	/* in seconds */ | ||||
| #define MGCP_MGW_TIMEOUT_TIMER_NR 1 | ||||
|  | ||||
| enum fsm_mgcp_client_states { | ||||
| 	ST_CRCX, | ||||
| 	ST_CRCX_RESP, | ||||
| 	ST_READY, | ||||
| 	ST_MDCX_RESP, | ||||
| 	ST_DLCX_RESP, | ||||
| }; | ||||
|  | ||||
| enum fsm_mgcp_client_evt { | ||||
| 	EV_CRCX, | ||||
| 	EV_CRCX_RESP, | ||||
| 	EV_MDCX, | ||||
| 	EV_MDCX_RESP, | ||||
| 	EV_DLCX, | ||||
| 	EV_DLCX_RESP, | ||||
| }; | ||||
|  | ||||
| static const struct value_string fsm_mgcp_client_evt_names[] = { | ||||
| 	OSMO_VALUE_STRING(EV_CRCX), | ||||
| 	OSMO_VALUE_STRING(EV_CRCX_RESP), | ||||
| 	OSMO_VALUE_STRING(EV_MDCX), | ||||
| 	OSMO_VALUE_STRING(EV_MDCX_RESP), | ||||
| 	OSMO_VALUE_STRING(EV_DLCX), | ||||
| 	OSMO_VALUE_STRING(EV_DLCX_RESP), | ||||
| 	{0, NULL} | ||||
| }; | ||||
|  | ||||
| static struct msgb *make_crcx_msg_bind(struct mgcp_ctx *mgcp_ctx) | ||||
| { | ||||
| 	struct mgcp_msg mgcp_msg; | ||||
|  | ||||
| 	mgcp_msg = (struct mgcp_msg) { | ||||
| 		.verb = MGCP_VERB_CRCX, | ||||
| 		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE), | ||||
| 		.call_id = mgcp_ctx->conn_peer_local.call_id, | ||||
| 		.conn_mode = MGCP_CONN_RECV_ONLY, | ||||
| 		.ptime = mgcp_ctx->conn_peer_local.ptime, | ||||
| 		.codecs_len = mgcp_ctx->conn_peer_local.codecs_len | ||||
| 	}; | ||||
| 	osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN); | ||||
| 	memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs)); | ||||
|  | ||||
| 	return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); | ||||
| } | ||||
|  | ||||
| static struct msgb *make_crcx_msg_bind_connect(struct mgcp_ctx *mgcp_ctx) | ||||
| { | ||||
| 	struct mgcp_msg mgcp_msg; | ||||
|  | ||||
| 	mgcp_msg = (struct mgcp_msg) { | ||||
| 		.verb = MGCP_VERB_CRCX, | ||||
| 		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | | ||||
| 			     MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP | | ||||
| 			     MGCP_MSG_PRESENCE_AUDIO_PORT), | ||||
| 		.call_id = mgcp_ctx->conn_peer_local.call_id, | ||||
| 		.conn_mode = MGCP_CONN_RECV_SEND, | ||||
| 		.audio_ip = mgcp_ctx->conn_peer_local.addr, | ||||
| 		.audio_port = mgcp_ctx->conn_peer_local.port, | ||||
| 		.ptime = mgcp_ctx->conn_peer_local.ptime, | ||||
| 		.codecs_len = mgcp_ctx->conn_peer_local.codecs_len | ||||
| 	}; | ||||
| 	osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN); | ||||
| 	memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs)); | ||||
|  | ||||
| 	return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); | ||||
| } | ||||
|  | ||||
| static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx) | ||||
| { | ||||
| 	struct mgcp_msg mgcp_msg; | ||||
|  | ||||
| 	mgcp_msg = (struct mgcp_msg) { | ||||
| 		.verb = MGCP_VERB_MDCX, | ||||
| 		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID | | ||||
| 			     MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT), | ||||
| 		.call_id =  mgcp_ctx->conn_peer_remote.call_id, | ||||
| 		.conn_id = mgcp_ctx->conn_id, | ||||
| 		.conn_mode = MGCP_CONN_RECV_SEND, | ||||
| 		.audio_ip = mgcp_ctx->conn_peer_local.addr, | ||||
| 		.audio_port = mgcp_ctx->conn_peer_local.port, | ||||
| 		.ptime = mgcp_ctx->conn_peer_local.ptime, | ||||
| 		.codecs_len = mgcp_ctx->conn_peer_local.codecs_len | ||||
| 	}; | ||||
| 	osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN); | ||||
| 	memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs)); | ||||
|  | ||||
| 	/* Note: We take the endpoint and the call_id from the remote | ||||
| 	 * connection info, because we can be confident that the | ||||
| 	 * information there is valid. For the local info, we explicitly | ||||
| 	 * allow endpoint and call_id to be optional */ | ||||
| 	return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); | ||||
| } | ||||
|  | ||||
| struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx) | ||||
| { | ||||
| 	struct mgcp_msg mgcp_msg; | ||||
|  | ||||
| 	mgcp_msg = (struct mgcp_msg) { | ||||
| 		.verb = MGCP_VERB_DLCX, | ||||
| 		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID), | ||||
| 		.call_id = mgcp_ctx->conn_peer_remote.call_id, | ||||
| 		.conn_id = mgcp_ctx->conn_id, | ||||
| 	}; | ||||
| 	osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN); | ||||
|  | ||||
| 	return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); | ||||
| } | ||||
|  | ||||
| static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv); | ||||
|  | ||||
| static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = data; | ||||
| 	struct mgcp_client *mgcp; | ||||
| 	struct msgb *msg; | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
| 	mgcp = mgcp_ctx->mgcp; | ||||
| 	OSMO_ASSERT(mgcp); | ||||
|  | ||||
| 	switch (event) { | ||||
| 	case EV_CRCX: | ||||
| 		LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: creating connection on MGW endpoint:%s...\n", | ||||
| 			 mgcp_ctx->conn_peer_local.endpoint); | ||||
|  | ||||
| 		if (mgcp_ctx->conn_peer_local.port) | ||||
| 			msg = make_crcx_msg_bind_connect(mgcp_ctx); | ||||
| 		else | ||||
| 			msg = make_crcx_msg_bind(mgcp_ctx); | ||||
| 		OSMO_ASSERT(msg); | ||||
|  | ||||
| 		mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); | ||||
| 		mgcp_ctx->mgw_trans_pending = true; | ||||
| 		rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi); | ||||
| 		if (rc < 0) { | ||||
| 			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		osmo_fsm_inst_state_chg(fi, ST_CRCX_RESP, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); | ||||
| 		break; | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Return the CI that the MGW allocated during CRCX response. This is purely informational for logging | ||||
|  * and identity tracking; the mgcp_conn_*() functions take care of using the right CI internally. */ | ||||
| const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||
| 	return mgcp_ctx->conn_id; | ||||
| } | ||||
|  | ||||
| static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv) | ||||
| { | ||||
| 	struct osmo_fsm_inst *fi = priv; | ||||
| 	struct mgcp_ctx *mgcp_ctx; | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	mgcp_ctx = fi->priv; | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	mgcp_ctx->mgw_trans_pending = false; | ||||
|  | ||||
| 	if (r->head.response_code != 200) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, | ||||
| 			 "MGW/CRCX: response yields error: %d %s\n", r->head.response_code, r->head.comment); | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	osmo_strlcpy(mgcp_ctx->conn_id, r->head.conn_id, sizeof(mgcp_ctx->conn_id)); | ||||
| 	LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with CI: %s\n", mgcp_ctx->conn_id); | ||||
|  | ||||
| 	rc = mgcp_response_parse_params(r); | ||||
| 	if (rc) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: Cannot parse CRCX response\n"); | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
| 	LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port); | ||||
|  | ||||
| 	osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr)); | ||||
| 	mgcp_ctx->conn_peer_remote.port = r->audio_port; | ||||
|  | ||||
| 	if (strlen(r->head.endpoint) > 0) { | ||||
| 		/* If we get an endpoint identifier back from the MGW, take it */ | ||||
| 		osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, r->head.endpoint, | ||||
| 			     sizeof(mgcp_ctx->conn_peer_remote.endpoint)); | ||||
| 	} else if (strstr(mgcp_ctx->conn_peer_local.endpoint, "*") == NULL) { | ||||
| 		/* If we do not get an endpoint identifier back and the | ||||
| 		 * identifier we used to create the connection is not a | ||||
| 		 * wildcarded one, we take the local endpoint identifier | ||||
| 		 * instead */ | ||||
| 		osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, mgcp_ctx->conn_peer_local.endpoint, | ||||
| 			     sizeof(mgcp_ctx->conn_peer_local.endpoint)); | ||||
| 	} else { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: CRCX yielded not suitable endpoint identifier\n"); | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	mgcp_ctx->conn_peer_remote.call_id = mgcp_ctx->conn_peer_local.call_id; | ||||
|  | ||||
| 	osmo_fsm_inst_dispatch(fi, EV_CRCX_RESP, mgcp_ctx); | ||||
| } | ||||
|  | ||||
| static void fsm_crcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = data; | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	switch (event) { | ||||
| 	case EV_CRCX_RESP: | ||||
| 		osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0); | ||||
| 		if (mgcp_ctx->terminate) { | ||||
| 			/* Trigger immediate DLCX if DLCX was requested while the FSM was | ||||
| 			 * busy with the previous operation */ | ||||
| 			LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: FSM was busy while DLCX was requested, executing now...\n"); | ||||
| 			osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx); | ||||
| 		} else | ||||
| 			osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote); | ||||
| 		break; | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv); | ||||
| static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv); | ||||
|  | ||||
| static void fsm_ready_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = data; | ||||
| 	struct msgb *msg; | ||||
| 	struct mgcp_client *mgcp; | ||||
| 	uint32_t new_state; | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
| 	mgcp = mgcp_ctx->mgcp; | ||||
| 	OSMO_ASSERT(mgcp); | ||||
|  | ||||
| 	switch (event) { | ||||
| 	case EV_MDCX: | ||||
| 		msg = make_mdcx_msg(mgcp_ctx); | ||||
| 		OSMO_ASSERT(msg); | ||||
| 		rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_resp_cb, fi); | ||||
| 		new_state = ST_MDCX_RESP; | ||||
| 		break; | ||||
| 	case EV_DLCX: | ||||
| 		msg = make_dlcx_msg(mgcp_ctx); | ||||
| 		OSMO_ASSERT(msg); | ||||
| 		rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_resp_cb, fi); | ||||
| 		new_state = ST_DLCX_RESP; | ||||
| 		break; | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); | ||||
| 	mgcp_ctx->mgw_trans_pending = true; | ||||
|  | ||||
| 	if (rc < 0) { | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	osmo_fsm_inst_state_chg(fi, new_state, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); | ||||
| } | ||||
|  | ||||
| static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv) | ||||
| { | ||||
| 	struct osmo_fsm_inst *fi = priv; | ||||
| 	struct mgcp_ctx *mgcp_ctx; | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	mgcp_ctx = fi->priv; | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	mgcp_ctx->mgw_trans_pending = false; | ||||
|  | ||||
| 	if (r->head.response_code != 200) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: response yields error: %d %s\n", r->head.response_code, | ||||
| 			 r->head.comment); | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	rc = mgcp_response_parse_params(r); | ||||
| 	if (rc) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: Cannot parse MDCX response\n"); | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
| 	LOGPFSML(fi, LOGL_DEBUG, "MGW/MDCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port); | ||||
|  | ||||
| 	osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr)); | ||||
| 	mgcp_ctx->conn_peer_remote.port = r->audio_port; | ||||
|  | ||||
| 	osmo_fsm_inst_dispatch(fi, EV_MDCX_RESP, mgcp_ctx); | ||||
| } | ||||
|  | ||||
| static void fsm_mdcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = data; | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	switch (event) { | ||||
| 	case EV_MDCX_RESP: | ||||
| 		osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0); | ||||
| 		if (mgcp_ctx->terminate) { | ||||
| 			/* Trigger immediate DLCX if DLCX was requested while the FSM was | ||||
| 			 * busy with the previous operation */ | ||||
| 			LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: FSM was busy while DLCX was requested, executing now...\n"); | ||||
| 			osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx); | ||||
| 		} else | ||||
| 			osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote); | ||||
| 		break; | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv) | ||||
| { | ||||
| 	struct osmo_fsm_inst *fi = priv; | ||||
| 	struct mgcp_ctx *mgcp_ctx; | ||||
|  | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	mgcp_ctx = fi->priv; | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	mgcp_ctx->mgw_trans_pending = false; | ||||
|  | ||||
| 	if (r->head.response_code != 250) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, | ||||
| 			 "MGW/DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment); | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	osmo_fsm_inst_dispatch(fi, EV_DLCX_RESP, mgcp_ctx); | ||||
| } | ||||
|  | ||||
| static void fsm_dlcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = data; | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	switch (event) { | ||||
| 	case EV_DLCX_RESP: | ||||
| 		/* Rub out the connection identifier, since the connection | ||||
| 		 * is no longer present and we will use the connection id | ||||
| 		 * to know in error cases if the connection is still present | ||||
| 		 * or not */ | ||||
| 		memset(mgcp_ctx->conn_id, 0, sizeof(mgcp_ctx->conn_id)); | ||||
|  | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); | ||||
| 		break; | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int fsm_timeout_cb(struct osmo_fsm_inst *fi) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||
| 	struct mgcp_client *mgcp; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
| 	mgcp = mgcp_ctx->mgcp; | ||||
| 	OSMO_ASSERT(mgcp); | ||||
|  | ||||
| 	if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) { | ||||
| 		/* Note: We were unable to communicate with the MGW, | ||||
| 		 * unfortunately there is no meaningful action we can take | ||||
| 		 * now other than giving up. */ | ||||
| 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); | ||||
| 	} else { | ||||
| 		/* Note: Ther must not be any unsolicited timers | ||||
| 		 * in this FSM. If so, we have serious problem. */ | ||||
| 		OSMO_ASSERT(false); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||
| 	struct mgcp_client *mgcp; | ||||
| 	struct msgb *msg; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
| 	mgcp = mgcp_ctx->mgcp; | ||||
| 	OSMO_ASSERT(mgcp); | ||||
|  | ||||
| 	/* If there is still a transaction pending, cancel it now. */ | ||||
| 	if (mgcp_ctx->mgw_trans_pending) | ||||
| 		mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans); | ||||
|  | ||||
| 	/* Should the FSM be terminated while there are still open connections | ||||
| 	 * on the MGW, we send an unconditional DLCX to terminate the | ||||
| 	 * connection. This is not the normal case. The user should always use | ||||
| 	 * mgcp_conn_delete() to instruct the FSM to perform a graceful exit */ | ||||
| 	if (strlen(mgcp_ctx->conn_id)) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, | ||||
| 			 "MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n"); | ||||
| 		msg = make_dlcx_msg(mgcp_ctx); | ||||
| 		OSMO_ASSERT(msg); | ||||
| 		mgcp_client_tx(mgcp, msg, NULL, NULL); | ||||
| 	} | ||||
|  | ||||
| 	talloc_free(mgcp_ctx); | ||||
| } | ||||
|  | ||||
| static struct osmo_fsm_state fsm_mgcp_client_states[] = { | ||||
|  | ||||
| 	/* Initial CRCX state. This state is immediately entered and executed | ||||
| 	 * when the FSM is started. The rationale is that we first have to | ||||
| 	 * create a connectin before we can execute other operations on that | ||||
| 	 * connection. */ | ||||
| 	[ST_CRCX] = { | ||||
| 		     .in_event_mask = S(EV_CRCX), | ||||
| 		     .out_state_mask = S(ST_CRCX_RESP), | ||||
| 		     .name = OSMO_STRINGIFY(ST_CRCX), | ||||
| 		     .action = fsm_crcx_cb, | ||||
| 		     }, | ||||
|  | ||||
| 	/* Wait for the response to a CRCX operation, check and process the | ||||
| 	 * results, change to ST_READY afterwards. */ | ||||
| 	[ST_CRCX_RESP] = { | ||||
| 			  .in_event_mask = S(EV_CRCX_RESP), | ||||
| 			  .out_state_mask = S(ST_READY), | ||||
| 			  .name = OSMO_STRINGIFY(ST_CRCX_RESP), | ||||
| 			  .action = fsm_crcx_resp_cb, | ||||
| 			  }, | ||||
|  | ||||
| 	/* In this idle state we wait for further operations (e.g. MDCX) that | ||||
| 	 * can be executed by the user using the API. There is no timeout in | ||||
| 	 * this state. The connection lives on until the user decides to | ||||
| 	 * terminate it (DLCX). */ | ||||
| 	[ST_READY] = { | ||||
| 		      .in_event_mask = S(EV_MDCX) | S(EV_DLCX), | ||||
| 		      .out_state_mask = S(ST_MDCX_RESP) | S(ST_DLCX_RESP), | ||||
| 		      .name = OSMO_STRINGIFY(ST_READY), | ||||
| 		      .action = fsm_ready_cb, | ||||
| 		      }, | ||||
|  | ||||
| 	/* Wait for the response of a MDCX operation, check and process the | ||||
| 	 * results, change to ST_READY afterwards. */ | ||||
| 	[ST_MDCX_RESP] = { | ||||
| 			  .in_event_mask = S(EV_MDCX_RESP), | ||||
| 			  .out_state_mask = S(ST_READY), | ||||
| 			  .name = OSMO_STRINGIFY(ST_MDCX_RESP), | ||||
| 			  .action = fsm_mdcx_resp_cb, | ||||
| 			  }, | ||||
|  | ||||
| 	/* Wait for the response of a DLCX operation and terminate the FSM | ||||
| 	 * normally. */ | ||||
| 	[ST_DLCX_RESP] = { | ||||
| 			  .in_event_mask = S(EV_DLCX_RESP), | ||||
| 			  .out_state_mask = 0, | ||||
| 			  .name = OSMO_STRINGIFY(ST_DLCX_RESP), | ||||
| 			  .action = fsm_dlcx_resp_cb, | ||||
| 			  }, | ||||
| }; | ||||
|  | ||||
| static struct osmo_fsm fsm_mgcp_client = { | ||||
| 	.name = "MGCP_CONN", | ||||
| 	.states = fsm_mgcp_client_states, | ||||
| 	.num_states = ARRAY_SIZE(fsm_mgcp_client_states), | ||||
| 	.timer_cb = fsm_timeout_cb, | ||||
| 	.cleanup = fsm_cleanup_cb, | ||||
| 	.event_names = fsm_mgcp_client_evt_names, | ||||
| }; | ||||
|  | ||||
| /*! allocate FSM, and create a new connection on the MGW. | ||||
|  *  \param[in] mgcp MGCP client descriptor. | ||||
|  *  \param[in] parent_fi Parent FSM instance. | ||||
|  *  \param[in] parent_term_evt Event to be sent to parent when terminating. | ||||
|  *  \param[in] parent_evt Event to be sent to parent when operation is done. | ||||
|  *  \param[in] conn_peer Connection parameters (ip, port...). | ||||
|  *  \returns newly-allocated, initialized and registered FSM instance, NULL on error. */ | ||||
| struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, | ||||
| 				       uint32_t parent_term_evt, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx; | ||||
| 	static bool fsm_registered = false; | ||||
| 	struct osmo_fsm_inst *fi; | ||||
| 	struct in_addr ip_test; | ||||
|  | ||||
| 	OSMO_ASSERT(parent_fi); | ||||
| 	OSMO_ASSERT(mgcp); | ||||
| 	OSMO_ASSERT(conn_peer); | ||||
|  | ||||
| 	/* Check if IP/Port information in conn info makes sense */ | ||||
| 	if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0) | ||||
| 		return NULL; | ||||
|  | ||||
| 	/* Register the fsm description (if not already done) */ | ||||
| 	if (fsm_registered == false) { | ||||
| 		osmo_fsm_register(&fsm_mgcp_client); | ||||
| 		fsm_registered = true; | ||||
| 	} | ||||
|  | ||||
| 	/* Allocate and configure a new fsm instance */ | ||||
| 	fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt); | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	mgcp_ctx = talloc_zero(fi, struct mgcp_ctx); | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
| 	mgcp_ctx->mgcp = mgcp; | ||||
| 	mgcp_ctx->parent_evt = parent_evt; | ||||
|  | ||||
| 	memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local)); | ||||
| 	fi->priv = mgcp_ctx; | ||||
|  | ||||
| 	/* start state machine */ | ||||
| 	OSMO_ASSERT(fi->state == ST_CRCX); | ||||
| 	osmo_fsm_inst_dispatch(fi, EV_CRCX, mgcp_ctx); | ||||
|  | ||||
| 	return fi; | ||||
| } | ||||
|  | ||||
| /*! modify an existing connection on the MGW. | ||||
|  *  \param[in] fi FSM instance. | ||||
|  *  \param[in] parent_evt Event to be sent to parent when operation is done. | ||||
|  *  \param[in] conn_peer New connection information (ip, port...). | ||||
|  *  \returns 0 on success, -EINVAL on error. */ | ||||
| int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer) | ||||
| { | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||
| 	struct in_addr ip_test; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
| 	OSMO_ASSERT(conn_peer); | ||||
|  | ||||
| 	/* The user must not issue an MDCX before the CRCX has completed, | ||||
| 	 * if this happens, it means that the parent FSM has overhead the | ||||
| 	 * parent_evt (mandatory!) and executed the MDCX without even | ||||
| 	 * waiting for the results. Another reason could be that the | ||||
| 	 * parent FSM got messed up */ | ||||
| 	OSMO_ASSERT(fi->state != ST_CRCX_RESP); | ||||
|  | ||||
| 	/* If the user tries to issue an MDCX while an DLCX operation is | ||||
| 	 * pending, there must be a serious problem with the paren FSM. | ||||
| 	 * Eeither the parent_term_evt (mandatory!) has been overheard, | ||||
| 	 * or the parant FSM got messed so badly that it still assumes | ||||
| 	 * a live connection although it as killed it. */ | ||||
| 	OSMO_ASSERT(fi->state != ST_DLCX_RESP); | ||||
|  | ||||
| 	/* Check if IP/Port parameters make sense */ | ||||
| 	if (conn_peer->port == 0) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	if (inet_aton(conn_peer->addr, &ip_test) == 0) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/*! The user may supply an endpoint identifier in conn_peer. The | ||||
| 	 *  identifier is then checked. This check is optional. Later steps do | ||||
| 	 *  not depend on the endpoint identifier supplied here because it is | ||||
| 	 *  already implicitly known from the CRCX phase. */ | ||||
| 	if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint)) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, endpoint mismatches: requested %s, should be %s\n", | ||||
| 			 conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/*! Note: The call-id is implicitly known from the previous CRCX and | ||||
| 	 *  will not be checked even when it is set in conn_peer. */ | ||||
|  | ||||
| 	mgcp_ctx->parent_evt = parent_evt; | ||||
| 	memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local)); | ||||
| 	osmo_fsm_inst_dispatch(fi, EV_MDCX, mgcp_ctx); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! delete existing connection on the MGW, destroy FSM afterwards. | ||||
|  *  \param[in] fi FSM instance. */ | ||||
| void mgcp_conn_delete(struct osmo_fsm_inst *fi) | ||||
| { | ||||
| 	OSMO_ASSERT(fi); | ||||
| 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_ctx); | ||||
|  | ||||
| 	/* Unlink FSM from parent */ | ||||
| 	osmo_fsm_inst_unlink_parent(fi, NULL); | ||||
|  | ||||
| 	/* An error situation where the parent FSM must be killed immediately | ||||
| 	 * may lead into a situation where the DLCX can not be executed right | ||||
| 	 * at that moment because the FSM is still busy with another operation. | ||||
| 	 * In those cases we postpone the DLCX so that the FSM and the | ||||
| 	 * connections on the MGW get cleaned up gracefully. */ | ||||
| 	if (fi->state != ST_READY) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, "MGW: operation still pending, DLCX will be postponed.\n"); | ||||
| 		mgcp_ctx->terminate = true; | ||||
| 		return; | ||||
| 	} | ||||
| 	osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx); | ||||
| } | ||||
| @@ -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,18 +25,19 @@ | ||||
| #include <talloc.h> | ||||
|  | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/vty/misc.h> | ||||
| #include <osmocom/core/utils.h> | ||||
|  | ||||
| #include <osmocom/mgcp_client/mgcp_client.h> | ||||
|  | ||||
| #define MGCPGW_STR "MGCP gateway configuration for RTP streams\n" | ||||
| #define MGW_STR "Configure MGCP connection to Media Gateway\n" | ||||
|  | ||||
| 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) | ||||
| @@ -46,10 +47,14 @@ DEFUN(cfg_mgcpgw_local_ip, cfg_mgcpgw_local_ip_cmd, | ||||
| 		talloc_strdup(global_mgcp_client_ctx, 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) | ||||
| @@ -57,11 +62,15 @@ 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; | ||||
| @@ -70,23 +79,31 @@ DEFUN(cfg_mgcpgw_remote_ip, cfg_mgcpgw_remote_ip_cmd, | ||||
| 		talloc_strdup(global_mgcp_client_ctx, 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(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd, | ||||
|       "mgw endpoint-range <1-65534> <1-65534>", | ||||
|       MGW_STR "usable range of endpoint identifiers\n" | ||||
|       "set first usable endpoint identifier\n" | ||||
|       "set last usable endpoint identifier\n") | ||||
| { | ||||
| 	uint16_t first_endpoint = atoi(argv[0]); | ||||
| 	uint16_t last_endpoint = atoi(argv[1]); | ||||
| @@ -101,19 +118,30 @@ DEFUN(cfg_mgcpgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd, | ||||
| 	global_mgcp_client_conf->last_endpoint = last_endpoint; | ||||
| 	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, | ||||
|       "mgcpgw bts-base <0-65534>", | ||||
|       MGCPGW_STR | ||||
| DEFUN(cfg_mgw_rtp_bts_base_port, | ||||
|       cfg_mgw_rtp_bts_base_port_cmd, | ||||
|       "mgw bts-base <0-65534>", | ||||
|       MGW_STR | ||||
|       BTS_START_STR | ||||
|       UDP_PORT_STR) | ||||
| { | ||||
| 	global_mgcp_client_conf->bts_base = atoi(argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port, | ||||
|       cfg_mgcpgw_rtp_bts_base_port_cmd, | ||||
|       "mgcpgw bts-base <0-65534>", | ||||
|       MGW_STR | ||||
|       BTS_START_STR | ||||
|       UDP_PORT_STR) | ||||
|  | ||||
| int mgcp_client_config_write(struct vty *vty, const char *indent) | ||||
| { | ||||
| @@ -125,32 +153,32 @@ int mgcp_client_config_write(struct vty *vty, const char *indent) | ||||
|  | ||||
| 	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, | ||||
| 		vty_out(vty, "%smgw 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, | ||||
| 		vty_out(vty, "%smgw bts-base %u%s", indent, | ||||
| 			bts_base, VTY_NEWLINE); | ||||
| 	} | ||||
|  | ||||
| @@ -162,10 +190,20 @@ 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); | ||||
|  | ||||
| 	/* 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(); | ||||
| } | ||||
|   | ||||
| @@ -7,6 +7,7 @@ AM_CPPFLAGS = \ | ||||
| AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOGSM_CFLAGS) \ | ||||
| 	$(LIBOSMOVTY_CFLAGS) \ | ||||
| 	$(LIBOSMONETIF_CFLAGS) \ | ||||
| 	$(COVERAGE_CFLAGS) \ | ||||
| @@ -14,33 +15,29 @@ AM_CFLAGS = \ | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	$(COVERAGE_LDFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| # This is not at all related to the release version, but a range of supported | ||||
| # API versions. Read TODO_RELEASE in the source tree's root! | ||||
| MGCP_LIBVERSION=0:0:0 | ||||
|  | ||||
| lib_LTLIBRARIES = \ | ||||
| 	libosmo-mgcp.la \ | ||||
| noinst_LIBRARIES = \ | ||||
| 	libosmo-mgcp.a \ | ||||
| 	$(NULL) | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	g711common.h \ | ||||
| 	$(NULL) | ||||
|  | ||||
| libosmo_mgcp_la_SOURCES = \ | ||||
| libosmo_mgcp_a_SOURCES = \ | ||||
| 	mgcp_protocol.c \ | ||||
| 	mgcp_network.c \ | ||||
| 	mgcp_vty.c \ | ||||
| 	mgcp_osmux.c \ | ||||
| 	mgcp_sdp.c \ | ||||
| 	mgcp_codec.c \ | ||||
| 	mgcp_msg.c \ | ||||
| 	mgcp_conn.c \ | ||||
| 	mgcp_stat.c \ | ||||
| 	mgcp_ep.c \ | ||||
| 	mgcp_endp.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| libosmo_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_LIBVERSION) | ||||
|   | ||||
							
								
								
									
										343
									
								
								src/libosmo-mgcp/mgcp_codec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								src/libosmo-mgcp/mgcp_codec.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,343 @@ | ||||
| /* | ||||
|  * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|  * (C) 2009-2014 by On-Waves | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| /* Helper function to dump codec information of a specified codec to a printable | ||||
|  * string, used by dump_codec_summary() */ | ||||
| static char *dump_codec(struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	static char str[256]; | ||||
| 	char *pt_str; | ||||
|  | ||||
| 	if (codec->payload_type > 76) | ||||
| 		pt_str = "DYNAMIC"; | ||||
| 	else if (codec->payload_type > 72) | ||||
| 		pt_str = "RESERVED <!>"; | ||||
| 	else if (codec->payload_type != PTYPE_UNDEFINED) | ||||
| 		pt_str = codec->subtype_name; | ||||
| 	else | ||||
| 		pt_str = "INVALID <!>"; | ||||
|  | ||||
| 	snprintf(str, sizeof(str), "(pt:%i=%s, audio:%s subt=%s, rate=%u, ch=%i, t=%u/%u)", codec->payload_type, pt_str, | ||||
| 		 codec->audio_name, codec->subtype_name, codec->rate, codec->channels, codec->frame_duration_num, | ||||
| 		 codec->frame_duration_den); | ||||
| 	return str; | ||||
| } | ||||
|  | ||||
| /*! Dump a summary of all negotiated codecs to debug log | ||||
|  *  \param[in] conn related rtp-connection. */ | ||||
| void mgcp_codec_summary(struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	struct mgcp_rtp_end *rtp; | ||||
| 	unsigned int i; | ||||
| 	struct mgcp_rtp_codec *codec; | ||||
| 	struct mgcp_endpoint *endp; | ||||
|  | ||||
| 	rtp = &conn->end; | ||||
| 	endp = conn->conn->endp; | ||||
|  | ||||
| 	if (rtp->codecs_assigned == 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x conn:%s no codecs available\n", ENDPOINT_NUMBER(endp), | ||||
| 		     mgcp_conn_dump(conn->conn)); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* Store parsed codec information */ | ||||
| 	for (i = 0; i < rtp->codecs_assigned; i++) { | ||||
| 		codec = &rtp->codecs[i]; | ||||
|  | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x conn:%s codecs[%u]:%s", ENDPOINT_NUMBER(endp), | ||||
| 		     mgcp_conn_dump(conn->conn), i, dump_codec(codec)); | ||||
|  | ||||
| 		if (codec == rtp->codec) | ||||
| 			LOGPC(DLMGCP, LOGL_DEBUG, " [selected]"); | ||||
|  | ||||
| 		LOGPC(DLMGCP, LOGL_DEBUG, "\n"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Initalize or reset codec information with default data. */ | ||||
| void codec_init(struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	if (codec->subtype_name) | ||||
| 		talloc_free(codec->subtype_name); | ||||
| 	if (codec->audio_name) | ||||
| 		talloc_free(codec->audio_name); | ||||
| 	memset(codec, 0, sizeof(*codec)); | ||||
| 	codec->payload_type = -1; | ||||
| 	codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; | ||||
| 	codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; | ||||
| 	codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; | ||||
| 	codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS; | ||||
| } | ||||
|  | ||||
| /*! Initalize or reset codec information with default data. | ||||
|  *  \param[out] conn related rtp-connection. */ | ||||
| void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	memset(conn->end.codecs, 0, sizeof(conn->end.codecs)); | ||||
| 	conn->end.codecs_assigned = 0; | ||||
| 	conn->end.codec = NULL; | ||||
| } | ||||
|  | ||||
| /* Set members of struct mgcp_rtp_codec, extrapolate in missing information */ | ||||
| static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, | ||||
| 		     int payload_type, const char *audio_name, unsigned int pt_offset) | ||||
| { | ||||
| 	int rate; | ||||
| 	int channels; | ||||
| 	char audio_codec[64]; | ||||
|  | ||||
| 	/* Initalize the codec struct with some default data to begin with */ | ||||
| 	codec_init(codec); | ||||
|  | ||||
| 	if (payload_type != PTYPE_UNDEFINED) { | ||||
| 		/* Make sure we do not get any reserved or undefined type numbers */ | ||||
| 		/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */ | ||||
| 		if (payload_type == 1 || payload_type == 2 || payload_type == 19) | ||||
| 			goto error; | ||||
| 		if (payload_type >= 72 && payload_type <= 76) | ||||
| 			goto error; | ||||
| 		if (payload_type >= 127) | ||||
| 			goto error; | ||||
|  | ||||
| 		codec->payload_type = payload_type; | ||||
| 	} | ||||
|  | ||||
| 	/* When no audio name is given, we are forced to use the payload | ||||
| 	 * type to generate the audio name. This is only possible for | ||||
| 	 * non dynamic payload types, which are statically defined */ | ||||
| 	if (!audio_name) { | ||||
| 		switch (payload_type) { | ||||
| 		case 0: | ||||
| 			audio_name = talloc_strdup(ctx, "PCMU/8000/1"); | ||||
| 			break; | ||||
| 		case 3: | ||||
| 			audio_name = talloc_strdup(ctx, "GSM/8000/1"); | ||||
| 			break; | ||||
| 		case 8: | ||||
| 			audio_name = talloc_strdup(ctx, "PCMA/8000/1"); | ||||
| 			break; | ||||
| 		case 18: | ||||
| 			audio_name = talloc_strdup(ctx, "G729/8000/1"); | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* The given payload type is not known to us, or it | ||||
| 			 * it is a dynamic payload type for which we do not | ||||
| 			 * know the audio name. We must give up here */ | ||||
| 			goto error; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Now we extract the codec subtype name, rate and channels. The latter | ||||
| 	 * two are optional. If they are not present we use the safe defaults | ||||
| 	 * above. */ | ||||
| 	if (strlen(audio_name) > sizeof(audio_codec)) | ||||
| 		goto error; | ||||
| 	channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS; | ||||
| 	rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; | ||||
| 	if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) | ||||
| 		goto error; | ||||
|  | ||||
| 	/* Note: We only accept configurations with one audio channel! */ | ||||
| 	if (channels != 1) | ||||
| 		goto error; | ||||
|  | ||||
| 	codec->rate = rate; | ||||
| 	codec->channels = channels; | ||||
| 	codec->subtype_name = talloc_strdup(ctx, audio_codec); | ||||
| 	codec->audio_name = talloc_strdup(ctx, audio_name); | ||||
| 	codec->payload_type = payload_type; | ||||
|  | ||||
| 	if (!strcmp(audio_codec, "G729")) { | ||||
| 		codec->frame_duration_num = 10; | ||||
| 		codec->frame_duration_den = 1000; | ||||
| 	} else { | ||||
| 		codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; | ||||
| 		codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; | ||||
| 	} | ||||
|  | ||||
| 	/* Derive the payload type if it is unknown */ | ||||
| 	if (codec->payload_type == PTYPE_UNDEFINED) { | ||||
|  | ||||
| 		/* For the known codecs from the static range we restore | ||||
| 		 * the IANA or 3GPP assigned payload type number */ | ||||
| 		if (codec->rate == 8000 && codec->channels == 1) { | ||||
| 			/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */ | ||||
| 			if (!strcmp(codec->subtype_name, "GSM")) | ||||
| 				codec->payload_type = 3; | ||||
| 			else if (!strcmp(codec->subtype_name, "PCMA")) | ||||
| 				codec->payload_type = 8; | ||||
| 			else if (!strcmp(codec->subtype_name, "PCMU")) | ||||
| 				codec->payload_type = 0; | ||||
| 			else if (!strcmp(codec->subtype_name, "G729")) | ||||
| 				codec->payload_type = 18; | ||||
|  | ||||
| 			/* See also: 3GPP TS 48.103, chapter 5.4.2.2 RTP Payload | ||||
| 			 * Note: These are not fixed payload types as the IANA | ||||
| 			 * defined once, they still remain dymanic payload | ||||
| 			 * types, but with a payload type number preference. */ | ||||
| 			else if (!strcmp(codec->subtype_name, "GSM-EFR")) | ||||
| 				codec->payload_type = 110; | ||||
| 			else if (!strcmp(codec->subtype_name, "GSM-HR-08")) | ||||
| 				codec->payload_type = 111; | ||||
| 			else if (!strcmp(codec->subtype_name, "AMR")) | ||||
| 				codec->payload_type = 112; | ||||
| 			else if (!strcmp(codec->subtype_name, "AMR-WB")) | ||||
| 				codec->payload_type = 113; | ||||
| 		} | ||||
|  | ||||
| 		/* If we could not determine a payload type we assume that | ||||
| 		 * we are dealing with a codec from the dynamic range. We | ||||
| 		 * choose a fixed identifier from 96-109. (Note: normally, | ||||
| 		 * the dynamic payload type rante is from 96-127, but from | ||||
| 		 * 110 onwards 3gpp defines prefered codec types, which are | ||||
| 		 * also fixed, see above)  */ | ||||
| 		if (codec->payload_type < 0) { | ||||
| 			codec->payload_type = 96 + pt_offset; | ||||
| 			if (codec->payload_type > 109) | ||||
| 				goto error; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| error: | ||||
| 	/* Make sure we leave a clean codec entry on error. */ | ||||
| 	codec_init(codec); | ||||
| 	memset(codec, 0, sizeof(*codec)); | ||||
| 	return -EINVAL; | ||||
| } | ||||
|  | ||||
| /*! Add codec configuration depending on payload type and/or codec name. This | ||||
|  *  function uses the input parameters to extrapolate the full codec information. | ||||
|  *  \param[out] codec configuration (caller provided memory). | ||||
|  *  \param[out] conn related rtp-connection. | ||||
|  *  \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined). | ||||
|  *  \param[in] audio_name audio codec name (e.g. "GSM/8000/1"). | ||||
|  *  \returns 0 on success, -EINVAL on failure. */ | ||||
| int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name) | ||||
| { | ||||
| 	int rc; | ||||
|  | ||||
| 	/* The amount of codecs we can store is limited, make sure we do not | ||||
| 	 * overrun this limit. */ | ||||
| 	if (conn->end.codecs_assigned >= MGCP_MAX_CODECS) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name, | ||||
| 		       conn->end.codecs_assigned); | ||||
| 	if (rc != 0) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	conn->end.codecs_assigned++; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Check if the given codec is applicable on the specified endpoint | ||||
|  * Helper function for mgcp_codec_decide() */ | ||||
| static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	char codec_name[64]; | ||||
|  | ||||
| 	/* A codec name must be set, if not, this might mean that the codec | ||||
| 	 * (payload type) that was assigned is unknown to us so we must stop | ||||
| 	 * here. */ | ||||
| 	if (!codec->subtype_name) | ||||
| 		return false; | ||||
|  | ||||
| 	/* We now extract the codec_name (letters before the /, e.g. "GSM" | ||||
| 	 * from the audio name that is stored in the trunk configuration. | ||||
| 	 * We do not compare to the full audio_name because we expect that | ||||
| 	 * "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the | ||||
| 	 * audio name of the codec is set to "GSM" */ | ||||
| 	if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1) | ||||
| 		return false; | ||||
|  | ||||
| 	/* Finally we check if the subtype_name we have generated from the | ||||
| 	 * audio_name in the trunc struct patches the codec_name of the | ||||
| 	 * given codec */ | ||||
| 	if (strcasecmp(codec_name, codec->subtype_name) == 0) | ||||
| 		return true; | ||||
|  | ||||
| 	/* FIXME: It is questinable that the method to pick a compatible | ||||
| 	 * codec can work properly. Since this useses tcfg->audio_name, as | ||||
| 	 * a reference, which is set to "AMR/8000" permanently. | ||||
| 	 * tcfg->audio_name must be updated by the first connection that | ||||
| 	 * has been made on an endpoint, so that the second connection | ||||
| 	 * can make a meaningful decision here */ | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| /*! Decide for one suitable codec | ||||
|  *  \param[in] conn related rtp-connection. | ||||
|  *  \returns 0 on success, -EINVAL on failure. */ | ||||
| int mgcp_codec_decide(struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	struct mgcp_rtp_end *rtp; | ||||
| 	unsigned int i; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	bool codec_assigned = false; | ||||
|  | ||||
| 	endp = conn->conn->endp; | ||||
| 	rtp = &conn->end; | ||||
|  | ||||
| 	/* This function works on the results the SDP/LCO parser has extracted | ||||
| 	 * from the MGCP message. The goal is to select a suitable codec for | ||||
| 	 * the given connection. When transcoding is available, the first codec | ||||
| 	 * from the codec list is taken without further checking. When | ||||
| 	 * transcoding is not available, then the choice must be made more | ||||
| 	 * carefully. Each codec in the list is checked until one is found that | ||||
| 	 * is rated compatible. The rating is done by the helper function | ||||
| 	 * is_codec_compatible(), which does the actual checking. */ | ||||
| 	for (i = 0; i < rtp->codecs_assigned; i++) { | ||||
| 		/* When no transcoding is available, avoid codecs that would | ||||
| 		 * require transcoding. */ | ||||
| 		if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) { | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n", | ||||
| 			     rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		rtp->codec = &rtp->codecs[i]; | ||||
| 		codec_assigned = true; | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	/* FIXME: To the reviewes: This is problematic. I do not get why we | ||||
| 	 * need to reset the packet_duration_ms depending on the codec | ||||
| 	 * selection. I thought it were all 20ms? Is this to address some | ||||
| 	 * cornercase. (This piece of code was in the code path before, | ||||
| 	 * together with the note: "TODO/XXX: Store this per codec and derive | ||||
| 	 * it on use" */ | ||||
| 	if (codec_assigned) { | ||||
| 		if (rtp->maximum_packet_time >= 0 | ||||
| 		    && rtp->maximum_packet_time * rtp->codec->frame_duration_den > | ||||
| 		    rtp->codec->frame_duration_num * 1500) | ||||
| 			rtp->packet_duration_ms = 0; | ||||
|  | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return -EINVAL; | ||||
| } | ||||
| @@ -24,40 +24,88 @@ | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/mgcp/mgcp_common.h> | ||||
| #include <osmocom/mgcp/mgcp_ep.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
| #include <osmocom/mgcp/mgcp_sdp.h> | ||||
| #include <osmocom/mgcp/mgcp_codec.h> | ||||
| #include <osmocom/gsm/gsm_utils.h> | ||||
| #include <osmocom/core/rate_ctr.h> | ||||
| #include <ctype.h> | ||||
|  | ||||
| /* Reset codec state and free memory */ | ||||
| static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec) | ||||
| static const struct rate_ctr_desc rate_ctr_desc[] = { | ||||
| 	[IN_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:in", "Inbound rtp-stream timestamp errors."}, | ||||
| 	[OUT_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:out", "Outbound rtp-stream timestamp errors."}, | ||||
| 	[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_rx", "Outbound rtp octets."}, | ||||
| 	[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."} | ||||
| }; | ||||
|  | ||||
| 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(rate_ctr_desc), | ||||
| 	.ctr_desc = rate_ctr_desc | ||||
| }; | ||||
|  | ||||
|  | ||||
| /* Allocate a new connection identifier. According to RFC3435, they must | ||||
|  * be unique only within the scope of the endpoint. (Caller must provide | ||||
|  * memory for id) */ | ||||
| static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id) | ||||
| { | ||||
| 	codec->payload_type = -1; | ||||
| 	codec->subtype_name = NULL; | ||||
| 	codec->audio_name = NULL; | ||||
| 	codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; | ||||
| 	codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; | ||||
| 	codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; | ||||
| 	codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS; | ||||
| 	int i; | ||||
| 	int k; | ||||
| 	int rc; | ||||
| 	uint8_t id_bin[16]; | ||||
| 	char *id_hex; | ||||
|  | ||||
| 	/* see also mgcp_sdp.c, mgcp_set_audio_info() */ | ||||
| 	talloc_free(codec->subtype_name); | ||||
| 	talloc_free(codec->audio_name); | ||||
| 	/* Generate a connection id that is unique for the current endpoint. | ||||
| 	 * Technically a counter would be sufficient, but in order to | ||||
| 	 * be able to find a specific connection in large logfiles and to | ||||
| 	 * prevent unintentional connections we assign the connection | ||||
| 	 * identifiers randomly from a reasonable large number space */ | ||||
| 	for (i = 0; i < 32; i++) { | ||||
| 		rc = osmo_get_rand_id(id_bin, sizeof(id_bin)); | ||||
| 		if (rc < 0) | ||||
| 			return rc; | ||||
|  | ||||
| 		id_hex = osmo_hexdump_nospc(id_bin, sizeof(id_bin)); | ||||
| 		for (k = 0; k < strlen(id_hex); k++) | ||||
| 			id_hex[k] = toupper(id_hex[k]); | ||||
|  | ||||
| 		/* ensure that the generated conn_id is unique | ||||
| 		 * for this endpoint */ | ||||
| 		if (!mgcp_conn_get_rtp(endp, id_hex)) { | ||||
| 			osmo_strlcpy(id, id_hex, MGCP_CONN_ID_LENGTH); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x, unable to generate a unique connectionIdentifier\n", | ||||
| 	     ENDPOINT_NUMBER(endp)); | ||||
|  | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /* Reset states, free memory, set defaults and reset codec state */ | ||||
| static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn) | ||||
| /* Initialize rtp connection struct with default values */ | ||||
| static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn) | ||||
| { | ||||
| 	struct mgcp_rtp_end *end = &conn->end; | ||||
| 	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->type = MGCP_RTP_DEFAULT; | ||||
| 	conn->osmux.allocated_cid = -1; | ||||
| 	conn_rtp->type = MGCP_RTP_DEFAULT; | ||||
| 	conn_rtp->osmux.allocated_cid = -1; | ||||
|  | ||||
| 	/* backpointer to the generic part of the connection */ | ||||
| 	conn->u.rtp.conn = conn; | ||||
|  | ||||
| 	end->rtp.fd = -1; | ||||
| 	end->rtcp.fd = -1; | ||||
| 	end->local_port = 0; | ||||
| 	end->packets_rx = 0; | ||||
| 	end->octets_rx = 0; | ||||
| 	end->packets_tx = 0; | ||||
| 	end->octets_tx = 0; | ||||
| 	end->dropped_packets = 0; | ||||
| 	end->rtp_port = end->rtcp_port = 0; | ||||
| 	talloc_free(end->fmtp_extra); | ||||
| 	end->fmtp_extra = NULL; | ||||
| @@ -66,9 +114,24 @@ static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn) | ||||
| 	end->frames_per_packet = 0;	/* unknown */ | ||||
| 	end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS; | ||||
| 	end->output_enabled = 0; | ||||
| 	end->maximum_packet_time = -1; | ||||
|  | ||||
| 	mgcp_rtp_codec_reset(&end->codec); | ||||
| 	mgcp_rtp_codec_reset(&end->alt_codec); | ||||
| 	conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index); | ||||
| 	conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR]; | ||||
| 	conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR]; | ||||
| 	rate_ctr_index++; | ||||
|  | ||||
| 	/* Make sure codec table is reset */ | ||||
| 	mgcp_codec_reset_all(conn_rtp); | ||||
| } | ||||
|  | ||||
| /* Cleanup rtp connection struct */ | ||||
| static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp) | ||||
| { | ||||
| 	osmux_disable_conn(conn_rtp); | ||||
| 	osmux_release_cid(conn_rtp); | ||||
| 	mgcp_free_rtp_port(&conn_rtp->end); | ||||
| 	rate_ctr_group_free(conn_rtp->rate_ctr_group); | ||||
| } | ||||
|  | ||||
| /*! allocate a new connection list entry. | ||||
| @@ -78,22 +141,15 @@ static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn) | ||||
|  *  \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, | ||||
| 				  uint32_t id, enum mgcp_conn_type type, | ||||
| 				  char *name) | ||||
| 				  enum mgcp_conn_type type, char *name) | ||||
| { | ||||
| 	struct mgcp_conn *conn; | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL); | ||||
| 	OSMO_ASSERT(strlen(name) < sizeof(conn->name)); | ||||
| 	int rc; | ||||
|  | ||||
| 	/* Do not allow more then two connections */ | ||||
| 	if (llist_count(&endp->conns) >= endp->type->max_conns) | ||||
| 		return NULL; | ||||
|  | ||||
| 	/* Prevent duplicate connection IDs */ | ||||
| 	if (mgcp_conn_get(endp, id)) | ||||
| 		return NULL; | ||||
|  | ||||
| 	/* Create new connection and add it to the list */ | ||||
| 	conn = talloc_zero(ctx, struct mgcp_conn); | ||||
| 	if (!conn) | ||||
| @@ -102,13 +158,16 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, | ||||
| 	conn->type = type; | ||||
| 	conn->mode = MGCP_CONN_NONE; | ||||
| 	conn->mode_orig = MGCP_CONN_NONE; | ||||
| 	conn->id = id; | ||||
| 	conn->u.rtp.conn = conn; | ||||
| 	strcpy(conn->name, name); | ||||
| 	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: | ||||
| 		mgcp_rtp_conn_reset(&conn->u.rtp); | ||||
| 		mgcp_rtp_conn_init(&conn->u.rtp, conn); | ||||
| 		break; | ||||
| 	default: | ||||
| 		/* NOTE: This should never be called with an | ||||
| @@ -126,15 +185,12 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, | ||||
|  *  \param[in] endp associated endpoint | ||||
|  *  \param[in] id identification number of the connection | ||||
|  *  \returns pointer to allocated connection, NULL if not found */ | ||||
| struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id) | ||||
| struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id) | ||||
| { | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL); | ||||
|  | ||||
| 	struct mgcp_conn *conn; | ||||
|  | ||||
| 	llist_for_each_entry(conn, &endp->conns, entry) { | ||||
| 		if (conn->id == id) | ||||
| 		if (strncmp(conn->id, id, sizeof(conn->id)) == 0) | ||||
| 			return conn; | ||||
| 	} | ||||
|  | ||||
| @@ -145,11 +201,9 @@ struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t 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, uint32_t id) | ||||
| struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, | ||||
| 					const char *id) | ||||
| { | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL); | ||||
|  | ||||
| 	struct mgcp_conn *conn; | ||||
|  | ||||
| 	conn = mgcp_conn_get(endp, id); | ||||
| @@ -165,22 +219,23 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, uint32_t id) | ||||
| /*! 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, uint32_t id) | ||||
| void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id) | ||||
| { | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL); | ||||
|  | ||||
| 	struct mgcp_conn *conn; | ||||
|  | ||||
| 	conn = mgcp_conn_get(endp, id); | ||||
| 	if (!conn) | ||||
| 		return; | ||||
|  | ||||
| 	/* Run endpoint cleanup action. By this we inform the endpoint about | ||||
| 	 * the removal of the connection and allow it to clean up its inner | ||||
| 	 * state accordingly */ | ||||
| 	if (endp->type->cleanup_cb) | ||||
| 		endp->type->cleanup_cb(endp, conn); | ||||
|  | ||||
| 	switch (conn->type) { | ||||
| 	case MGCP_CONN_TYPE_RTP: | ||||
| 		osmux_disable_conn(&conn->u.rtp); | ||||
| 		osmux_release_cid(&conn->u.rtp); | ||||
| 		mgcp_free_rtp_port(&conn->u.rtp.end); | ||||
| 		mgcp_rtp_conn_cleanup(&conn->u.rtp); | ||||
| 		break; | ||||
| 	default: | ||||
| 		/* NOTE: This should never be called with an | ||||
| @@ -197,9 +252,6 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id) | ||||
|  *  \param[in] endp associated endpoint */ | ||||
| void mgcp_conn_free_oldest(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL); | ||||
|  | ||||
| 	struct mgcp_conn *conn; | ||||
|  | ||||
| 	if (llist_empty(&endp->conns)) | ||||
| @@ -216,9 +268,6 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp) | ||||
|  *  \param[in] endp associated endpoint */ | ||||
| void mgcp_conn_free_all(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL); | ||||
|  | ||||
| 	struct mgcp_conn *conn; | ||||
| 	struct mgcp_conn *conn_tmp; | ||||
|  | ||||
| @@ -230,12 +279,12 @@ void mgcp_conn_free_all(struct mgcp_endpoint *endp) | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| /*! dump basic connection information to human readble string. | ||||
| /*! dump basic connection information to human readable string. | ||||
|  *  \param[in] conn to dump | ||||
|  *  \returns human readble string */ | ||||
|  *  \returns human readable string */ | ||||
| char *mgcp_conn_dump(struct mgcp_conn *conn) | ||||
| { | ||||
| 	static char str[256]; | ||||
| 	static char str[sizeof(conn->name)+sizeof(conn->id)+256]; | ||||
|  | ||||
| 	if (!conn) { | ||||
| 		snprintf(str, sizeof(str), "(null connection)"); | ||||
| @@ -245,7 +294,7 @@ char *mgcp_conn_dump(struct mgcp_conn *conn) | ||||
| 	switch (conn->type) { | ||||
| 	case MGCP_CONN_TYPE_RTP: | ||||
| 		/* Dump RTP connection */ | ||||
| 		snprintf(str, sizeof(str), "(%s/rtp, id:%u, ip:%s, " | ||||
| 		snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, " | ||||
| 			 "rtp:%u rtcp:%u)", | ||||
| 			 conn->name, | ||||
| 			 conn->id, | ||||
|   | ||||
							
								
								
									
										56
									
								
								src/libosmo-mgcp/mgcp_endp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/libosmo-mgcp/mgcp_endp.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| /* 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) | ||||
| { | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:0x%x\n", | ||||
| 	     ENDPOINT_NUMBER(endp)); | ||||
|  | ||||
| 	/* Normally this function should only be called when | ||||
| 	 * all connections have been removed already. In case | ||||
| 	 * that there are still connections open (e.g. when | ||||
| 	 * RSIP is executed), free them all at once. */ | ||||
| 	mgcp_conn_free_all(endp); | ||||
|  | ||||
| 	/* Reset endpoint parameters and states */ | ||||
| 	talloc_free(endp->callid); | ||||
| 	endp->callid = NULL; | ||||
| 	talloc_free(endp->local_options.string); | ||||
| 	endp->local_options.string = NULL; | ||||
| 	talloc_free(endp->local_options.codec); | ||||
| 	endp->local_options.codec = NULL; | ||||
| 	endp->wildcarded_req = false; | ||||
| } | ||||
| @@ -28,6 +28,7 @@ | ||||
| #include <osmocom/mgcp/mgcp_common.h> | ||||
| #include <osmocom/mgcp/mgcp_msg.h> | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
|  | ||||
| /*! Display an mgcp message on the log output. | ||||
|  *  \param[in] message mgcp message string | ||||
| @@ -82,7 +83,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp, | ||||
|  | ||||
| 	if (!mode) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x missing connection mode\n", | ||||
| 		     "endpoint:0x%x missing connection mode\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return -1; | ||||
| 	} | ||||
| @@ -101,7 +102,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp, | ||||
| 		conn->mode = MGCP_CONN_LOOPBACK; | ||||
| 	else { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x unknown connection mode: '%s'\n", | ||||
| 		     "endpoint:0x%x unknown connection mode: '%s'\n", | ||||
| 		     ENDPOINT_NUMBER(endp), mode); | ||||
| 		ret = -1; | ||||
| 	} | ||||
| @@ -113,16 +114,16 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp, | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 	     "endpoint:%x conn:%s\n", | ||||
| 	     "endpoint:0x%x conn:%s\n", | ||||
| 	     ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn)); | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 	     "endpoint:%x connection mode '%s' %d\n", | ||||
| 	     "endpoint:0x%x connection mode '%s' %d\n", | ||||
| 	     ENDPOINT_NUMBER(endp), mode, conn->mode); | ||||
|  | ||||
| 	/* Special handling für RTP connections */ | ||||
| 	if (conn->type == MGCP_CONN_TYPE_RTP) { | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x output_enabled %d\n", | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x output_enabled %d\n", | ||||
| 		     ENDPOINT_NUMBER(endp), conn->u.rtp.end.output_enabled); | ||||
| 	} | ||||
|  | ||||
| @@ -142,6 +143,7 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg, | ||||
| 	char *rest = NULL; | ||||
| 	struct mgcp_trunk_config *tcfg; | ||||
| 	int trunk, endp; | ||||
| 	struct mgcp_endpoint *endp_ptr; | ||||
|  | ||||
| 	trunk = strtoul(mgcp + 6, &rest, 10); | ||||
| 	if (rest == NULL || rest[0] != '/' || trunk < 1) { | ||||
| @@ -178,25 +180,112 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg, | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	return &tcfg->endpoints[endp]; | ||||
| 	endp_ptr = &tcfg->endpoints[endp]; | ||||
| 	endp_ptr->wildcarded_req = false; | ||||
| 	return endp_ptr; | ||||
| } | ||||
|  | ||||
| /* Find an endpoint that is not in use. Do this by going through the endpoint | ||||
|  * array, check the callid. A callid nullpointer indicates that the endpoint | ||||
|  * is free */ | ||||
| static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints, | ||||
| 						unsigned int number_endpoints) | ||||
| { | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	for (i = 0; i < number_endpoints; i++) { | ||||
| 		if (endpoints[i].callid == NULL) { | ||||
| 			endp = &endpoints[i]; | ||||
| 			LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 			     "endpoint:0x%x found free endpoint\n", | ||||
| 			     ENDPOINT_NUMBER(endp)); | ||||
| 			endp->wildcarded_req = true; | ||||
| 			return endp; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n"); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /* Check if the domain name, which is supplied with the endpoint name | ||||
|  * matches the configuration. */ | ||||
| static int check_domain_name(struct mgcp_config *cfg, const char *mgcp) | ||||
| { | ||||
| 	char *domain_to_check; | ||||
|  | ||||
| 	domain_to_check = strstr(mgcp, "@"); | ||||
| 	if (!domain_to_check) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	if (strcmp(domain_to_check+1, cfg->domain) != 0) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Search the endpoint pool for the endpoint that had been selected via the | ||||
|  * MGCP message (helper function for mgcp_analyze_header()) */ | ||||
| static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, | ||||
| 					   const char *mgcp) | ||||
| 					   const char *mgcp, | ||||
| 					   int *cause) | ||||
| { | ||||
| 	char *endptr = NULL; | ||||
| 	unsigned int gw = INT_MAX; | ||||
| 	const char *endpoint_number_str; | ||||
| 	struct mgcp_endpoint *endp; | ||||
|  | ||||
| 	if (strncmp(mgcp, "ds/e1", 5) == 0) | ||||
| 		return find_e1_endpoint(cfg, mgcp); | ||||
| 	*cause = 0; | ||||
|  | ||||
| 	/* Check if the domainname in the request is correct */ | ||||
| 	if (check_domain_name(cfg, mgcp)) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s'\n", mgcp); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if the E1 trunk is requested */ | ||||
| 	if (strncmp(mgcp, "ds/e1", 5) == 0) { | ||||
| 		endp = find_e1_endpoint(cfg, mgcp); | ||||
| 		if (!endp) | ||||
| 			*cause = -500; | ||||
| 		return endp; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if the virtual trunk is addressed (new, correct way with prefix) */ | ||||
| 	if (strncmp | ||||
| 	    (mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, | ||||
| 	     strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) { | ||||
| 		endpoint_number_str = | ||||
| 		    mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK); | ||||
| 		if (endpoint_number_str[0] == '*') { | ||||
| 			endp = find_free_endpoint(cfg->trunk.endpoints, | ||||
| 						  cfg->trunk.number_endpoints); | ||||
| 			if (!endp) | ||||
| 				*cause = -403; | ||||
| 			return endp; | ||||
| 		} | ||||
| 		gw = strtoul(endpoint_number_str, &endptr, 16); | ||||
| 		if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') { | ||||
| 			endp = &cfg->trunk.endpoints[gw]; | ||||
| 			endp->wildcarded_req = false; | ||||
| 			return endp; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Deprecated method without prefix */ | ||||
| 	LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 	     "Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n", | ||||
| 	     MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp); | ||||
| 	gw = strtoul(mgcp, &endptr, 16); | ||||
| 	if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@') | ||||
| 		return &cfg->trunk.endpoints[gw]; | ||||
| 	if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') { | ||||
| 		endp = &cfg->trunk.endpoints[gw]; | ||||
| 		endp->wildcarded_req = false; | ||||
| 		return endp; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp); | ||||
| 	*cause = -500; | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| @@ -209,6 +298,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data) | ||||
| { | ||||
| 	int i = 0; | ||||
| 	char *elem, *save = NULL; | ||||
| 	int cause; | ||||
|  | ||||
| 	/*! This function will parse the header part of the received | ||||
| 	 *  MGCP message. The parsing results are stored in pdata. | ||||
| @@ -226,25 +316,25 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data) | ||||
| 			pdata->trans = elem; | ||||
| 			break; | ||||
| 		case 1: | ||||
| 			pdata->endp = find_endpoint(pdata->cfg, elem); | ||||
| 			pdata->endp = find_endpoint(pdata->cfg, elem, &cause); | ||||
| 			if (!pdata->endp) { | ||||
| 				LOGP(DLMGCP, LOGL_ERROR, | ||||
| 				     "Unable to find Endpoint `%s'\n", elem); | ||||
| 				return -1; | ||||
| 				return cause; | ||||
| 			} | ||||
| 			break; | ||||
| 		case 2: | ||||
| 			if (strcmp("MGCP", elem)) { | ||||
| 				LOGP(DLMGCP, LOGL_ERROR, | ||||
| 				     "MGCP header parsing error\n"); | ||||
| 				return -1; | ||||
| 				return -510; | ||||
| 			} | ||||
| 			break; | ||||
| 		case 3: | ||||
| 			if (strcmp("1.0", elem)) { | ||||
| 				LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' " | ||||
| 				     "not supported\n", elem); | ||||
| 				return -1; | ||||
| 				return -528; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| @@ -255,7 +345,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data) | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n"); | ||||
| 		pdata->trans = "000000"; | ||||
| 		pdata->endp = NULL; | ||||
| 		return -1; | ||||
| 		return -510; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| @@ -318,7 +408,7 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid) | ||||
|  | ||||
| 	if (strcmp(endp->callid, callid) != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x CallIDs does not match '%s' != '%s'\n", | ||||
| 		     "endpoint:0x%x CallIDs does not match '%s' != '%s'\n", | ||||
| 		     ENDPOINT_NUMBER(endp), endp->callid, callid); | ||||
| 		return -1; | ||||
| 	} | ||||
| @@ -330,21 +420,39 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid) | ||||
|   * \param[in] endp pointer to endpoint | ||||
|   * \param{in] connection id to verify | ||||
|   * \returns 1 when connection id seems plausible, 0 on error */ | ||||
| int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *ci) | ||||
| int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id) | ||||
| { | ||||
| 	uint32_t id; | ||||
|  | ||||
| 	if (!endp) | ||||
| 	/* Check for null identifiers */ | ||||
| 	if (!conn_id) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x invalid ConnectionIdentifier (missing)\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	id = strtoul(ci, NULL, 10); | ||||
| 	/* Check for empty connection identifiers */ | ||||
| 	if (strlen(conn_id) == 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x invalid ConnectionIdentifier (empty)\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (mgcp_conn_get(endp, id)) | ||||
| 	/* Check for over long connection identifiers */ | ||||
| 	if (strlen(conn_id) > MGCP_CONN_ID_LENGTH) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x invalid ConnectionIdentifier (too long) 0x%s\n", | ||||
| 		     ENDPOINT_NUMBER(endp), conn_id); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if connection exists */ | ||||
| 	if (mgcp_conn_get(endp, conn_id)) | ||||
| 		return 0; | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, | ||||
| 	     "endpoint:%x No connection found under ConnectionIdentifier %u\n", | ||||
| 	     ENDPOINT_NUMBER(endp), id); | ||||
| 	     "endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n", | ||||
| 	     ENDPOINT_NUMBER(endp), conn_id); | ||||
|  | ||||
| 	return -1; | ||||
| } | ||||
| @@ -386,20 +494,3 @@ char *mgcp_strline(char *str, char **saveptr) | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| /*! Parse CI from a given string. | ||||
|   * \param[out] caller provided memory to store the result | ||||
|   * \param{in] string containing the connection id | ||||
|   * \returns 0 on success, -1 on error */ | ||||
| int mgcp_parse_ci(uint32_t *conn_id, const char *ci) | ||||
| { | ||||
|  | ||||
| 	OSMO_ASSERT(conn_id); | ||||
|  | ||||
| 	if (!ci) | ||||
| 		return -1; | ||||
|  | ||||
| 	*conn_id = strtoul(ci, NULL, 10); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
| @@ -27,11 +27,11 @@ | ||||
| #include <errno.h> | ||||
| #include <time.h> | ||||
| #include <limits.h> | ||||
| #include <sys/socket.h> | ||||
| #include <arpa/inet.h> | ||||
|  | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/core/select.h> | ||||
| #include <osmocom/core/socket.h> | ||||
| #include <osmocom/netif/rtp.h> | ||||
| #include <osmocom/mgcp/mgcp.h> | ||||
| #include <osmocom/mgcp/mgcp_common.h> | ||||
| @@ -39,7 +39,8 @@ | ||||
| #include <osmocom/mgcp/mgcp_stat.h> | ||||
| #include <osmocom/mgcp/osmux.h> | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
| #include <osmocom/mgcp/mgcp_ep.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
| #include <osmocom/mgcp/debug.h> | ||||
|  | ||||
| #define RTP_SEQ_MOD		(1 << 16) | ||||
| #define RTP_MAX_DROPOUT		3000 | ||||
| @@ -51,6 +52,56 @@ enum { | ||||
| 	MGCP_PROTO_RTCP, | ||||
| }; | ||||
|  | ||||
| /*! Determine the local rtp bind IP-address. | ||||
|  *  \param[out] addr caller provided memory to store the resulting IP-Address | ||||
|  *  \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters | ||||
|  * | ||||
|  *  The local bind IP-address is automatically selected by probing the | ||||
|  *  IP-Address of the interface that is pointing towards the remote IP-Address, | ||||
|  *  if no remote IP-Address is known yet, the statically configured | ||||
|  *  IP-Addresses are used as fallback. */ | ||||
| void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn) | ||||
| { | ||||
|  | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	int rc; | ||||
| 	endp = conn->conn->endp; | ||||
|  | ||||
| 	/* Try probing the local IP-Address */ | ||||
| 	if (endp->cfg->net_ports.bind_addr_probe && conn->end.addr.s_addr != 0) { | ||||
| 		rc = osmo_sock_local_ip(addr, inet_ntoa(conn->end.addr)); | ||||
| 		if (rc < 0) | ||||
| 			LOGP(DRTP, LOGL_ERROR, | ||||
| 			     "endpoint:0x%x CI:%s local interface auto detection failed, using configured addresses...\n", | ||||
| 			     ENDPOINT_NUMBER(endp), conn->conn->id); | ||||
| 		else { | ||||
| 			LOGP(DRTP, LOGL_DEBUG, | ||||
| 			     "endpoint:0x%x CI:%s selected local rtp bind ip %s by probing using remote ip %s\n", | ||||
| 			     ENDPOINT_NUMBER(endp), conn->conn->id, addr, | ||||
| 			     inet_ntoa(conn->end.addr)); | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Select from preconfigured IP-Addresses */ | ||||
| 	if (endp->cfg->net_ports.bind_addr) { | ||||
| 		/* Check there is a bind IP for the RTP traffic configured, | ||||
| 		 * if so, use that IP-Address */ | ||||
| 		osmo_strlcpy(addr, endp->cfg->net_ports.bind_addr, INET_ADDRSTRLEN); | ||||
| 		LOGP(DRTP, LOGL_DEBUG, | ||||
| 		     "endpoint:0x%x CI:%s using configured rtp bind ip as local bind ip %s\n", | ||||
| 		     ENDPOINT_NUMBER(endp), conn->conn->id, addr); | ||||
| 	} else { | ||||
| 		/* No specific bind IP is configured for the RTP traffic, so | ||||
| 		 * assume the IP where we listen for incoming MGCP messages | ||||
| 		 * as bind IP */ | ||||
| 		osmo_strlcpy(addr, endp->cfg->source_addr, INET_ADDRSTRLEN); | ||||
| 		LOGP(DRTP, LOGL_DEBUG, | ||||
| 		     "endpoint:0x%x CI:%s using mgcp bind ip as local rtp bind ip: %s\n", | ||||
| 		     ENDPOINT_NUMBER(endp), conn->conn->id, addr); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* This does not need to be a precision timestamp and | ||||
|  * is allowed to wrap quite fast. The returned value is | ||||
|  * 1/codec_rate seconds. */ | ||||
| @@ -64,7 +115,7 @@ static uint32_t get_current_ts(unsigned codec_rate) | ||||
|  | ||||
| 	memset(&tp, 0, sizeof(tp)); | ||||
| 	if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, "Getting the clock failed.\n"); | ||||
| 		LOGP(DRTP, LOGL_NOTICE, "Getting the clock failed.\n"); | ||||
|  | ||||
| 	/* convert it to 1/unit seconds */ | ||||
| 	ret = tp.tv_sec; | ||||
| @@ -85,7 +136,7 @@ int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len) | ||||
| { | ||||
| 	struct sockaddr_in out; | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 	LOGP(DRTP, LOGL_DEBUG, | ||||
| 	     "sending %i bytes length packet to %s:%u ...\n", | ||||
| 	     len, inet_ntoa(*addr), ntohs(port)); | ||||
|  | ||||
| @@ -109,9 +160,9 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	OSMO_ASSERT(conn); | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 	     "endpoint:%x sending dummy packet...\n", ENDPOINT_NUMBER(endp)); | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x conn:%s\n", | ||||
| 	LOGP(DRTP, LOGL_DEBUG, | ||||
| 	     "endpoint:0x%x sending dummy packet...\n", ENDPOINT_NUMBER(endp)); | ||||
| 	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x conn:%s\n", | ||||
| 	     ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn)); | ||||
|  | ||||
| 	rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr, | ||||
| @@ -131,8 +182,8 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) | ||||
| 		return rc; | ||||
|  | ||||
| failed: | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, | ||||
| 	     "endpoint:%x Failed to send dummy %s packet.\n", | ||||
| 	LOGP(DRTP, LOGL_ERROR, | ||||
| 	     "endpoint:0x%x Failed to send dummy %s packet.\n", | ||||
| 	     ENDPOINT_NUMBER(endp), was_rtcp ? "RTCP" : "RTP"); | ||||
|  | ||||
| 	return -1; | ||||
| @@ -171,15 +222,15 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp, | ||||
|  | ||||
| 	if (seq == sstate->last_seq) { | ||||
| 		if (timestamp != sstate->last_timestamp) { | ||||
| 			sstate->err_ts_counter += 1; | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			rate_ctr_inc(sstate->err_ts_ctr); | ||||
| 			LOGP(DRTP, LOGL_ERROR, | ||||
| 			     "The %s timestamp delta is != 0 but the sequence " | ||||
| 			     "number %d is the same, " | ||||
| 			     "TS offset: %d, SeqNo offset: %d " | ||||
| 			     "on 0x%x SSRC: %u timestamp: %u " | ||||
| 			     "from %s:%d\n", | ||||
| 			     text, seq, | ||||
| 			     state->timestamp_offset, state->seq_offset, | ||||
| 			     state->patch.timestamp_offset, state->patch.seq_offset, | ||||
| 			     ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp, | ||||
| 			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); | ||||
| 		} | ||||
| @@ -192,7 +243,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp, | ||||
|  | ||||
| 	if (tsdelta == 0) { | ||||
| 		/* Don't update *tsdelta_out */ | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 		LOGP(DRTP, LOGL_NOTICE, | ||||
| 		     "The %s timestamp delta is %d " | ||||
| 		     "on 0x%x SSRC: %u timestamp: %u " | ||||
| 		     "from %s:%d\n", | ||||
| @@ -205,7 +256,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp, | ||||
|  | ||||
| 	if (sstate->last_tsdelta != tsdelta) { | ||||
| 		if (sstate->last_tsdelta) { | ||||
| 			LOGP(DLMGCP, LOGL_INFO, | ||||
| 			LOGP(DRTP, LOGL_INFO, | ||||
| 			     "The %s timestamp delta changes from %d to %d " | ||||
| 			     "on 0x%x SSRC: %u timestamp: %u from %s:%d\n", | ||||
| 			     text, sstate->last_tsdelta, tsdelta, | ||||
| @@ -221,8 +272,8 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp, | ||||
| 	    ts_alignment_error(sstate, state->packet_duration, timestamp); | ||||
|  | ||||
| 	if (timestamp_error) { | ||||
| 		sstate->err_ts_counter += 1; | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 		rate_ctr_inc(sstate->err_ts_ctr); | ||||
| 		LOGP(DRTP, LOGL_NOTICE, | ||||
| 		     "The %s timestamp has an alignment error of %d " | ||||
| 		     "on 0x%x SSRC: %u " | ||||
| 		     "SeqNo delta: %d, TS delta: %d, dTS/dSeq: %d " | ||||
| @@ -252,15 +303,15 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp, | ||||
| 	if (tsdelta == 0) { | ||||
| 		tsdelta = state->out_stream.last_tsdelta; | ||||
| 		if (tsdelta != 0) { | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 			LOGP(DRTP, LOGL_NOTICE, | ||||
| 			     "A fixed packet duration is not available on 0x%x, " | ||||
| 			     "using last output timestamp delta instead: %d " | ||||
| 			     "from %s:%d\n", | ||||
| 			     ENDPOINT_NUMBER(endp), tsdelta, | ||||
| 			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); | ||||
| 		} else { | ||||
| 			tsdelta = rtp_end->codec.rate * 20 / 1000; | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 			tsdelta = rtp_end->codec->rate * 20 / 1000; | ||||
| 			LOGP(DRTP, LOGL_NOTICE, | ||||
| 			     "Fixed packet duration and last timestamp delta " | ||||
| 			     "are not available on 0x%x, " | ||||
| 			     "using fixed 20ms instead: %d " | ||||
| @@ -273,15 +324,15 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp, | ||||
| 	out_timestamp = state->out_stream.last_timestamp + delta_seq * tsdelta; | ||||
| 	timestamp_offset = out_timestamp - in_timestamp; | ||||
|  | ||||
| 	if (state->timestamp_offset != timestamp_offset) { | ||||
| 		state->timestamp_offset = timestamp_offset; | ||||
| 	if (state->patch.timestamp_offset != timestamp_offset) { | ||||
| 		state->patch.timestamp_offset = timestamp_offset; | ||||
|  | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 		LOGP(DRTP, LOGL_NOTICE, | ||||
| 		     "Timestamp offset change on 0x%x SSRC: %u " | ||||
| 		     "SeqNo delta: %d, TS offset: %d, " | ||||
| 		     "from %s:%d\n", | ||||
| 		     ENDPOINT_NUMBER(endp), state->in_stream.ssrc, | ||||
| 		     delta_seq, state->timestamp_offset, | ||||
| 		     delta_seq, state->patch.timestamp_offset, | ||||
| 		     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); | ||||
| 	} | ||||
|  | ||||
| @@ -302,28 +353,28 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp, | ||||
| 	/* Align according to: T + Toffs - Tlast = k * Tptime */ | ||||
|  | ||||
| 	ts_error = ts_alignment_error(&state->out_stream, ptime, | ||||
| 				      timestamp + state->timestamp_offset); | ||||
| 				      timestamp + state->patch.timestamp_offset); | ||||
|  | ||||
| 	/* If there is an alignment error, we have to compensate it */ | ||||
| 	if (ts_error) { | ||||
| 		state->timestamp_offset += ptime - ts_error; | ||||
| 		state->patch.timestamp_offset += ptime - ts_error; | ||||
|  | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 		LOGP(DRTP, LOGL_NOTICE, | ||||
| 		     "Corrected timestamp alignment error of %d on 0x%x SSRC: %u " | ||||
| 		     "new TS offset: %d, " | ||||
| 		     "from %s:%d\n", | ||||
| 		     ts_error, | ||||
| 		     ENDPOINT_NUMBER(endp), state->in_stream.ssrc, | ||||
| 		     state->timestamp_offset, inet_ntoa(addr->sin_addr), | ||||
| 		     state->patch.timestamp_offset, inet_ntoa(addr->sin_addr), | ||||
| 		     ntohs(addr->sin_port)); | ||||
| 	} | ||||
|  | ||||
| 	/* Check we really managed to compensate the timestamp | ||||
| 	 * offset. There should not be any remaining error, failing | ||||
| 	 * here would point to a serous problem with the alingnment | ||||
| 	 * error computation fuction */ | ||||
| 	 * here would point to a serous problem with the alignment | ||||
| 	 * error computation function */ | ||||
| 	ts_check = ts_alignment_error(&state->out_stream, ptime, | ||||
| 				      timestamp + state->timestamp_offset); | ||||
| 				      timestamp + state->patch.timestamp_offset); | ||||
| 	OSMO_ASSERT(ts_check == 0); | ||||
|  | ||||
| 	/* Return alignment error before compensation */ | ||||
| @@ -335,27 +386,27 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp, | ||||
|  *  \param[in] destination RTP end | ||||
|  *  \param[in,out] pointer to buffer with voice data | ||||
|  *  \param[in] voice data length | ||||
|  *  \param[in] maxmimum size of caller provided voice data buffer | ||||
|  *  \param[in] maximum size of caller provided voice data buffer | ||||
|  *  \returns ignores input parameters, return always 0 */ | ||||
| int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, | ||||
| 				struct mgcp_rtp_end *dst_end, | ||||
| 				char *data, int *len, int buf_size) | ||||
| { | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x transcoding disabled\n", | ||||
| 	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x transcoding disabled\n", | ||||
| 	     ENDPOINT_NUMBER(endp)); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! dummy callback to disable transcoding (see also cfg->setup_rtp_processing_cb). | ||||
|  *  \param[in] associated endpoint | ||||
|  *  \param[in] destination RTP end | ||||
|  *  \param[in] source RTP end | ||||
|  *  \param[in] destination RTP connnection | ||||
|  *  \param[in] source RTP connection | ||||
|  *  \returns ignores input parameters, return always 0 */ | ||||
| int mgcp_setup_rtp_processing_default(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) | ||||
| { | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x transcoding disabled\n", | ||||
| 	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x transcoding disabled\n", | ||||
| 	     ENDPOINT_NUMBER(endp)); | ||||
| 	return 0; | ||||
| } | ||||
| @@ -366,12 +417,12 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, | ||||
| 					  const char **fmtp_extra, | ||||
| 					  struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 	     "endpoint:%x conn:%s using format defaults\n", | ||||
| 	LOGP(DRTP, LOGL_DEBUG, | ||||
| 	     "endpoint:0x%x conn:%s using format defaults\n", | ||||
| 	     ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn)); | ||||
|  | ||||
| 	*payload_type = conn->end.codec.payload_type; | ||||
| 	*audio_name = conn->end.codec.audio_name; | ||||
| 	*payload_type = conn->end.codec->payload_type; | ||||
| 	*audio_name = conn->end.codec->audio_name; | ||||
| 	*fmtp_extra = conn->end.fmtp_extra; | ||||
| } | ||||
|  | ||||
| @@ -382,14 +433,14 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, | ||||
| 	int32_t d; | ||||
|  | ||||
| 	/* initialize or re-initialize */ | ||||
| 	if (!state->stats_initialized || state->stats_ssrc != ssrc) { | ||||
| 		state->stats_initialized = 1; | ||||
| 		state->stats_base_seq = seq; | ||||
| 		state->stats_max_seq = seq - 1; | ||||
| 		state->stats_ssrc = ssrc; | ||||
| 		state->stats_jitter = 0; | ||||
| 		state->stats_transit = transit; | ||||
| 		state->stats_cycles = 0; | ||||
| 	if (!state->stats.initialized || state->stats.ssrc != ssrc) { | ||||
| 		state->stats.initialized = 1; | ||||
| 		state->stats.base_seq = seq; | ||||
| 		state->stats.max_seq = seq - 1; | ||||
| 		state->stats.ssrc = ssrc; | ||||
| 		state->stats.jitter = 0; | ||||
| 		state->stats.transit = transit; | ||||
| 		state->stats.cycles = 0; | ||||
| 	} else { | ||||
| 		uint16_t udelta; | ||||
|  | ||||
| @@ -400,12 +451,12 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, | ||||
| 		 * It can't wrap during the initialization so let's | ||||
| 		 * skip it here. The Appendix A probably doesn't have | ||||
| 		 * this issue because of the probation. */ | ||||
| 		udelta = seq - state->stats_max_seq; | ||||
| 		udelta = seq - state->stats.max_seq; | ||||
| 		if (udelta < RTP_MAX_DROPOUT) { | ||||
| 			if (seq < state->stats_max_seq) | ||||
| 				state->stats_cycles += RTP_SEQ_MOD; | ||||
| 			if (seq < state->stats.max_seq) | ||||
| 				state->stats.cycles += RTP_SEQ_MOD; | ||||
| 		} else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) { | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 			LOGP(DRTP, LOGL_NOTICE, | ||||
| 			     "RTP seqno made a very large jump on 0x%x delta: %u\n", | ||||
| 			     ENDPOINT_NUMBER(endp), udelta); | ||||
| 		} | ||||
| @@ -415,12 +466,12 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, | ||||
| 	 * taken closer to the read function. This was taken from the | ||||
| 	 * Appendix A of RFC 3550. Timestamp and arrival_time have a 1/rate | ||||
| 	 * resolution. */ | ||||
| 	d = transit - state->stats_transit; | ||||
| 	state->stats_transit = transit; | ||||
| 	d = transit - state->stats.transit; | ||||
| 	state->stats.transit = transit; | ||||
| 	if (d < 0) | ||||
| 		d = -d; | ||||
| 	state->stats_jitter += d - ((state->stats_jitter + 8) >> 4); | ||||
| 	state->stats_max_seq = seq; | ||||
| 	state->stats.jitter += d - ((state->stats.jitter + 8) >> 4); | ||||
| 	state->stats.max_seq = seq; | ||||
| } | ||||
|  | ||||
| /* The RFC 3550 Appendix A assumes there are multiple sources but | ||||
| @@ -439,7 +490,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, | ||||
| 	uint16_t seq; | ||||
| 	uint32_t timestamp, ssrc; | ||||
| 	struct rtp_hdr *rtp_hdr; | ||||
| 	int payload = rtp_end->codec.payload_type; | ||||
| 	int payload = rtp_end->codec->payload_type; | ||||
|  | ||||
| 	if (len < sizeof(*rtp_hdr)) | ||||
| 		return; | ||||
| @@ -447,7 +498,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, | ||||
| 	rtp_hdr = (struct rtp_hdr *)data; | ||||
| 	seq = ntohs(rtp_hdr->sequence); | ||||
| 	timestamp = ntohl(rtp_hdr->timestamp); | ||||
| 	arrival_time = get_current_ts(rtp_end->codec.rate); | ||||
| 	arrival_time = get_current_ts(rtp_end->codec->rate); | ||||
| 	ssrc = ntohl(rtp_hdr->ssrc); | ||||
| 	transit = arrival_time - timestamp; | ||||
|  | ||||
| @@ -456,31 +507,33 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, | ||||
| 	if (!state->initialized) { | ||||
| 		state->initialized = 1; | ||||
| 		state->in_stream.last_seq = seq - 1; | ||||
| 		state->in_stream.ssrc = state->orig_ssrc = ssrc; | ||||
| 		state->in_stream.ssrc = state->patch.orig_ssrc = ssrc; | ||||
| 		state->in_stream.last_tsdelta = 0; | ||||
| 		state->packet_duration = | ||||
| 		    mgcp_rtp_packet_duration(endp, rtp_end); | ||||
| 		state->out_stream = state->in_stream; | ||||
| 		state->out_stream.last_seq = seq - 1; | ||||
| 		state->out_stream.ssrc = state->patch.orig_ssrc = ssrc; | ||||
| 		state->out_stream.last_tsdelta = 0; | ||||
| 		state->out_stream.last_timestamp = timestamp; | ||||
| 		state->out_stream.ssrc = ssrc - 1;	/* force output SSRC change */ | ||||
| 		LOGP(DLMGCP, LOGL_INFO, | ||||
| 		     "endpoint:%x initializing stream, SSRC: %u timestamp: %u " | ||||
| 		LOGP(DRTP, LOGL_INFO, | ||||
| 		     "endpoint:0x%x initializing stream, SSRC: %u timestamp: %u " | ||||
| 		     "pkt-duration: %d, from %s:%d\n", | ||||
| 		     ENDPOINT_NUMBER(endp), state->in_stream.ssrc, | ||||
| 		     state->seq_offset, state->packet_duration, | ||||
| 		     state->patch.seq_offset, state->packet_duration, | ||||
| 		     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); | ||||
| 		if (state->packet_duration == 0) { | ||||
| 			state->packet_duration = | ||||
| 			    rtp_end->codec.rate * 20 / 1000; | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 			     "endpoint:%x fixed packet duration is not available, " | ||||
| 			    rtp_end->codec->rate * 20 / 1000; | ||||
| 			LOGP(DRTP, LOGL_NOTICE, | ||||
| 			     "endpoint:0x%x fixed packet duration is not available, " | ||||
| 			     "using fixed 20ms instead: %d from %s:%d\n", | ||||
| 			     ENDPOINT_NUMBER(endp), state->packet_duration, | ||||
| 			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); | ||||
| 		} | ||||
| 	} else if (state->in_stream.ssrc != ssrc) { | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 		     "endpoint:%x SSRC changed: %u -> %u  " | ||||
| 		LOGP(DRTP, LOGL_NOTICE, | ||||
| 		     "endpoint:0x%x SSRC changed: %u -> %u  " | ||||
| 		     "from %s:%d\n", | ||||
| 		     ENDPOINT_NUMBER(endp), | ||||
| 		     state->in_stream.ssrc, rtp_hdr->ssrc, | ||||
| @@ -491,7 +544,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, | ||||
| 			int16_t delta_seq; | ||||
|  | ||||
| 			/* Always increment seqno by 1 */ | ||||
| 			state->seq_offset = | ||||
| 			state->patch.seq_offset = | ||||
| 			    (state->out_stream.last_seq + 1) - seq; | ||||
|  | ||||
| 			/* Estimate number of packets that would have been sent */ | ||||
| @@ -503,17 +556,17 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, | ||||
| 			adjust_rtp_timestamp_offset(endp, state, rtp_end, addr, | ||||
| 						    delta_seq, timestamp); | ||||
|  | ||||
| 			state->patch_ssrc = 1; | ||||
| 			ssrc = state->orig_ssrc; | ||||
| 			state->patch.patch_ssrc = 1; | ||||
| 			ssrc = state->patch.orig_ssrc; | ||||
| 			if (rtp_end->force_constant_ssrc != -1) | ||||
| 				rtp_end->force_constant_ssrc -= 1; | ||||
|  | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 			     "endpoint:%x SSRC patching enabled, SSRC: %u " | ||||
| 			LOGP(DRTP, LOGL_NOTICE, | ||||
| 			     "endpoint:0x%x SSRC patching enabled, SSRC: %u " | ||||
| 			     "SeqNo offset: %d, TS offset: %d " | ||||
| 			     "from %s:%d\n", | ||||
| 			     ENDPOINT_NUMBER(endp), state->in_stream.ssrc, | ||||
| 			     state->seq_offset, state->timestamp_offset, | ||||
| 			     state->patch.seq_offset, state->patch.timestamp_offset, | ||||
| 			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); | ||||
| 		} | ||||
|  | ||||
| @@ -524,8 +577,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, | ||||
| 				    addr, seq, timestamp, "input", | ||||
| 				    &state->in_stream.last_tsdelta); | ||||
|  | ||||
| 		if (state->patch_ssrc) | ||||
| 			ssrc = state->orig_ssrc; | ||||
| 		if (state->patch.patch_ssrc) | ||||
| 			ssrc = state->patch.orig_ssrc; | ||||
| 	} | ||||
|  | ||||
| 	/* Save before patching */ | ||||
| @@ -540,15 +593,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, | ||||
| 					   timestamp); | ||||
|  | ||||
| 	/* Store the updated SSRC back to the packet */ | ||||
| 	if (state->patch_ssrc) | ||||
| 	if (state->patch.patch_ssrc) | ||||
| 		rtp_hdr->ssrc = htonl(ssrc); | ||||
|  | ||||
| 	/* Apply the offset and store it back to the packet. | ||||
| 	 * This won't change anything if the offset is 0, so the conditional is | ||||
| 	 * omitted. */ | ||||
| 	seq += state->seq_offset; | ||||
| 	seq += state->patch.seq_offset; | ||||
| 	rtp_hdr->sequence = htons(seq); | ||||
| 	timestamp += state->timestamp_offset; | ||||
| 	timestamp += state->patch.timestamp_offset; | ||||
| 	rtp_hdr->timestamp = htonl(timestamp); | ||||
|  | ||||
| 	/* Check again, whether the timestamps are still valid */ | ||||
| @@ -566,8 +619,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, | ||||
| 		return; | ||||
|  | ||||
| #if 0 | ||||
| 	DEBUGP(DLMGCP, | ||||
| 	       "endpoint:%x payload hdr payload %u -> endp payload %u\n", | ||||
| 	DEBUGP(DRTP, | ||||
| 	       "endpoint:0x%x payload hdr payload %u -> endp payload %u\n", | ||||
| 	       ENDPOINT_NUMBER(endp), rtp_hdr->payload_type, payload); | ||||
| 	rtp_hdr->payload_type = payload; | ||||
| #endif | ||||
| @@ -575,14 +628,20 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, | ||||
|  | ||||
| /* Forward data to a debug tap. This is debug function that is intended for | ||||
|  * debugging the voice traffic with tools like gstreamer */ | ||||
| static int forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf, | ||||
| 			int len) | ||||
| static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf, | ||||
| 			 int len) | ||||
| { | ||||
| 	if (!tap->enabled) | ||||
| 		return 0; | ||||
| 	int rc; | ||||
|  | ||||
| 	return sendto(fd, buf, len, 0, | ||||
| 		      (struct sockaddr *)&tap->forward, sizeof(tap->forward)); | ||||
| 	if (!tap->enabled) | ||||
| 		return; | ||||
|  | ||||
| 	rc = sendto(fd, buf, len, 0, (struct sockaddr *)&tap->forward, | ||||
| 		    sizeof(tap->forward)); | ||||
|  | ||||
| 	if (rc < 0) | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "Forwarding tapped (debug) voice data failed.\n"); | ||||
| } | ||||
|  | ||||
| /*! Send RTP/RTCP data to a specified destination connection. | ||||
| @@ -611,22 +670,19 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, | ||||
| 	OSMO_ASSERT(conn_dst); | ||||
|  | ||||
| 	if (is_rtp) { | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 		     "endpoint:%x delivering RTP packet...\n", | ||||
| 		LOGP(DRTP, LOGL_DEBUG, | ||||
| 		     "endpoint:0x%x delivering RTP packet...\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 	} else { | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 		     "endpoint:%x delivering RTCP packet...\n", | ||||
| 		LOGP(DRTP, LOGL_DEBUG, | ||||
| 		     "endpoint:0x%x delivering RTCP packet...\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 	     "endpoint:%x loop:%d, mode:%d ", | ||||
| 	     ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode); | ||||
| 	if (conn_src->conn->mode == MGCP_CONN_LOOPBACK) | ||||
| 		LOGPC(DLMGCP, LOGL_DEBUG, "(loopback)\n"); | ||||
| 	else | ||||
| 		LOGPC(DLMGCP, LOGL_DEBUG, "\n"); | ||||
| 	LOGP(DRTP, LOGL_DEBUG, | ||||
| 	     "endpoint:0x%x loop:%d, mode:%d%s\n", | ||||
| 	     ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode, | ||||
| 	     conn_src->conn->mode == MGCP_CONN_LOOPBACK ? " (loopback)" : ""); | ||||
|  | ||||
| 	/* Note: In case of loopback configuration, both, the source and the | ||||
| 	 * destination will point to the same connection. */ | ||||
| @@ -635,9 +691,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, | ||||
| 	dest_name = conn_dst->conn->name; | ||||
|  | ||||
| 	if (!rtp_end->output_enabled) { | ||||
| 		rtp_end->dropped_packets += 1; | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 		     "endpoint:%x output disabled, drop to %s %s " | ||||
| 		rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR]); | ||||
| 		LOGP(DRTP, LOGL_DEBUG, | ||||
| 		     "endpoint:0x%x output disabled, drop to %s %s " | ||||
| 		     "rtp_port:%u rtcp_port:%u\n", | ||||
| 		     ENDPOINT_NUMBER(endp), | ||||
| 		     dest_name, | ||||
| @@ -659,8 +715,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, | ||||
| 			if (addr) | ||||
| 				mgcp_patch_and_count(endp, rtp_state, rtp_end, | ||||
| 						     addr, buf, buflen); | ||||
| 			LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 			     "endpoint:%x process/send to %s %s " | ||||
| 			LOGP(DRTP, LOGL_DEBUG, | ||||
| 			     "endpoint:0x%x process/send to %s %s " | ||||
| 			     "rtp_port:%u rtcp_port:%u\n", | ||||
| 			     ENDPOINT_NUMBER(endp), dest_name, | ||||
| 			     inet_ntoa(rtp_end->addr), ntohs(rtp_end->rtp_port), | ||||
| @@ -676,11 +732,18 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, | ||||
| 			 * 'e400', or it will reject the RAB assignment. It seems to not harm other femto | ||||
| 			 * cells (as long as we patch only the first RTP payload in each stream). | ||||
| 			 */ | ||||
| 			if (!rtp_state->patched_first_rtp_payload) { | ||||
| 			if (!rtp_state->patched_first_rtp_payload | ||||
| 			    && conn_src->conn->mode == MGCP_CONN_LOOPBACK) { | ||||
| 				uint8_t *data = (uint8_t *) & buf[12]; | ||||
| 				data[0] = 0xe4; | ||||
| 				data[1] = 0x00; | ||||
| 				rtp_state->patched_first_rtp_payload = true; | ||||
| 				if (data[0] == 0xe0) { | ||||
| 					data[0] = 0xe4; | ||||
| 					data[1] = 0x00; | ||||
| 					rtp_state->patched_first_rtp_payload = true; | ||||
| 					LOGP(DRTP, LOGL_DEBUG, | ||||
| 					     "endpoint:0x%x Patching over first two bytes" | ||||
| 					     " to fake an IuUP Initialization Ack\n", | ||||
| 					     ENDPOINT_NUMBER(endp)); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			len = mgcp_udp_send(rtp_end->rtp.fd, | ||||
| @@ -690,16 +753,16 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, | ||||
| 			if (len <= 0) | ||||
| 				return len; | ||||
|  | ||||
| 			conn_dst->end.packets_tx += 1; | ||||
| 			conn_dst->end.octets_tx += len; | ||||
| 			rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]); | ||||
| 			rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len); | ||||
|  | ||||
| 			nbytes += len; | ||||
| 			buflen = cont; | ||||
| 		} while (buflen > 0); | ||||
| 		return nbytes; | ||||
| 	} else if (!tcfg->omit_rtcp) { | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 		     "endpoint:%x send to %s %s rtp_port:%u rtcp_port:%u\n", | ||||
| 		LOGP(DRTP, LOGL_DEBUG, | ||||
| 		     "endpoint:0x%x send to %s %s rtp_port:%u rtcp_port:%u\n", | ||||
| 		     ENDPOINT_NUMBER(endp), | ||||
| 		     dest_name, | ||||
| 		     inet_ntoa(rtp_end->addr), | ||||
| @@ -710,8 +773,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, | ||||
| 				    &rtp_end->addr, | ||||
| 				    rtp_end->rtcp_port, buf, len); | ||||
|  | ||||
| 		conn_dst->end.packets_tx += 1; | ||||
| 		conn_dst->end.octets_tx += len; | ||||
| 		rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]); | ||||
| 		rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len); | ||||
|  | ||||
| 		return len; | ||||
| 	} | ||||
| @@ -740,19 +803,19 @@ static int receive_from(struct mgcp_endpoint *endp, int fd, | ||||
|  | ||||
| 	rc = recvfrom(fd, buf, bufsize, 0, (struct sockaddr *)addr, &slen); | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 	LOGP(DRTP, LOGL_DEBUG, | ||||
| 	     "receiving %u bytes length packet from %s:%u ...\n", | ||||
| 	     rc, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); | ||||
|  | ||||
| 	if (rc < 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x failed to receive packet, errno: %d/%s\n", | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x failed to receive packet, errno: %d/%s\n", | ||||
| 		     ENDPOINT_NUMBER(endp), errno, strerror(errno)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (tossed) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "endpoint:%x packet tossed\n", | ||||
| 		LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 	} | ||||
|  | ||||
| @@ -771,12 +834,12 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, | ||||
| 	 * which we send our outgoing RTP traffic. */ | ||||
| 	if (memcmp(&addr->sin_addr, &conn->end.addr, sizeof(addr->sin_addr)) | ||||
| 	    != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x data from wrong address: %s, ", | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x data from wrong address: %s, ", | ||||
| 		     ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr)); | ||||
| 		LOGPC(DLMGCP, LOGL_ERROR, "expected: %s\n", | ||||
| 		LOGPC(DRTP, LOGL_ERROR, "expected: %s\n", | ||||
| 		      inet_ntoa(conn->end.addr)); | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "endpoint:%x packet tossed\n", | ||||
| 		LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return -1; | ||||
| 	} | ||||
| @@ -787,13 +850,13 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, | ||||
| 	 * plausibility. */ | ||||
| 	if (conn->end.rtp_port != addr->sin_port && | ||||
| 	    conn->end.rtcp_port != addr->sin_port) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x data from wrong source port: %d, ", | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x data from wrong source port: %d, ", | ||||
| 		     ENDPOINT_NUMBER(endp), ntohs(addr->sin_port)); | ||||
| 		LOGPC(DLMGCP, LOGL_ERROR, | ||||
| 		LOGPC(DRTP, LOGL_ERROR, | ||||
| 		      "expected: %d for RTP or %d for RTCP\n", | ||||
| 		      ntohs(conn->end.rtp_port), ntohs(conn->end.rtcp_port)); | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "endpoint:%x packet tossed\n", | ||||
| 		LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return -1; | ||||
| 	} | ||||
| @@ -808,16 +871,25 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn) | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	endp = conn->conn->endp; | ||||
|  | ||||
| 	/* Note: it is legal to create a connection but never setting a port | ||||
| 	 * and IP-address for outgoing data. */ | ||||
| 	if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0 && conn->end.rtp_port == 0) { | ||||
| 		LOGP(DRTP, LOGL_DEBUG, | ||||
| 		     "endpoint:0x%x destination IP-address and rtp port is (not yet) known\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x destination IP-address is invalid\n", | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x destination IP-address is invalid\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (conn->end.rtp_port == 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x destination rtp port is invalid\n", | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x destination rtp port is invalid\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return -1; | ||||
| 	} | ||||
| @@ -839,7 +911,7 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf, | ||||
| 	endp = conn->conn->endp; | ||||
| 	tcfg = endp->tcfg; | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x receiving RTP/RTCP packet...\n", | ||||
| 	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x receiving RTP/RTCP packet...\n", | ||||
| 	     ENDPOINT_NUMBER(endp)); | ||||
|  | ||||
| 	rc = receive_from(endp, fd->fd, addr, buf, buf_size); | ||||
| @@ -847,11 +919,11 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf, | ||||
| 		return -1; | ||||
| 	*proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP; | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x ", ENDPOINT_NUMBER(endp)); | ||||
| 	LOGPC(DLMGCP, LOGL_DEBUG, "receiveing from %s %s %d\n", | ||||
| 	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x ", ENDPOINT_NUMBER(endp)); | ||||
| 	LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n", | ||||
| 	      conn->conn->name, inet_ntoa(addr->sin_addr), | ||||
| 	      ntohs(addr->sin_port)); | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x conn:%s\n", ENDPOINT_NUMBER(endp), | ||||
| 	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x conn:%s\n", ENDPOINT_NUMBER(endp), | ||||
| 	     mgcp_conn_dump(conn->conn)); | ||||
|  | ||||
| 	/* Check if the origin of the RTP packet seems plausible */ | ||||
| @@ -862,17 +934,17 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf, | ||||
|  | ||||
| 	/* Filter out dummy message */ | ||||
| 	if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) { | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 		     "endpoint:%x dummy message received\n", | ||||
| 		LOGP(DRTP, LOGL_NOTICE, | ||||
| 		     "endpoint:0x%x dummy message received\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x packet tossed\n", ENDPOINT_NUMBER(endp)); | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x packet tossed\n", ENDPOINT_NUMBER(endp)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Increment RX statistics */ | ||||
| 	conn->end.packets_rx += 1; | ||||
| 	conn->end.octets_rx += rc; | ||||
| 	rate_ctr_inc(&conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]); | ||||
| 	rate_ctr_add(&conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], rc); | ||||
|  | ||||
| 	/* Forward a copy of the RTP data to a debug ip/port */ | ||||
| 	forward_data(fd->fd, &conn->tap_in, buf, rc); | ||||
| @@ -890,7 +962,7 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf, | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	endp = conn_src->conn->endp; | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x destin conn:%s\n", | ||||
| 	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x destin conn:%s\n", | ||||
| 	     ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_dst->conn)); | ||||
|  | ||||
| 	/* Before we try to deliver the packet, we check if the destination | ||||
| @@ -903,16 +975,16 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf, | ||||
| 	 * destination connection. */ | ||||
| 	switch (conn_dst->type) { | ||||
| 	case MGCP_RTP_DEFAULT: | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 		     "endpoint:%x endpoint type is MGCP_RTP_DEFAULT, " | ||||
| 		LOGP(DRTP, LOGL_DEBUG, | ||||
| 		     "endpoint:0x%x endpoint type is MGCP_RTP_DEFAULT, " | ||||
| 		     "using mgcp_send() to forward data directly\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return mgcp_send(endp, proto == MGCP_PROTO_RTP, | ||||
| 				 addr, buf, buf_size, conn_src, conn_dst); | ||||
| 	case MGCP_OSMUX_BSC_NAT: | ||||
| 	case MGCP_OSMUX_BSC: | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 		     "endpoint:%x endpoint type is MGCP_OSMUX_BSC_NAT, " | ||||
| 		LOGP(DRTP, LOGL_DEBUG, | ||||
| 		     "endpoint:0x%x endpoint type is MGCP_OSMUX_BSC_NAT, " | ||||
| 		     "using osmux_xfrm_to_osmux() to forward data through OSMUX\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return osmux_xfrm_to_osmux(buf, buf_size, conn_dst); | ||||
| @@ -921,8 +993,8 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf, | ||||
| 	/* If the data has not been handled/forwarded until here, it will | ||||
| 	 * be discarded, this should not happen, normally the MGCP type | ||||
| 	 * should be properly set */ | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, | ||||
| 	     "endpoint:%x bad MGCP type -- data discarded!\n", | ||||
| 	LOGP(DRTP, LOGL_ERROR, | ||||
| 	     "endpoint:0x%x bad MGCP type -- data discarded!\n", | ||||
| 	     ENDPOINT_NUMBER(endp)); | ||||
|  | ||||
| 	return -1; | ||||
| @@ -966,16 +1038,16 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf, | ||||
|  | ||||
| 	/* There is no destination conn, stop here */ | ||||
| 	if (!conn_dst) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x unable to find destination conn\n", | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x unable to find destination conn\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* The destination conn is not an RTP connection */ | ||||
| 	if (conn_dst->type != MGCP_CONN_TYPE_RTP) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x unable to find suitable destination conn\n", | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x unable to find suitable destination conn\n", | ||||
| 		     ENDPOINT_NUMBER(endp)); | ||||
| 		return -1; | ||||
| 	} | ||||
| @@ -986,6 +1058,25 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf, | ||||
|  | ||||
| } | ||||
|  | ||||
| /*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed. | ||||
|  *  \param[in] endp Endpoint on which the connection resides. | ||||
|  *  \param[in] conn Connection that is about to be removed (ignored). | ||||
|  *  \returns 0 on success, -1 on ERROR. */ | ||||
| void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn) | ||||
| { | ||||
| 	struct mgcp_conn *conn_cleanup; | ||||
|  | ||||
| 	/* In mgcp_dispatch_rtp_bridge_cb() we use conn->priv to cache the | ||||
| 	 * pointer to the destination connection, so that we do not have | ||||
| 	 * to go through the list every time an RTP packet arrives. To prevent | ||||
| 	 * a use-after-free situation we invalidate this information for all | ||||
| 	 * connections present when one connection is removed from the | ||||
| 	 * endpoint. */ | ||||
| 	llist_for_each_entry(conn_cleanup, &endp->conns, entry) { | ||||
| 		conn_cleanup->priv = NULL; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Handle incoming RTP data from NET */ | ||||
| static int rtp_data_net(struct osmo_fd *fd, unsigned int what) | ||||
| { | ||||
| @@ -1002,31 +1093,40 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) | ||||
|  | ||||
| 	char buf[RTP_BUF_SIZE]; | ||||
| 	int proto; | ||||
| 	int rc; | ||||
| 	int len; | ||||
|  | ||||
| 	conn_src = (struct mgcp_conn_rtp *)fd->data; | ||||
| 	OSMO_ASSERT(conn_src); | ||||
| 	endp = conn_src->conn->endp; | ||||
| 	OSMO_ASSERT(endp); | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x source conn:%s\n", | ||||
| 	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x source conn:%s\n", | ||||
| 	     ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_src->conn)); | ||||
|  | ||||
| 	/* Receive packet */ | ||||
| 	rc = mgcp_recv(&proto, &addr, buf, sizeof(buf), fd); | ||||
| 	if (rc < 0) | ||||
| 	len = mgcp_recv(&proto, &addr, buf, sizeof(buf), fd); | ||||
| 	if (len < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	/* Check if the connection is in loopback mode, if yes, just send the | ||||
| 	 * incoming data back to the origin */ | ||||
|  | ||||
| 	if (conn_src->conn->mode == MGCP_CONN_LOOPBACK) { | ||||
| 		/* When we are in loopback mode, we loop back all incoming | ||||
| 		 * packets back to their origin. We will use the originating | ||||
| 		 * address data from the UDP packet header to patch the | ||||
| 		 * outgoing address in connection on the fly */ | ||||
| 		if (conn_src->end.rtp_port == 0) { | ||||
| 			conn_src->end.addr = addr.sin_addr; | ||||
| 			conn_src->end.rtp_port = addr.sin_port; | ||||
| 		} | ||||
| 		return mgcp_send_rtp(proto, &addr, buf, | ||||
| 				     sizeof(buf), conn_src, conn_src); | ||||
| 				     len, conn_src, conn_src); | ||||
| 	} | ||||
|  | ||||
| 	/* Execute endpoint specific implementation that handles the | ||||
| 	 * dispatching of the RTP data */ | ||||
| 	return endp->type->dispatch_rtp_cb(proto, &addr, buf, sizeof(buf), | ||||
| 	return endp->type->dispatch_rtp_cb(proto, &addr, buf, len, | ||||
| 					   conn_src->conn); | ||||
| } | ||||
|  | ||||
| @@ -1051,41 +1151,17 @@ int mgcp_set_ip_tos(int fd, int tos) | ||||
|  *  \returns 0 on success, -1 on ERROR */ | ||||
| int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port) | ||||
| { | ||||
| 	struct sockaddr_in addr; | ||||
| 	int on = 1; | ||||
| 	int rc; | ||||
|  | ||||
| 	fd->fd = socket(AF_INET, SOCK_DGRAM, 0); | ||||
| 	if (fd->fd < 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "failed to create UDP port (%s:%i).\n", | ||||
| 	rc = osmo_sock_init2(AF_INET, SOCK_DGRAM, IPPROTO_UDP, source_addr, port, | ||||
| 			     NULL, 0, OSMO_SOCK_F_BIND); | ||||
| 	if (rc < 0) { | ||||
| 		LOGP(DRTP, LOGL_ERROR, "failed to bind UDP port (%s:%i).\n", | ||||
| 		     source_addr, port); | ||||
| 		return -1; | ||||
| 	} else { | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 		     "created UDP port (%s:%i).\n", source_addr, port); | ||||
| 	} | ||||
|  | ||||
| 	if (setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "failed to set socket options (%s:%i).\n", source_addr, | ||||
| 		     port); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	memset(&addr, 0, sizeof(addr)); | ||||
| 	addr.sin_family = AF_INET; | ||||
| 	addr.sin_port = htons(port); | ||||
| 	inet_aton(source_addr, &addr.sin_addr); | ||||
|  | ||||
| 	if (bind(fd->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { | ||||
| 		close(fd->fd); | ||||
| 		fd->fd = -1; | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "failed to bind UDP port (%s:%i).\n", | ||||
| 		     source_addr, port); | ||||
| 		return -1; | ||||
| 	} else { | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, | ||||
| 		     "bound UDP port (%s:%i).\n", source_addr, port); | ||||
| 	} | ||||
| 	fd->fd = rc; | ||||
| 	LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%i).\n", source_addr, port); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
| @@ -1099,16 +1175,16 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr, | ||||
|  | ||||
| 	if (mgcp_create_bind(source_addr, &rtp_end->rtp, | ||||
| 			     rtp_end->local_port) != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x failed to create RTP port: %s:%d\n", endpno, | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x failed to create RTP port: %s:%d\n", endpno, | ||||
| 		     source_addr, rtp_end->local_port); | ||||
| 		goto cleanup0; | ||||
| 	} | ||||
|  | ||||
| 	if (mgcp_create_bind(source_addr, &rtp_end->rtcp, | ||||
| 			     rtp_end->local_port + 1) != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x failed to create RTCP port: %s:%d\n", endpno, | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x failed to create RTCP port: %s:%d\n", endpno, | ||||
| 		     source_addr, rtp_end->local_port + 1); | ||||
| 		goto cleanup1; | ||||
| 	} | ||||
| @@ -1119,16 +1195,16 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr, | ||||
|  | ||||
| 	rtp_end->rtp.when = BSC_FD_READ; | ||||
| 	if (osmo_fd_register(&rtp_end->rtp) != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x failed to register RTP port %d\n", endpno, | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x failed to register RTP port %d\n", endpno, | ||||
| 		     rtp_end->local_port); | ||||
| 		goto cleanup2; | ||||
| 	} | ||||
|  | ||||
| 	rtp_end->rtcp.when = BSC_FD_READ; | ||||
| 	if (osmo_fd_register(&rtp_end->rtcp) != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x failed to register RTCP port %d\n", endpno, | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x failed to register RTCP port %d\n", endpno, | ||||
| 		     rtp_end->local_port + 1); | ||||
| 		goto cleanup3; | ||||
| 	} | ||||
| @@ -1157,13 +1233,14 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port, | ||||
| { | ||||
| 	char name[512]; | ||||
| 	struct mgcp_rtp_end *end; | ||||
| 	char local_ip_addr[INET_ADDRSTRLEN]; | ||||
|  | ||||
| 	snprintf(name, sizeof(name), "%s-%u", conn->conn->name, conn->conn->id); | ||||
| 	snprintf(name, sizeof(name), "%s-%s", conn->conn->name, conn->conn->id); | ||||
| 	end = &conn->end; | ||||
|  | ||||
| 	if (end->rtp.fd != -1 || end->rtcp.fd != -1) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "endpoint:%x %u was already bound on conn:%s\n", | ||||
| 		LOGP(DRTP, LOGL_ERROR, | ||||
| 		     "endpoint:0x%x %u was already bound on conn:%s\n", | ||||
| 		     ENDPOINT_NUMBER(endp), rtp_port, | ||||
| 		     mgcp_conn_dump(conn->conn)); | ||||
|  | ||||
| @@ -1181,7 +1258,9 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port, | ||||
| 	end->rtcp.data = conn; | ||||
| 	end->rtcp.cb = rtp_data_net; | ||||
|  | ||||
| 	return bind_rtp(endp->cfg, mgcp_net_src_addr(endp), end, | ||||
| 	mgcp_get_local_addr(local_ip_addr, conn); | ||||
|  | ||||
| 	return bind_rtp(endp->cfg, local_ip_addr, end, | ||||
| 			ENDPOINT_NUMBER(endp)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/mgcp/osmux.h> | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
|  | ||||
| static struct osmo_fd osmux_fd; | ||||
|  | ||||
| @@ -142,7 +143,7 @@ osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) | ||||
| } | ||||
|  | ||||
| /* Lookup existing handle for a specified address, if the handle can not be | ||||
|  * foud a the function will automatically allocate one */ | ||||
|  * found, the function will automatically allocate one */ | ||||
| static struct osmux_in_handle * | ||||
| osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) | ||||
| { | ||||
| @@ -207,12 +208,18 @@ endpoint_lookup(struct mgcp_config *cfg, int cid, | ||||
| 		case MGCP_DEST_NET: | ||||
| 			/* FIXME: Get rid of CONN_ID_XXX! */ | ||||
| 			conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET); | ||||
| 			this = &conn_net->end.addr; | ||||
| 			if (conn_net) | ||||
| 				this = &conn_net->end.addr; | ||||
| 			else | ||||
| 				this = NULL; | ||||
| 			break; | ||||
| 		case MGCP_DEST_BTS: | ||||
| 			/* FIXME: Get rid of CONN_ID_XXX! */ | ||||
| 			conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS); | ||||
| 			this = &conn_bts->end.addr; | ||||
| 			if (conn_bts) | ||||
| 				this = &conn_bts->end.addr; | ||||
| 			else | ||||
| 				this = NULL; | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* Should not ever happen */ | ||||
| @@ -222,7 +229,8 @@ endpoint_lookup(struct mgcp_config *cfg, int cid, | ||||
|  | ||||
| 		/* FIXME: Get rid of CONN_ID_XXX! */ | ||||
| 		conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET); | ||||
| 		if (conn_net->osmux.cid == cid && this->s_addr == from_addr->s_addr) | ||||
| 		if (conn_net && this && conn_net->osmux.cid == cid | ||||
| 		    && this->s_addr == from_addr->s_addr) | ||||
| 			return endp; | ||||
| 	} | ||||
|  | ||||
| @@ -248,8 +256,8 @@ static void scheduled_tx_net_cb(struct msgb *msg, void *data) | ||||
| 		.sin_port = conn_net->end.rtp_port, | ||||
| 	}; | ||||
|  | ||||
| 	conn_bts->end.octets_tx += msg->len; | ||||
| 	conn_bts->end.packets_tx++; | ||||
| 	rate_ctr_inc(&conn_bts->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]); | ||||
| 	rate_ctr_add(&conn_bts->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len); | ||||
|  | ||||
| 	/* Send RTP data to NET */ | ||||
| 	/* FIXME: Get rid of conn_bts and conn_net! */ | ||||
| @@ -275,8 +283,8 @@ static void scheduled_tx_bts_cb(struct msgb *msg, void *data) | ||||
| 		.sin_port = conn_bts->end.rtp_port, | ||||
| 	}; | ||||
|  | ||||
| 	conn_net->end.octets_tx += msg->len; | ||||
| 	conn_net->end.packets_tx++; | ||||
| 	rate_ctr_inc(&conn_net->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]); | ||||
| 	rate_ctr_add(&conn_net->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);	 | ||||
|  | ||||
| 	/* Send RTP data to BTS */ | ||||
| 	/* FIXME: Get rid of conn_bts and conn_net! */ | ||||
| @@ -314,11 +322,10 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| { | ||||
| 	struct msgb *msg; | ||||
| 	struct osmux_hdr *osmuxh; | ||||
| 	struct llist_head list; | ||||
| 	struct sockaddr_in addr; | ||||
| 	struct mgcp_config *cfg = ofd->data; | ||||
| 	uint32_t rem; | ||||
| 	struct mgcp_conn_rtp *conn_net = NULL; | ||||
| 	struct mgcp_conn_rtp *conn_bts = NULL; | ||||
|  | ||||
| 	msg = osmux_recv(ofd, &addr); | ||||
| 	if (!msg) | ||||
| @@ -337,8 +344,8 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| 				       &addr.sin_addr, MGCP_DEST_NET); | ||||
|  | ||||
| 		/* FIXME: Get rid of CONN_ID_XXX! */ | ||||
| 		conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET); | ||||
| 		if (!conn_net) | ||||
| 		conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS); | ||||
| 		if (!conn_bts) | ||||
| 			goto out; | ||||
|  | ||||
| 		if (!endp) { | ||||
| @@ -347,12 +354,11 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| 			     osmuxh->circuit_id); | ||||
| 			goto out; | ||||
| 		} | ||||
| 		conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem); | ||||
| 		conn_net->osmux.stats.chunks++; | ||||
| 		conn_bts->osmux.stats.octets += osmux_chunk_length(msg, rem); | ||||
| 		conn_bts->osmux.stats.chunks++; | ||||
| 		rem = msg->len; | ||||
|  | ||||
| 		osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list); | ||||
| 		osmux_tx_sched(&list, scheduled_tx_bts_cb, endp); | ||||
| 		osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh); | ||||
| 	} | ||||
| out: | ||||
| 	msgb_free(msg); | ||||
| @@ -418,7 +424,6 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| { | ||||
| 	struct msgb *msg; | ||||
| 	struct osmux_hdr *osmuxh; | ||||
| 	struct llist_head list; | ||||
| 	struct sockaddr_in addr; | ||||
| 	struct mgcp_config *cfg = ofd->data; | ||||
| 	uint32_t rem; | ||||
| @@ -455,8 +460,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| 		conn_net->osmux.stats.chunks++; | ||||
| 		rem = msg->len; | ||||
|  | ||||
| 		osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list); | ||||
| 		osmux_tx_sched(&list, scheduled_tx_net_cb, endp); | ||||
| 		osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh); | ||||
| 	} | ||||
| out: | ||||
| 	msgb_free(msg); | ||||
| @@ -545,9 +549,13 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn, | ||||
| 	switch (endp->cfg->role) { | ||||
| 		case MGCP_BSC_NAT: | ||||
| 			conn->type = MGCP_OSMUX_BSC_NAT; | ||||
| 			osmux_xfrm_output_set_tx_cb(&conn->osmux.out, | ||||
| 							scheduled_tx_net_cb, endp); | ||||
| 			break; | ||||
| 		case MGCP_BSC: | ||||
| 			conn->type = MGCP_OSMUX_BSC; | ||||
| 			osmux_xfrm_output_set_tx_cb(&conn->osmux.out, | ||||
| 							scheduled_tx_bts_cb, endp); | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| @@ -566,8 +574,13 @@ void osmux_disable_conn(struct mgcp_conn_rtp *conn) | ||||
| 	if (conn->osmux.state != OSMUX_STATE_ENABLED) | ||||
| 		return; | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_INFO, "Releasing connection %u using Osmux CID %u\n", | ||||
| 	LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n", | ||||
| 	     conn->conn->id, conn->osmux.cid); | ||||
|  | ||||
| 	/* We are closing, we don't need pending RTP packets to be transmitted */ | ||||
| 	osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL); | ||||
| 	osmux_xfrm_output_flush(&conn->osmux.out); | ||||
|  | ||||
| 	osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid); | ||||
| 	conn->osmux.state = OSMUX_STATE_DISABLED; | ||||
| 	conn->osmux.cid = -1; | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -20,12 +20,18 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #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 <errno.h> | ||||
|  | ||||
| /* A struct to store intermediate parsing results. The function | ||||
|  * mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP | ||||
|  * codec information. */ | ||||
| struct sdp_rtp_map { | ||||
| 	/* the type */ | ||||
| 	int payload_type; | ||||
| @@ -38,76 +44,9 @@ struct sdp_rtp_map { | ||||
| 	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) | ||||
| /* 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; | ||||
|  | ||||
| @@ -133,11 +72,18 @@ void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used) | ||||
| 			codecs[i].rate = 8000; | ||||
| 			codecs[i].channels = 1; | ||||
| 			break; | ||||
| 		default: | ||||
| 			codecs[i].codec_name = NULL; | ||||
| 			codecs[i].rate = 0; | ||||
| 			codecs[i].channels = 0; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, char *audio_name) | ||||
| /* 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; | ||||
|  | ||||
| @@ -145,11 +91,17 @@ void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, | ||||
| 		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); | ||||
| 			   audio_codec, &rate, &channels) < 1) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n", | ||||
| 			     audio_name); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| @@ -160,38 +112,90 @@ void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, audio_name); | ||||
| 	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) | ||||
| /* 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 *bts_codec; | ||||
| 	char audio_codec[64]; | ||||
| 	char *str; | ||||
| 	char *str_ptr; | ||||
| 	char *pt_str; | ||||
| 	unsigned int pt; | ||||
| 	unsigned int count = 0; | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	if (!codec->codec_name) | ||||
| 		return 0; | ||||
| 	str = talloc_zero_size(ctx, strlen(sdp) + 1); | ||||
| 	str_ptr = str; | ||||
| 	strcpy(str_ptr, sdp); | ||||
|  | ||||
| 	/* | ||||
| 	 * GSM, GSM/8000 and GSM/8000/1 should all be compatible.. let's go | ||||
| 	 * by name first. | ||||
| 	 */ | ||||
| 	bts_codec = endp->tcfg->audio_name; | ||||
| 	if (sscanf(bts_codec, "%63[^/]/%*d/%*d", audio_codec) < 1) | ||||
| 		return 0; | ||||
| 	str_ptr = strstr(str_ptr, "RTP/AVP "); | ||||
| 	if (!str_ptr) | ||||
| 		goto exit; | ||||
|  | ||||
| 	return strcasecmp(audio_codec, codec->codec_name) == 0; | ||||
| 	pt_str = strtok(str_ptr, " "); | ||||
| 	if (!pt_str) | ||||
| 		goto exit; | ||||
|  | ||||
| 	while (1) { | ||||
| 		/* Do not allow excessive payload types */ | ||||
| 		if (count > codecs_len) | ||||
| 			goto error; | ||||
|  | ||||
| 		pt_str = strtok(NULL, " "); | ||||
| 		if (!pt_str) | ||||
| 			break; | ||||
|  | ||||
| 		pt = atoi(pt_str); | ||||
|  | ||||
| 		/* Do not allow duplicate payload types */ | ||||
| 		for (i = 0; i < count; i++) | ||||
| 			if (codecs[i].payload_type == pt) | ||||
| 				goto error; | ||||
|  | ||||
| 		codecs[count].payload_type = pt; | ||||
| 		count++; | ||||
| 	} | ||||
|  | ||||
| exit: | ||||
| 	talloc_free(str); | ||||
| 	return count; | ||||
| error: | ||||
| 	talloc_free(str); | ||||
| 	return -EINVAL; | ||||
| } | ||||
|  | ||||
| int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) | ||||
| /*! 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[10]; | ||||
| 	int codecs_used = 0; | ||||
| 	struct sdp_rtp_map codecs[MGCP_MAX_CODECS]; | ||||
| 	unsigned int codecs_used = 0; | ||||
| 	char *line; | ||||
| 	int maxptime = -1; | ||||
| 	int i; | ||||
| 	int codecs_assigned = 0; | ||||
| 	unsigned int i; | ||||
| 	void *tmp_ctx = talloc_new(NULL); | ||||
| 	struct mgcp_rtp_end *rtp; | ||||
|  | ||||
| 	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) { | ||||
| @@ -202,62 +206,46 @@ int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, st | ||||
| 		case 'v': | ||||
| 			/* skip these SDP attributes */ | ||||
| 			break; | ||||
| 		case 'a': { | ||||
| 			int payload; | ||||
| 			int ptime, ptime2 = 0; | ||||
| 			char audio_name[64]; | ||||
|  | ||||
|  | ||||
| 		case 'a': | ||||
| 			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) { | ||||
| 				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; | ||||
| 				rtp->maximum_packet_time = 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) { | ||||
| 		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); | ||||
| 				codecs_used = rc - 1; | ||||
| 				codecs_initialize(tmp_ctx, codecs, codecs_used); | ||||
| 			} | ||||
|  | ||||
| 			rc = pt_from_sdp(conn->conn, codecs, | ||||
| 					 ARRAY_SIZE(codecs), line); | ||||
| 			if (rc > 0) | ||||
| 				codecs_used = rc; | ||||
| 			break; | ||||
| 		} | ||||
| 		case 'c': { | ||||
| 			char ipv4[16]; | ||||
| 		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)); | ||||
| 				     line[0], line[0], | ||||
| 				     ENDPOINT_NUMBER(p->endp)); | ||||
| 			else | ||||
| 				LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 				     "Unhandled SDP option: '%c'/%d\n", | ||||
| @@ -265,42 +253,108 @@ int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, st | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS); | ||||
|  | ||||
| 	/* Now select the primary and alt_codec */ | ||||
| 	for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) { | ||||
| 		struct mgcp_rtp_codec *codec = codecs_assigned == 0 ? | ||||
| 					&rtp->codec : &rtp->alt_codec; | ||||
| 	/* So far we have only set the payload type in the codec struct. Now we | ||||
| 	 * fill up the remaining fields of the codec description with some default | ||||
| 	 * information */ | ||||
| 	codecs_initialize(tmp_ctx, codecs, codecs_used); | ||||
|  | ||||
| 		if (endp->tcfg->no_audio_transcoding && | ||||
| 			!is_codec_compatible(endp, &codecs[i])) { | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n", | ||||
| 				codecs[i].codec_name); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		mgcp_set_audio_info(p->cfg, codec, | ||||
| 					codecs[i].payload_type, | ||||
| 					codecs[i].map_line); | ||||
| 		codecs_assigned += 1; | ||||
| 	} | ||||
|  | ||||
| 	if (codecs_assigned > 0) { | ||||
| 		/* TODO/XXX: Store this per codec and derive it on use */ | ||||
| 		if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den > | ||||
| 				rtp->codec.frame_duration_num * 1500) { | ||||
| 			/* more than 1 frame */ | ||||
| 			rtp->packet_duration_ms = 0; | ||||
| 		} | ||||
|  | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 		     "Got media info via SDP: port %d, payload %d (%s), " | ||||
| 		     "duration %d, addr %s\n", | ||||
| 		     ntohs(rtp->rtp_port), rtp->codec.payload_type, | ||||
| 		     rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown", | ||||
| 		     rtp->packet_duration_ms, inet_ntoa(rtp->addr)); | ||||
| 	/* Store parsed codec information */ | ||||
| 	for (i = 0; i < codecs_used; i++) { | ||||
| 		rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line); | ||||
| 		if (rc < 0) | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp)); | ||||
| 	} | ||||
|  | ||||
| 	talloc_free(tmp_ctx); | ||||
| 	return codecs_assigned > 0; | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 	     "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:", | ||||
| 	     ntohs(rtp->rtp_port), inet_ntoa(rtp->addr), | ||||
| 	     rtp->packet_duration_ms); | ||||
| 	if (codecs_used == 0) | ||||
| 		LOGPC(DLMGCP, LOGL_NOTICE, "none"); | ||||
| 	for (i = 0; i < codecs_used; i++) { | ||||
| 		LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s", | ||||
| 		      rtp->codecs[i].payload_type, | ||||
| 		      rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown"); | ||||
| 		LOGPC(DLMGCP, LOGL_NOTICE, " "); | ||||
| 	} | ||||
| 	LOGPC(DLMGCP, LOGL_NOTICE, "\n"); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! Generate SDP response string. | ||||
|  *  \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 char *fmtp_extra; | ||||
| 	const char *audio_name; | ||||
| 	int payload_type; | ||||
| 	int rc; | ||||
|  | ||||
| 	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, | ||||
| 					      &payload_type, &audio_name, | ||||
| 					      &fmtp_extra, | ||||
| 					      (struct mgcp_conn_rtp *)conn); | ||||
|  | ||||
| 	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) { | ||||
| 		rc = msgb_printf(sdp, "m=audio %d RTP/AVP %d\r\n", | ||||
| 				 conn->end.local_port, payload_type); | ||||
| 		if (rc < 0) | ||||
| 			goto buffer_too_small; | ||||
|  | ||||
| 		/* FIXME: Check if the payload type is from the static range, | ||||
| 		 * if yes, omitthe a=rtpmap since it is unnecessary */ | ||||
| 		if (audio_name && endp->tcfg->audio_send_name && (payload_type >= 96 && payload_type <= 127)) { | ||||
| 			rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", | ||||
| 					 payload_type, audio_name); | ||||
|  | ||||
| 			if (rc < 0) | ||||
| 				goto buffer_too_small; | ||||
| 		} | ||||
|  | ||||
| 		if (fmtp_extra) { | ||||
| 			rc = msgb_printf(sdp, "%s\r\n", 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: | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n"); | ||||
| 	return -1; | ||||
| } | ||||
|   | ||||
| @@ -23,17 +23,20 @@ | ||||
|  */ | ||||
|  | ||||
| #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_rtp_state *state, | ||||
| 			struct mgcp_rtp_end *end, uint32_t *expected, | ||||
| 			int *loss) | ||||
| void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss) | ||||
| { | ||||
| 	*expected = state->stats_cycles + state->stats_max_seq; | ||||
| 	*expected = *expected - state->stats_base_seq + 1; | ||||
| 	struct mgcp_rtp_state *state = &conn->state; | ||||
| 	struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]; | ||||
|  | ||||
| 	if (!state->stats_initialized) { | ||||
| 	*expected = state->stats.cycles + state->stats.max_seq; | ||||
| 	*expected = *expected - state->stats.base_seq + 1; | ||||
|  | ||||
| 	if (!state->stats.initialized) { | ||||
| 		*expected = 0; | ||||
| 		*loss = 0; | ||||
| 		return; | ||||
| @@ -43,8 +46,8 @@ void calc_loss(struct mgcp_rtp_state *state, | ||||
| 	 * Make sure the sign is correct and use the biggest | ||||
| 	 * positive/negative number that fits. | ||||
| 	 */ | ||||
| 	*loss = *expected - end->packets_rx; | ||||
| 	if (*expected < end->packets_rx) { | ||||
| 	*loss = *expected - packets_rx->current; | ||||
| 	if (*expected < packets_rx->current) { | ||||
| 		if (*loss > 0) | ||||
| 			*loss = INT_MIN; | ||||
| 	} else { | ||||
| @@ -56,9 +59,9 @@ void calc_loss(struct mgcp_rtp_state *state, | ||||
| /* Helper function for mgcp_format_stats_rtp() to calculate jitter */ | ||||
| uint32_t calc_jitter(struct mgcp_rtp_state *state) | ||||
| { | ||||
| 	if (!state->stats_initialized) | ||||
| 	if (!state->stats.initialized) | ||||
| 		return 0; | ||||
| 	return state->stats_jitter >> 4; | ||||
| 	return state->stats.jitter >> 4; | ||||
| } | ||||
|  | ||||
| /* Generate statistics for an RTP connection */ | ||||
| @@ -69,13 +72,18 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len, | ||||
| 	int ploss; | ||||
| 	int nchars; | ||||
|  | ||||
| 	calc_loss(&conn->state, &conn->end, &expected, &ploss); | ||||
| 	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=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u", | ||||
| 			  conn->end.packets_tx, conn->end.octets_tx, | ||||
| 			  conn->end.packets_rx, conn->end.octets_rx, | ||||
| 			  "\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; | ||||
| @@ -83,21 +91,23 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len, | ||||
| 	str += nchars; | ||||
| 	str_len -= nchars; | ||||
|  | ||||
| 	/* Error Counter */ | ||||
| 	nchars = snprintf(str, str_len, | ||||
| 			  "\r\nX-Osmo-CP: EC TI=%u, TO=%u", | ||||
| 			  conn->state.in_stream.err_ts_counter, | ||||
| 			  conn->state.out_stream.err_ts_counter); | ||||
| 	if (nchars < 0 || nchars >= str_len) | ||||
| 		goto truncate; | ||||
| 	if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) { | ||||
| 		/* Error Counter */ | ||||
| 		nchars = snprintf(str, str_len, | ||||
| 				  "\r\nX-Osmo-CP: EC TI=%" 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; | ||||
| 		str += nchars; | ||||
| 		str_len -= nchars; | ||||
|  | ||||
| 	if (conn->osmux.state == OSMUX_STATE_ENABLED) { | ||||
| 		snprintf(str, str_len, | ||||
| 			 "\r\nX-Osmux-ST: CR=%u, BR=%u", | ||||
| 			 conn->osmux.stats.chunks, conn->osmux.stats.octets); | ||||
| 		if (conn->osmux.state == OSMUX_STATE_ENABLED) { | ||||
| 			snprintf(str, str_len, | ||||
| 				 "\r\nX-Osmux-ST: CR=%u, BR=%u", | ||||
| 				 conn->osmux.stats.chunks, conn->osmux.stats.octets); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| truncate: | ||||
|   | ||||
| @@ -27,8 +27,10 @@ | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/mgcp/vty.h> | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
|  | ||||
| #include <string.h> | ||||
| #include <inttypes.h> | ||||
|  | ||||
| #define RTCP_OMIT_STR "Drop RTCP packets in both directions\n" | ||||
| #define RTP_PATCH_STR "Modify RTP packet header in both directions\n" | ||||
| @@ -63,18 +65,21 @@ struct cmd_node trunk_node = { | ||||
| static int config_write_mgcp(struct vty *vty) | ||||
| { | ||||
| 	vty_out(vty, "mgcp%s", VTY_NEWLINE); | ||||
| 	vty_out(vty, "  domain %s%s", g_cfg->domain, VTY_NEWLINE); | ||||
| 	if (g_cfg->local_ip) | ||||
| 		vty_out(vty, "  local ip %s%s", g_cfg->local_ip, VTY_NEWLINE); | ||||
| 	vty_out(vty, "  bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE); | ||||
| 	vty_out(vty, "  bind port %u%s", g_cfg->source_port, VTY_NEWLINE); | ||||
| 	vty_out(vty, "  rtp net-range %u %u%s", | ||||
| 	vty_out(vty, "  rtp port-range %u %u%s", | ||||
| 		g_cfg->net_ports.range_start, g_cfg->net_ports.range_end, | ||||
| 		VTY_NEWLINE); | ||||
|  | ||||
| 	if (g_cfg->net_ports.bind_addr) | ||||
| 		vty_out(vty, "  rtp net-bind-ip %s%s", | ||||
| 		vty_out(vty, "  rtp bind-ip %s%s", | ||||
| 			g_cfg->net_ports.bind_addr, VTY_NEWLINE); | ||||
|  | ||||
| 	if (g_cfg->net_ports.bind_addr_probe) | ||||
| 		vty_out(vty, "  rtp ip-probing%s", VTY_NEWLINE); | ||||
| 	else | ||||
| 		vty_out(vty, "  no rtp ip-probing%s", VTY_NEWLINE); | ||||
| 	vty_out(vty, "  rtp ip-dscp %d%s", g_cfg->endp_dscp, VTY_NEWLINE); | ||||
| 	if (g_cfg->trunk.keepalive_interval == MGCP_KEEPALIVE_ONCE) | ||||
| 		vty_out(vty, "  rtp keep-alive once%s", VTY_NEWLINE); | ||||
| @@ -113,7 +118,7 @@ static int config_write_mgcp(struct vty *vty) | ||||
| 		g_cfg->trunk.audio_send_name ? "" : "no ", VTY_NEWLINE); | ||||
| 	vty_out(vty, "  loop %u%s", ! !g_cfg->trunk.audio_loop, VTY_NEWLINE); | ||||
| 	vty_out(vty, "  number endpoints %u%s", | ||||
| 		g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE); | ||||
| 		g_cfg->trunk.vty_number_endpoints - 1, VTY_NEWLINE); | ||||
| 	vty_out(vty, "  %sallow-transcoding%s", | ||||
| 		g_cfg->trunk.no_audio_transcoding ? "no " : "", VTY_NEWLINE); | ||||
| 	if (g_cfg->call_agent_addr) | ||||
| @@ -150,22 +155,27 @@ static int config_write_mgcp(struct vty *vty) | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state, | ||||
| 			 struct mgcp_rtp_end *end) | ||||
| static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	struct mgcp_rtp_codec *codec = &end->codec; | ||||
| 	struct mgcp_rtp_state *state = &conn->state; | ||||
| 	struct mgcp_rtp_end *end = &conn->end; | ||||
| 	struct mgcp_rtp_codec *codec = end->codec; | ||||
| 	struct rate_ctr *dropped_packets; | ||||
|  | ||||
| 	dropped_packets = &conn->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR]; | ||||
|  | ||||
| 	vty_out(vty, | ||||
| 		"   Timestamp Errs: %d->%d%s" | ||||
| 		"   Dropped Packets: %d%s" | ||||
| 		"   Timestamp Errs: %" PRIu64 "->%" PRIu64 "%s" | ||||
| 		"   Dropped Packets: %" PRIu64 "%s" | ||||
| 		"   Payload Type: %d Rate: %u Channels: %d %s" | ||||
| 		"   Frame Duration: %u Frame Denominator: %u%s" | ||||
| 		"   FPP: %d Packet Duration: %u%s" | ||||
| 		"   FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s" | ||||
| 		"   Output-Enabled: %d Force-PTIME: %d%s", | ||||
| 		state->in_stream.err_ts_counter, | ||||
| 		state->out_stream.err_ts_counter, VTY_NEWLINE, | ||||
| 		end->dropped_packets, VTY_NEWLINE, | ||||
| 		state->in_stream.err_ts_ctr->current, | ||||
| 		state->out_stream.err_ts_ctr->current, | ||||
| 	        VTY_NEWLINE, | ||||
| 		dropped_packets->current, VTY_NEWLINE, | ||||
| 		codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE, | ||||
| 		codec->frame_duration_num, codec->frame_duration_den, | ||||
| 		VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms, | ||||
| @@ -203,8 +213,7 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, | ||||
| 				 * connection types (E1) as soon as | ||||
| 				 * the implementation is available */ | ||||
| 				if (conn->type == MGCP_CONN_TYPE_RTP) { | ||||
| 					dump_rtp_end(vty, &conn->u.rtp.state, | ||||
| 						     &conn->u.rtp.end); | ||||
| 					dump_rtp_end(vty, &conn->u.rtp); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @@ -277,41 +286,70 @@ DEFUN(cfg_mgcp_bind_early, | ||||
| 	return CMD_WARNING; | ||||
| } | ||||
|  | ||||
| static void parse_range(struct mgcp_port_range *range, const char **argv) | ||||
| { | ||||
| 	range->range_start = atoi(argv[0]); | ||||
| 	range->range_end = atoi(argv[1]); | ||||
| 	range->last_port = g_cfg->net_ports.range_start; | ||||
| } | ||||
|  | ||||
| #define RTP_STR "RTP configuration\n" | ||||
| #define UDP_PORT_STR "UDP Port number\n" | ||||
| #define NET_START_STR "First UDP port allocated\n" | ||||
| #define RANGE_START_STR "Start of the range of ports\n" | ||||
| #define RANGE_END_STR "End of the range of ports\n" | ||||
|  | ||||
| DEFUN(cfg_mgcp_rtp_net_range, | ||||
|       cfg_mgcp_rtp_net_range_cmd, | ||||
|       "rtp net-range <0-65534> <0-65534>", | ||||
| DEFUN(cfg_mgcp_rtp_port_range, | ||||
|       cfg_mgcp_rtp_port_range_cmd, | ||||
|       "rtp port-range <1024-65534> <1025-65535>", | ||||
|       RTP_STR "Range of ports to use for the NET side\n" | ||||
|       RANGE_START_STR RANGE_END_STR) | ||||
| { | ||||
| 	parse_range(&g_cfg->net_ports, argv); | ||||
| 	int start; | ||||
| 	int end; | ||||
|  | ||||
| 	start = atoi(argv[0]); | ||||
| 	end = atoi(argv[1]); | ||||
|  | ||||
| 	if (end < start) { | ||||
| 		vty_out(vty, "range end port (%i) must be greater than the range start port (%i)!%s", | ||||
| 			end, start, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	if (start & 1) { | ||||
| 		vty_out(vty, "range must begin at an even port number, autocorrecting port (%i) to: %i%s", | ||||
| 			start, start & 0xFFFE, VTY_NEWLINE); | ||||
| 		start &= 0xFFFE; | ||||
| 	} | ||||
|  | ||||
| 	if ((end & 1) == 0) { | ||||
| 		vty_out(vty, "range must end at an odd port number, autocorrecting port (%i) to: %i%s", | ||||
| 			end, end | 1, VTY_NEWLINE); | ||||
| 		end |= 1; | ||||
| 	} | ||||
|  | ||||
| 	g_cfg->net_ports.range_start = start; | ||||
| 	g_cfg->net_ports.range_end = end; | ||||
| 	g_cfg->net_ports.last_port = g_cfg->net_ports.range_start; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgcp_rtp_port_range, | ||||
| 		 cfg_mgcp_rtp_net_range_cmd, | ||||
| 		 "rtp net-range <0-65534> <0-65534>", | ||||
| 		 RTP_STR "Range of ports to use for the NET side\n" | ||||
| 		 RANGE_START_STR RANGE_END_STR) | ||||
|  | ||||
| DEFUN(cfg_mgcp_rtp_net_bind_ip, | ||||
|       cfg_mgcp_rtp_net_bind_ip_cmd, | ||||
|       "rtp net-bind-ip A.B.C.D", | ||||
| DEFUN(cfg_mgcp_rtp_bind_ip, | ||||
|       cfg_mgcp_rtp_bind_ip_cmd, | ||||
|       "rtp bind-ip A.B.C.D", | ||||
|       RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n") | ||||
| { | ||||
| 	osmo_talloc_replace_string(g_cfg, &g_cfg->net_ports.bind_addr, argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgcp_rtp_bind_ip, | ||||
| 		 cfg_mgcp_rtp_net_bind_ip_cmd, | ||||
| 		 "rtp net-bind-ip A.B.C.D", | ||||
| 		 RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n") | ||||
|  | ||||
| DEFUN(cfg_mgcp_rtp_no_net_bind_ip, | ||||
|       cfg_mgcp_rtp_no_net_bind_ip_cmd, | ||||
|       "no rtp net-bind-ip", | ||||
| DEFUN(cfg_mgcp_rtp_no_bind_ip, | ||||
|       cfg_mgcp_rtp_no_bind_ip_cmd, | ||||
|       "no rtp bind-ip", | ||||
|       NO_STR RTP_STR "Bind endpoints facing the Network\n" | ||||
|       "Address to bind to\n") | ||||
| { | ||||
| @@ -319,6 +357,29 @@ DEFUN(cfg_mgcp_rtp_no_net_bind_ip, | ||||
| 	g_cfg->net_ports.bind_addr = NULL; | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgcp_rtp_no_bind_ip, | ||||
| 		 cfg_mgcp_rtp_no_net_bind_ip_cmd, | ||||
| 		 "no rtp net-bind-ip", | ||||
| 		 NO_STR RTP_STR "Bind endpoints facing the Network\n" | ||||
| 		 "Address to bind to\n") | ||||
|  | ||||
| DEFUN(cfg_mgcp_rtp_net_bind_ip_probing, | ||||
|       cfg_mgcp_rtp_net_bind_ip_probing_cmd, | ||||
|       "rtp ip-probing", | ||||
|       RTP_STR "automatic rtp bind ip selection\n") | ||||
| { | ||||
| 	g_cfg->net_ports.bind_addr_probe = true; | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgcp_rtp_no_net_bind_ip_probing, | ||||
|       cfg_mgcp_rtp_no_net_bind_ip_probing_cmd, | ||||
|       "no rtp ip-probing", | ||||
|       NO_STR RTP_STR "no automatic rtp bind ip selection\n") | ||||
| { | ||||
| 	g_cfg->net_ports.bind_addr_probe = false; | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgcp_rtp_ip_dscp, | ||||
|       cfg_mgcp_rtp_ip_dscp_cmd, | ||||
| @@ -490,7 +551,7 @@ DEFUN(cfg_mgcp_number_endp, | ||||
|       "Number options\n" "Endpoints available\n" "Number endpoints\n") | ||||
| { | ||||
| 	/* + 1 as we start counting at one */ | ||||
| 	g_cfg->trunk.number_endpoints = atoi(argv[0]) + 1; | ||||
| 	g_cfg->trunk.vty_number_endpoints = atoi(argv[0]) + 1; | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -569,7 +630,7 @@ DEFUN(cfg_mgcp_no_rtp_keepalive, | ||||
|       cfg_mgcp_no_rtp_keepalive_cmd, | ||||
|       "no rtp keep-alive", NO_STR RTP_STR RTP_KEEPALIVE_STR) | ||||
| { | ||||
| 	mgcp_trunk_set_keepalive(&g_cfg->trunk, 0); | ||||
| 	mgcp_trunk_set_keepalive(&g_cfg->trunk, MGCP_KEEPALIVE_NEVER); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -936,7 +997,7 @@ DEFUN(tap_rtp, | ||||
| 	struct mgcp_trunk_config *trunk; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	struct mgcp_conn_rtp *conn; | ||||
| 	uint32_t conn_id; | ||||
|         const char *conn_id = NULL; | ||||
|  | ||||
| 	trunk = find_trunk(g_cfg, atoi(argv[0])); | ||||
| 	if (!trunk) { | ||||
| @@ -960,11 +1021,11 @@ DEFUN(tap_rtp, | ||||
|  | ||||
| 	endp = &trunk->endpoints[endp_no]; | ||||
|  | ||||
| 	conn_id = strtoul(argv[2], NULL, 10); | ||||
| 	conn_id = argv[2]; | ||||
| 	conn = mgcp_conn_get_rtp(endp, conn_id); | ||||
| 	if (!conn) { | ||||
| 		vty_out(vty, "Conn ID %s/%d is invalid.%s", | ||||
| 			argv[2], conn_id, VTY_NEWLINE); | ||||
| 		vty_out(vty, "Conn ID %s is invalid.%s", | ||||
| 			conn_id, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| @@ -1012,7 +1073,7 @@ DEFUN(free_endp, free_endp_cmd, | ||||
| 	} | ||||
|  | ||||
| 	endp = &trunk->endpoints[endp_no]; | ||||
| 	mgcp_release_endp(endp); | ||||
| 	mgcp_endp_release(endp); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1082,7 +1143,7 @@ DEFUN(cfg_mgcp_osmux, | ||||
| 	 * allow to turn it on yet. */ | ||||
| 	vty_out(vty, "OSMUX currently unavailable in this software version.%s", VTY_NEWLINE); | ||||
| 	return CMD_WARNING; | ||||
|  | ||||
| #if 0 | ||||
| 	if (strcmp(argv[0], "on") == 0) | ||||
| 		g_cfg->osmux = OSMUX_USAGE_ON; | ||||
| 	else if (strcmp(argv[0], "only") == 0) | ||||
| @@ -1094,6 +1155,7 @@ DEFUN(cfg_mgcp_osmux, | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgcp_osmux_ip, | ||||
| @@ -1144,6 +1206,14 @@ DEFUN(cfg_mgcp_osmux_dummy, | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgcp_domain, | ||||
|       cfg_mgcp_domain_cmd, | ||||
|       "domain NAME", "domain\n" "qualified domain name\n") | ||||
| { | ||||
| 	osmo_strlcpy(g_cfg->domain, argv[0], sizeof(g_cfg->domain)); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| int mgcp_vty_init(void) | ||||
| { | ||||
| 	install_element_ve(&show_mgcp_cmd); | ||||
| @@ -1156,14 +1226,18 @@ int mgcp_vty_init(void) | ||||
| 	install_element(CONFIG_NODE, &cfg_mgcp_cmd); | ||||
| 	install_node(&mgcp_node, config_write_mgcp); | ||||
|  | ||||
| 	vty_install_default(MGCP_NODE); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_rtp_net_range_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_rtp_port_range_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_rtp_bind_ip_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_rtp_no_bind_ip_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_probing_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_probing_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_rtp_force_ptime_cmd); | ||||
| @@ -1201,10 +1275,10 @@ int mgcp_vty_init(void) | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_domain_cmd); | ||||
|  | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd); | ||||
| 	install_node(&trunk_node, config_write_trunk); | ||||
| 	vty_install_default(TRUNK_NODE); | ||||
| 	install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_cmd); | ||||
| 	install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_once_cmd); | ||||
| 	install_element(TRUNK_NODE, &cfg_trunk_no_rtp_keepalive_cmd); | ||||
| @@ -1231,18 +1305,6 @@ int mgcp_vty_init(void) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int allocate_trunk(struct mgcp_trunk_config *trunk) | ||||
| { | ||||
| 	if (mgcp_endpoints_allocate(trunk) != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Failed to allocate %d endpoints on trunk %d.\n", | ||||
| 		     trunk->number_endpoints, trunk->trunk_nr); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, | ||||
| 		      enum mgcp_role role) | ||||
| { | ||||
| @@ -1266,17 +1328,18 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (allocate_trunk(&g_cfg->trunk) != 0) { | ||||
| 	if (mgcp_endpoints_allocate(&g_cfg->trunk) != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Failed to initialize the virtual trunk.\n"); | ||||
| 		     "Failed to initialize the virtual trunk (%d endpoints)\n", | ||||
| 		     g_cfg->trunk.number_endpoints); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	llist_for_each_entry(trunk, &g_cfg->trunks, entry) { | ||||
| 		if (allocate_trunk(trunk) != 0) { | ||||
| 		if (mgcp_endpoints_allocate(trunk) != 0) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Failed to initialize E1 trunk %d.\n", | ||||
| 			     trunk->trunk_nr); | ||||
| 			     "Failed to initialize trunk %d (%d endpoints)\n", | ||||
| 			     trunk->trunk_nr, trunk->number_endpoints); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -8,6 +8,7 @@ AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOVTY_CFLAGS) \ | ||||
| 	$(LIBOSMONETIF_CFLAGS) \ | ||||
| 	$(LIBBCG729_CFLAGS) \ | ||||
| 	$(COVERAGE_CFLAGS) \ | ||||
| 	$(NULL) | ||||
| @@ -24,6 +25,7 @@ osmo_bsc_mgcp_LDADD = \ | ||||
| 	$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	$(LIBBCG729_LIBS) \ | ||||
| 	$(LIBRARY_GSM) \ | ||||
| 	$(NULL) | ||||
|   | ||||
| @@ -189,33 +189,33 @@ 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; | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -231,8 +231,8 @@ static const struct log_info_cat log_categories[] = { | ||||
| }; | ||||
|  | ||||
| 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) | ||||
| @@ -244,7 +244,7 @@ int main(int argc, char **argv) | ||||
| 	msgb_talloc_ctx_init(tall_bsc_ctx, 0); | ||||
|  | ||||
| 	osmo_init_ignore_signals(); | ||||
| 	osmo_init_logging(&log_info); | ||||
| 	osmo_init_logging2(tall_bsc_ctx, &log_info); | ||||
|  | ||||
| 	cfg = mgcp_config_alloc(); | ||||
| 	if (!cfg) | ||||
| @@ -282,8 +282,8 @@ int main(int argc, char **argv) | ||||
| 	/* set some callbacks */ | ||||
| 	cfg->reset_cb = mgcp_rsip_cb; | ||||
|  | ||||
|         /* we need to bind a socket */ | ||||
|         if (rc == 0) { | ||||
| 	/* 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); | ||||
|   | ||||
| @@ -8,6 +8,8 @@ AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOVTY_CFLAGS) \ | ||||
| 	$(LIBOSMOGSM_CFLAGS) \ | ||||
| 	$(LIBOSMONETIF_CFLAGS) \ | ||||
| 	$(COVERAGE_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| @@ -20,7 +22,9 @@ osmo_mgw_SOURCES = \ | ||||
| 	$(NULL) | ||||
|  | ||||
| osmo_mgw_LDADD = \ | ||||
| 	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \ | ||||
| 	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	$(NULL) | ||||
|   | ||||
| @@ -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 | ||||
| @@ -35,6 +36,8 @@ | ||||
| #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,12 +46,14 @@ | ||||
| #include <osmocom/core/stats.h> | ||||
| #include <osmocom/core/rate_ctr.h> | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/core/socket.h> | ||||
|  | ||||
| #include <osmocom/vty/telnet_interface.h> | ||||
| #include <osmocom/vty/logging.h> | ||||
| #include <osmocom/vty/ports.h> | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/vty/stats.h> | ||||
| #include <osmocom/vty/misc.h> | ||||
|  | ||||
| #include "../../bscconfig.h" | ||||
|  | ||||
| @@ -62,16 +67,16 @@ 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" | ||||
| 	"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n" | ||||
| 	"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" | ||||
| 	"Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\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; | ||||
| @@ -186,7 +191,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what) | ||||
| 		/* Walk over all endpoints and trigger a release, this will release all | ||||
| 		 * endpoints, possible open connections are forcefully dropped */ | ||||
| 		for (i = 1; i < reset_trunk->number_endpoints; ++i) | ||||
| 			mgcp_release_endp(&reset_trunk->endpoints[i]); | ||||
| 			mgcp_endp_release(&reset_trunk->endpoints[i]); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| @@ -194,33 +199,33 @@ 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; | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -233,31 +238,40 @@ 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; | ||||
|  | ||||
| 	vty_info.copyright = openbsc_copyright; | ||||
| 	vty_info.copyright = osmomgw_copyright; | ||||
| 	vty_init(&vty_info); | ||||
| 	logging_vty_add_cmds(NULL); | ||||
| 	osmo_talloc_vty_add_cmds(); | ||||
| 	osmo_stats_vty_add_cmds(&log_info); | ||||
| 	mgcp_vty_init(); | ||||
|  | ||||
| @@ -272,7 +286,7 @@ int main(int argc, char **argv) | ||||
|  | ||||
| 	/* start telnet after reading config for vty_get_bind_addr() */ | ||||
| 	rc = telnet_init_dynif(tall_bsc_ctx, NULL, | ||||
| 			       vty_get_bind_addr(), OSMO_VTY_PORT_BSC_MGCP); | ||||
| 			       vty_get_bind_addr(), OSMO_VTY_PORT_MGW); | ||||
| 	if (rc < 0) | ||||
| 		return rc; | ||||
|  | ||||
| @@ -280,54 +294,29 @@ int main(int argc, char **argv) | ||||
| 	 * 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)); | ||||
|  | ||||
|   | ||||
| @@ -268,7 +268,9 @@ static void test_strline(void) | ||||
| 		 "C: 2\r\n" | ||||
|  | ||||
| #define DLCX_RET "250 7 OK\r\n"			\ | ||||
| 		 "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" \ | ||||
| 		 "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" | ||||
|  | ||||
|  #define DLCX_RET_OSMUX DLCX_RET                 \ | ||||
| 		 "X-Osmo-CP: EC TIS=0, TOS=0, TIR=0, TOR=0\r\n" | ||||
|  | ||||
| #define RQNT	 "RQNT 186908780 1@mgw MGCP 1.0\r\n"	\ | ||||
| @@ -1212,8 +1214,9 @@ const struct log_info log_info = { | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	msgb_talloc_ctx_init(NULL, 0); | ||||
| 	osmo_init_logging(&log_info); | ||||
| 	void *ctx = talloc_named_const(NULL, 0, "mgcp_test"); | ||||
| 	void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0); | ||||
| 	osmo_init_logging2(ctx, &log_info); | ||||
|  | ||||
| 	test_strline(); | ||||
| 	test_values(); | ||||
| @@ -1231,6 +1234,9 @@ int main(int argc, char **argv) | ||||
| 	test_no_name(); | ||||
| 	test_osmux_cid(); | ||||
|  | ||||
| 	OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0); | ||||
| 	OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1); | ||||
| 	talloc_free(msgb_ctx); | ||||
| 	printf("Done\n"); | ||||
| 	return EXIT_SUCCESS; | ||||
| } | ||||
|   | ||||
| @@ -588,7 +588,8 @@ const struct log_info log_info = { | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	int rc; | ||||
| 	osmo_init_logging(&log_info); | ||||
| 	void *ctx = talloc_named_const(NULL, 0, "mgcp_transcoding_test"); | ||||
| 	osmo_init_logging2(ctx, &log_info); | ||||
|  | ||||
| 	printf("=== Transcoding Good Cases ===\n"); | ||||
|  | ||||
|   | ||||
| @@ -8,6 +8,8 @@ AM_CFLAGS = \ | ||||
| 	-Wall \ | ||||
| 	-ggdb3 \ | ||||
| 	$(LIBOSMOCORE_CFLAGS) \ | ||||
| 	$(LIBOSMOVTY_CFLAGS) \ | ||||
| 	$(LIBOSMOGSM_CFLAGS) \ | ||||
| 	$(LIBOSMONETIF_CFLAGS) \ | ||||
| 	$(COVERAGE_CFLAGS) \ | ||||
| 	$(NULL) | ||||
| @@ -29,9 +31,10 @@ mgcp_test_SOURCES = \ | ||||
| 	$(NULL) | ||||
|  | ||||
| mgcp_test_LDADD = \ | ||||
| 	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \ | ||||
| 	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(LIBRARY_DL) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	-lm  \ | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -11,87 +11,588 @@ line: 'mixed (4 lines)' | ||||
| line: '' | ||||
| line: '' | ||||
| line: '' | ||||
|  | ||||
| ================================================ | ||||
| Testing AUEP1 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing AUEP2 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX1 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX2 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing CRCX | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 1@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| L: p:20 | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 97 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
| Dummy packets: 2 | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-20 | ||||
| Connection mode: 1: RECV | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX3 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983215 1@mgw MGCP 1.0 | ||||
| I: %s | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
| Dummy packets: 2 | ||||
| Detected packet duration: 40 | ||||
| Requested packetization period not set | ||||
| Connection mode: 1: RECV | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX4 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983216 1@mgw MGCP 1.0 | ||||
| M: sendrecv | ||||
| C: 2 | ||||
| I: %s | ||||
| L: p:20, a:AMR, nt:IN | ||||
|  | ||||
| v=0 | ||||
| o=- %s 23 IN IP4 0.0.0.0 | ||||
| c=IN IP4 0.0.0.0 | ||||
| t=0 0 | ||||
| m=audio 4441 RTP/AVP 99 | ||||
| a=rtpmap:99 AMR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-20 | ||||
| Connection mode: 3: SEND RECV | ||||
| Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX4_PT1 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983217 1@mgw MGCP 1.0 | ||||
| M: sendrecv | ||||
| C: 2 | ||||
| I: %s | ||||
| L: p:20-40, a:AMR, nt:IN | ||||
|  | ||||
| v=0 | ||||
| o=- %s 23 IN IP4 0.0.0.0 | ||||
| c=IN IP4 0.0.0.0 | ||||
| t=0 0 | ||||
| m=audio 4441 RTP/AVP 99 | ||||
| a=rtpmap:99 AMR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-40 | ||||
| Connection mode: 3: SEND RECV | ||||
| (response does not contain a connection id) | ||||
| Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX4_PT2 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983218 1@mgw MGCP 1.0 | ||||
| M: sendrecv | ||||
| C: 2 | ||||
| I: %s | ||||
| L: p:20-20, a:AMR, nt:IN | ||||
|  | ||||
| v=0 | ||||
| o=- %s 23 IN IP4 0.0.0.0 | ||||
| c=IN IP4 0.0.0.0 | ||||
| t=0 0 | ||||
| m=audio 4441 RTP/AVP 99 | ||||
| a=rtpmap:99 AMR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-20 | ||||
| Connection mode: 3: SEND RECV | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
| Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX4_PT3 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983219 1@mgw MGCP 1.0 | ||||
| M: sendrecv | ||||
| C: 2 | ||||
| I: %s | ||||
| L: a:AMR, nt:IN | ||||
|  | ||||
| v=0 | ||||
| o=- %s 23 IN IP4 0.0.0.0 | ||||
| c=IN IP4 0.0.0.0 | ||||
| t=0 0 | ||||
| m=audio 4441 RTP/AVP 99 | ||||
| a=rtpmap:99 AMR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| Detected packet duration: 40 | ||||
| Requested packetization period not set | ||||
| Connection mode: 3: SEND RECV | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-20 | ||||
| Connection mode: 2: SEND | ||||
| Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX4_SO | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983220 1@mgw MGCP 1.0 | ||||
| M: sendonly | ||||
| C: 2 | ||||
| I: %s | ||||
| L: p:20, a:AMR, nt:IN | ||||
|  | ||||
| v=0 | ||||
| o=- %s 23 IN IP4 0.0.0.0 | ||||
| c=IN IP4 0.0.0.0 | ||||
| t=0 0 | ||||
| m=audio 4441 RTP/AVP 99 | ||||
| a=rtpmap:99 AMR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX4_RO | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983221 1@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| I: %s | ||||
| L: p:20, a:AMR, nt:IN | ||||
|  | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-20 | ||||
| Connection mode: 1: RECV | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
| Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| Testing DLCX | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| DLCX 7 1@mgw MGCP 1.0 | ||||
| I: %s | ||||
| C: 2 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing CRCX_ZYN | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 1@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| Detected packet duration: 20 | ||||
| Requested packetization period not set | ||||
| Connection mode: 1: RECV | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 97 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
| Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| Testing EMPTY | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
|  | ||||
|  | ||||
| ---------8<--------- | ||||
|  | ||||
| ================================================ | ||||
| Testing SHORT1 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX  | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing SHORT2 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 1 | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing SHORT3 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 1 1@mgw | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing SHORT4 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 1 1@mgw MGCP | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing RQNT1 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| RQNT 186908780 1@mgw MGCP 1.0 | ||||
| X: B244F267488 | ||||
| S: D/9 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing RQNT2 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| RQNT 186908781 1@mgw MGCP 1.0 | ||||
| X: ADD4F26746F | ||||
| R: D/[0-9#*](N), G/ft, fxr/t38 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing DLCX | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| DLCX 7 1@mgw MGCP 1.0 | ||||
| I: %s | ||||
| C: 2 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing CRCX | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 1@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| L: p:20 | ||||
|  | ||||
| v=0 | ||||
| Detected packet duration: 40 | ||||
| Requested packetetization period: 20-20 | ||||
| Connection mode: 1: RECV | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 97 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
| Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX3 | ||||
| Detected packet duration: 40 | ||||
| Requested packetization period not set | ||||
| Connection mode: 1: RECV | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983215 1@mgw MGCP 1.0 | ||||
| I: %s | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
| Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| Testing DLCX | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| DLCX 7 1@mgw MGCP 1.0 | ||||
| I: %s | ||||
| C: 2 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing CRCX | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 6@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
| Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| Testing CRCX | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 1@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| L: p:20 | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 97 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| Re-transmitting CRCX | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 1@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| L: p:20 | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 97 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
|  | ||||
| ================================================ | ||||
| Testing RQNT1 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| RQNT 186908780 1@mgw MGCP 1.0 | ||||
| X: B244F267488 | ||||
| S: D/9 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| Re-transmitting RQNT1 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| RQNT 186908780 1@mgw MGCP 1.0 | ||||
| X: B244F267488 | ||||
| S: D/9 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
|  | ||||
| ================================================ | ||||
| Testing RQNT2 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| RQNT 186908781 1@mgw MGCP 1.0 | ||||
| X: ADD4F26746F | ||||
| R: D/[0-9#*](N), G/ft, fxr/t38 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| Re-transmitting RQNT2 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| RQNT 186908781 1@mgw MGCP 1.0 | ||||
| X: ADD4F26746F | ||||
| R: D/[0-9#*](N), G/ft, fxr/t38 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX3 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983215 1@mgw MGCP 1.0 | ||||
| I: %s | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| Re-transmitting MDCX3 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983215 1@mgw MGCP 1.0 | ||||
| I: %s | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
|  | ||||
| ================================================ | ||||
| Testing DLCX | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| DLCX 7 1@mgw MGCP 1.0 | ||||
| I: %s | ||||
| C: 2 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| Re-transmitting DLCX | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| DLCX 7 1@mgw MGCP 1.0 | ||||
| I: %s | ||||
| C: 2 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| Testing packet loss calculation. | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 1@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| L: p:20 | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 97 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| RQNT 186908780 1@mgw MGCP 1.0 | ||||
| X: B244F267488 | ||||
| S: D/9 | ||||
|  | ||||
| ---------8<--------- | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| DLCX 7 1@mgw MGCP 1.0 | ||||
| I: %s | ||||
| C: 2 | ||||
|  | ||||
| ---------8<--------- | ||||
| Testing stat parsing | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| 250 7 OK | ||||
| P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0 | ||||
|  | ||||
| @@ -466,6 +967,170 @@ In TS: 160320, dTS: 160, Seq: 1002 | ||||
| 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 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 1@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| X | ||||
| L: p:20 | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 18 97 | ||||
| a=rtpmap:18 G729/8000 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 2@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| X | ||||
| L: p:20 | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 18 97 101 | ||||
| a=rtpmap:18 G729/8000 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| a=rtpmap:101 FOO/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 3@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| X | ||||
| L: p:20 | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP | ||||
| a=rtpmap:18 G729/8000 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| a=rtpmap:101 FOO/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 4@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| X | ||||
| L: p:20 | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 18 | ||||
| a=rtpmap:18 G729/8000 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| a=rtpmap:101 FOO/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 259260421 5@mgw MGCP 1.0 | ||||
| C: 1355c6041e | ||||
| L: p:20, a:GSM, nt:IN | ||||
| M: recvonly | ||||
|  | ||||
| v=0 | ||||
| o=- 1439038275 1439038275 IN IP4 192.168.181.247 | ||||
| s=- | ||||
| c=IN IP4 192.168.181.247 | ||||
| t=0 0 | ||||
| m=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101 | ||||
| a=rtpmap:0 PCMU/8000 | ||||
| a=rtpmap:8 PCMA/8000 | ||||
| a=rtpmap:3 gsm/8000 | ||||
| a=rtpmap:18 G729/8000 | ||||
| a=fmtp:18 annexb=no | ||||
| a=rtpmap:4 G723/8000 | ||||
| a=rtpmap:96 iLBC/8000 | ||||
| a=fmtp:96 mode=20 | ||||
| a=rtpmap:97 iLBC/8000 | ||||
| a=fmtp:97 mode=30 | ||||
| a=rtpmap:101 telephone-event/8000 | ||||
| a=fmtp:101 0-15 | ||||
| a=recvonly | ||||
|  | ||||
| ---------8<--------- | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 23 5@mgw MGCP 1.0 | ||||
| C: 1355c6041e | ||||
| I: %s | ||||
|  | ||||
| c=IN IP4 8.8.8.8 | ||||
| m=audio 16434 RTP/AVP 3 | ||||
|  | ||||
| ---------8<--------- | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 259260421 5@mgw MGCP 1.0 | ||||
| C: 1355c6041e | ||||
| L: p:20, a:GSM, nt:IN | ||||
| M: recvonly | ||||
|  | ||||
| v=0 | ||||
| o=- 1439038275 1439038275 IN IP4 192.168.181.247 | ||||
| s=- | ||||
| c=IN IP4 192.168.181.247 | ||||
| t=0 0 | ||||
| m=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101 | ||||
| a=rtpmap:0 PCMU/8000 | ||||
| a=rtpmap:8 PCMA/8000 | ||||
| a=rtpmap:3 gsm/8000 | ||||
| a=rtpmap:18 G729/8000 | ||||
| a=fmtp:18 annexb=no | ||||
| a=rtpmap:4 G723/8000 | ||||
| a=rtpmap:96 iLBC/8000 | ||||
| a=fmtp:96 mode=20 | ||||
| a=rtpmap:97 iLBC/8000 | ||||
| a=fmtp:97 mode=30 | ||||
| a=rtpmap:101 telephone-event/8000 | ||||
| a=fmtp:101 0-15 | ||||
| a=recvonly | ||||
|  | ||||
| ---------8<--------- | ||||
| Testing no sequence flow on initial packet | ||||
| Testing no rtpmap name | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 1@mgw MGCP 1.0 | ||||
| M: recvonly | ||||
| C: 2 | ||||
| L: p:20 | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 97 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| Testing get_lco_identifier() | ||||
| p:10, a:PCMU -> p:10, a:PCMU | ||||
| p:10, a:PCMU -> p:10, a:PCMU | ||||
| 'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU' | ||||
|   | ||||
| @@ -37,3 +37,6 @@ mgcp_client_test_LDADD = \ | ||||
| 	$(LIBRARY_DL) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| update_exp: | ||||
| 	$(builddir)/mgcp_client_test >$(srcdir)/mgcp_client_test.ok 2>$(srcdir)/mgcp_client_test.err | ||||
|   | ||||
| @@ -18,12 +18,15 @@ | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | ||||
|  | ||||
| #include <string.h> | ||||
|  | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_internal.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| void *ctx; | ||||
|  | ||||
| @@ -46,14 +49,14 @@ static struct msgb *mgcp_from_str(const char *head, const char *params) | ||||
| 	l = strlen(head); | ||||
| 	msg->l2h = msgb_put(msg, l); | ||||
| 	data = (char*)msgb_l2(msg); | ||||
| 	strncpy(data, head, l); | ||||
| 	osmo_strlcpy(data, head, l); | ||||
|  | ||||
| 	data = (char*)msgb_put(msg, 1); | ||||
| 	*data = '\n'; | ||||
|  | ||||
| 	l = strlen(params); | ||||
| 	data = (char*)msgb_put(msg, l); | ||||
| 	strncpy(data, params, l); | ||||
| 	osmo_strlcpy(data, params, l); | ||||
|  | ||||
| 	return msg; | ||||
| } | ||||
| @@ -66,14 +69,14 @@ static struct msgb *from_str(const char *str) | ||||
| 	char *data; | ||||
| 	msg->l2h = msgb_put(msg, l); | ||||
| 	data = (char*)msgb_l2(msg); | ||||
| 	strncpy(data, str, l); | ||||
| 	osmo_strlcpy(data, str, l); | ||||
| 	return msg; | ||||
| } | ||||
|  | ||||
| static struct mgcp_client_conf conf; | ||||
| struct mgcp_client *mgcp = NULL; | ||||
|  | ||||
| static void reply_to(mgcp_trans_id_t trans_id, int code, const char *comment, | ||||
| static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment, | ||||
| 		     int conn_id, const char *params) | ||||
| { | ||||
| 	static char compose[4096 - 128]; | ||||
| @@ -87,24 +90,31 @@ static void reply_to(mgcp_trans_id_t trans_id, int code, const char *comment, | ||||
|  | ||||
| 	printf("composed response:\n-----\n%s\n-----\n", | ||||
| 	       compose); | ||||
| 	mgcp_client_rx(mgcp, from_str(compose)); | ||||
| 	return mgcp_client_rx(mgcp, from_str(compose)); | ||||
| } | ||||
|  | ||||
| void test_response_cb(struct mgcp_response *response, void *priv) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	OSMO_ASSERT(priv == mgcp); | ||||
| 	mgcp_response_parse_params(response); | ||||
|  | ||||
| 	printf("response cb received:\n" | ||||
| 	       "  head.response_code = %d\n" | ||||
| 	       "  head.trans_id = %u\n" | ||||
| 	       "  head.comment = %s\n" | ||||
| 	       "  audio_port = %u\n", | ||||
| 	       response->head.response_code, | ||||
| 	       response->head.trans_id, | ||||
| 	       response->head.comment, | ||||
| 	       response->audio_port | ||||
| 	      ); | ||||
| 	printf("response cb received:\n"); | ||||
| 	printf("  head.response_code = %d\n", response->head.response_code); | ||||
| 	printf("  head.trans_id = %u\n", response->head.trans_id); | ||||
| 	printf("  head.comment = %s\n", response->head.comment); | ||||
| 	printf("  audio_port = %u\n", response->audio_port); | ||||
| 	printf("  audio_ip = %s\n", response->audio_ip); | ||||
| 	printf("  ptime = %u\n", response->ptime); | ||||
| 	printf("  codecs_len = %u\n", response->codecs_len); | ||||
| 	for(i=0;i<response->codecs_len;i++) | ||||
| 		printf("  codecs[%u] = %u\n", i, response->codecs[i]); | ||||
| 	printf("  ptmap_len = %u\n", response->ptmap_len); | ||||
| 	for(i=0;i<response->ptmap_len;i++) { | ||||
| 		printf("  ptmap[%u].codec = %u\n", i, response->ptmap[i].codec); | ||||
| 		printf("  ptmap[%u].pt = %u\n", i, response->ptmap[i].pt);		 | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
|  | ||||
| mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg) | ||||
| @@ -144,11 +154,380 @@ void test_crcx(void) | ||||
| 		"s=-\r\n" | ||||
| 		"c=IN IP4 10.9.1.120\r\n" | ||||
| 		"t=0 0\r\n" | ||||
| 		"m=audio 16002 RTP/AVP 98\r\n" | ||||
| 		"a=rtpmap:98 AMR/8000\r\n" | ||||
| 		"m=audio 16002 RTP/AVP 110 96\r\n" | ||||
| 		"a=rtpmap:110 AMR/8000\r\n" | ||||
| 		"a=rtpmap:96 GSM-EFR/8000\r\n" | ||||
| 		"a=ptime:20\r\n"); | ||||
| } | ||||
|  | ||||
| void test_mgcp_msg(void) | ||||
| { | ||||
| 	struct msgb *msg; | ||||
| 	char audio_ip_overflow[5000]; | ||||
|  | ||||
| 	/* A message struct prefilled with some arbitary values */ | ||||
| 	struct mgcp_msg mgcp_msg = { | ||||
| 		.audio_ip = "192.168.100.23", | ||||
| 		.endpoint = "23@mgw", | ||||
| 		.audio_port = 1234, | ||||
| 		.call_id = 47, | ||||
| 		.conn_id = "11", | ||||
| 		.conn_mode = MGCP_CONN_RECV_SEND, | ||||
| 		.ptime = 20, | ||||
| 		.codecs[0] = CODEC_GSM_8000_1, | ||||
| 		.codecs[1] = CODEC_AMR_8000_1, | ||||
| 		.codecs[2] = CODEC_GSMEFR_8000_1, | ||||
| 		.codecs_len = 1, | ||||
| 		.ptmap[0].codec = CODEC_GSMEFR_8000_1, | ||||
| 		.ptmap[0].pt = 96, | ||||
| 		.ptmap_len = 1 | ||||
| 	}; | ||||
|  | ||||
| 	if (mgcp) | ||||
| 		talloc_free(mgcp); | ||||
| 	mgcp = mgcp_client_init(ctx, &conf); | ||||
|  | ||||
| 	printf("\n"); | ||||
|  | ||||
| 	printf("Generated CRCX message:\n"); | ||||
| 	mgcp_msg.verb = MGCP_VERB_CRCX; | ||||
| 	mgcp_msg.presence = | ||||
| 	    (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | | ||||
| 	     MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE); | ||||
| 	msg = mgcp_msg_gen(mgcp, &mgcp_msg); | ||||
| 	printf("%s\n", (char *)msg->data); | ||||
|  | ||||
| 	printf("Generated CRCX message (two codecs):\n"); | ||||
| 	mgcp_msg.verb = MGCP_VERB_CRCX; | ||||
| 	mgcp_msg.presence = | ||||
| 	    (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | | ||||
| 	     MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE); | ||||
| 	mgcp_msg.codecs_len = 2; | ||||
| 	msg = mgcp_msg_gen(mgcp, &mgcp_msg); | ||||
| 	mgcp_msg.codecs_len = 1;	 | ||||
| 	printf("%s\n", (char *)msg->data); | ||||
|  | ||||
| 	printf("Generated CRCX message (three codecs, one with custom pt):\n"); | ||||
| 	mgcp_msg.verb = MGCP_VERB_CRCX; | ||||
| 	mgcp_msg.presence = | ||||
| 	    (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | | ||||
| 	     MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE); | ||||
| 	mgcp_msg.codecs_len = 3; | ||||
| 	msg = mgcp_msg_gen(mgcp, &mgcp_msg); | ||||
| 	mgcp_msg.codecs_len = 1;	 | ||||
| 	printf("%s\n", (char *)msg->data);		 | ||||
|  | ||||
| 	printf("Generated MDCX message:\n"); | ||||
| 	mgcp_msg.verb = MGCP_VERB_MDCX; | ||||
| 	mgcp_msg.presence = | ||||
| 	    (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); | ||||
| 	msg = mgcp_msg_gen(mgcp, &mgcp_msg); | ||||
| 	printf("%s\n", (char *)msg->data); | ||||
|  | ||||
| 	printf("Generated MDCX message (two codecs):\n"); | ||||
| 	mgcp_msg.verb = MGCP_VERB_MDCX; | ||||
| 	mgcp_msg.presence = | ||||
| 	    (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | | ||||
| 	     MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE | | ||||
| 	     MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT); | ||||
| 	mgcp_msg.codecs_len = 2; | ||||
| 	msg = mgcp_msg_gen(mgcp, &mgcp_msg); | ||||
| 	mgcp_msg.codecs_len = 1;	 | ||||
| 	printf("%s\n", (char *)msg->data); | ||||
|  | ||||
| 	printf("Generated MDCX message (three codecs, one with custom pt):\n"); | ||||
| 	mgcp_msg.verb = MGCP_VERB_MDCX; | ||||
| 	mgcp_msg.presence = | ||||
| 	    (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | | ||||
| 	     MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE | | ||||
| 	     MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT); | ||||
| 	mgcp_msg.codecs_len = 3; | ||||
| 	msg = mgcp_msg_gen(mgcp, &mgcp_msg); | ||||
| 	mgcp_msg.codecs_len = 1;	 | ||||
| 	printf("%s\n", (char *)msg->data);	 | ||||
|  | ||||
| 	printf("Generated DLCX message:\n"); | ||||
| 	mgcp_msg.verb = MGCP_VERB_DLCX; | ||||
| 	mgcp_msg.presence = | ||||
| 	    (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | | ||||
| 	     MGCP_MSG_PRESENCE_CONN_ID); | ||||
| 	msg = mgcp_msg_gen(mgcp, &mgcp_msg); | ||||
| 	printf("%s\n", (char *)msg->data); | ||||
|  | ||||
| 	printf("Generated AUEP message:\n"); | ||||
| 	mgcp_msg.verb = MGCP_VERB_AUEP; | ||||
| 	mgcp_msg.presence = (MGCP_MSG_PRESENCE_ENDPOINT); | ||||
| 	msg = mgcp_msg_gen(mgcp, &mgcp_msg); | ||||
| 	printf("%s\n", msg->data); | ||||
|  | ||||
| 	printf("Generated RSIP message:\n"); | ||||
| 	mgcp_msg.verb = MGCP_VERB_RSIP; | ||||
| 	mgcp_msg.presence = (MGCP_MSG_PRESENCE_ENDPOINT); | ||||
| 	msg = mgcp_msg_gen(mgcp, &mgcp_msg); | ||||
| 	printf("%s\n", (char *)msg->data); | ||||
|  | ||||
| 	printf("Overfolow test:\n"); | ||||
| 	mgcp_msg.verb = MGCP_VERB_MDCX; | ||||
| 	mgcp_msg.presence = | ||||
| 	    (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | | ||||
| 	     MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE | | ||||
| 	     MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT); | ||||
| 	memset(audio_ip_overflow, 'X', sizeof(audio_ip_overflow)); | ||||
| 	audio_ip_overflow[sizeof(audio_ip_overflow) - 1] = '\0'; | ||||
| 	mgcp_msg.audio_ip = audio_ip_overflow; | ||||
| 	msg = mgcp_msg_gen(mgcp, &mgcp_msg); | ||||
| 	OSMO_ASSERT(msg == NULL); | ||||
|  | ||||
| 	printf("\n"); | ||||
| 	msgb_free(msg); | ||||
| } | ||||
|  | ||||
| void test_mgcp_client_cancel() | ||||
| { | ||||
| 	mgcp_trans_id_t trans_id; | ||||
| 	struct msgb *msg; | ||||
| 	struct mgcp_msg mgcp_msg = { | ||||
| 		.verb = MGCP_VERB_CRCX, | ||||
| 		.audio_ip = "192.168.100.23", | ||||
| 		.endpoint = "23@mgw", | ||||
| 		.audio_port = 1234, | ||||
| 		.call_id = 47, | ||||
| 		.conn_id = "11", | ||||
| 		.conn_mode = MGCP_CONN_RECV_SEND, | ||||
| 		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | ||||
| 			     | MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE), | ||||
| 		.ptime = 20, | ||||
| 		.codecs[0] = CODEC_AMR_8000_1, | ||||
| 		.codecs_len = 1		 | ||||
| 	}; | ||||
|  | ||||
| 	printf("\n%s():\n", __func__); | ||||
| 	fprintf(stderr, "\n%s():\n", __func__); | ||||
|  | ||||
| 	if (mgcp) | ||||
| 		talloc_free(mgcp); | ||||
| 	mgcp = mgcp_client_init(ctx, &conf); | ||||
|  | ||||
| 	msg = mgcp_msg_gen(mgcp, &mgcp_msg); | ||||
| 	trans_id = mgcp_msg_trans_id(msg); | ||||
| 	fprintf(stderr, "- composed msg with trans_id=%u\n", trans_id); | ||||
|  | ||||
| 	fprintf(stderr, "- not in queue yet, cannot cancel yet\n"); | ||||
| 	OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == -ENOENT); | ||||
|  | ||||
| 	fprintf(stderr, "- enqueue\n"); | ||||
| 	dummy_mgcp_send(msg); | ||||
|  | ||||
| 	fprintf(stderr, "- cancel succeeds\n"); | ||||
| 	OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == 0); | ||||
|  | ||||
| 	fprintf(stderr, "- late response gets discarded\n"); | ||||
| 	OSMO_ASSERT(reply_to(trans_id, 200, "OK", 1, "v=0\r\n") == -ENOENT); | ||||
|  | ||||
| 	fprintf(stderr, "- canceling again does nothing\n"); | ||||
| 	OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == -ENOENT); | ||||
|  | ||||
| 	fprintf(stderr, "%s() done\n", __func__); | ||||
| } | ||||
|  | ||||
| struct sdp_section_start_test { | ||||
| 	const char *body; | ||||
| 	int expect_rc; | ||||
| 	struct mgcp_response expect_params; | ||||
| }; | ||||
|  | ||||
| static struct sdp_section_start_test sdp_section_start_tests[] = { | ||||
| 	{ | ||||
| 		.body = "", | ||||
| 		.expect_rc = -EINVAL, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.body = "\n\n", | ||||
| 	}, | ||||
| 	{ | ||||
| 		.body = "\r\n\r\n", | ||||
| 	}, | ||||
| 	{ | ||||
| 		.body = "\n\r\n\r", | ||||
| 	}, | ||||
| 	{ | ||||
| 		.body = "some mgcp header data\r\nand header params" | ||||
| 			"\n\n" | ||||
| 			"m=audio 23\r\n", | ||||
| 		.expect_params = { | ||||
| 			.audio_port = 23, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.body = "some mgcp header data\r\nand header params" | ||||
| 			"\r\n\r\n" | ||||
| 			"m=audio 23\r\n", | ||||
| 		.expect_params = { | ||||
| 			.audio_port = 23, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.body = "some mgcp header data\r\nand header params" | ||||
| 			"\n\r\n\r" | ||||
| 			"m=audio 23\r\n", | ||||
| 		.expect_params = { | ||||
| 			.audio_port = 23, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.body = "some mgcp header data\r\nand header params" | ||||
| 			"\n\r\n" | ||||
| 			"m=audio 23\r\n", | ||||
| 		.expect_rc = -EINVAL, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.body = "some mgcp header data\r\nand header params" | ||||
| 			"\r\n\r" | ||||
| 			"m=audio 23\r\n", | ||||
| 		.expect_rc = -EINVAL, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.body = "some mgcp header data\r\nand header params" | ||||
| 			"\n\r\r" | ||||
| 			"m=audio 23\r\n", | ||||
| 		.expect_rc = -EINVAL, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| void test_sdp_section_start() | ||||
| { | ||||
| 	int i; | ||||
| 	int failures = 0; | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(sdp_section_start_tests); i++) { | ||||
| 		int rc; | ||||
| 		struct sdp_section_start_test *t = &sdp_section_start_tests[i]; | ||||
| 		struct mgcp_response *r = talloc_zero(ctx, struct mgcp_response); | ||||
|  | ||||
| 		r->body = talloc_strdup(r, t->body); | ||||
|  | ||||
| 		printf("\n%s() test [%d]:\n", __func__, i); | ||||
| 		fprintf(stderr, "\n%s() test [%d]:\n", __func__, i); | ||||
| 		fprintf(stderr, "body: \"%s\"\n", osmo_escape_str(r->body, -1)); | ||||
|  | ||||
| 		rc = mgcp_response_parse_params(r); | ||||
|  | ||||
| 		fprintf(stderr, "got rc=%d\n", rc); | ||||
| 		if (rc != t->expect_rc) { | ||||
| 			fprintf(stderr, "FAIL: Expected rc=%d\n", t->expect_rc); | ||||
| 			failures++; | ||||
| 		} | ||||
| 		if (rc) { | ||||
| 			talloc_free(r); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		fprintf(stderr, "got audio_port=%u\n", t->expect_params.audio_port); | ||||
| 		if (r->audio_port != t->expect_params.audio_port) { | ||||
| 			fprintf(stderr, "FAIL: Expected audio_port=%u\n", t->expect_params.audio_port); | ||||
| 			failures++; | ||||
| 		} | ||||
| 		talloc_free(r); | ||||
| 	} | ||||
|  | ||||
| 	OSMO_ASSERT(!failures); | ||||
| } | ||||
|  | ||||
| static void test_map_pt_to_codec(void) | ||||
| { | ||||
| 	/* Full form */ | ||||
| 	OSMO_ASSERT(map_str_to_codec("PCMU/8000/1") == CODEC_PCMU_8000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("GSM/8000/1") == CODEC_GSM_8000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("PCMA/8000/1") == CODEC_PCMA_8000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("G729/8000/1") == CODEC_G729_8000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("GSM-EFR/8000/1") == CODEC_GSMEFR_8000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("GSM-HR-08/8000/1") == CODEC_GSMHR_8000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("AMR/8000/1") == CODEC_AMR_8000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1") == CODEC_AMRWB_16000_1); | ||||
|  | ||||
| 	/* Short form */ | ||||
| 	OSMO_ASSERT(map_str_to_codec("GSM-EFR") == CODEC_GSMEFR_8000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("G729") == CODEC_G729_8000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("GSM-HR-08") == CODEC_GSMHR_8000_1); | ||||
|  | ||||
| 	/* We do not care about what is after the first delimiter */ | ||||
| 	OSMO_ASSERT(map_str_to_codec("AMR-WB/123///456") == CODEC_AMRWB_16000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("PCMA/asdf") == CODEC_PCMA_8000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("GSM/qwertz") == CODEC_GSM_8000_1); | ||||
|  | ||||
| 	/* A trailing delimiter should not hurt */ | ||||
| 	OSMO_ASSERT(map_str_to_codec("AMR/") == CODEC_AMR_8000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("G729/") == CODEC_G729_8000_1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("GSM/") == CODEC_GSM_8000_1); | ||||
|  | ||||
| 	/* This is expected to fail */ | ||||
| 	OSMO_ASSERT(map_str_to_codec("INVALID/1234/7") == -1); | ||||
| 	OSMO_ASSERT(map_str_to_codec(NULL) == -1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("") == -1); | ||||
| 	OSMO_ASSERT(map_str_to_codec("/////") == -1); | ||||
|  | ||||
| 	/* The buffers are 64 bytes long, check what happens with overlong | ||||
| 	 * strings as input (This schould still work.) */ | ||||
| 	OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1############################################################################################################") == CODEC_AMRWB_16000_1); | ||||
|  | ||||
| 	/* This should not work, as there is no delimiter after the codec | ||||
| 	 * name */ | ||||
| 	OSMO_ASSERT(map_str_to_codec("AMR-WB####################################################################################################################") == -1); | ||||
| } | ||||
|  | ||||
| static void test_map_codec_to_pt_and_map_pt_to_codec(void) | ||||
| { | ||||
| 	struct ptmap ptmap[10]; | ||||
| 	unsigned int ptmap_len; | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	ptmap[0].codec = CODEC_GSMEFR_8000_1; | ||||
| 	ptmap[0].pt = 96; | ||||
| 	ptmap[1].codec = CODEC_GSMHR_8000_1; | ||||
| 	ptmap[1].pt = 97; | ||||
| 	ptmap[2].codec = CODEC_AMR_8000_1; | ||||
| 	ptmap[2].pt = 98; | ||||
| 	ptmap[3].codec = CODEC_AMRWB_16000_1; | ||||
| 	ptmap[3].pt = 99; | ||||
| 	ptmap_len = 4; | ||||
|  | ||||
| 	/* Mappings that are covered by the table */ | ||||
| 	for (i = 0; i < ptmap_len; i++) | ||||
| 		printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec)); | ||||
| 	for (i = 0; i < ptmap_len; i++) | ||||
| 		printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt)); | ||||
| 	printf("\n"); | ||||
|  | ||||
| 	/* Map some codecs/payload types from the static range, result must | ||||
| 	 * always be a 1:1 mapping */ | ||||
| 	printf(" %u => %u\n", CODEC_PCMU_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMU_8000_1)); | ||||
| 	printf(" %u => %u\n", CODEC_GSM_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_GSM_8000_1)); | ||||
| 	printf(" %u => %u\n", CODEC_PCMA_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMA_8000_1)); | ||||
| 	printf(" %u => %u\n", CODEC_G729_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_G729_8000_1)); | ||||
| 	printf(" %u <= %u\n", CODEC_PCMU_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMU_8000_1)); | ||||
| 	printf(" %u <= %u\n", CODEC_GSM_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_GSM_8000_1)); | ||||
| 	printf(" %u <= %u\n", CODEC_PCMA_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMA_8000_1)); | ||||
| 	printf(" %u <= %u\n", CODEC_G729_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_G729_8000_1)); | ||||
| 	printf("\n"); | ||||
|  | ||||
| 	/* Try to do mappings from statically defined range to danymic range and vice versa. This | ||||
| 	 * is illegal and should result into a 1:1 mapping */ | ||||
| 	ptmap[3].codec = CODEC_AMRWB_16000_1; | ||||
| 	ptmap[3].pt = 2; | ||||
| 	ptmap[4].codec = CODEC_PCMU_8000_1; | ||||
| 	ptmap[4].pt = 100; | ||||
| 	ptmap_len = 5; | ||||
|  | ||||
| 	/* Apply all mappings again, the illegal ones we defined should result into 1:1 mappings */ | ||||
| 	for (i = 0; i < ptmap_len; i++) | ||||
| 		printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec)); | ||||
| 	for (i = 0; i < ptmap_len; i++) | ||||
| 		printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt)); | ||||
| 	printf("\n"); | ||||
| } | ||||
|  | ||||
| static const struct log_info_cat log_categories[] = { | ||||
| }; | ||||
|  | ||||
| @@ -162,11 +541,22 @@ int main(int argc, char **argv) | ||||
| { | ||||
| 	ctx = talloc_named_const(NULL, 1, "mgcp_client_test"); | ||||
| 	msgb_talloc_ctx_init(ctx, 0); | ||||
| 	osmo_init_logging(&log_info); | ||||
| 	osmo_init_logging2(ctx, &log_info); | ||||
| 	log_set_print_filename(osmo_stderr_target, 0); | ||||
| 	log_set_print_timestamp(osmo_stderr_target, 0); | ||||
| 	log_set_use_color(osmo_stderr_target, 0); | ||||
| 	log_set_print_category(osmo_stderr_target, 1); | ||||
|  | ||||
| 	log_set_category_filter(osmo_stderr_target, DLMGCP, 1, LOGL_DEBUG); | ||||
|  | ||||
| 	mgcp_client_conf_init(&conf); | ||||
|  | ||||
| 	test_crcx(); | ||||
| 	test_mgcp_msg(); | ||||
| 	test_mgcp_client_cancel(); | ||||
| 	test_sdp_section_start(); | ||||
| 	test_map_codec_to_pt_and_map_pt_to_codec(); | ||||
| 	test_map_pt_to_codec(); | ||||
|  | ||||
| 	printf("Done\n"); | ||||
| 	fprintf(stderr, "Done\n"); | ||||
|   | ||||
| @@ -1 +1,69 @@ | ||||
| DLMGCP message buffer to small, can not generate MGCP message | ||||
|  | ||||
| test_mgcp_client_cancel(): | ||||
| - composed msg with trans_id=1 | ||||
| - not in queue yet, cannot cancel yet | ||||
| DLMGCP Cannot cancel, no such transaction: 1 | ||||
| - enqueue | ||||
| - cancel succeeds | ||||
| DLMGCP Canceled transaction 1 | ||||
| - late response gets discarded | ||||
| DLMGCP Cannot find matching MGCP transaction for trans_id 1 | ||||
| - canceling again does nothing | ||||
| DLMGCP Cannot cancel, no such transaction: 1 | ||||
| test_mgcp_client_cancel() done | ||||
|  | ||||
| test_sdp_section_start() test [0]: | ||||
| body: "" | ||||
| DLMGCP MGCP response: cannot find start of SDP parameters | ||||
| got rc=-22 | ||||
|  | ||||
| test_sdp_section_start() test [1]: | ||||
| body: "\n\n" | ||||
| got rc=0 | ||||
| got audio_port=0 | ||||
|  | ||||
| test_sdp_section_start() test [2]: | ||||
| body: "\r\n\r\n" | ||||
| got rc=0 | ||||
| got audio_port=0 | ||||
|  | ||||
| test_sdp_section_start() test [3]: | ||||
| body: "\n\r\n\r" | ||||
| got rc=0 | ||||
| got audio_port=0 | ||||
|  | ||||
| test_sdp_section_start() test [4]: | ||||
| body: "some mgcp header data\r\nand header params\n\nm=audio 23\r\n" | ||||
| got rc=0 | ||||
| got audio_port=23 | ||||
|  | ||||
| test_sdp_section_start() test [5]: | ||||
| body: "some mgcp header data\r\nand header params\r\n\r\nm=audio 23\r\n" | ||||
| got rc=0 | ||||
| got audio_port=23 | ||||
|  | ||||
| test_sdp_section_start() test [6]: | ||||
| body: "some mgcp header data\r\nand header params\n\r\n\rm=audio 23\r\n" | ||||
| got rc=0 | ||||
| got audio_port=23 | ||||
|  | ||||
| test_sdp_section_start() test [7]: | ||||
| body: "some mgcp header data\r\nand header params\n\r\nm=audio 23\r\n" | ||||
| DLMGCP MGCP response: cannot find start of SDP parameters | ||||
| got rc=-22 | ||||
|  | ||||
| test_sdp_section_start() test [8]: | ||||
| body: "some mgcp header data\r\nand header params\r\n\rm=audio 23\r\n" | ||||
| DLMGCP MGCP response: cannot find start of SDP parameters | ||||
| got rc=-22 | ||||
|  | ||||
| test_sdp_section_start() test [9]: | ||||
| body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n" | ||||
| DLMGCP MGCP response: cannot find start of SDP parameters | ||||
| got rc=-22 | ||||
| DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2 | ||||
| DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100 | ||||
| DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2 | ||||
| DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100 | ||||
| Done | ||||
|   | ||||
| @@ -18,8 +18,9 @@ o=- 1 23 IN IP4 10.9.1.120 | ||||
| s=- | ||||
| c=IN IP4 10.9.1.120 | ||||
| t=0 0 | ||||
| m=audio 16002 RTP/AVP 98 | ||||
| a=rtpmap:98 AMR/8000 | ||||
| m=audio 16002 RTP/AVP 110 96 | ||||
| a=rtpmap:110 AMR/8000 | ||||
| a=rtpmap:96 GSM-EFR/8000 | ||||
| a=ptime:20 | ||||
|  | ||||
| ----- | ||||
| @@ -28,4 +29,162 @@ response cb received: | ||||
|   head.trans_id = 1 | ||||
|   head.comment = OK | ||||
|   audio_port = 16002 | ||||
|   audio_ip = 10.9.1.120 | ||||
|   ptime = 20 | ||||
|   codecs_len = 2 | ||||
|   codecs[0] = 112 | ||||
|   codecs[1] = 110 | ||||
|   ptmap_len = 2 | ||||
|   ptmap[0].codec = 112 | ||||
|   ptmap[0].pt = 110 | ||||
|   ptmap[1].codec = 110 | ||||
|   ptmap[1].pt = 96 | ||||
|  | ||||
| Generated CRCX message: | ||||
| CRCX 1 23@mgw MGCP 1.0 | ||||
| C: 2f | ||||
| I: 11 | ||||
| L: p:20, a:GSM, nt:IN | ||||
| M: sendrecv | ||||
|  | ||||
| Generated CRCX message (two codecs): | ||||
| CRCX 2 23@mgw MGCP 1.0 | ||||
| C: 2f | ||||
| I: 11 | ||||
| L: p:20, a:GSM;AMR, nt:IN | ||||
| M: sendrecv | ||||
|  | ||||
| Generated CRCX message (three codecs, one with custom pt): | ||||
| CRCX 3 23@mgw MGCP 1.0 | ||||
| C: 2f | ||||
| I: 11 | ||||
| L: p:20, a:GSM;AMR;GSM-EFR, nt:IN | ||||
| M: sendrecv | ||||
|  | ||||
| Generated MDCX message: | ||||
| MDCX 4 23@mgw MGCP 1.0 | ||||
| C: 2f | ||||
| I: 11 | ||||
| M: sendrecv | ||||
|  | ||||
| v=0 | ||||
| o=- 2f 23 IN IP4 127.0.0.1 | ||||
| s=- | ||||
| c=IN IP4 192.168.100.23 | ||||
| t=0 0 | ||||
| m=audio 1234 RTP/AVP 3 | ||||
| a=ptime:20 | ||||
|  | ||||
| Generated MDCX message (two codecs): | ||||
| MDCX 5 23@mgw MGCP 1.0 | ||||
| C: 2f | ||||
| I: 11 | ||||
| M: sendrecv | ||||
|  | ||||
| v=0 | ||||
| o=- 2f 23 IN IP4 127.0.0.1 | ||||
| s=- | ||||
| c=IN IP4 192.168.100.23 | ||||
| t=0 0 | ||||
| m=audio 1234 RTP/AVP 3 112 | ||||
| a=rtpmap:112 AMR/8000/1 | ||||
| a=ptime:20 | ||||
|  | ||||
| Generated MDCX message (three codecs, one with custom pt): | ||||
| MDCX 6 23@mgw MGCP 1.0 | ||||
| C: 2f | ||||
| I: 11 | ||||
| M: sendrecv | ||||
|  | ||||
| v=0 | ||||
| o=- 2f 23 IN IP4 127.0.0.1 | ||||
| s=- | ||||
| c=IN IP4 192.168.100.23 | ||||
| t=0 0 | ||||
| m=audio 1234 RTP/AVP 3 112 96 | ||||
| a=rtpmap:112 AMR/8000/1 | ||||
| a=rtpmap:96 GSM-EFR/8000/1 | ||||
| a=ptime:20 | ||||
|  | ||||
| Generated DLCX message: | ||||
| DLCX 7 23@mgw MGCP 1.0 | ||||
| C: 2f | ||||
| I: 11 | ||||
|  | ||||
| Generated AUEP message: | ||||
| AUEP 8 23@mgw MGCP 1.0 | ||||
|  | ||||
| Generated RSIP message: | ||||
| RSIP 9 23@mgw MGCP 1.0 | ||||
|  | ||||
| Overfolow test: | ||||
|  | ||||
|  | ||||
| test_mgcp_client_cancel(): | ||||
| composed: | ||||
| ----- | ||||
| CRCX 1 23@mgw MGCP 1.0 | ||||
| C: 2f | ||||
| I: 11 | ||||
| L: p:20, a:AMR, nt:IN | ||||
| M: sendrecv | ||||
|  | ||||
| ----- | ||||
| composed response: | ||||
| ----- | ||||
| 200 1 OK | ||||
| I: 1 | ||||
|  | ||||
| v=0 | ||||
|  | ||||
| ----- | ||||
|  | ||||
| test_sdp_section_start() test [0]: | ||||
|  | ||||
| test_sdp_section_start() test [1]: | ||||
|  | ||||
| test_sdp_section_start() test [2]: | ||||
|  | ||||
| test_sdp_section_start() test [3]: | ||||
|  | ||||
| test_sdp_section_start() test [4]: | ||||
|  | ||||
| test_sdp_section_start() test [5]: | ||||
|  | ||||
| test_sdp_section_start() test [6]: | ||||
|  | ||||
| test_sdp_section_start() test [7]: | ||||
|  | ||||
| test_sdp_section_start() test [8]: | ||||
|  | ||||
| test_sdp_section_start() test [9]: | ||||
|  110 => 96 | ||||
|  111 => 97 | ||||
|  112 => 98 | ||||
|  113 => 99 | ||||
|  96 <= 110 | ||||
|  97 <= 111 | ||||
|  98 <= 112 | ||||
|  99 <= 113 | ||||
|  | ||||
|  0 => 0 | ||||
|  3 => 3 | ||||
|  8 => 8 | ||||
|  18 => 18 | ||||
|  0 <= 0 | ||||
|  3 <= 3 | ||||
|  8 <= 8 | ||||
|  18 <= 18 | ||||
|  | ||||
|  110 => 96 | ||||
|  111 => 97 | ||||
|  112 => 98 | ||||
|  113 => 113 | ||||
|  0 => 0 | ||||
|  96 <= 110 | ||||
|  97 <= 111 | ||||
|  98 <= 112 | ||||
|  2 <= 2 | ||||
|  100 <= 100 | ||||
|  | ||||
| Done | ||||
|   | ||||
		Reference in New Issue
	
	Block a user