mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-10-31 12:03:50 +00:00 
			
		
		
		
	Compare commits
	
		
			174 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 2bdad960ff | ||
|  | 58d5b97831 | ||
|  | 6225b5cecb | ||
|  | c415ed5113 | ||
|  | 730a1f28d2 | ||
|  | af67178581 | ||
|  | dc7dfd0bbd | ||
|  | c364f4ab5a | ||
|  | e9989b9e76 | ||
|  | 9423817311 | ||
|  | 292ba1bdb1 | ||
|  | 10d9f8dcf4 | ||
|  | 4c0658955e | ||
|  | e9164c295d | ||
|  | 563386e8bb | ||
|  | af0f58fac7 | ||
|  | 2e411f33a0 | ||
|  | 8e8d59ff0e | ||
|  | 14da3a33cb | ||
|  | ea2035326d | ||
|  | 2bfa708527 | ||
|  | 72f911513e | ||
|  | 3d0676a791 | ||
|  | 4c4d227624 | ||
|  | 0e8310fa6c | ||
|  | e3624562dc | ||
|  | d5057ec7b8 | ||
|  | 72761b312b | ||
|  | a312523be4 | ||
|  | 45fe47cea7 | ||
|  | ce356eec1f | ||
|  | cca1d180f5 | ||
|  | 833ab4b083 | ||
|  | d358ca14a9 | ||
|  | 3699ef06a4 | ||
|  | 9dd80ca1f8 | ||
|  | 621e8666ff | ||
|  | ec967d74f7 | ||
|  | 3ccf02a300 | ||
|  | b6795ddfd2 | ||
|  | 747fbce4ce | ||
|  | e1da8c9d2c | ||
|  | e78221d273 | ||
|  | 6c303664f5 | ||
|  | 544016d8e6 | ||
|  | 263a1a3e8f | ||
|  | 30bdf94102 | ||
|  | 0dbaaadc06 | ||
|  | 671c37b72b | ||
|  | 2a9ba66922 | ||
|  | f8abd0ec0c | ||
|  | 1a7048d69a | ||
|  | c70c08cd49 | ||
|  | a8f13286ff | ||
|  | b9e7569217 | ||
|  | 057decfe3a | ||
|  | 342a9a9418 | ||
|  | 22f9cf2687 | ||
|  | 26d6b2b5ce | ||
|  | 169d50ed4a | ||
|  | 82dfb505db | ||
|  | 9c0d105d09 | ||
|  | ded02cfc29 | ||
|  | 0cbed362cd | ||
|  | 310e41cdf3 | ||
|  | a55bfdc436 | ||
|  | 6ea8d7dac6 | ||
|  | 9befdeb673 | ||
|  | 5fb814b6b9 | ||
|  | 9ac9cb963d | ||
|  | 377d39122d | ||
|  | d1e94c7ac3 | ||
|  | 6db3f7f1cb | ||
|  | 58c0b0a6f7 | ||
|  | cca55247ed | ||
|  | d266c374ee | ||
|  | d358d71206 | ||
|  | 5e6661eece | ||
|  | c1468ef7a5 | ||
|  | 24763eaa73 | ||
|  | d8c2f31a87 | ||
|  | 01b812ea76 | ||
|  | 0807771b9e | ||
|  | 103e50a561 | ||
|  | 3a914e33e9 | ||
|  | 2172f402a5 | ||
|  | 8c138fe89c | ||
|  | 7881f7da0b | ||
|  | d17a9de79e | ||
|  | 8e74c63e15 | ||
|  | 80dd576ad2 | ||
|  | cb922b646f | ||
|  | 5d8b5b0935 | ||
|  | 35bd2523e2 | ||
|  | f45a9b9760 | ||
|  | f48cd95bbc | ||
|  | 7558c20973 | ||
|  | 6ddc79b6ed | ||
|  | ff5921573a | ||
|  | d4ad77d326 | ||
|  | a6917fecb1 | ||
|  | 58877f04e3 | ||
|  | d4707e2185 | ||
|  | f633302c73 | ||
|  | 36413c011f | ||
|  | 833281dcb9 | ||
|  | 07bbd766c5 | ||
|  | c7c8e649f1 | ||
|  | c0e1b1aec5 | ||
|  | 9735d21b9d | ||
|  | 705565d745 | ||
|  | 63088e0e61 | ||
|  | 887c93e198 | ||
|  | 2ca48823b1 | ||
|  | e7de137b29 | ||
|  | c1ad7fd92b | ||
|  | 3779919551 | ||
|  | 8e0d101997 | ||
|  | 654b68d602 | ||
|  | 70c03f5e44 | ||
|  | e56ec4a146 | ||
|  | 5ffd127384 | ||
|  | 051eb4d563 | ||
|  | e740665cab | ||
|  | abb9d47c90 | ||
|  | 2dd2b33030 | ||
|  | 69f15e4e95 | ||
|  | b0ea7976bf | ||
|  | 9d939b6d5d | ||
|  | de805b6772 | ||
|  | cb25a0aa48 | ||
|  | 72eff2cf6a | ||
|  | 1c5fcb0791 | ||
|  | 48fb176fbc | ||
|  | 4d090d85f4 | ||
|  | 81c5847434 | ||
|  | d48a811f6c | ||
|  | b7f52b414e | ||
|  | ea7aaf2eca | ||
|  | 432ee9d150 | ||
|  | b4a59475d2 | ||
|  | 2177919edb | ||
|  | 87f114a1d0 | ||
|  | 928a20b540 | ||
|  | b9022497d8 | ||
|  | 582c2bf7b0 | ||
|  | 663ba5c41f | ||
|  | daf5bcea99 | ||
|  | a29f155301 | ||
|  | 941e317c97 | ||
|  | 3fbf035923 | ||
|  | 21bcf6abd8 | ||
|  | 26e810d8a9 | ||
|  | 4d1a52d463 | ||
|  | 83df47cd7a | ||
|  | c5bbb78ffe | ||
|  | e0e5a772f4 | ||
|  | e39ae87ea3 | ||
|  | 8b0a614e43 | ||
|  | c61664c2b2 | ||
|  | 15e7e4f37d | ||
|  | ab8371c6d7 | ||
|  | 8c777990a5 | ||
|  | ec5e85b84e | ||
|  | ab4e8663e1 | ||
|  | a4f783b51b | ||
|  | 479cf76e55 | ||
|  | 33639166a2 | ||
|  | 709aad28ed | ||
|  | 5892b2e4db | ||
|  | f1f73b8e1d | ||
|  | 0fdc08c551 | ||
|  | 5be57da075 | ||
|  | 147184eb06 | 
							
								
								
									
										18
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -44,15 +44,16 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""]) | ||||
| AC_SUBST(LIBRARY_DLSYM) | ||||
|  | ||||
|  | ||||
| PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.7.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.7.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.7.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.7.0) | ||||
| PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.2.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.3.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.3.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0) | ||||
| PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.5.0) | ||||
|  | ||||
| CFLAGS="$CFLAGS -pthread" | ||||
| CFLAGS="$CFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread" | ||||
| CPPFLAGS="$CPPFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread" | ||||
| LDFLAGS="$LDFLAGS -pthread" | ||||
|  | ||||
| AC_ARG_ENABLE(sanitize, | ||||
| @@ -79,6 +80,7 @@ AC_ARG_ENABLE(werror, | ||||
| if test x"$werror" = x"yes" | ||||
| then | ||||
| 	WERROR_FLAGS="-Werror" | ||||
| 	WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition" | ||||
| 	WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" | ||||
| 	WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" | ||||
| 	CFLAGS="$CFLAGS $WERROR_FLAGS" | ||||
|   | ||||
| @@ -29,30 +29,30 @@ BuildRequires:  pkgconfig >= 0.20 | ||||
| %if 0%{?suse_version} | ||||
| BuildRequires:  systemd-rpm-macros | ||||
| %endif | ||||
| BuildRequires:  pkgconfig(libosmo-netif) >= 1.2.0 | ||||
| BuildRequires:  pkgconfig(libosmocore) >= 1.7.0 | ||||
| BuildRequires:  pkgconfig(libosmoctrl) >= 1.7.0 | ||||
| BuildRequires:  pkgconfig(libosmogsm) >= 1.7.0 | ||||
| BuildRequires:  pkgconfig(libosmovty) >= 1.7.0 | ||||
| BuildRequires:  pkgconfig(libosmocoding) >= 1.7.0 | ||||
| BuildRequires:  pkgconfig(libosmoabis) >= 1.3.0 | ||||
| BuildRequires:  pkgconfig(libosmotrau) >= 1.3.0 | ||||
| BuildRequires:  pkgconfig(libosmo-netif) >= 1.4.0 | ||||
| BuildRequires:  pkgconfig(libosmocore) >= 1.9.0 | ||||
| BuildRequires:  pkgconfig(libosmoctrl) >= 1.9.0 | ||||
| BuildRequires:  pkgconfig(libosmogsm) >= 1.9.0 | ||||
| BuildRequires:  pkgconfig(libosmovty) >= 1.9.0 | ||||
| BuildRequires:  pkgconfig(libosmocoding) >= 1.9.0 | ||||
| BuildRequires:  pkgconfig(libosmoabis) >= 1.5.0 | ||||
| BuildRequires:  pkgconfig(libosmotrau) >= 1.5.0 | ||||
| %{?systemd_requires} | ||||
|  | ||||
| %description | ||||
| OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks. | ||||
|  | ||||
| %package -n libosmo-mgcp-client9 | ||||
| %package -n libosmo-mgcp-client12 | ||||
| Summary:        Osmocom's Media Gateway Control Protocol client library | ||||
| Group:          System/Libraries | ||||
|  | ||||
| %description -n libosmo-mgcp-client9 | ||||
| %description -n libosmo-mgcp-client12 | ||||
| Osmocom's Media Gateway Control Protocol client library. | ||||
|  | ||||
| %package -n libosmo-mgcp-client-devel | ||||
| Summary:        Development files for Osmocom's Media Gateway Control Protocol client library | ||||
| Group:          Development/Libraries/C and C++ | ||||
| Requires:       libosmo-mgcp-client9 = %{version} | ||||
| Requires:       libosmo-mgcp-client12 = %{version} | ||||
|  | ||||
| %description -n libosmo-mgcp-client-devel | ||||
| Osmocom's Media Gateway Control Protocol client librarary. | ||||
| @@ -90,8 +90,8 @@ find %{buildroot} -type f -name "*.la" -delete -print | ||||
| %check | ||||
| make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +) | ||||
|  | ||||
| %post   -n libosmo-mgcp-client9 -p /sbin/ldconfig | ||||
| %postun -n libosmo-mgcp-client9 -p /sbin/ldconfig | ||||
| %post   -n libosmo-mgcp-client12 -p /sbin/ldconfig | ||||
| %postun -n libosmo-mgcp-client12 -p /sbin/ldconfig | ||||
|  | ||||
| %if 0%{?suse_version} | ||||
| %preun | ||||
| @@ -119,8 +119,8 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +) | ||||
| %dir %{_sysconfdir}/osmocom | ||||
| %config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg | ||||
|  | ||||
| %files -n libosmo-mgcp-client9 | ||||
| %{_libdir}/libosmo-mgcp-client.so.9* | ||||
| %files -n libosmo-mgcp-client12 | ||||
| %{_libdir}/libosmo-mgcp-client.so.12* | ||||
|  | ||||
| %files -n libosmo-mgcp-client-devel | ||||
| %{_libdir}/libosmo-mgcp-client.so | ||||
|   | ||||
| @@ -1,11 +1,20 @@ | ||||
| [Unit] | ||||
| Description=Osmocom Media Gateway (MGW) | ||||
| After=network-online.target | ||||
| Wants=network-online.target | ||||
|  | ||||
| [Service] | ||||
| Type=simple | ||||
| StateDirectory=osmocom | ||||
| WorkingDirectory=%S/osmocom | ||||
| Restart=always | ||||
| ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg | ||||
| RestartSec=2 | ||||
| # CPU scheduling policy: | ||||
| CPUSchedulingPolicy=rr | ||||
| # For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority): | ||||
| CPUSchedulingPriority=1 | ||||
| # See sched(7) for further details on real-time policies and priorities | ||||
|  | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
|   | ||||
							
								
								
									
										215
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										215
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,218 @@ | ||||
| osmo-mgw (1.12.1) unstable; urgency=medium | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * mgcp-client: Fix missing include in mgcp_client_pool.h | ||||
|   * mgcp-client: Introduce API osmo_mgcpc_ep_local_name() | ||||
|   * mgw: Configure IuUP if codec set during MDCX | ||||
|  | ||||
|  -- Oliver Smith <osmith@sysmocom.de>  Thu, 28 Sep 2023 15:58:17 +0200 | ||||
|  | ||||
| osmo-mgw (1.12.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Philipp Maier ] | ||||
|   * mgcp_sdp: add spec reference | ||||
|   * mgcp_sdp: cosmetic: remove newline | ||||
|   * mgcp_endp: cosmetic move mgcp_endp_release to the end | ||||
|   * mgcp_endp: cosmetic: remove unnecessary new line | ||||
|   * mgcp_e1: fix log output | ||||
|   * mgcp_e1: be more frugal withe E1 line resources | ||||
|   * mgcp_client: fix sourcecode formatting | ||||
|   * mgcp_e1: fix typo | ||||
|   * mgcp_e1: rewrite comment | ||||
|   * mgcp_e1: rename e1_send to e1_send_ts_frame | ||||
|   * mgcp_e1: cosmetic: rewrite comment | ||||
|   * Revert "mgcp_codec: do not differentiate between oa and bwe when comparing codec" | ||||
|   * mgcp_codec: fix oa/bwe comparison in mgcp_codec_pt_translate() | ||||
|   * mgcp_codec: refactor payload type converstion | ||||
|   * mgcp_codec: cosmetic: remove line break in api-doc | ||||
|   * mgcp_network: fix apidoc | ||||
|   * mgcp_vty: add warnings for deprecated config options | ||||
|   * mgcp_codec: move mgcp_codec_decide down | ||||
|   * mgcp_codec: fix codec decision | ||||
|   * mgcp_network: do not deliver RTP packets with unpatched PT | ||||
|   * mgcp_codec: be sensitive about IuFP when checking codecs | ||||
|   * mgcp_client.h: also add spec ref to the other 3gpp defined payload types | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * cosmetic: Fix grammar suggesting reading _the_ user manual | ||||
|  | ||||
|   [ arehbein ] | ||||
|   * Transition to use of 'telnet_init_default' | ||||
|  | ||||
|   [ Oliver Smith ] | ||||
|   * mgcp_client: mgcp_msg_gen: add more error logs | ||||
|   * mgcp_client_pool: add mgcp_client_pool_empty() | ||||
|   * Cosmetic: mgcp_client: fix typo | ||||
|   * debian: set compat level to 10 | ||||
|   * systemd: depend on networking-online.target | ||||
|   * Cosmetic: fix a typo | ||||
|   * mgcp_client: check rc of map_str_to_codec | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * mgcp_network: Unregister osmo_fd before closing fd | ||||
|   * mgcp-client: Call osmo_fd_unregister() before closing and changing bfd->fd | ||||
|   * mgcp-client: Drop unused struct mgcp_client field | ||||
|   * mgcp_client: Introduce mgcp_client_conf_alloc(), deprecate mgcp_client_conf_init() | ||||
|   * mgcp-client: Move some static functions further above | ||||
|   * mgw: Allow auditing speciall 'null' endpoint | ||||
|   * mgcp-client: Add keepalive feature | ||||
|   * mgcp_client: pool: Only pick clients with an MGCP link considered to be UP | ||||
|   * mgcp-client: Always mark client as UP if keepalive request-interval disabled | ||||
|   * mgcp-client: Mark client as UP when keepalive request-interval/timeout is disabled through VTY | ||||
|  | ||||
|   [ Vadim Yanitskiy ] | ||||
|   * */Makefile.am: libraries shall not be in AM_LDFLAGS | ||||
|   * tests: use -no-install libtool flag to avoid ./lt-* scripts | ||||
|   * tests: $(BUILT_SOURCES) is not defined, depend on osmo-mgw | ||||
|   * copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * mgcp_find_section_end(): skip spaces at start of SDP | ||||
|   * mgcp_client: simpler error handling | ||||
|   * mgcp: fix "L: a:" header parsing: heed ";" separator | ||||
|   * mgcp_client: tweak extract_codec_name() implementation | ||||
|  | ||||
|   [ Andreas Eversberg ] | ||||
|   * ASCI: Add new mode for voice group/broadcast call | ||||
|   * ASCI: Support conference briding with 1..n connections | ||||
|  | ||||
|  -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 12 Sep 2023 14:48:51 +0200 | ||||
|  | ||||
| osmo-mgw (1.11.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * mgcp-client: Remove impossible code path | ||||
|   * mgcp_osmux: Drop duplicated conn_osmux_release_cid() in code path | ||||
|   * Use Osmux default port define from libosmo-netif | ||||
|   * osmux: Use new osmux APIs to let libosmo-netif alloc struct osmux_out_handle | ||||
|   * mgw: Fix osmux conn local IP selection | ||||
|   * cosmetic: osmux: Fix wrong indentation | ||||
|   * mgw: Use X-Osmux string define | ||||
|   * Add Osmux log category | ||||
|   * cosmetic: main: Properly format log_info_cat | ||||
|   * osmux: Use better name for function which may allocate a new struct | ||||
|   * osmux: Use osmo_sockaddr wherever possible | ||||
|   * osmux: Log refcounting of osmux_handle_list | ||||
|   * osmux: Fix memleak on error code path | ||||
|   * osmux: don't store conn ptr inside shared osmux_handle | ||||
|   * osmux: set log level of expected code path to INFO | ||||
|   * cosmetic: vty: Fix indentation whitespace | ||||
|   * osmux: Log sendto() error | ||||
|   * cosmetic: mgcp_conn.h: fix indentation whitespace | ||||
|   * osmux: Attach osmux to virtual trunk | ||||
|   * osmux: Clean up helper macro osmux_chunk_length() | ||||
|   * osmux: Get rid of static NULL talloc context | ||||
|   * Fix typo in ratectr description | ||||
|   * mgcp_conn: rename field s/rate_ctr_group/ctrg/g | ||||
|   * Use 'static const' instead of 'const static' everywhere | ||||
|   * osmux: Add connection and global rate counters | ||||
|   * cosmetic: osmux: Fix formatting of if-else brackets | ||||
|   * osmux: Rename field osmux usage policy and define it with proper type | ||||
|   * Use bool type instead of int in config field | ||||
|   * osmux: Support local CID != remote CID | ||||
|   * osmux: Fix incorrect rate_ctr_group used in mgcp_osmux.c | ||||
|   * osmux: Improve per-conn tx rate counters | ||||
|   * vty: show per-connection Osmux VTY stats | ||||
|   * osmux: Rename field s/init/initialized | ||||
|   * osmux: Allocate rate counters during initialization of osmux conn | ||||
|   * cosmetic: osmux: Drop extra empty line | ||||
|   * osmux: Drop unneeded comment block | ||||
|   * osmux: Lower log level when osmux batch received for unknown CID | ||||
|   * osmux: Keep decoding osmux pkt if a batch contains an unknown CID | ||||
|   * Allocate struct osmux_in_handle through new libosmo-netif APIs | ||||
|   * osmux: Drop logging of osmux internal counters | ||||
|   * osmux: cleanup misleading code calling rtp_bridge_cb | ||||
|   * osmux: Unify rtp_conn osmux type into a single type | ||||
|   * osmux: Match remote address in osmux_conn_lookup() | ||||
|   * osmux: Fill in from_addr in struct osmo_rtp_msg_ctx | ||||
|   * osmux: Log remote address upon rx of osmux pkt | ||||
|   * send_dummy: Use proper condition to test if conn is osmux | ||||
|   * Fix regression in detection of legacy dummy packets | ||||
|   * Use new libosmocore API osmo_sockaddr_is_any() | ||||
|   * Get rid of separate rtp_port field | ||||
|   * osmux: Use available API to check if remote end is known | ||||
|   * Add Osmux IPv6 support | ||||
|   * Clean up local var pointers in mgcp_get_local_addr() | ||||
|   * osmux: Change couple log lines to OSMUX category | ||||
|   * osmux: Add square brackets around IPv6 address to distinguish port in log line | ||||
|   * osmux: Drop unused role parameter | ||||
|   * osmux: cosmetic: Fix indentation | ||||
|   * Check once if remote addr is available when sending dummy packet | ||||
|   * osmux: Simplify and constify param passing | ||||
|   * osmux: Set conn->type during osmux_init_conn() | ||||
|   * osmux: Make conn_osmux_{allocate,release}_local_cid() APIs static | ||||
|   * osmux: Define osmux_dummy cfg as boolean | ||||
|   * osmux: Move setting OSMUX_STATE_DISABLED to initializer function | ||||
|   * osmux: Erase references to bsc-nat | ||||
|   * mgcp_conn_dump(): Separate dump for osmux and iuup connections | ||||
|   * osmux: Introduce osmux peer-behind-nat (on|off) and rework conn activation | ||||
|   * osmux: Clean up mgcp_config osmux fields | ||||
|   * cosmetic: Fix typo in comment | ||||
|   * mgcp-client: pool: Improve documentation of some internal fields | ||||
|   * mgcp-client: Avoid double iteration picking client from pool | ||||
|   * mgcp-client: Fix typo in internal function name | ||||
|   * mgcp-client: Rename internal field in mgcp_client_pool | ||||
|   * mgcp-client: Move & rename helper function outside of vty code | ||||
|   * mgcp-client: Create alloc() and free() internal APIs for mgcp_client_pool_member | ||||
|   * mgcp-client: Move internal API acting on mgcp_client_pool to the correct file section | ||||
|   * mgcp-client: Refactor reinit of mgcp clients | ||||
|   * mgcp-client: Rearrange order of structs and APIs in header | ||||
|   * mgcp-client: Rearrange internal backpointers | ||||
|   * mgcp-client: Introduce APIs to manually select mgcp_client from pool | ||||
|   * mgcp-client: vty: Write deprecation warning using non-mgw nodes | ||||
|   * mgcp-client: Use random free local port by default | ||||
|   * mgcp-client: Convert users supporting new MGW Pool VTY node during write-config | ||||
|   * mgcp-client: Introduce API mgcp_client_pool_config_write | ||||
|   * mgcp-client: Add new VTY commands under mgw node without mgw prefix | ||||
|   * mgcp-client: Fix 'mgw endpoint-range' command dropped from old VTY node | ||||
|   * mgcp-client: Refactor system keeping old users not calling mgcp_client_pool_config_write() working | ||||
|   * mgcp-client: Fix no 'mgw ' prefix written in old VTY node | ||||
|   * Fix misleading error log | ||||
|   * mgcp_send: Use mgcp_conn_rtp_is_iuup() helper | ||||
|   * cosmetic: Clarify and fix typos in comment | ||||
|   * mgw: Log unexpected RTP AMR OA-vs-BE payload | ||||
|   * Rename and move func checking if amr mode is explicitly configured | ||||
|   * mgw: rx_rtp(): reorder checks and handlings | ||||
|   * mgw: Rename s/mgcp_send_rtp/mgcp_conn_rtp_dispatch_rtp/ | ||||
|   * osmux: Rename function and pass msgb directly to it | ||||
|   * osmux: Make sure RTP AMR feed to osmux is in octet-aligned mode | ||||
|   * Improve logging on AMR OA<->BWE conversion failure | ||||
|   * osmux: Rotate over available Osmux CID when allocating a new one | ||||
|   * osmux: Use new osmux_xfrm_input API to set name on each link | ||||
|   * iuup: Use osmo_amr_ft_valid() API | ||||
|  | ||||
|   [ Vadim Yanitskiy ] | ||||
|   * libosmo-mgcp-client: add -no-undefined to *_la_LDFLAGS | ||||
|  | ||||
|   [ Philipp Maier ] | ||||
|   * mgcp_e1: fix apidoc | ||||
|   * mgcp_network: improve coment | ||||
|  | ||||
|   [ Max ] | ||||
|   * Set working directory in systemd service file | ||||
|   * Add realtime scheduling and set priority in service file | ||||
|   * ctrl: take both address and port from vty config | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * Make osmo_mgcpc_ep_fsm_pre_term() static | ||||
|   * Support building with -Werror=strict-prototypes / -Werror=old-style-definition | ||||
|   * Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition | ||||
|   * update outdated vty copyright statement | ||||
|  | ||||
|   [ Oliver Smith ] | ||||
|   * mgcp_client_pool.h: add missing stdbool include | ||||
|   * Fix various typos | ||||
|   * mgcp_client: add new clearmode codec | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * AMR->IuUP: do not crash on AMR data before IuUP Init | ||||
|   * AMR->IuUP: properly translate Q -> FQC | ||||
|   * IuUP->AMR: do not patch payload type a second time | ||||
|   * IuUP->AMR: log whether converting to AMR OA or BE | ||||
|   * AMR->IuUP: log conversion, like for the flipside | ||||
|  | ||||
|  -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 07 Feb 2023 16:58:41 +0100 | ||||
|  | ||||
| osmo-mgw (1.10.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Eric ] | ||||
|   | ||||
							
								
								
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1 @@ | ||||
| 9 | ||||
| 10 | ||||
|   | ||||
							
								
								
									
										14
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -2,14 +2,14 @@ Source: osmo-mgw | ||||
| Section: net | ||||
| Priority: extra | ||||
| Maintainer: Osmocom team <openbsc@lists.osmocom.org> | ||||
| Build-Depends: debhelper (>=9), | ||||
| Build-Depends: debhelper (>= 10), | ||||
|                dh-autoreconf, | ||||
|                pkg-config, | ||||
|                autotools-dev, | ||||
|                libosmocore-dev (>= 1.7.0), | ||||
|                libosmo-netif-dev (>= 1.2.0), | ||||
|                libosmo-abis-dev (>= 1.3.0), | ||||
|                osmo-gsm-manuals-dev (>= 1.3.0) | ||||
|                libosmocore-dev (>= 1.9.0), | ||||
|                libosmo-netif-dev (>= 1.4.0), | ||||
|                libosmo-abis-dev (>= 1.5.0), | ||||
|                osmo-gsm-manuals-dev (>= 1.5.0) | ||||
| Standards-Version: 3.9.8 | ||||
| Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw | ||||
| Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw | ||||
| @@ -21,7 +21,7 @@ Multi-Arch: foreign | ||||
| Depends: ${misc:Depends}, ${shlibs:Depends} | ||||
| Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks | ||||
|  | ||||
| Package: libosmo-mgcp-client9 | ||||
| Package: libosmo-mgcp-client12 | ||||
| Section: libs | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| @@ -33,7 +33,7 @@ Package: libosmo-mgcp-client-dev | ||||
| Section: libdevel | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| Depends: libosmo-mgcp-client9 (= ${binary:Version}), ${misc:Depends} | ||||
| Depends: libosmo-mgcp-client12 (= ${binary:Version}), ${misc:Depends} | ||||
| Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities | ||||
|  | ||||
| Package: osmo-mgw-doc | ||||
|   | ||||
							
								
								
									
										4
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							| @@ -6,7 +6,7 @@ Files:     * | ||||
| Copyright: 2009-2014 On-Waves | ||||
|            2009-2015 Holger Hans Peter Freyther <zecke@selfish.org> | ||||
|            2013 Jacob Erlbeck <jerlbeck@sysmocom.de> | ||||
|            2016-2017 sysmocom s.m.f.c. GmbH <info@sysmocom.de> | ||||
|            2016-2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
| License:   AGPL-3.0+ | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU Affero General Public License as published by | ||||
| @@ -22,7 +22,7 @@ License:   AGPL-3.0+ | ||||
|  along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| Files:     src/libosmo-mgcp-client/* include/osmocom/mgcp_client/* | ||||
| Copyright: 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de> | ||||
| Copyright: 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|            Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c) | ||||
|            2009 by Harald Welte <laforge@gnumonks.org> | ||||
|            2009-2011 by Holger Hans Peter Freyther | ||||
|   | ||||
| @@ -91,4 +91,10 @@ encoding/decoding for 16K and 8K subslots. Endpoints with other bitrates are | ||||
| not yet useable. | ||||
|  | ||||
| NOTE: the VTY command "show mgcp" can be used to get a list of all available | ||||
| endpoints (including identifiers) | ||||
| endpoints (including identifiers) | ||||
|  | ||||
| === The `null` endpoint | ||||
|  | ||||
| OsmoMGW offers a special `null@<domain>` endpoint which can be audited at all times. | ||||
| This is useful for MGCP clients who wish to submit requests to OsmoMGW | ||||
| periodically to find out whether it is still reachable and in a working state. | ||||
| @@ -3,6 +3,7 @@ SUBDIRS = \ | ||||
| 	$(NULL) | ||||
|  | ||||
| nobase_include_HEADERS = \ | ||||
| 	osmocom/mgcp_client/defs.h \ | ||||
| 	osmocom/mgcp_client/mgcp_client.h \ | ||||
| 	osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \ | ||||
| 	osmocom/mgcp_client/mgcp_client_fsm.h \ | ||||
|   | ||||
| @@ -6,7 +6,6 @@ noinst_HEADERS = \ | ||||
| 	mgcp_endp.h \ | ||||
| 	mgcp_sdp.h \ | ||||
| 	mgcp_codec.h \ | ||||
| 	mgcp_ctrl.h \ | ||||
| 	mgcp_trunk.h \ | ||||
| 	debug.h \ | ||||
| 	mgcp_ratectr.h \ | ||||
|   | ||||
| @@ -30,6 +30,7 @@ | ||||
| enum { | ||||
| 	DRTP, | ||||
| 	DE1, | ||||
| 	DOSMUX, | ||||
| 	Debug_LastEntry, | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -29,6 +29,7 @@ | ||||
| #include <osmocom/core/logging.h> | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp_common.h> | ||||
| #include <osmocom/mgcp/osmux.h> | ||||
|  | ||||
| #include <arpa/inet.h> | ||||
| #include <sys/types.h> | ||||
| @@ -156,24 +157,26 @@ struct mgcp_config { | ||||
|  | ||||
| 	enum mgcp_role role; | ||||
|  | ||||
| 	/* osmux translator: 0 means disabled, 1 means enabled */ | ||||
| 	int osmux; | ||||
| 	/* addr to bind the server to */ | ||||
| 	char osmux_addr[INET6_ADDRSTRLEN]; | ||||
| 	/* The BSC-NAT may ask for enabling osmux on demand. This tells us if | ||||
| 	 * the osmux socket is already initialized. | ||||
| 	 */ | ||||
| 	int osmux_init; | ||||
| 	/* osmux batch factor: from 1 to 4 maximum */ | ||||
| 	int osmux_batch; | ||||
| 	/* osmux batch size (in bytes) */ | ||||
| 	int osmux_batch_size; | ||||
| 	/* osmux port */ | ||||
| 	uint16_t osmux_port; | ||||
| 	/* Pad circuit with dummy messages until we see the first voice | ||||
| 	 * message. | ||||
| 	 */ | ||||
| 	uint16_t osmux_dummy; | ||||
| 	struct { | ||||
| 		/* Osmux usage policy: */ | ||||
| 		enum osmux_usage usage; | ||||
| 		/* addr to bind the server to */ | ||||
| 		char *local_addr_v4; | ||||
| 		char *local_addr_v6; | ||||
| 		/* osmux port */ | ||||
| 		uint16_t local_port; | ||||
| 		/* The osmux socket is allocated on demand (1st time used). | ||||
| 		* This tells us if the osmux socket is already initialized.  */ | ||||
| 		bool initialized; | ||||
| 		/* osmux batch factor: from 1 to 4 maximum */ | ||||
| 		int batch_factor; | ||||
| 		/* osmux batch size (in bytes) */ | ||||
| 		int batch_size; | ||||
| 		/* Pad circuit with dummy AMR frames if no payload to transmit is available */ | ||||
| 		bool dummy_padding; | ||||
| 		/* Whether peer is behind NAT (Retrieve remote addr from 1st received Osmux packet) */ | ||||
| 		bool peer_behind_nat; | ||||
| 	} osmux; | ||||
| 	/* domain name of the media gateway */ | ||||
| 	char domain[255+1]; | ||||
|  | ||||
| @@ -207,4 +210,4 @@ int mgcp_send_reset_all(struct mgcp_config *cfg); | ||||
|  | ||||
| int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp, | ||||
| 		     uint8_t prio); | ||||
| int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, const char *buf, int len); | ||||
| int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len); | ||||
|   | ||||
| @@ -13,8 +13,9 @@ struct mgcp_conn_rtp; | ||||
| void mgcp_codec_summary(struct mgcp_conn_rtp *conn); | ||||
| void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn); | ||||
| int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param); | ||||
| int mgcp_codec_decide(struct mgcp_conn_rtp *conn); | ||||
| int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type); | ||||
| int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst); | ||||
| const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn, | ||||
| 								const char *subtype_name, unsigned int match_nr); | ||||
| bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec); | ||||
| bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec); | ||||
| struct mgcp_rtp_codec *mgcp_codec_from_pt(struct mgcp_conn_rtp *conn, int payload_type); | ||||
|   | ||||
| @@ -47,6 +47,7 @@ enum mgcp_connection_mode { | ||||
| 	MGCP_CONN_SEND_ONLY = 2, | ||||
| 	MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY, | ||||
| 	MGCP_CONN_LOOPBACK  = 4 | MGCP_CONN_RECV_SEND, | ||||
| 	MGCP_CONN_CONFECHO  = 8 | MGCP_CONN_RECV_SEND, | ||||
| }; | ||||
|  | ||||
| #define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:" | ||||
|   | ||||
| @@ -46,8 +46,7 @@ LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \ | ||||
| /* Specific rtp connection type (see struct mgcp_conn_rtp) */ | ||||
| enum mgcp_conn_rtp_type { | ||||
| 	MGCP_RTP_DEFAULT	= 0, | ||||
| 	MGCP_OSMUX_BSC, | ||||
| 	MGCP_OSMUX_BSC_NAT, | ||||
| 	MGCP_RTP_OSMUX, | ||||
| 	MGCP_RTP_IUUP, | ||||
| }; | ||||
|  | ||||
| @@ -80,19 +79,20 @@ struct mgcp_conn_rtp { | ||||
| 	struct { | ||||
| 		/* Osmux state: disabled, activating, active */ | ||||
| 		enum osmux_state state; | ||||
| 		/* Is cid holding valid data? is it allocated from pool? */ | ||||
| 		bool cid_allocated; | ||||
| 		/* Allocated Osmux circuit ID for this conn */ | ||||
| 		uint8_t cid; | ||||
| 		/* handle to batch messages */ | ||||
| 		/* Is local_cid holding valid data? is it allocated from pool? */ | ||||
| 		bool local_cid_allocated; | ||||
| 		/* Allocated local Osmux circuit ID for this conn */ | ||||
| 		uint8_t local_cid; | ||||
| 		/* Is remote_cid holding valid data? was it already received from client? */ | ||||
| 		bool remote_cid_present; | ||||
| 		/* Received remote Osmux circuit ID for this conn */ | ||||
| 		uint8_t remote_cid; | ||||
| 		/* handle to batch messages, shared (refcounted) among several conns */ | ||||
| 		struct osmux_in_handle *in; | ||||
| 		/* handle to unbatch messages */ | ||||
| 		struct osmux_out_handle out; | ||||
| 		/* statistics */ | ||||
| 		struct { | ||||
| 			uint32_t chunks; | ||||
| 			uint32_t octets; | ||||
| 		} stats; | ||||
| 		/* handle to unbatch messages, one allocated and owned per conn */ | ||||
| 		struct osmux_out_handle *out; | ||||
| 		/* statistics: */ | ||||
| 		struct rate_ctr_group *ctrg; | ||||
| 	} osmux; | ||||
|  | ||||
| 	struct { | ||||
| @@ -103,7 +103,7 @@ struct mgcp_conn_rtp { | ||||
| 		struct osmo_iuup_rnl_prim *init_ind; | ||||
| 	} iuup; | ||||
|  | ||||
| 	struct rate_ctr_group *rate_ctr_group; | ||||
| 	struct rate_ctr_group *ctrg; | ||||
| }; | ||||
|  | ||||
| /*! MGCP connection (untyped) */ | ||||
| @@ -145,12 +145,12 @@ struct mgcp_conn { | ||||
| enum { | ||||
| 	IN_STREAM_ERR_TSTMP_CTR, | ||||
| 	OUT_STREAM_ERR_TSTMP_CTR, | ||||
|         RTP_PACKETS_RX_CTR, | ||||
|         RTP_OCTETS_RX_CTR, | ||||
|         RTP_PACKETS_TX_CTR, | ||||
|         RTP_OCTETS_TX_CTR, | ||||
|         RTP_DROPPED_PACKETS_CTR, | ||||
|         RTP_NUM_CONNECTIONS, | ||||
| 	RTP_PACKETS_RX_CTR, | ||||
| 	RTP_OCTETS_RX_CTR, | ||||
| 	RTP_PACKETS_TX_CTR, | ||||
| 	RTP_OCTETS_TX_CTR, | ||||
| 	RTP_DROPPED_PACKETS_CTR, | ||||
| 	RTP_NUM_CONNECTIONS, | ||||
| }; | ||||
|  | ||||
| /* RTP per-connection statistics. Instances of the corresponding rate counter group | ||||
| @@ -163,7 +163,7 @@ static const struct rate_ctr_desc mgcp_conn_rate_ctr_desc[] = { | ||||
| 	[RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."}, | ||||
| 	[RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."}, | ||||
| 	[RTP_OCTETS_TX_CTR] = {"rtp:octets_tx", "Outbound rtp octets."}, | ||||
| 	[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."} | ||||
| 	[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "Dropped rtp packets."} | ||||
| }; | ||||
|  | ||||
| /* Aggregated RTP connection stats. These are updated when an RTP connection is freed. | ||||
| @@ -181,9 +181,49 @@ static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = { | ||||
| 	[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."} | ||||
| }; | ||||
|  | ||||
| /* Osmux connection related counters */ | ||||
| enum { | ||||
| 	OSMUX_CHUNKS_RX_CTR, | ||||
| 	OSMUX_OCTETS_RX_CTR, | ||||
| 	OSMUX_RTP_PACKETS_TX_CTR, | ||||
| 	OSMUX_RTP_PACKETS_TX_DROPPED_CTR, | ||||
| 	OSMUX_AMR_OCTETS_TX_CTR, | ||||
| 	/* Only available in global stats: */ | ||||
| 	OSMUX_NUM_CONNECTIONS, | ||||
| 	OSMUX_PACKETS_RX_CTR, | ||||
| 	OSMUX_PACKETS_TX_CTR, | ||||
| 	OSMUX_DROPPED_PACKETS_CTR, | ||||
| }; | ||||
|  | ||||
| /* RTP per-connection statistics. Instances of the corresponding rate counter group | ||||
|  * exist for the lifetime of an RTP connection. | ||||
|  * Must be kept in sync with all_rtp_conn_rate_ctr_desc below */ | ||||
| static const struct rate_ctr_desc mgcp_conn_osmux_rate_ctr_desc[] = { | ||||
| 	[OSMUX_CHUNKS_RX_CTR] = {"osmux:chunks_rx", "Inbound Osmux chunks."}, | ||||
| 	[OSMUX_OCTETS_RX_CTR] = {"osmux:octets_rx", "Inbound Osmux octets."}, | ||||
| 	[OSMUX_RTP_PACKETS_TX_CTR] = {"osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."}, | ||||
| 	[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."}, | ||||
| 	[OSMUX_AMR_OCTETS_TX_CTR] = {"osmux:amr_octets_tx", "Tx outbound AMD payload octets."}, | ||||
| }; | ||||
|  | ||||
| /* Aggregated Osmux connection stats. These are updated when an Osmux connection is freed. | ||||
|  * Must be kept in sync with mgcp_conn_osmux_rate_ctr_desc above */ | ||||
| static const struct rate_ctr_desc all_osmux_conn_rate_ctr_desc[] = { | ||||
| 	[OSMUX_CHUNKS_RX_CTR] = {"all_osmux:chunks_rx", "Inbound Osmux chunks."}, | ||||
| 	[OSMUX_OCTETS_RX_CTR] = {"all_osmux:octets_rx", "Inbound Osmux octets."}, | ||||
| 	[OSMUX_RTP_PACKETS_TX_CTR] = {"all_osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."}, | ||||
| 	[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"all_osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."}, | ||||
| 	[OSMUX_AMR_OCTETS_TX_CTR] = {"all_osmux:amr_octets_tx", "Tx outbound AMD payload octets."}, | ||||
| 	/* These last counters below do not exist in per-connection stats, only here: */ | ||||
| 	[OSMUX_NUM_CONNECTIONS] = {"all_osmux:num_closed_conns", "Total number of osmux connections closed."}, | ||||
| 	[OSMUX_PACKETS_RX_CTR] = {"all_osmux:packets_rx", "Total inbound UDP/Osmux packets."}, | ||||
| 	[OSMUX_PACKETS_TX_CTR] = {"all_osmux:packets_tx", "Total outbound UDP/Osmux packets."}, | ||||
| 	[OSMUX_DROPPED_PACKETS_CTR] = {"all_osmux:dropped_packets", "Dropped outbound UDP/Osmux packets."} | ||||
| }; | ||||
|  | ||||
| /* Was conn configured to handle Osmux? */ | ||||
| static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) { | ||||
| 	return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT; | ||||
| 	return conn->type == MGCP_RTP_OSMUX; | ||||
| } | ||||
|  | ||||
| /* Was conn configured to handle Osmux? */ | ||||
|   | ||||
| @@ -1,24 +0,0 @@ | ||||
| /* | ||||
|  * (C) 2020 by Harald Welte <laforge@gnumonks.org> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg, | ||||
|                                              const char *bind_addr, uint16_t port); | ||||
|  | ||||
| @@ -23,5 +23,5 @@ static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, | ||||
|  | ||||
| int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs); | ||||
| void mgcp_e1_endp_update(struct mgcp_endpoint *endp); | ||||
| void mgcp_e1_endp_release(struct mgcp_endpoint *endp); | ||||
| void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts); | ||||
| int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg); | ||||
|   | ||||
| @@ -40,8 +40,13 @@ LOGP(cat, level, "endpoint:%s " fmt, \ | ||||
|      endp ? endp->name : "none", \ | ||||
|      ## args) | ||||
|  | ||||
| enum rtp_proto { | ||||
| 	MGCP_PROTO_RTP, | ||||
| 	MGCP_PROTO_RTCP, | ||||
| }; | ||||
|  | ||||
| struct osmo_rtp_msg_ctx { | ||||
| 	int proto; | ||||
| 	enum rtp_proto proto; | ||||
| 	struct mgcp_conn_rtp *conn_src; | ||||
| 	struct osmo_sockaddr *from_addr; | ||||
| }; | ||||
| @@ -125,10 +130,10 @@ struct mgcp_endpoint { | ||||
| }; | ||||
|  | ||||
| struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index); | ||||
| void mgcp_endp_release(struct mgcp_endpoint *endp); | ||||
| int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid); | ||||
| void mgcp_endp_update(struct mgcp_endpoint *endp); | ||||
| bool mgcp_endp_is_wildcarded(const char *epname); | ||||
| bool mgcp_endp_is_null(const char *epname); | ||||
| struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname, | ||||
| 					      const struct mgcp_trunk *trunk); | ||||
| struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname, | ||||
| @@ -140,3 +145,4 @@ void mgcp_endp_strip_name(char *epname_stripped, const char *epname, | ||||
| 			 const struct mgcp_trunk *trunk); | ||||
| struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname, | ||||
| 			const struct mgcp_trunk *trunk); | ||||
| void mgcp_endp_release(struct mgcp_endpoint *endp); | ||||
|   | ||||
| @@ -9,7 +9,8 @@ | ||||
|  | ||||
| /* The following constant defines an RTP dummy payload that is used for | ||||
|  * "UDP Hole Punching" (NAT) */ | ||||
| static const char rtp_dummy_payload[] = { 0x23 }; | ||||
| #define MGCP_DUMMY_LOAD 0x23 | ||||
| static const char rtp_dummy_payload[] = { MGCP_DUMMY_LOAD }; | ||||
|  | ||||
| /* Check if the data in a given message buffer matches the rtp dummy payload | ||||
|  * defined above */ | ||||
| @@ -92,7 +93,7 @@ struct mgcp_rtp_end { | ||||
| 	struct osmo_sockaddr addr; | ||||
|  | ||||
| 	/* in network byte order */ | ||||
| 	int rtp_port, rtcp_port; | ||||
| 	int rtcp_port; | ||||
|  | ||||
| 	/* currently selected audio codec */ | ||||
| 	struct mgcp_rtp_codec *codec; | ||||
| @@ -156,7 +157,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp, | ||||
| 			  struct mgcp_rtp_state *state, | ||||
| 			  struct mgcp_rtp_end *rtp_end, | ||||
| 			  struct osmo_sockaddr *addr, struct msgb *msg); | ||||
| void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn); | ||||
| int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn); | ||||
|  | ||||
| /* payload processing default functions */ | ||||
| int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, | ||||
| @@ -178,5 +179,9 @@ void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_stat | ||||
|  | ||||
| void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, | ||||
| 				 int id, int inc); | ||||
| void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, | ||||
| 				 int id); | ||||
| void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg); | ||||
| uint32_t mgcp_get_current_ts(unsigned codec_rate); | ||||
|  | ||||
| int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, bool target_is_oa); | ||||
|   | ||||
| @@ -80,6 +80,8 @@ struct mgcp_ratectr_trunk { | ||||
| 	struct rate_ctr_group *mgcp_dlcx_ctr_group; | ||||
| 	/* Rate counter group which aggregates stats of individual RTP connections. */ | ||||
| 	struct rate_ctr_group *all_rtp_conn_stats; | ||||
| 	/* Rate counter group which aggregates stats of individual Osmux connections. */ | ||||
| 	struct rate_ctr_group *all_osmux_conn_stats; | ||||
| 	/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */ | ||||
| 	struct rate_ctr_group *e1_stats; | ||||
| }; | ||||
|   | ||||
| @@ -31,8 +31,6 @@ struct mgcp_trunk { | ||||
| 	int audio_send_ptime; | ||||
| 	int audio_send_name; | ||||
|  | ||||
| 	int no_audio_transcoding; | ||||
|  | ||||
| 	int omit_rtcp; | ||||
| 	int keepalive_interval; | ||||
|  | ||||
| @@ -66,7 +64,7 @@ struct mgcp_trunk { | ||||
| 		/* E1 specific */ | ||||
| 		struct { | ||||
| 			unsigned int vty_line_nr; | ||||
| 			bool ts_in_use[NUM_E1_TS-1]; | ||||
| 			uint8_t ts_usecount[NUM_E1_TS-1]; | ||||
| 			struct osmo_i460_timeslot i460_ts[NUM_E1_TS-1]; | ||||
| 			/* Note: on an E1 line TS 0 is devoted to framing and | ||||
| 			 * alignment and therefore only NUM_E1_TS-1 timeslots | ||||
|   | ||||
| @@ -1,25 +1,22 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdint.h> | ||||
|  | ||||
| #include <osmocom/core/socket.h> | ||||
|  | ||||
| #include <osmocom/netif/osmux.h> | ||||
| struct mgcp_conn_rtp; | ||||
| struct mgcp_trunk; | ||||
| struct mgcp_endpoint; | ||||
| struct mgcp_conn_rtp; | ||||
|  | ||||
| #define OSMUX_PORT	1984 | ||||
|  | ||||
| enum { | ||||
| 	OSMUX_ROLE_BSC = 0, | ||||
| 	OSMUX_ROLE_BSC_NAT, | ||||
| }; | ||||
|  | ||||
| int osmux_init(int role, struct mgcp_config *cfg); | ||||
| int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn, | ||||
| 		      struct osmo_sockaddr *addr, uint16_t port); | ||||
| int osmux_init(struct mgcp_trunk *trunk); | ||||
| int osmux_init_conn(struct mgcp_conn_rtp *conn); | ||||
| int conn_osmux_enable(struct mgcp_conn_rtp *conn); | ||||
| void conn_osmux_disable(struct mgcp_conn_rtp *conn); | ||||
| int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid); | ||||
| void conn_osmux_release_cid(struct mgcp_conn_rtp *conn); | ||||
| int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn); | ||||
| int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn); | ||||
| int conn_osmux_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn); | ||||
| int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg); | ||||
| int osmux_send_dummy(struct mgcp_conn_rtp *conn); | ||||
|  | ||||
| void osmux_cid_pool_get(uint8_t osmux_cid); | ||||
| int osmux_cid_pool_get_next(void); | ||||
| @@ -29,10 +26,14 @@ int osmux_cid_pool_count_used(void); | ||||
|  | ||||
| enum osmux_state { | ||||
| 	OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */ | ||||
| 	OSMUX_STATE_ACTIVATING,   /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */ | ||||
| 	OSMUX_STATE_ENABLED,	  /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */ | ||||
| 	OSMUX_STATE_ACTIVATING,   /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref conn_osmux_enable. */ | ||||
| 	OSMUX_STATE_ENABLED,	  /* Osmux was initialized by \ref conn_osmux_enable and can process frames */ | ||||
| }; | ||||
|  | ||||
| extern const struct value_string osmux_state_strs[]; | ||||
| static inline const char *osmux_state_str(enum osmux_state val) | ||||
| { return get_value_string(osmux_state_strs, val); } | ||||
|  | ||||
| enum osmux_usage { | ||||
| 	OSMUX_USAGE_OFF = 0, | ||||
| 	OSMUX_USAGE_ON = 1, | ||||
|   | ||||
							
								
								
									
										7
									
								
								include/osmocom/mgcp_client/defs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								include/osmocom/mgcp_client/defs.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| #include <osmocom/core/defs.h> | ||||
|  | ||||
| #if BUILDING_LIBOSMOMGCPCLIENT | ||||
| # define OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT(text) | ||||
| #else | ||||
| # define OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT(text) OSMO_DEPRECATED(text) | ||||
| #endif | ||||
| @@ -3,14 +3,15 @@ | ||||
| #include <stdint.h> | ||||
| #include <arpa/inet.h> | ||||
|  | ||||
| #include <osmocom/mgcp_client/defs.h> | ||||
| #include <osmocom/mgcp_client/mgcp_common.h> | ||||
|  | ||||
| /* See also: RFC 3435, chapter 3.5 Transmission over UDP */ | ||||
| #define MGCP_CLIENT_LOCAL_ADDR_DEFAULT NULL /* INADDR(6)_ANY */ | ||||
| #define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727 | ||||
| #define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0 | ||||
| #define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1" | ||||
| #define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427 | ||||
|  | ||||
| #define MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP "null" | ||||
| #define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n" | ||||
|  | ||||
| struct msgb; | ||||
| @@ -35,6 +36,12 @@ struct mgcp_client_conf { | ||||
|  | ||||
| 	/* human readable name / description */ | ||||
| 	char *description; | ||||
|  | ||||
| 	struct { | ||||
| 		uint32_t timeout_sec; | ||||
| 		uint32_t req_interval_sec; | ||||
| 		char req_endpoint_name[MGCP_ENDPOINT_MAXLEN]; | ||||
| 	} keepalive; | ||||
| }; | ||||
|  | ||||
| typedef unsigned int mgcp_trans_id_t; | ||||
| @@ -45,11 +52,12 @@ enum mgcp_codecs { | ||||
| 	CODEC_GSM_8000_1 = 3, | ||||
| 	CODEC_PCMA_8000_1 = 8, | ||||
| 	CODEC_G729_8000_1 = 18, | ||||
| 	CODEC_GSMEFR_8000_1 = 110, | ||||
| 	CODEC_GSMHR_8000_1 = 111, | ||||
| 	CODEC_AMR_8000_1 = 112, | ||||
| 	CODEC_AMRWB_16000_1 = 113, | ||||
| 	CODEC_GSMEFR_8000_1 = 110,	/* 3GPP TS 48.103 table 5.4.2.2.1 */ | ||||
| 	CODEC_GSMHR_8000_1 = 111,	/* 3GPP TS 48.103 table 5.4.2.2.1 */ | ||||
| 	CODEC_AMR_8000_1 = 112,		/* 3GPP TS 48.103 table 5.4.2.2.1 */ | ||||
| 	CODEC_AMRWB_16000_1 = 113,	/* 3GPP TS 48.103 table 5.4.2.2.1 */ | ||||
| 	CODEC_IUFP = 96, | ||||
| 	CODEC_CLEARMODE = 120,		/* 3GPP TS 48.103 table 5.4.2.2.1 */ | ||||
| }; | ||||
| /* Note: when new codec types are added, the corresponding value strings | ||||
|  * in mgcp_client.c (codec_table) must be updated as well. Enumerations | ||||
| @@ -132,7 +140,8 @@ struct mgcp_msg { | ||||
| 	struct mgcp_codec_param param; | ||||
| }; | ||||
|  | ||||
| void mgcp_client_conf_init(struct mgcp_client_conf *conf); | ||||
| struct mgcp_client_conf *mgcp_client_conf_alloc(void *ctx); | ||||
| void mgcp_client_conf_init(struct mgcp_client_conf *conf) OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use mgcp_client_conf_alloc() (or even better, switch to the mgcp_client_pool API!)"); | ||||
| void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf); | ||||
| int mgcp_client_config_write(struct vty *vty, const char *indent); | ||||
| struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp); | ||||
| @@ -140,7 +149,7 @@ struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp); | ||||
| struct mgcp_client *mgcp_client_init(void *ctx, | ||||
| 				     struct mgcp_client_conf *conf); | ||||
| int mgcp_client_connect(struct mgcp_client *mgcp); | ||||
| int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports); | ||||
| int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports) OSMO_DEPRECATED("Use mgcp_client_connect() instead"); | ||||
| void mgcp_client_disconnect(struct mgcp_client *mgcp); | ||||
|  | ||||
| const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp); | ||||
|   | ||||
| @@ -45,6 +45,7 @@ static inline void osmo_mgcpc_ep_ci_dlcx(struct osmo_mgcpc_ep_ci *ci) | ||||
| void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep); | ||||
|  | ||||
| const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep); | ||||
| const char *osmo_mgcpc_ep_local_name(const struct osmo_mgcpc_ep *ep); | ||||
| const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci); | ||||
| const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci); | ||||
| struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/core/write_queue.h> | ||||
| #include <osmocom/core/timer.h> | ||||
|  | ||||
| #define MSGB_CB_MGCP_TRANS_ID 0 | ||||
|  | ||||
| @@ -15,13 +16,10 @@ struct mgcp_client { | ||||
| 	struct osmo_wqueue wq; | ||||
| 	mgcp_trans_id_t next_trans_id; | ||||
| 	struct llist_head responses_pending; | ||||
| 	struct llist_head inuse_endpoints; | ||||
| 	struct mgcp_client_pool *pool; | ||||
| }; | ||||
|  | ||||
| struct mgcp_inuse_endpoint { | ||||
| 	struct llist_head entry; | ||||
| 	uint16_t id; | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
| 	struct osmo_timer_list keepalive_tx_timer; | ||||
| 	struct osmo_timer_list keepalive_rx_timer; | ||||
| 	bool conn_up; | ||||
| }; | ||||
|  | ||||
| struct mgcp_response_pending { | ||||
|   | ||||
| @@ -1,11 +1,24 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #include <osmocom/vty/vty.h> | ||||
|  | ||||
| struct mgcp_client; | ||||
| struct mgcp_client_pool; | ||||
| struct mgcp_client_pool_member; | ||||
|  | ||||
| struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx); | ||||
|  | ||||
| void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool); | ||||
| int mgcp_client_pool_config_write(struct vty *vty, const char *indent); | ||||
| unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool); | ||||
| void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client); | ||||
| bool mgcp_client_pool_empty(const struct mgcp_client_pool *pool); | ||||
|  | ||||
| struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool); | ||||
| void mgcp_client_pool_put(struct mgcp_client *mgcp_client); | ||||
|  | ||||
| struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr); | ||||
| struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member); | ||||
| bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member); | ||||
|   | ||||
| @@ -1,9 +1,31 @@ | ||||
| #pragma once | ||||
|  | ||||
| /* Struct to handle a pool of MGWs. (Use _pool functions) */ | ||||
| struct mgcp_client_pool { | ||||
|  | ||||
| 	/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using | ||||
| 	 * mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */ | ||||
| 	struct mgcp_client *mgcp_client_single; | ||||
|  | ||||
| 	/* A list that manages the pool members (see mgcp_client_pool_member->list above) */ | ||||
| 	struct llist_head member_list; | ||||
|  | ||||
| 	/* String to use for indentation when writing the configuration file to the VTY. This field is populated by | ||||
| 	 * mgcp_client_pool_vty_init() */ | ||||
| 	char *vty_indent; | ||||
|  | ||||
| 	/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */ | ||||
| 	struct cmd_node *vty_node; | ||||
| }; | ||||
|  | ||||
| /* Struct to handle a member of a pool of MGWs. */ | ||||
| struct mgcp_client_pool_member { | ||||
| 	/* Entry in llist mgcp_client_pool->pool. */ | ||||
| 	struct llist_head list; | ||||
|  | ||||
| 	/* The pool managing this object: */ | ||||
| 	struct mgcp_client_pool *pool; | ||||
|  | ||||
| 	/* Reference number assinged by VTY. This number is used to manage the pool from the VTY and to identify it in | ||||
| 	 * the log. */ | ||||
| 	unsigned int nr; | ||||
| @@ -24,22 +46,7 @@ struct mgcp_client_pool_member { | ||||
| 	unsigned int refcount; | ||||
| }; | ||||
|  | ||||
| /* Struct to handle a pool of MGWs. (Use _pool functions) */ | ||||
| struct mgcp_client_pool { | ||||
|  | ||||
| 	/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using | ||||
| 	 * mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */ | ||||
| 	struct mgcp_client *mgcp_client_single; | ||||
|  | ||||
| 	/* A list that manages the pool members (see above) */ | ||||
| 	struct llist_head pool; | ||||
|  | ||||
| 	/* String to use for indentation when writing the configuration file to the VTY. This field is populated by | ||||
| 	 * mgcp_client_pool_vty_init() */ | ||||
| 	char *vty_indent; | ||||
|  | ||||
| 	/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */ | ||||
| 	struct cmd_node *vty_node; | ||||
| }; | ||||
|  | ||||
| struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr); | ||||
| void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member); | ||||
| int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member); | ||||
| const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member); | ||||
|   | ||||
| @@ -13,12 +13,6 @@ AM_CFLAGS = \ | ||||
| 	$(COVERAGE_CFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(COVERAGE_LDFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| # Libraries | ||||
| SUBDIRS = \ | ||||
| 	libosmo-mgcp-client \ | ||||
|   | ||||
| @@ -14,14 +14,12 @@ AM_CFLAGS = \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(COVERAGE_LDFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| # This is not at all related to the release version, but a range of supported | ||||
| # API versions. Read TODO_RELEASE in the source tree's root! | ||||
| MGCP_CLIENT_LIBVERSION=10:0:1 | ||||
| MGCP_CLIENT_LIBVERSION=13:0:1 | ||||
|  | ||||
| lib_LTLIBRARIES = \ | ||||
| 	libosmo-mgcp-client.la \ | ||||
| @@ -35,4 +33,13 @@ libosmo_mgcp_client_la_SOURCES = \ | ||||
| 	mgcp_client_pool.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION) | ||||
| libosmo_mgcp_client_la_LDFLAGS = \ | ||||
| 	$(AM_LDFLAGS) \ | ||||
| 	-version-info $(MGCP_CLIENT_LIBVERSION) \ | ||||
| 	-no-undefined \ | ||||
| 	$(NULL) | ||||
|  | ||||
| libosmo_mgcp_client_la_LIBADD = \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(NULL) | ||||
|   | ||||
| @@ -60,6 +60,7 @@ const struct value_string osmo_mgcpc_codec_names[] = { | ||||
| 	{ CODEC_AMR_8000_1, "AMR/8000/1" }, | ||||
| 	{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" }, | ||||
| 	{ CODEC_IUFP, "VND.3GPP.IUFP/16000" }, | ||||
| 	{ CODEC_CLEARMODE, "CLEARMODE/8000" }, | ||||
| 	{ 0, NULL }, | ||||
| }; | ||||
|  | ||||
| @@ -68,17 +69,16 @@ const struct value_string osmo_mgcpc_codec_names[] = { | ||||
| static char *extract_codec_name(const char *str) | ||||
| { | ||||
| 	static char buf[64]; | ||||
| 	unsigned int i; | ||||
| 	char *pos; | ||||
|  | ||||
| 	if (!str) | ||||
| 		return NULL; | ||||
|  | ||||
| 	osmo_strlcpy(buf, str, sizeof(buf)); | ||||
|  | ||||
| 	for (i = 0; i < strlen(buf); i++) { | ||||
| 		if (buf[i] == '/') | ||||
| 			buf[i] = '\0'; | ||||
| 	} | ||||
| 	pos = strchr(buf, '/'); | ||||
| 	if (pos) | ||||
| 		*pos = '\0'; | ||||
|  | ||||
| 	return buf; | ||||
| } | ||||
| @@ -147,7 +147,7 @@ unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len, | ||||
| 	 *  is also ignored and a 1:1 mapping is performed instead. */ | ||||
|  | ||||
| 	/* we may return the codec directly since enum mgcp_codecs directly | ||||
| 	 * corresponds to the statićally assigned payload types */ | ||||
| 	 * corresponds to the statically assigned payload types */ | ||||
| 	if (codec < 96 || codec > 127) | ||||
| 		return codec; | ||||
|  | ||||
| @@ -193,9 +193,7 @@ enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len, | ||||
| 	return pt; | ||||
| } | ||||
|  | ||||
| /*! Initialize MGCP client configuration struct with default values. | ||||
|  *  \param[out] conf Client configuration.*/ | ||||
| void mgcp_client_conf_init(struct mgcp_client_conf *conf) | ||||
| static void _mgcp_client_conf_init(struct mgcp_client_conf *conf) | ||||
| { | ||||
| 	/* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */ | ||||
| 	*conf = (struct mgcp_client_conf){ | ||||
| @@ -203,11 +201,39 @@ void mgcp_client_conf_init(struct mgcp_client_conf *conf) | ||||
| 		.local_port = -1, | ||||
| 		.remote_addr = NULL, | ||||
| 		.remote_port = -1, | ||||
| 		.keepalive = { | ||||
| 			.timeout_sec = 0, /* disabled */ | ||||
| 			.req_interval_sec = 0, /* disabled */ | ||||
| 			.req_endpoint_name = MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP, | ||||
| 		}, | ||||
| 	}; | ||||
|  | ||||
| 	INIT_LLIST_HEAD(&conf->reset_epnames); | ||||
| } | ||||
|  | ||||
| /*! Allocate and initialize MGCP client configuration struct with default values. | ||||
|  *  \param[in] ctx talloc context to use as a parent during allocation. | ||||
|  * | ||||
|  * The returned struct can be freed using talloc_free(). | ||||
|  */ | ||||
| struct mgcp_client_conf *mgcp_client_conf_alloc(void *ctx) | ||||
| { | ||||
| 	struct mgcp_client_conf *conf = talloc(ctx, struct mgcp_client_conf); | ||||
| 	_mgcp_client_conf_init(conf); | ||||
| 	return conf; | ||||
| } | ||||
|  | ||||
| /*! Initialize MGCP client configuration struct with default values. | ||||
|  *  \param[out] conf Client configuration. | ||||
|  * | ||||
|  * This function is deprecated and should not be used, as it may break if size | ||||
|  * of struct mgcp_client_conf changes in the future! | ||||
|  */ | ||||
| void mgcp_client_conf_init(struct mgcp_client_conf *conf) | ||||
| { | ||||
| 	_mgcp_client_conf_init(conf); | ||||
| } | ||||
|  | ||||
| static void mgcp_client_handle_response(struct mgcp_client *mgcp, | ||||
| 					struct mgcp_response_pending *pending, | ||||
| 					struct mgcp_response *response) | ||||
| @@ -344,7 +370,7 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li | ||||
| { | ||||
| 	unsigned int pt; | ||||
| 	char codec_resp[64]; | ||||
| 	enum mgcp_codecs codec; | ||||
| 	int rc; | ||||
|  | ||||
| #define A_PTIME "a=ptime:" | ||||
| #define A_RTPMAP "a=rtpmap:" | ||||
| @@ -365,9 +391,14 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, "No more space in ptmap array (len=%u)\n", r->ptmap_len); | ||||
| 			return -ENOSPC; | ||||
| 		} | ||||
| 		codec = map_str_to_codec(codec_resp); | ||||
| 		rc = map_str_to_codec(codec_resp); | ||||
| 		if (rc < 0) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Failed to parse SDP parameter, can't parse codec in rtpmap: %s\n", osmo_quote_str(line, -1)); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 		r->ptmap[r->ptmap_len].pt = pt; | ||||
| 		r->ptmap[r->ptmap_len].codec = codec; | ||||
| 		r->ptmap[r->ptmap_len].codec = rc; | ||||
| 		r->ptmap_len++; | ||||
| 	} | ||||
|  | ||||
| @@ -442,7 +473,7 @@ static int mgcp_parse_osmux_cid(const char *line) | ||||
| 		     osmux_cid, OSMUX_CID_MAX); | ||||
| 		return -2; | ||||
| 	} | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid); | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "MGW offered Osmux CID %u\n", osmux_cid); | ||||
|  | ||||
| 	return osmux_cid; | ||||
| } | ||||
| @@ -455,15 +486,15 @@ static char *mgcp_find_section_end(char *string) | ||||
|  | ||||
| 	rc = strstr(string, "\n\n"); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
| 		return rc + 2; | ||||
|  | ||||
| 	rc = strstr(string, "\n\r\n\r"); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
| 		return rc + 4; | ||||
|  | ||||
| 	rc = strstr(string, "\r\n\r\n"); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
| 		return rc + 4; | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
| @@ -674,6 +705,14 @@ int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg) | ||||
| 	r = talloc_zero(mgcp, struct mgcp_response); | ||||
| 	OSMO_ASSERT(r); | ||||
|  | ||||
| 	/* Re-arm keepalive timer if enabled */ | ||||
| 	if (OSMO_UNLIKELY(mgcp->conn_up == false)) { | ||||
| 		LOGPMGW(mgcp, LOGL_NOTICE, "MGCP link to MGW now considered UP\n"); | ||||
| 		mgcp->conn_up = true; | ||||
| 	} | ||||
| 	if (mgcp->actual.keepalive.timeout_sec > 0) | ||||
| 		osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0); | ||||
|  | ||||
| 	rc = mgcp_response_parse_head(r, msg); | ||||
| 	if (rc) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Cannot parse MGCP response (head)\n"); | ||||
| @@ -726,10 +765,6 @@ static int mgcp_do_read(struct osmo_fd *fd) | ||||
|  | ||||
| 		msgb_free(msg); | ||||
| 		return -1; | ||||
| 	} else if (ret > 4096 - 128) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret); | ||||
| 		msgb_free(msg); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	msg->l2h = msgb_put(msg, ret); | ||||
| @@ -748,13 +783,84 @@ static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg) | ||||
| 		osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len))); | ||||
|  | ||||
| 	ret = write(fd->fd, msg->data, msg->len); | ||||
| 	if (ret != msg->len) | ||||
| 	if (OSMO_UNLIKELY(ret != msg->len)) | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n", | ||||
| 			osmo_sock_get_name2(fd->fd), errno, strerror(errno), | ||||
| 			msg->len, osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len))); | ||||
|  | ||||
| 	/* Re-arm the keepalive Tx timer: */ | ||||
| 	if (mgcp->actual.keepalive.req_interval_sec > 0) | ||||
| 		osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, const char *name) | ||||
| { | ||||
| 	static char endpoint[MGCP_ENDPOINT_MAXLEN]; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp)); | ||||
| 	if (rc > sizeof(endpoint) - 1) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '%s@%s'\n", | ||||
| 			sizeof(endpoint) - 1, name, mgcp_client_endpoint_domain(mgcp)); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (rc < 1) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Cannot compose MGCP endpoint name\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	return endpoint; | ||||
| } | ||||
|  | ||||
| /* Safely ignore the MGCP response to the DLCX sent via _mgcp_client_send_dlcx() */ | ||||
| static void _ignore_mgcp_response(struct mgcp_response *response, void *priv) { } | ||||
|  | ||||
| /* Format DLCX message (fire and forget) and send it off to the MGW */ | ||||
| static void _mgcp_client_send_dlcx(struct mgcp_client *mgcp, const char *epname) | ||||
| { | ||||
| 	struct msgb *msgb_dlcx; | ||||
| 	struct mgcp_msg mgcp_msg_dlcx = { | ||||
| 		.verb = MGCP_VERB_DLCX, | ||||
| 		.presence = MGCP_MSG_PRESENCE_ENDPOINT, | ||||
| 	}; | ||||
| 	osmo_strlcpy(mgcp_msg_dlcx.endpoint, epname, sizeof(mgcp_msg_dlcx.endpoint)); | ||||
| 	msgb_dlcx = mgcp_msg_gen(mgcp, &mgcp_msg_dlcx); | ||||
| 	mgcp_client_tx(mgcp, msgb_dlcx, &_ignore_mgcp_response, NULL); | ||||
| } | ||||
|  | ||||
| /* Format AuditEndpoint message (fire and forget) and send it off to the MGW */ | ||||
| static void _mgcp_client_send_auep(struct mgcp_client *mgcp, const char *epname) | ||||
| { | ||||
| 	struct msgb *msgb_auep; | ||||
| 	struct mgcp_msg mgcp_msg_auep = { | ||||
| 		.verb = MGCP_VERB_AUEP, | ||||
| 		.presence = MGCP_MSG_PRESENCE_ENDPOINT, | ||||
| 	}; | ||||
| 	OSMO_STRLCPY_ARRAY(mgcp_msg_auep.endpoint, epname); | ||||
| 	msgb_auep = mgcp_msg_gen(mgcp, &mgcp_msg_auep); | ||||
| 	mgcp_client_tx(mgcp, msgb_auep, &_ignore_mgcp_response, NULL); | ||||
| } | ||||
|  | ||||
| static void mgcp_client_keepalive_tx_timer_cb(void *data) | ||||
| { | ||||
| 	struct mgcp_client *mgcp = (struct mgcp_client *)data; | ||||
| 	LOGPMGW(mgcp, LOGL_INFO, "Triggering keepalive MGCP request\n"); | ||||
| 	const char *epname = _mgcp_client_name_append_domain(mgcp, mgcp->actual.keepalive.req_endpoint_name); | ||||
| 	_mgcp_client_send_auep(mgcp, epname); | ||||
|  | ||||
| 	/* Re-arm the timer: */ | ||||
| 	osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0); | ||||
| } | ||||
|  | ||||
| static void mgcp_client_keepalive_rx_timer_cb(void *data) | ||||
| { | ||||
| 	struct mgcp_client *mgcp = (struct mgcp_client *)data; | ||||
| 	LOGPMGW(mgcp, LOGL_ERROR, "MGCP link to MGW now considered DOWN (keepalive timeout, more than %u seconds with no answer from MGW)\n", | ||||
| 		mgcp->actual.keepalive.timeout_sec); | ||||
| 	mgcp->conn_up = false; | ||||
| 	/* TODO: Potentially time out all ongoing transactions for that MGW. Maybe based on VTY cfg? */ | ||||
| } | ||||
|  | ||||
| struct mgcp_client *mgcp_client_init(void *ctx, | ||||
| 				     struct mgcp_client_conf *conf) | ||||
| { | ||||
| @@ -767,13 +873,12 @@ struct mgcp_client *mgcp_client_init(void *ctx, | ||||
| 		return NULL; | ||||
|  | ||||
| 	INIT_LLIST_HEAD(&mgcp->responses_pending); | ||||
| 	INIT_LLIST_HEAD(&mgcp->inuse_endpoints); | ||||
|  | ||||
| 	mgcp->next_trans_id = 1; | ||||
|  | ||||
| 	mgcp->actual.local_addr = conf->local_addr ? conf->local_addr : | ||||
| 		MGCP_CLIENT_LOCAL_ADDR_DEFAULT; | ||||
| 	mgcp->actual.local_port = conf->local_port >= 0 ? (uint16_t)conf->local_port : | ||||
| 	mgcp->actual.local_port = conf->local_port > 0 ? (uint16_t)conf->local_port : | ||||
| 		MGCP_CLIENT_LOCAL_PORT_DEFAULT; | ||||
|  | ||||
| 	mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr : | ||||
| @@ -800,110 +905,36 @@ struct mgcp_client *mgcp_client_init(void *ctx, | ||||
| 	if (conf->description) | ||||
| 		mgcp->actual.description = talloc_strdup(mgcp, conf->description); | ||||
|  | ||||
| 	osmo_wqueue_init(&mgcp->wq, 1024); | ||||
| 	mgcp->wq.read_cb = mgcp_do_read; | ||||
| 	mgcp->wq.write_cb = mgcp_do_write; | ||||
| 	osmo_fd_setup(&mgcp->wq.bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, mgcp, 0); | ||||
|  | ||||
| 	memcpy(&mgcp->actual.keepalive, &conf->keepalive, sizeof(conf->keepalive)); | ||||
| 	osmo_timer_setup(&mgcp->keepalive_tx_timer, mgcp_client_keepalive_tx_timer_cb, mgcp); | ||||
| 	osmo_timer_setup(&mgcp->keepalive_rx_timer, mgcp_client_keepalive_rx_timer_cb, mgcp); | ||||
|  | ||||
| 	return mgcp; | ||||
| } | ||||
|  | ||||
| static int init_socket(struct mgcp_client *mgcp, unsigned int retry_n_ports) | ||||
| { | ||||
| 	int rc; | ||||
| 	struct osmo_wqueue *wq; | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	wq = &mgcp->wq; | ||||
|  | ||||
| 	for (i = 0; i < retry_n_ports + 1; i++) { | ||||
|  | ||||
| 		/* Initialize socket with the currently configured port number */ | ||||
| 		rc = osmo_sock_init2_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr, | ||||
| 					 mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port, | ||||
| 					 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT); | ||||
| 		if (rc > 0) | ||||
| 			return rc; | ||||
|  | ||||
| 		/* If there is a different port than the default port configured then we assume that the user has | ||||
| 		 * chosen that port conciously and we will not try to resolve this by silently choosing a different | ||||
| 		 * port. */ | ||||
| 		if (mgcp->actual.local_port != MGCP_CLIENT_LOCAL_PORT_DEFAULT && i == 0) | ||||
| 			return -EINVAL; | ||||
|  | ||||
| 		if (i == retry_n_ports) { | ||||
| 			/* Last try failed */ | ||||
| 			LOGPMGW(mgcp, LOGL_NOTICE, "Failed to bind to %s:%d -- check configuration!\n", | ||||
| 			     mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port); | ||||
| 			if (retry_n_ports == 0) | ||||
| 				return -EINVAL; | ||||
| 		} else { | ||||
| 			/* Choose a new port number to try next */ | ||||
| 			LOGPMGW(mgcp, LOGL_NOTICE, | ||||
| 			     "Failed to bind to %s:%d, retrying with port %d -- check configuration!\n", | ||||
| 			     mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port, | ||||
| 			     mgcp->actual.local_port + 1); | ||||
| 			mgcp->actual.local_port++; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGPMGW(mgcp, LOGL_FATAL, "Failed to find a port to bind on %u times -- check configuration!\n", i); | ||||
| 	return -EINVAL; | ||||
| } | ||||
|  | ||||
| /* Safely ignore the MGCP response to the DLCX sent via _mgcp_client_send_dlcx() */ | ||||
| static void _ignore_mgcp_response(struct mgcp_response *response, void *priv) { } | ||||
|  | ||||
| /* Format DLCX message (fire and forget) and send it off to the MGW */ | ||||
| static void _mgcp_client_send_dlcx(struct mgcp_client *mgcp, const char *epname) | ||||
| { | ||||
| 	struct msgb *msgb_dlcx; | ||||
| 	struct mgcp_msg mgcp_msg_dlcx = { | ||||
| 		.verb = MGCP_VERB_DLCX, | ||||
| 		.presence = MGCP_MSG_PRESENCE_ENDPOINT, | ||||
| 	}; | ||||
| 	osmo_strlcpy(mgcp_msg_dlcx.endpoint, epname, sizeof(mgcp_msg_dlcx.endpoint)); | ||||
| 	msgb_dlcx = mgcp_msg_gen(mgcp, &mgcp_msg_dlcx); | ||||
| 	mgcp_client_tx(mgcp, msgb_dlcx, &_ignore_mgcp_response, NULL); | ||||
| } | ||||
|  | ||||
| static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, const char *name) | ||||
| { | ||||
| 	static char endpoint[MGCP_ENDPOINT_MAXLEN]; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp)); | ||||
| 	if (rc > sizeof(endpoint) - 1) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '%s@%s'\n", | ||||
| 			sizeof(endpoint) - 1, name, mgcp_client_endpoint_domain(mgcp)); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (rc < 1) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Cannot compose MGCP endpoint name\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	return endpoint; | ||||
| } | ||||
|  | ||||
| /*! Initialize client connection (opens socket) | ||||
|  *  \param[in,out] mgcp MGCP client descriptor. | ||||
|  *  \param[in] retry_n_ports number of consecutive local ports that should be used to retry on failure. | ||||
|  *  \returns 0 on success, -EINVAL on error. */ | ||||
| int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports) | ||||
| int mgcp_client_connect(struct mgcp_client *mgcp) | ||||
| { | ||||
| 	struct osmo_wqueue *wq; | ||||
| 	int rc; | ||||
| 	struct reset_ep *reset_ep; | ||||
| 	const char *epname; | ||||
| 	bool some_dlcx_sent = false; | ||||
|  | ||||
| 	if (!mgcp) { | ||||
| 		LOGPMGW(mgcp, LOGL_FATAL, "Client not initialized properly\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	wq = &mgcp->wq; | ||||
| 	osmo_wqueue_init(wq, 1024); | ||||
| 	wq->read_cb = mgcp_do_read; | ||||
| 	wq->write_cb = mgcp_do_write; | ||||
|  | ||||
| 	osmo_fd_setup(&wq->bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, mgcp, 0); | ||||
|  | ||||
| 	rc = init_socket(mgcp, retry_n_ports); | ||||
| 	rc = osmo_sock_init2_ofd(&mgcp->wq.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr, | ||||
| 				 mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port, | ||||
| 				 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT); | ||||
| 	if (rc < 0) { | ||||
| 		LOGPMGW(mgcp, LOGL_FATAL, | ||||
| 		     "Failed to initialize socket %s:%u -> %s:%u for MGW: %s\n", | ||||
| @@ -913,7 +944,7 @@ int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports) | ||||
| 		goto error_close_fd; | ||||
| 	} | ||||
|  | ||||
| 	LOGPMGW(mgcp, LOGL_INFO, "MGW connection: %s\n", osmo_sock_get_name2(wq->bfd.fd)); | ||||
| 	LOGPMGW(mgcp, LOGL_INFO, "MGW connection: %s\n", osmo_sock_get_name2(mgcp->wq.bfd.fd)); | ||||
|  | ||||
| 	/* If configured, send a DLCX message to the endpoints that are configured to | ||||
| 	 * be reset on startup. Usually this is a wildcarded endpoint. */ | ||||
| @@ -921,20 +952,36 @@ int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports) | ||||
| 		epname = _mgcp_client_name_append_domain(mgcp, reset_ep->name); | ||||
| 		LOGPMGW(mgcp, LOGL_INFO, "Sending DLCX to: %s\n", epname); | ||||
| 		_mgcp_client_send_dlcx(mgcp, epname); | ||||
| 		some_dlcx_sent = true; | ||||
| 	} | ||||
|  | ||||
| 	if (mgcp->actual.keepalive.req_interval_sec > 0) { | ||||
| 		if (!some_dlcx_sent) { | ||||
| 			/* Attempt an immediate probe to find out if link is UP or DOWN: */ | ||||
| 			osmo_timer_schedule(&mgcp->keepalive_tx_timer, 0, 0); | ||||
| 		} | ||||
| 		/* else: keepalive_tx_timer was already scheduled (if needed) down in the stack during Tx DLCX above */ | ||||
| 	} else { | ||||
| 		/* Assume link is UP by default, so that this MGW can be selected: */ | ||||
| 		mgcp->conn_up = true; | ||||
| 	} | ||||
|  | ||||
| 	if (mgcp->actual.keepalive.timeout_sec > 0) | ||||
| 		osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0); | ||||
|  | ||||
| 	return 0; | ||||
| error_close_fd: | ||||
| 	close(wq->bfd.fd); | ||||
| 	wq->bfd.fd = -1; | ||||
| 	close(mgcp->wq.bfd.fd); | ||||
| 	mgcp->wq.bfd.fd = -1; | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /*! Initialize client connection (opens socket) | ||||
| /*! DEPRECATED: Initialize client connection (opens socket) | ||||
|  *  \param[in,out] mgcp MGCP client descriptor. | ||||
|  *  \returns 0 on success, -EINVAL on error. */ | ||||
| int mgcp_client_connect(struct mgcp_client *mgcp) | ||||
| int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports) | ||||
| { | ||||
| 	return mgcp_client_connect2(mgcp, 99); | ||||
| 	return mgcp_client_connect(mgcp); | ||||
| } | ||||
|  | ||||
| /*! Terminate client connection | ||||
| @@ -949,13 +996,18 @@ void mgcp_client_disconnect(struct mgcp_client *mgcp) | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* Disarm keepalive Tx/Rx timer until next connect() */ | ||||
| 	osmo_timer_del(&mgcp->keepalive_rx_timer); | ||||
| 	osmo_timer_del(&mgcp->keepalive_tx_timer); | ||||
| 	mgcp->conn_up = false; | ||||
|  | ||||
| 	wq = &mgcp->wq; | ||||
| 	osmo_wqueue_clear(wq); | ||||
| 	LOGPMGW(mgcp, LOGL_INFO, "MGCP association: %s -- closed!\n", osmo_sock_get_name2(wq->bfd.fd)); | ||||
| 	close(wq->bfd.fd); | ||||
| 	wq->bfd.fd = -1; | ||||
| 	if (osmo_fd_is_registered(&wq->bfd)) | ||||
| 		osmo_fd_unregister(&wq->bfd); | ||||
| 	close(wq->bfd.fd); | ||||
| 	wq->bfd.fd = -1; | ||||
| } | ||||
|  | ||||
| /*! Get the IP-Aaddress of the associated MGW as string. | ||||
| @@ -1051,11 +1103,8 @@ const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uin | ||||
| 	return epname; | ||||
| } | ||||
|  | ||||
| struct mgcp_response_pending * mgcp_client_pending_add( | ||||
| 					struct mgcp_client *mgcp, | ||||
| 					mgcp_trans_id_t trans_id, | ||||
| 					mgcp_response_cb_t response_cb, | ||||
| 					void *priv) | ||||
| struct mgcp_response_pending *mgcp_client_pending_add(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id, | ||||
| 						      mgcp_response_cb_t response_cb, void *priv) | ||||
| { | ||||
| 	struct mgcp_response_pending *pending; | ||||
|  | ||||
| @@ -1183,87 +1232,87 @@ static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp) | ||||
| static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	int rc = 0; | ||||
| 	const char *codec; | ||||
| 	unsigned int pt; | ||||
|  | ||||
| 	rc |= msgb_printf(msg, "L:"); | ||||
| #define MSGB_PRINTF_OR_RET(FMT, ARGS...) do { \ | ||||
| 		if (msgb_printf(msg, FMT, ##ARGS) != 0) { \ | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, "Message buffer too small, can not generate MGCP/SDP message\n"); \ | ||||
| 			return -ENOBUFS; \ | ||||
| 		} \ | ||||
| 	} while (0) | ||||
|  | ||||
| 	MSGB_PRINTF_OR_RET("L:"); | ||||
|  | ||||
| 	if (mgcp_msg->ptime) | ||||
| 		rc |= msgb_printf(msg, " p:%u,", mgcp_msg->ptime); | ||||
| 		MSGB_PRINTF_OR_RET(" p:%u,", mgcp_msg->ptime); | ||||
|  | ||||
| 	if (mgcp_msg->codecs_len) { | ||||
| 		rc |= msgb_printf(msg, " a:"); | ||||
| 		MSGB_PRINTF_OR_RET(" a:"); | ||||
| 		for (i = 0; i < mgcp_msg->codecs_len; i++) { | ||||
| 			pt = mgcp_msg->codecs[i]; | ||||
| 			codec = get_value_string_or_null(osmo_mgcpc_codec_names, pt); | ||||
|  | ||||
| 			/* Note: Use codec descriptors from enum mgcp_codecs | ||||
| 			 * in mgcp_client only! */ | ||||
| 			if (!codec) { | ||||
| 				msgb_free(msg); | ||||
| 			if (!codec) | ||||
| 				return -EINVAL; | ||||
| 			} | ||||
|  | ||||
| 			rc |= msgb_printf(msg, "%s", extract_codec_name(codec)); | ||||
| 			MSGB_PRINTF_OR_RET("%s", extract_codec_name(codec)); | ||||
| 			if (i < mgcp_msg->codecs_len - 1) | ||||
| 				rc |= msgb_printf(msg, ";"); | ||||
| 				MSGB_PRINTF_OR_RET(";"); | ||||
| 		} | ||||
| 		rc |= msgb_printf(msg, ","); | ||||
| 		MSGB_PRINTF_OR_RET(","); | ||||
| 	} | ||||
|  | ||||
| 	rc |= msgb_printf(msg, " nt:IN\r\n"); | ||||
|  | ||||
| 	if (rc != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "message buffer to small, can not generate MGCP message (LCO)\n"); | ||||
| 		msgb_free(msg); | ||||
| 		return -ENOBUFS; | ||||
| 	} | ||||
| 	MSGB_PRINTF_OR_RET(" nt:IN\r\n"); | ||||
| 	return 0; | ||||
|  | ||||
| #undef MSGB_PRINTF_OR_RET | ||||
| } | ||||
|  | ||||
| /* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */ | ||||
| static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_client *mgcp) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	int rc = 0; | ||||
| 	char local_ip[INET6_ADDRSTRLEN]; | ||||
| 	int local_ip_family, audio_ip_family; | ||||
| 	const char *codec; | ||||
| 	unsigned int pt; | ||||
|  | ||||
| #define MSGB_PRINTF_OR_RET(FMT, ARGS...) do { \ | ||||
| 		if (msgb_printf(msg, FMT, ##ARGS) != 0) { \ | ||||
| 			LOGPMGW(mgcp, LOGL_ERROR, "Message buffer too small, can not generate MGCP message (SDP)\n"); \ | ||||
| 			return -ENOBUFS; \ | ||||
| 		} \ | ||||
| 	} while (0) | ||||
|  | ||||
| 	/* Add separator to mark the beginning of the SDP block */ | ||||
| 	rc |= msgb_printf(msg, "\r\n"); | ||||
| 	MSGB_PRINTF_OR_RET("\r\n"); | ||||
|  | ||||
| 	/* Add SDP protocol version */ | ||||
| 	rc |= msgb_printf(msg, "v=0\r\n"); | ||||
| 	MSGB_PRINTF_OR_RET("v=0\r\n"); | ||||
|  | ||||
| 	/* Determine local IP-Address */ | ||||
| 	if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, | ||||
| 			"Could not determine local IP-Address!\n"); | ||||
| 		msgb_free(msg); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	local_ip_family = osmo_ip_str_type(local_ip); | ||||
| 	if (local_ip_family == AF_UNSPEC) { | ||||
| 		msgb_free(msg); | ||||
| 	if (local_ip_family == AF_UNSPEC) | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	audio_ip_family = osmo_ip_str_type(mgcp_msg->audio_ip); | ||||
| 	if (audio_ip_family == AF_UNSPEC) { | ||||
| 		msgb_free(msg); | ||||
| 	if (audio_ip_family == AF_UNSPEC) | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/* Add owner/creator (SDP) */ | ||||
| 	rc |= msgb_printf(msg, "o=- %x 23 IN IP%c %s\r\n", mgcp_msg->call_id, | ||||
| 	MSGB_PRINTF_OR_RET("o=- %x 23 IN IP%c %s\r\n", mgcp_msg->call_id, | ||||
| 			  local_ip_family == AF_INET6 ? '6' : '4', | ||||
| 			  local_ip); | ||||
|  | ||||
| 	/* Add session name (none) */ | ||||
| 	rc |= msgb_printf(msg, "s=-\r\n"); | ||||
| 	MSGB_PRINTF_OR_RET("s=-\r\n"); | ||||
|  | ||||
| 	/* Add RTP address and port */ | ||||
| 	if (mgcp_msg->audio_port == 0) { | ||||
| @@ -1278,20 +1327,20 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie | ||||
| 		msgb_free(msg); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	rc |= msgb_printf(msg, "c=IN IP%c %s\r\n", | ||||
| 	MSGB_PRINTF_OR_RET("c=IN IP%c %s\r\n", | ||||
| 			  audio_ip_family == AF_INET6 ? '6' : '4', | ||||
| 			  mgcp_msg->audio_ip); | ||||
|  | ||||
| 	/* Add time description, active time (SDP) */ | ||||
| 	rc |= msgb_printf(msg, "t=0 0\r\n"); | ||||
| 	MSGB_PRINTF_OR_RET("t=0 0\r\n"); | ||||
|  | ||||
| 	rc |= msgb_printf(msg, "m=audio %u RTP/AVP", mgcp_msg->audio_port); | ||||
| 	MSGB_PRINTF_OR_RET("m=audio %u RTP/AVP", mgcp_msg->audio_port); | ||||
| 	for (i = 0; i < mgcp_msg->codecs_len; i++) { | ||||
| 		pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]); | ||||
| 		rc |= msgb_printf(msg, " %u", pt); | ||||
| 		MSGB_PRINTF_OR_RET(" %u", pt); | ||||
|  | ||||
| 	} | ||||
| 	rc |= msgb_printf(msg, "\r\n"); | ||||
| 	MSGB_PRINTF_OR_RET("\r\n"); | ||||
|  | ||||
| 	/* Add optional codec parameters (fmtp) */ | ||||
| 	if (mgcp_msg->param_present) { | ||||
| @@ -1301,9 +1350,9 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie | ||||
| 				   continue; | ||||
| 			pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]); | ||||
| 			if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned) | ||||
| 				rc |= msgb_printf(msg, "a=fmtp:%u octet-align=1\r\n", pt); | ||||
| 				MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=1\r\n", pt); | ||||
| 			else if (mgcp_msg->param.amr_octet_aligned_present && !mgcp_msg->param.amr_octet_aligned) | ||||
| 				rc |= msgb_printf(msg, "a=fmtp:%u octet-align=0\r\n", pt); | ||||
| 				MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=0\r\n", pt); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -1318,25 +1367,18 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie | ||||
|  | ||||
| 			/* Note: Use codec descriptors from enum mgcp_codecs | ||||
| 			 * in mgcp_client only! */ | ||||
| 			if (!codec) { | ||||
| 				msgb_free(msg); | ||||
| 			if (!codec) | ||||
| 				return -EINVAL; | ||||
| 			} | ||||
|  | ||||
| 			rc |= msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec); | ||||
| 			MSGB_PRINTF_OR_RET("a=rtpmap:%u %s\r\n", pt, codec); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (mgcp_msg->ptime) | ||||
| 		rc |= msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime); | ||||
|  | ||||
| 	if (rc != 0) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Message buffer to small, can not generate MGCP message (SDP)\n"); | ||||
| 		msgb_free(msg); | ||||
| 		return -ENOBUFS; | ||||
| 	} | ||||
| 		MSGB_PRINTF_OR_RET("a=ptime:%u\r\n", mgcp_msg->ptime); | ||||
|  | ||||
| 	return 0; | ||||
| #undef MSGB_PRINTF_OR_RET | ||||
| } | ||||
|  | ||||
| /*! Generate an MGCP message | ||||
| @@ -1348,10 +1390,16 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) | ||||
| 	mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp); | ||||
| 	uint32_t mandatory_mask; | ||||
| 	struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx"); | ||||
| 	int rc = 0; | ||||
| 	bool use_sdp = false; | ||||
| 	char buf[32]; | ||||
|  | ||||
| #define MSGB_PRINTF_OR_RET(FMT, ARGS...) do { \ | ||||
| 		if (msgb_printf(msg, FMT, ##ARGS) != 0) { \ | ||||
| 			LOGPMGW(mgcp, LOGL_ERROR, "Message buffer too small, can not generate MGCP/SDP message\n"); \ | ||||
| 			goto exit_error; \ | ||||
| 		} \ | ||||
| 	} while (0) | ||||
|  | ||||
| 	msg->l2h = msg->data; | ||||
| 	msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id; | ||||
|  | ||||
| @@ -1359,75 +1407,70 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) | ||||
| 	switch (mgcp_msg->verb) { | ||||
| 	case MGCP_VERB_CRCX: | ||||
| 		mandatory_mask = MGCP_CRCX_MANDATORY; | ||||
| 		rc |= msgb_printf(msg, "CRCX %u", trans_id); | ||||
| 		MSGB_PRINTF_OR_RET("CRCX %u", trans_id); | ||||
| 		break; | ||||
| 	case MGCP_VERB_MDCX: | ||||
| 		mandatory_mask = MGCP_MDCX_MANDATORY; | ||||
| 		rc |= msgb_printf(msg, "MDCX %u", trans_id); | ||||
| 		MSGB_PRINTF_OR_RET("MDCX %u", trans_id); | ||||
| 		break; | ||||
| 	case MGCP_VERB_DLCX: | ||||
| 		mandatory_mask = MGCP_DLCX_MANDATORY; | ||||
| 		rc |= msgb_printf(msg, "DLCX %u", trans_id); | ||||
| 		MSGB_PRINTF_OR_RET("DLCX %u", trans_id); | ||||
| 		break; | ||||
| 	case MGCP_VERB_AUEP: | ||||
| 		mandatory_mask = MGCP_AUEP_MANDATORY; | ||||
| 		rc |= msgb_printf(msg, "AUEP %u", trans_id); | ||||
| 		MSGB_PRINTF_OR_RET("AUEP %u", trans_id); | ||||
| 		break; | ||||
| 	case MGCP_VERB_RSIP: | ||||
| 		mandatory_mask = MGCP_RSIP_MANDATORY; | ||||
| 		rc |= msgb_printf(msg, "RSIP %u", trans_id); | ||||
| 		MSGB_PRINTF_OR_RET("RSIP %u", trans_id); | ||||
| 		break; | ||||
| 	default: | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Invalid command verb, can not generate MGCP message\n"); | ||||
| 		msgb_free(msg); | ||||
| 		return NULL; | ||||
| 		goto exit_error; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if mandatory fields are missing */ | ||||
| 	if (!((mgcp_msg->presence & mandatory_mask) == mandatory_mask)) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, | ||||
| 			"One or more missing mandatory fields, can not generate MGCP message\n"); | ||||
| 		msgb_free(msg); | ||||
| 		return NULL; | ||||
| 		goto exit_error; | ||||
| 	} | ||||
|  | ||||
| 	/* Add endpoint name */ | ||||
| 	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_ENDPOINT) { | ||||
| 		if (strlen(mgcp_msg->endpoint) <= 0) { | ||||
| 			LOGPMGW(mgcp, LOGL_ERROR, "Empty endpoint name, can not generate MGCP message\n"); | ||||
| 			msgb_free(msg); | ||||
| 			return NULL; | ||||
| 			goto exit_error; | ||||
| 		} | ||||
|  | ||||
| 		if (strstr(mgcp_msg->endpoint, "@") == NULL) { | ||||
| 			LOGPMGW(mgcp, LOGL_ERROR, | ||||
| 				"Endpoint name (%s) lacks separator (@), can not generate MGCP message\n", | ||||
| 				mgcp_msg->endpoint); | ||||
| 			msgb_free(msg); | ||||
| 			return NULL; | ||||
| 			goto exit_error; | ||||
| 		} | ||||
|  | ||||
| 		rc |= msgb_printf(msg, " %s", mgcp_msg->endpoint); | ||||
| 		MSGB_PRINTF_OR_RET(" %s", mgcp_msg->endpoint); | ||||
| 	} | ||||
|  | ||||
| 	/* Add protocol version */ | ||||
| 	rc |= msgb_printf(msg, " MGCP 1.0\r\n"); | ||||
| 	MSGB_PRINTF_OR_RET(" MGCP 1.0\r\n"); | ||||
|  | ||||
| 	/* Add call id */ | ||||
| 	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CALL_ID) | ||||
| 		rc |= msgb_printf(msg, "C: %x\r\n", mgcp_msg->call_id); | ||||
| 		MSGB_PRINTF_OR_RET("C: %x\r\n", mgcp_msg->call_id); | ||||
|  | ||||
| 	/* Add connection id */ | ||||
| 	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID) { | ||||
| 		if (strlen(mgcp_msg->conn_id) <= 0) { | ||||
| 			LOGPMGW(mgcp, LOGL_ERROR, "Empty connection id, can not generate MGCP message\n"); | ||||
| 			msgb_free(msg); | ||||
| 			return NULL; | ||||
| 			goto exit_error; | ||||
| 		} | ||||
| 		rc |= msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id); | ||||
| 		MSGB_PRINTF_OR_RET("I: %s\r\n", mgcp_msg->conn_id); | ||||
| 	} | ||||
|  | ||||
| 	/* Using SDP makes sense when a valid IP/Port combination is specifiec, | ||||
| 	/* Using SDP makes sense when a valid IP/Port combination is specified, | ||||
| 	 * if we do not know this information yet, we fall back to LCO */ | ||||
| 	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP | ||||
| 	    && mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) | ||||
| @@ -1437,35 +1480,32 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) | ||||
| 	if (!use_sdp | ||||
| 	    && (mgcp_msg->verb == MGCP_VERB_CRCX | ||||
| 		|| mgcp_msg->verb == MGCP_VERB_MDCX)) { | ||||
| 	        if (add_lco(msg, mgcp_msg) < 0) | ||||
| 			return NULL; | ||||
| 		if (add_lco(msg, mgcp_msg) < 0) { | ||||
| 			LOGPMGW(mgcp, LOGL_ERROR, "Failed to add LCO, can not generate MGCP message\n"); | ||||
| 			goto exit_error; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Add mode */ | ||||
| 	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE) | ||||
| 		rc |= | ||||
| 		    msgb_printf(msg, "M: %s\r\n", | ||||
| 				mgcp_client_cmode_name(mgcp_msg->conn_mode)); | ||||
| 		MSGB_PRINTF_OR_RET("M: %s\r\n", mgcp_client_cmode_name(mgcp_msg->conn_mode)); | ||||
|  | ||||
| 	/* Add X-Osmo-IGN */ | ||||
| 	if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_IGN) | ||||
| 	    && (mgcp_msg->x_osmo_ign != 0)) | ||||
| 		rc |= | ||||
| 		    msgb_printf(msg, MGCP_X_OSMO_IGN_HEADER "%s\r\n", | ||||
| 				mgcp_msg->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID ? " C": ""); | ||||
| 		MSGB_PRINTF_OR_RET(MGCP_X_OSMO_IGN_HEADER "%s\r\n", | ||||
| 				   mgcp_msg->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID ? " C" : ""); | ||||
|  | ||||
| 	/* Add X-Osmo-Osmux */ | ||||
| 	if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID)) { | ||||
| 		if (mgcp_msg->x_osmo_osmux_cid < -1 || mgcp_msg->x_osmo_osmux_cid > OSMUX_CID_MAX) { | ||||
| 			LOGPMGW(mgcp, LOGL_ERROR, "Wrong Osmux CID %d, can not generate MGCP message\n", | ||||
| 				mgcp_msg->x_osmo_osmux_cid); | ||||
| 			msgb_free(msg); | ||||
| 			return NULL; | ||||
| 			goto exit_error; | ||||
| 		} | ||||
| 		snprintf(buf, sizeof(buf), " %d", mgcp_msg->x_osmo_osmux_cid); | ||||
| 		rc |= | ||||
| 		    msgb_printf(msg, MGCP_X_OSMO_OSMUX_HEADER "%s\r\n", | ||||
| 				mgcp_msg->x_osmo_osmux_cid == -1 ? " *": buf); | ||||
| 		MSGB_PRINTF_OR_RET(MGCP_X_OSMO_OSMUX_HEADER "%s\r\n", | ||||
| 				   mgcp_msg->x_osmo_osmux_cid == -1 ? " *" : buf); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @@ -1473,17 +1513,18 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) | ||||
| 	if (use_sdp | ||||
| 	    && (mgcp_msg->verb == MGCP_VERB_CRCX | ||||
| 		|| mgcp_msg->verb == MGCP_VERB_MDCX)) { | ||||
| 	        if (add_sdp(msg, mgcp_msg, mgcp) < 0) | ||||
| 			return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (rc != 0) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Message buffer to small, can not generate MGCP message\n"); | ||||
| 		msgb_free(msg); | ||||
| 		msg = NULL; | ||||
| 		if (add_sdp(msg, mgcp_msg, mgcp) < 0) { | ||||
| 			LOGPMGW(mgcp, LOGL_ERROR, "Failed to add SDP, can not generate MGCP message\n"); | ||||
| 			goto exit_error; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return msg; | ||||
|  | ||||
| exit_error: | ||||
| 	msgb_free(msg); | ||||
| 	return NULL; | ||||
| #undef MSGB_PRINTF_OR_RET | ||||
| } | ||||
|  | ||||
| /*! Retrieve the MGCP transaction ID from a msgb generated by mgcp_msg_gen() | ||||
| @@ -1507,6 +1548,7 @@ const struct value_string mgcp_client_connection_mode_strs[] = { | ||||
| 	{ MGCP_CONN_RECV_SEND, "sendrecv" }, | ||||
| 	{ MGCP_CONN_SEND_ONLY, "sendonly" }, | ||||
| 	{ MGCP_CONN_RECV_ONLY, "recvonly" }, | ||||
| 	{ MGCP_CONN_CONFECHO, "confecho" }, | ||||
| 	{ MGCP_CONN_LOOPBACK, "loopback" }, | ||||
| 	{ 0, NULL } | ||||
| }; | ||||
|   | ||||
| @@ -173,6 +173,30 @@ const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep) | ||||
| 	return osmo_fsm_inst_name(ep->fi); | ||||
| } | ||||
|  | ||||
| /*! Get "local endpoint name" part of the endpoint name: (local-endpoint-name@domain-name) | ||||
|  * | ||||
|  * \param ep  The MGCP Endpoint | ||||
|  * \returns the local endpoint name if found, NULL on error. | ||||
|  */ | ||||
| const char *osmo_mgcpc_ep_local_name(const struct osmo_mgcpc_ep *ep) | ||||
| { | ||||
| 	static char buf[1024]; | ||||
| 	const char *sep; | ||||
|  | ||||
| 	OSMO_ASSERT(ep); | ||||
| 	sep = strchr(ep->endpoint, '@'); | ||||
| 	if (!sep) { | ||||
| 		OSMO_STRLCPY_ARRAY(buf, ep->endpoint); | ||||
| 		return buf; | ||||
| 	} | ||||
| 	if (sep - ep->endpoint >= sizeof(buf)) | ||||
| 		return NULL; | ||||
|  | ||||
| 	memcpy(buf, ep->endpoint, sep - ep->endpoint); | ||||
| 	buf[sep - ep->endpoint] = '\0'; | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| const char *mgcp_conn_peer_name(const struct mgcp_conn_peer *info) | ||||
| { | ||||
| 	/* I'd be fine with a smaller buffer and accept truncation, but gcc possibly refuses to build if | ||||
| @@ -227,7 +251,7 @@ static struct value_string osmo_mgcpc_ep_fsm_event_names[33] = {}; | ||||
|  | ||||
| static char osmo_mgcpc_ep_fsm_event_name_bufs[32][32] = {}; | ||||
|  | ||||
| static void fill_event_names() | ||||
| static void fill_event_names(void) | ||||
| { | ||||
| 	int i; | ||||
| 	for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) { | ||||
| @@ -243,7 +267,7 @@ static void fill_event_names() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init() | ||||
| static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init(void) | ||||
| { | ||||
| 	OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0); | ||||
| 	fill_event_names(); | ||||
| @@ -997,7 +1021,7 @@ static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) | ||||
| static void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) | ||||
| { | ||||
| 	int i; | ||||
| 	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi); | ||||
|   | ||||
| @@ -760,7 +760,7 @@ const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info) | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| static __attribute__((constructor)) void osmo_mgcp_client_fsm_init() | ||||
| static __attribute__((constructor)) void osmo_mgcp_client_fsm_init(void) | ||||
| { | ||||
| 	OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0); | ||||
| } | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <asm-generic/errno.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_internal.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_pool_internal.h> | ||||
| @@ -27,32 +28,6 @@ | ||||
| #define LOGPPMGW(pool_member, level, fmt, args...) \ | ||||
| LOGP(DLMGCP, level, "MGW-pool(%s) " fmt, mgcp_client_pool_member_name(pool_member), ## args) | ||||
|  | ||||
| /* Get a human readable name for a given pool member. */ | ||||
| const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member) | ||||
| { | ||||
| 	const struct mgcp_client *mpcp_client; | ||||
| 	struct mgcp_client mpcp_client_dummy; | ||||
| 	static char name[512]; | ||||
| 	const char *description; | ||||
|  | ||||
| 	if (!pool_member) | ||||
| 		return "(null)"; | ||||
|  | ||||
| 	/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the | ||||
| 	 * initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */ | ||||
| 	if (!pool_member->client) { | ||||
| 		memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual)); | ||||
| 		mpcp_client = &mpcp_client_dummy; | ||||
| 	} else { | ||||
| 		mpcp_client = pool_member->client; | ||||
| 	} | ||||
|  | ||||
| 	description = mgcp_client_name(mpcp_client); | ||||
| 	snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description); | ||||
|  | ||||
| 	return name; | ||||
| } | ||||
|  | ||||
| /*! Allocate MGCP client pool. This is called once on startup and before the pool is used with | ||||
|  *  mgcp_client_pool_vty_init(). Since the pool is linked with the VTY it must exist througout the entire runtime. | ||||
|  *  \param[in] talloc_ctx talloc context. */ | ||||
| @@ -64,7 +39,7 @@ struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx) | ||||
| 	if (!pool) | ||||
| 		return NULL; | ||||
|  | ||||
| 	INIT_LLIST_HEAD(&pool->pool); | ||||
| 	INIT_LLIST_HEAD(&pool->member_list); | ||||
|  | ||||
| 	return pool; | ||||
| } | ||||
| @@ -77,29 +52,11 @@ unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool) | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
| 	unsigned int pool_members_initialized = 0; | ||||
|  | ||||
| 	llist_for_each_entry(pool_member, &pool->pool, list) { | ||||
| 	llist_for_each_entry(pool_member, &pool->member_list, list) { | ||||
|  | ||||
| 		/* Initialize client */ | ||||
| 		pool_member->client = mgcp_client_init(pool_member, &pool_member->conf); | ||||
| 		if (!pool_member->client) { | ||||
| 			LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n"); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		/* Set backpointer so that we can detect later that this MGCP client is managed | ||||
| 		 * by this pool. */ | ||||
| 		pool_member->client->pool = pool; | ||||
|  | ||||
| 		/* Connect client */ | ||||
| 		if (mgcp_client_connect2(pool_member->client, 0)) { | ||||
| 			LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n", | ||||
| 				 pool_member->conf.remote_addr, pool_member->conf.remote_port); | ||||
| 			talloc_free(pool_member->client); | ||||
| 			pool_member->client = NULL; | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		pool_members_initialized++; | ||||
| 		if (mgcp_client_pool_member_reinit_client(pool_member) == 0) | ||||
| 			pool_members_initialized++; | ||||
| 	} | ||||
|  | ||||
| 	return pool_members_initialized; | ||||
| @@ -118,23 +75,43 @@ void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp | ||||
| 	pool->mgcp_client_single = mgcp_client; | ||||
| } | ||||
|  | ||||
| bool mgcp_client_pool_empty(const struct mgcp_client_pool *pool) | ||||
| { | ||||
| 	return llist_empty(&pool->member_list); | ||||
| } | ||||
|  | ||||
| /*! Lookup the selected MGCP client config by its reference number */ | ||||
| struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr) | ||||
| { | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
|  | ||||
| 	llist_for_each_entry(pool_member, &pool->member_list, list) { | ||||
| 		if (pool_member->nr == nr) | ||||
| 			return pool_member; | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /* Not every pool member may have a functional MGCP client, we will run through the pool once until we meet a | ||||
|  * pool member that is suitable (has a client, is not blocked, has a low load). */ | ||||
|  * pool member that is suitable (is not blocked, has a client with a working link, has a low load). */ | ||||
| static struct mgcp_client_pool_member *mgcp_client_pool_pick(struct mgcp_client_pool *pool) | ||||
| { | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
| 	struct mgcp_client_pool_member *pool_member_picked = NULL; | ||||
| 	unsigned int n_pool_members = llist_count(&pool->pool); | ||||
| 	unsigned int n_pool_members = 0; | ||||
|  | ||||
| 	llist_for_each_entry(pool_member, &pool->pool, list) { | ||||
| 		if (pool_member->blocked == false && pool_member->client) { | ||||
| 	llist_for_each_entry(pool_member, &pool->member_list, list) { | ||||
| 		n_pool_members++; | ||||
| 		bool conn_up = pool_member->client && pool_member->client->conn_up; | ||||
| 		if (pool_member->blocked == false && conn_up) { | ||||
| 			if (!pool_member_picked) | ||||
| 				pool_member_picked = pool_member; | ||||
| 			else if (pool_member_picked->refcount > pool_member->refcount) | ||||
| 				pool_member_picked = pool_member; | ||||
| 		} else { | ||||
| 			LOGPPMGW(pool_member, LOGL_DEBUG, "MGW pool has %u members -- MGW %u is unusable\n", n_pool_members, | ||||
| 				 pool_member->nr); | ||||
| 			LOGPPMGW(pool_member, LOGL_DEBUG, "%s -- MGW %u is unusable (blocked=%u, cli=%u, link=%u)\n", | ||||
| 				 __func__, pool_member->nr, pool_member->blocked, !!pool_member->client, conn_up); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -163,26 +140,24 @@ struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool) | ||||
| 	 *  by the application code. */ | ||||
|  | ||||
| 	/* When the pool is empty, return a single MGCP client if it is registered. */ | ||||
| 	if (llist_empty(&pool->pool) && pool->mgcp_client_single) { | ||||
| 	if (llist_empty(&pool->member_list) && pool->mgcp_client_single) { | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "MGW pool is empty -- using (single) MGW %s\n", | ||||
| 		     mgcp_client_name(pool->mgcp_client_single)); | ||||
| 		return pool->mgcp_client_single; | ||||
| 	} | ||||
|  | ||||
| 	/* Abort when the pool is empty */ | ||||
| 	if (llist_empty(&pool->pool)) { | ||||
| 	if (llist_empty(&pool->member_list)) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "MGW pool is empty -- no MGW available!\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* Pick a suitable pool member */ | ||||
| 	pool_member = mgcp_client_pool_pick(pool); | ||||
| 	if (pool_member) { | ||||
| 		pool_member->refcount++; | ||||
| 		return pool_member->client; | ||||
| 	} | ||||
| 	if (!pool_member) | ||||
| 		return NULL; | ||||
|  | ||||
| 	return NULL; | ||||
| 	return mgcp_client_pool_member_get(pool_member); | ||||
| } | ||||
|  | ||||
| /*! put an MGCP client back into the pool (decrement reference counter). | ||||
| @@ -194,23 +169,130 @@ struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool) | ||||
| void mgcp_client_pool_put(struct mgcp_client *mgcp_client) | ||||
| { | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
| 	struct mgcp_client_pool *pool; | ||||
|  | ||||
| 	if (!mgcp_client) | ||||
| 		return; | ||||
|  | ||||
| 	if (mgcp_client->pool) | ||||
| 		pool = mgcp_client->pool; | ||||
| 	else | ||||
| 	if (!mgcp_client->pool_member) | ||||
| 		return; | ||||
| 	pool_member = mgcp_client->pool_member; | ||||
|  | ||||
| 	llist_for_each_entry(pool_member, &pool->pool, list) { | ||||
| 		if (pool_member->client == mgcp_client) { | ||||
| 			if (pool_member->refcount == 0) { | ||||
| 				LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n"); | ||||
| 				return; | ||||
| 			} | ||||
| 			pool_member->refcount--; | ||||
| 		} | ||||
| 	if (pool_member->refcount == 0) { | ||||
| 		LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 	pool_member->refcount--; | ||||
| } | ||||
|  | ||||
| /*************************** | ||||
|  * mgcp_client_pool_member: | ||||
|  ***************************/ | ||||
|  | ||||
| /*! Allocate an mgcp_client_pool_member. | ||||
|  *  \param[in] pool MGCP client pool descriptor. | ||||
|  *  \param[in] nr Reference number of the pool member. | ||||
|  */ | ||||
| struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr) | ||||
| { | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
|  | ||||
| 	pool_member = talloc_zero(pool, struct mgcp_client_pool_member); | ||||
| 	OSMO_ASSERT(pool_member); | ||||
| 	mgcp_client_conf_init(&pool_member->conf); | ||||
| 	pool_member->pool = pool; | ||||
| 	pool_member->nr = nr; | ||||
| 	llist_add_tail(&pool_member->list, &pool->member_list); | ||||
| 	return pool_member; | ||||
| } | ||||
|  | ||||
| /*! Free an mgcp_client_pool_member allocated through mgcp_client_pool_member_alloc(). | ||||
|  *  \param[in] pool_member MGCP client pool descriptor. | ||||
|  * | ||||
|  * It also frees the associated MGCP client if present. | ||||
|  */ | ||||
| void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member) | ||||
| { | ||||
| 	llist_del(&pool_member->list); | ||||
| 	if (pool_member->client) { | ||||
| 		mgcp_client_disconnect(pool_member->client); | ||||
| 		talloc_free(pool_member->client); | ||||
| 	} | ||||
| 	talloc_free(pool_member); | ||||
| } | ||||
|  | ||||
| /*! Recreate and reconnect the MGCP client associated to the pool descriptor. | ||||
|  *  \param[in] pool_member MGCP client pool descriptor. | ||||
|  */ | ||||
| int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member) | ||||
| { | ||||
| 	/* Get rid of a possibly existing old MGCP client instance first */ | ||||
| 	if (pool_member->client) { | ||||
| 		mgcp_client_disconnect(pool_member->client); | ||||
| 		talloc_free(pool_member->client); | ||||
| 	} | ||||
|  | ||||
| 	/* Initialize client */ | ||||
| 	pool_member->client = mgcp_client_init(pool_member, &pool_member->conf); | ||||
| 	if (!pool_member->client) { | ||||
| 		LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */ | ||||
| 	pool_member->client->pool_member = pool_member; | ||||
|  | ||||
| 	/* Connect client */ | ||||
| 	if (mgcp_client_connect(pool_member->client)) { | ||||
| 		LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n", | ||||
| 				pool_member->conf.remote_addr, pool_member->conf.remote_port); | ||||
| 		talloc_free(pool_member->client); | ||||
| 		pool_member->client = NULL; | ||||
| 		return -ECONNABORTED; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Get a human readable name for a given pool member. */ | ||||
| const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member) | ||||
| { | ||||
| 	const struct mgcp_client *mpcp_client; | ||||
| 	struct mgcp_client mpcp_client_dummy; | ||||
| 	static char name[512]; | ||||
| 	const char *description; | ||||
|  | ||||
| 	if (!pool_member) | ||||
| 		return "(null)"; | ||||
|  | ||||
| 	/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the | ||||
| 	 * initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */ | ||||
| 	if (!pool_member->client) { | ||||
| 		memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual)); | ||||
| 		mpcp_client = &mpcp_client_dummy; | ||||
| 	} else { | ||||
| 		mpcp_client = pool_member->client; | ||||
| 	} | ||||
|  | ||||
| 	description = mgcp_client_name(mpcp_client); | ||||
| 	snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description); | ||||
|  | ||||
| 	return name; | ||||
| } | ||||
|  | ||||
| /*! Get the MGCP client associated with the pool reference from the pool (increment reference counter). | ||||
|  *  \param[in] pool_member MGCP client pool descriptor. | ||||
|  *  \returns MGCP client descriptor, NULL if no member was not ready. | ||||
|  */ | ||||
| struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member) | ||||
| { | ||||
| 	pool_member->refcount++; | ||||
| 	return pool_member->client; | ||||
| } | ||||
|  | ||||
| /*! Get whether the MGCP client associated with the pool reference is blocked by policy. | ||||
|  *  \param[in] pool_member MGCP client pool descriptor. | ||||
|  *  \returns true if blocked, false otherwise | ||||
|  */ | ||||
| bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member) | ||||
| { | ||||
| 	return pool_member->blocked; | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* MGCP client interface to quagga VTY */ | ||||
| /* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de> | ||||
| /* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c) | ||||
|  * (C) 2009 by Harald Welte <laforge@gnumonks.org> | ||||
|  * (C) 2009-2011 by Holger Hans Peter Freyther | ||||
| @@ -28,14 +28,16 @@ | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/vty/misc.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/timer.h> | ||||
|  | ||||
| #include <osmocom/mgcp_client/mgcp_client.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_internal.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_pool_internal.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_pool.h> | ||||
|  | ||||
| #define MGW_STR MGCP_CLIENT_MGW_STR | ||||
|  | ||||
| /* Only common (non-pooled) VTY connands will use this talloc context. All | ||||
| /* Only common (non-pooled) VTY commands will use this talloc context. All | ||||
|  * pooled VTY commands will use the pool (global_mgcp_client_pool) as | ||||
|  * talloc context. */ | ||||
| static void *global_mgcp_client_ctx = NULL; | ||||
| @@ -47,17 +49,46 @@ static struct mgcp_client_conf *global_mgcp_client_conf = NULL; | ||||
| /* Pointer to the MGCP pool that is managed by mgcp_client_pool_vty_init() */ | ||||
| static struct mgcp_client_pool *global_mgcp_client_pool = NULL; | ||||
|  | ||||
| struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty) | ||||
| static struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty) | ||||
| { | ||||
| 	if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node) | ||||
| 		return vty->index; | ||||
| 	else | ||||
| 		return global_mgcp_client_conf; | ||||
|  | ||||
| 	/* Global single MGCP config, deprecated: */ | ||||
| 	vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. " | ||||
| 		"You should consider reading the User Manual and migrating to 'mgw' node.%s", | ||||
| 		VTY_NEWLINE); | ||||
|  | ||||
| 	return global_mgcp_client_conf; | ||||
| } | ||||
|  | ||||
| static struct mgcp_client *get_mgcp_client(struct vty *vty) | ||||
| { | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
|  | ||||
| 	if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node) { | ||||
| 		llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) { | ||||
| 			/* Find matching the conf pointer: */ | ||||
| 			if (&pool_member->conf != conf) | ||||
| 				continue; | ||||
| 			return pool_member->client; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Global single MGCP config, deprecated: */ | ||||
| 	vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. " | ||||
| 		"You should consider reading the User Manual and migrating to 'mgw' node.%s", | ||||
| 		VTY_NEWLINE); | ||||
|  | ||||
| 	/* There's no way to obtain the struct mgcp_client in old interface, but anyway it's deprecated. */ | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
|  | ||||
| DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd, | ||||
|       "mgw local-ip " VTY_IPV46_CMD, | ||||
|       MGW_STR "local bind to connect to MGW from\n" | ||||
|       "local-ip " VTY_IPV46_CMD, | ||||
|       "local bind to connect to MGW from\n" | ||||
|       "local bind IPv4 address\n" | ||||
|       "local bind IPv6 address\n") | ||||
| { | ||||
| @@ -72,10 +103,16 @@ ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd, | ||||
| 		 "mgcpgw local-ip A.B.C.D", | ||||
| 		 MGW_STR "local bind to connect to MGCP gateway with\n" | ||||
| 		 "local bind IP address\n") | ||||
| ALIAS_DEPRECATED(cfg_mgw_local_ip, | ||||
| 		 cfg_mgw_mgw_local_ip_cmd, | ||||
| 		 "mgw local-ip " VTY_IPV46_CMD, | ||||
| 		 MGW_STR "local bind to connect to MGW from\n" | ||||
| 		 "local bind IPv4 address\n" | ||||
| 		 "local bind IPv6 address\n") | ||||
|  | ||||
| DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd, | ||||
|       "mgw local-port <0-65535>", | ||||
|       MGW_STR "local port to connect to MGW from\n" | ||||
|       "local-port <0-65535>", | ||||
|       "local port to connect to MGW from\n" | ||||
|       "local bind port\n") | ||||
| { | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
| @@ -87,10 +124,15 @@ ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd, | ||||
| 		 "mgcpgw local-port <0-65535>", | ||||
| 		 MGW_STR "local bind to connect to MGCP gateway with\n" | ||||
| 		 "local bind port\n") | ||||
| ALIAS_DEPRECATED(cfg_mgw_local_port, | ||||
| 		 cfg_mgw_mgw_local_port_cmd, | ||||
| 		 "mgw local-port <0-65535>", | ||||
| 		 MGW_STR "local port to connect to MGW from\n" | ||||
| 		 "local bind port\n") | ||||
|  | ||||
| DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd, | ||||
|       "mgw remote-ip " VTY_IPV46_CMD, | ||||
|       MGW_STR "remote IP address to reach the MGW at\n" | ||||
|       "remote-ip " VTY_IPV46_CMD, | ||||
|       "remote IP address to reach the MGW at\n" | ||||
|       "remote IPv4 address\n" | ||||
|       "remote IPv6 address\n") | ||||
| { | ||||
| @@ -104,10 +146,16 @@ ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd, | ||||
| 		 "mgcpgw remote-ip A.B.C.D", | ||||
| 		 MGW_STR "remote bind to connect to MGCP gateway with\n" | ||||
| 		 "remote bind IP address\n") | ||||
| ALIAS_DEPRECATED(cfg_mgw_remote_ip, | ||||
| 		 cfg_mgw_mgw_remote_ip_cmd, | ||||
| 		 "mgw remote-ip " VTY_IPV46_CMD, | ||||
| 		 MGW_STR "remote IP address to reach the MGW at\n" | ||||
| 		 "remote IPv4 address\n" | ||||
| 		 "remote IPv6 address\n") | ||||
|  | ||||
| DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd, | ||||
|       "mgw remote-port <0-65535>", | ||||
|       MGW_STR "remote port to reach the MGW at\n" | ||||
|       "remote-port <0-65535>", | ||||
|       "remote port to reach the MGW at\n" | ||||
|       "remote port\n") | ||||
| { | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
| @@ -119,8 +167,13 @@ ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd, | ||||
| 		 "mgcpgw remote-port <0-65535>", | ||||
| 		 MGW_STR "remote bind to connect to MGCP gateway with\n" | ||||
| 		 "remote bind port\n") | ||||
| ALIAS_DEPRECATED(cfg_mgw_remote_port, | ||||
| 		 cfg_mgw_mgw_remote_port_cmd, | ||||
| 		 "mgw remote-port <0-65535>", | ||||
| 		 MGW_STR "remote port to reach the MGW at\n" | ||||
| 		 "remote port\n") | ||||
|  | ||||
| DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd, | ||||
| DEFUN_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgw_mgw_endpoint_range_cmd, | ||||
|       "mgw endpoint-range <1-65534> <1-65534>", | ||||
|       MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n" | ||||
|       "-\n" "-\n") | ||||
| @@ -130,7 +183,7 @@ DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd, | ||||
| 		VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd, | ||||
| ALIAS_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd, | ||||
|       "mgcpgw endpoint-range <1-65534> <1-65534>", | ||||
|       MGW_STR "usable range of endpoint identifiers\n" | ||||
|       "set first useable endpoint identifier\n" | ||||
| @@ -158,8 +211,8 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port, | ||||
|  | ||||
| DEFUN(cfg_mgw_endpoint_domain_name, | ||||
|       cfg_mgw_endpoint_domain_name_cmd, | ||||
|       "mgw endpoint-domain NAME", | ||||
|       MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n" | ||||
|       "endpoint-domain NAME", | ||||
|       "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n" | ||||
|       "Domain name, should be alphanumeric.\n") | ||||
| { | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
| @@ -172,11 +225,16 @@ DEFUN(cfg_mgw_endpoint_domain_name, | ||||
| 	} | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgw_endpoint_domain_name, | ||||
|       cfg_mgw_mgw_endpoint_domain_name_cmd, | ||||
|       "mgw endpoint-domain NAME", | ||||
|       MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n" | ||||
|       "Domain name, should be alphanumeric.\n") | ||||
|  | ||||
| DEFUN(cfg_mgw_reset_ep_name, | ||||
|       cfg_mgw_reset_ep_name_cmd, | ||||
|       "mgw reset-endpoint NAME", | ||||
|       MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list," | ||||
|       "reset-endpoint NAME", | ||||
|       "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list," | ||||
|       "e.g. 'rtpbridge/*'\n" | ||||
|       "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n") | ||||
| { | ||||
| @@ -214,11 +272,17 @@ DEFUN(cfg_mgw_reset_ep_name, | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgw_reset_ep_name, | ||||
|       cfg_mgw_mgw_reset_ep_name_cmd, | ||||
|       "mgw reset-endpoint NAME", | ||||
|       MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list," | ||||
|       "e.g. 'rtpbridge/*'\n" | ||||
|       "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n") | ||||
|  | ||||
| DEFUN(cfg_mgw_no_reset_ep_name, | ||||
|       cfg_mgw_no_reset_ep_name_cmd, | ||||
|       "no mgw reset-endpoint NAME", | ||||
|       NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n" | ||||
|       "no reset-endpoint NAME", | ||||
|       NO_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n" | ||||
|       "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n") | ||||
| { | ||||
| 	struct reset_ep *reset_ep; | ||||
| @@ -235,6 +299,96 @@ DEFUN(cfg_mgw_no_reset_ep_name, | ||||
| 	vty_out(vty, "%% no such endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||
| 	return CMD_WARNING; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgw_no_reset_ep_name, | ||||
|       cfg_mgw_mgw_no_reset_ep_name_cmd, | ||||
|       "no mgw reset-endpoint NAME", | ||||
|       NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n" | ||||
|       "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n") | ||||
|  | ||||
| DEFUN(cfg_mgw_mgw_keepalive_req_interval, | ||||
|       cfg_mgw_mgw_keepalive_req_interval_cmd, | ||||
|       "keepalive request-interval <0-4294967295>", | ||||
|       "Monitor if the MGCP link against MGW is still usable\n" | ||||
|       "Send an MGCP command to the MGW at given interval if no other commands are sent\n" | ||||
|       "The interval at which send MGCP commands (s), 0 to disable\n") | ||||
| { | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
| 	struct mgcp_client *mgcp = get_mgcp_client(vty); | ||||
|  | ||||
| 	conf->keepalive.req_interval_sec = atoi(argv[0]); | ||||
|  | ||||
| 	if (!mgcp) | ||||
| 		return CMD_SUCCESS; | ||||
|  | ||||
| 	/* If client already exists, apply the change immediately if possible: */ | ||||
| 	mgcp->actual.keepalive.req_interval_sec = atoi(argv[0]); | ||||
| 	if (mgcp->wq.bfd.fd != -1) { /* UDP MGCP socket connected */ | ||||
| 		if (mgcp->actual.keepalive.req_interval_sec > 0) { | ||||
| 			/* Re-schedule: */ | ||||
| 			osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0); | ||||
| 		} else { | ||||
| 			if (osmo_timer_pending(&mgcp->keepalive_tx_timer)) | ||||
| 				osmo_timer_del(&mgcp->keepalive_tx_timer); | ||||
| 			/* Assume link is UP by default, so that this MGW can be selected: */ | ||||
| 			mgcp->conn_up = true; | ||||
| 		} | ||||
| 	} /* else: wait until connect() to do first scheduling */ | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgw_mgw_keepalive_req_endpoint, | ||||
|       cfg_mgw_mgw_keepalive_req_endpoint_cmd, | ||||
|       "keepalive request-endpoint NAME", | ||||
|       "Monitor if the MGCP link against MGW is still usable\n" | ||||
|       "Use a given endpoint name when sending an MGCP command to the MGW for keepalive purposes\n" | ||||
|       "The name of the endpoint to use\n") | ||||
| { | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
| 	struct mgcp_client *mgcp = get_mgcp_client(vty); | ||||
|  | ||||
| 	OSMO_STRLCPY_ARRAY(conf->keepalive.req_endpoint_name, argv[0]); | ||||
|  | ||||
| 	if (!mgcp) | ||||
| 		return CMD_SUCCESS; | ||||
|  | ||||
| 	/* If client already exists, apply the change immediately if possible: */ | ||||
| 	OSMO_STRLCPY_ARRAY(mgcp->actual.keepalive.req_endpoint_name, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgw_mgw_keepalive_timeout, | ||||
|       cfg_mgw_mgw_keepalive_timeout_cmd, | ||||
|       "keepalive timeout <0-4294967295>", | ||||
|       "Monitor if the MGCP link against MGW is still usable\n" | ||||
|       "Consider the link to the MGW to be down after time without receiving any message from it\n" | ||||
|       "The timeout (s), 0 to disable\n") | ||||
| { | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
| 	struct mgcp_client *mgcp = get_mgcp_client(vty); | ||||
|  | ||||
| 	conf->keepalive.timeout_sec = atoi(argv[0]); | ||||
|  | ||||
| 	if (!mgcp) | ||||
| 		return CMD_SUCCESS; | ||||
|  | ||||
| 	/* If client already exists, apply the change immediately if possible: */ | ||||
| 	mgcp->actual.keepalive.timeout_sec = atoi(argv[0]); | ||||
| 	if (mgcp->wq.bfd.fd != -1) { /* UDP MGCP socket connected */ | ||||
| 		if (mgcp->actual.keepalive.timeout_sec > 0) { | ||||
| 			/* Re-schedule: */ | ||||
| 			osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0); | ||||
| 		} else { | ||||
| 			if (osmo_timer_pending(&mgcp->keepalive_rx_timer)) | ||||
| 				osmo_timer_del(&mgcp->keepalive_rx_timer); | ||||
| 			/* Assume link is UP by default, so that this MGW can be selected: */ | ||||
| 			mgcp->conn_up = true; | ||||
| 		} | ||||
| 	} /* else: wait until connect() to do first scheduling */ | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static int config_write(struct vty *vty, const char *indent, struct mgcp_client_conf *conf) | ||||
| { | ||||
| @@ -242,33 +396,47 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_ | ||||
| 	int port; | ||||
| 	struct reset_ep *reset_ep; | ||||
|  | ||||
| 	if (conf->description) | ||||
| 	/* If caller doesn't the MGW pool API (mgcp_client_pool_vty_init was never called), | ||||
| 	 * then the "mgw" cmd prefix must be added since the old node always contained it. | ||||
| 	 */ | ||||
| 	const char *mgw_prefix = global_mgcp_client_pool ? "" : "mgw "; | ||||
|  | ||||
| 	if (conf->description) /* description never had "mgw" prefix even on old node: */ | ||||
| 		vty_out(vty, "%sdescription %s%s", indent, conf->description, VTY_NEWLINE); | ||||
|  | ||||
| 	addr = conf->local_addr; | ||||
| 	if (addr) | ||||
| 		vty_out(vty, "%smgw local-ip %s%s", indent, addr, | ||||
| 			VTY_NEWLINE); | ||||
| 		vty_out(vty, "%s%slocal-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE); | ||||
| 	port = conf->local_port; | ||||
| 	if (port >= 0) | ||||
| 		vty_out(vty, "%smgw local-port %u%s", indent, | ||||
| 		vty_out(vty, "%s%slocal-port %u%s", indent, mgw_prefix, | ||||
| 			(uint16_t)port, VTY_NEWLINE); | ||||
|  | ||||
| 	addr = conf->remote_addr; | ||||
| 	if (addr) | ||||
| 		vty_out(vty, "%smgw remote-ip %s%s", indent, addr, | ||||
| 			VTY_NEWLINE); | ||||
| 		vty_out(vty, "%s%sremote-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE); | ||||
| 	port = conf->remote_port; | ||||
| 	if (port >= 0) | ||||
| 		vty_out(vty, "%smgw remote-port %u%s", indent, | ||||
| 		vty_out(vty, "%s%sremote-port %u%s", indent, mgw_prefix, | ||||
| 			(uint16_t)port, VTY_NEWLINE); | ||||
|  | ||||
| 	if (conf->endpoint_domain_name[0]) | ||||
| 		vty_out(vty, "%smgw endpoint-domain %s%s", indent, | ||||
| 		vty_out(vty, "%s%sendpoint-domain %s%s", indent, mgw_prefix, | ||||
| 			conf->endpoint_domain_name, VTY_NEWLINE); | ||||
|  | ||||
| 	llist_for_each_entry(reset_ep, &conf->reset_epnames, list) | ||||
| 		vty_out(vty, "%smgw reset-endpoint %s%s", indent, reset_ep->name, VTY_NEWLINE); | ||||
| 		vty_out(vty, "%s%sreset-endpoint %s%s", indent, mgw_prefix, reset_ep->name, VTY_NEWLINE); | ||||
|  | ||||
| 	if (conf->keepalive.req_interval_sec != 0) | ||||
| 		vty_out(vty, "%s%skeepalive request-interval %u%s", indent, mgw_prefix, | ||||
| 			conf->keepalive.req_interval_sec, VTY_NEWLINE); | ||||
| 	if (strncmp(conf->keepalive.req_endpoint_name, MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP, | ||||
| 		    sizeof(conf->keepalive.req_endpoint_name)) != 0) | ||||
| 		vty_out(vty, "%s%skeepalive request-endpoint %s%s", indent,  mgw_prefix, | ||||
| 			conf->keepalive.req_endpoint_name, VTY_NEWLINE); | ||||
| 	if (conf->keepalive.timeout_sec != 0) | ||||
| 		vty_out(vty, "%s%skeepalive timeout %u%s", indent,  mgw_prefix, | ||||
| 			conf->keepalive.timeout_sec, VTY_NEWLINE); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| @@ -279,6 +447,11 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_ | ||||
|  *  \returns CMD_SUCCESS on success, CMD_WARNING on error */ | ||||
| int mgcp_client_config_write(struct vty *vty, const char *indent) | ||||
| { | ||||
| 	/* If caller supports MGW pool API (mgcp_client_pool_vty_init was | ||||
| 	 * called), then skip printing any config in this node and print it when | ||||
| 	 * the whole 'mgw' node is printed. */ | ||||
| 	if (global_mgcp_client_pool) | ||||
| 		return CMD_SUCCESS; | ||||
| 	return config_write(vty, indent, global_mgcp_client_conf); | ||||
| } | ||||
|  | ||||
| @@ -286,15 +459,18 @@ static void vty_init_common(void *talloc_ctx, int node) | ||||
| { | ||||
| 	global_mgcp_client_ctx = talloc_ctx; | ||||
|  | ||||
| 	install_lib_element(node, &cfg_mgw_local_ip_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_local_port_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_remote_ip_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_remote_port_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_endpoint_range_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_rtp_bts_base_port_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_endpoint_domain_name_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_reset_ep_name_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_no_reset_ep_name_cmd); | ||||
| 	/* deprecated 'mgw' commands ('mgw' prepended as first arg) */ | ||||
| 	install_lib_element(node, &cfg_mgw_mgw_local_ip_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_mgw_local_port_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_mgw_remote_ip_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_mgw_remote_port_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_mgw_endpoint_range_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_mgw_endpoint_domain_name_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_mgw_reset_ep_name_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_mgw_no_reset_ep_name_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_mgw_keepalive_req_interval_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_mgw_keepalive_req_endpoint_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_mgw_keepalive_timeout_cmd); | ||||
|  | ||||
| 	osmo_fsm_vty_add_cmds(); | ||||
| } | ||||
| @@ -319,38 +495,62 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c | ||||
| 	vty_init_common(talloc_ctx, node); | ||||
| } | ||||
|  | ||||
| static int config_write_pool(struct vty *vty) | ||||
| /* Mark whether user called mgcp_client_pool_config_write() and hence support new API */ | ||||
| static bool mgcp_client_pool_config_write_called = false; | ||||
|  | ||||
| static int _mgcp_client_pool_config_write(struct vty *vty, const char *indent) | ||||
| { | ||||
| 	struct mgcp_client_pool *pool = global_mgcp_client_pool; | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
| 	unsigned int indent_buf_len = strlen(pool->vty_indent) + 1 + 1; | ||||
| 	char *indent = talloc_zero_size(vty, indent_buf_len); | ||||
| 	unsigned int subindent_buf_len; | ||||
| 	char *subindent; | ||||
|  | ||||
| 	snprintf(indent, indent_buf_len, "%s ", pool->vty_indent); | ||||
| 	if (!indent) | ||||
| 		indent = pool->vty_indent ? : ""; | ||||
| 	subindent_buf_len = strlen(indent) + 1 + 1; | ||||
| 	subindent = talloc_zero_size(vty, subindent_buf_len); | ||||
|  | ||||
| 	llist_for_each_entry(pool_member, &pool->pool, list) { | ||||
| 		vty_out(vty, "%smgw %u%s", pool->vty_indent, pool_member->nr, VTY_NEWLINE); | ||||
| 		config_write(vty, indent, &pool_member->conf); | ||||
| 	snprintf(subindent, subindent_buf_len, "%s ", indent); | ||||
|  | ||||
| 	llist_for_each_entry(pool_member, &pool->member_list, list) { | ||||
| 		vty_out(vty, "%smgw %u%s", indent, pool_member->nr, VTY_NEWLINE); | ||||
| 		config_write(vty, subindent, &pool_member->conf); | ||||
| 	} | ||||
|  | ||||
| 	talloc_free(indent); | ||||
| 	/* MGW pool API is supported by user (global_mgcp_client_pool is set | ||||
| 	 * because mgcp_client_pool_vty_init was called). If single MGW was | ||||
| 	 * configured through old VTY and no mgw in the new MGW pool VTY is | ||||
| 	 * replacing it, then output the single MGW converted to the new MGW | ||||
| 	 * pool VTY. */ | ||||
| 	if (llist_empty(&pool->member_list) && pool->mgcp_client_single) { | ||||
| 		vty_out(vty, "%smgw 0%s", indent, VTY_NEWLINE); | ||||
| 		config_write(vty, subindent, global_mgcp_client_conf); | ||||
| 	} | ||||
|  | ||||
| 	talloc_free(subindent); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| /* Lookup the selected MGCP client config by its reference number */ | ||||
| static struct mgcp_client_pool_member *pool_member_by_nr(unsigned int nr) | ||||
| /* Deprecated, used for backward compatibility with older users which didn't call | ||||
|  * mgcp_client_pool_config_write(): */ | ||||
| static int config_write_pool(struct vty *vty) | ||||
| { | ||||
| 	struct mgcp_client_pool_member *pool_member = NULL; | ||||
| 	struct mgcp_client_pool_member *pool_member_tmp; | ||||
| 	if (mgcp_client_pool_config_write_called) | ||||
| 		return CMD_SUCCESS; | ||||
|  | ||||
| 	llist_for_each_entry(pool_member_tmp, &global_mgcp_client_pool->pool, list) { | ||||
| 		if (pool_member_tmp->nr == nr) { | ||||
| 			pool_member = pool_member_tmp; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	return _mgcp_client_pool_config_write(vty, NULL); | ||||
| } | ||||
|  | ||||
| 	return pool_member; | ||||
| /*! Write out MGCP client config to VTY. | ||||
|  *  \param[in] vty VTY to which we should print. | ||||
|  *  \param[in] indent string used for indentation (e.g. " "). | ||||
| 	       If NULL, indentation passed during mgcp_client_pool_vty_init() will be used. | ||||
|  *  \returns CMD_SUCCESS on success, CMD_WARNING on error */ | ||||
| int mgcp_client_pool_config_write(struct vty *vty, const char *indent) | ||||
| { | ||||
| 	/* Tell internal node write function that the user supports calling proper API: */ | ||||
| 	mgcp_client_pool_config_write_called = true; | ||||
| 	return _mgcp_client_pool_config_write(vty, indent); | ||||
| } | ||||
|  | ||||
| DEFUN_ATTR(cfg_mgw, | ||||
| @@ -359,13 +559,10 @@ DEFUN_ATTR(cfg_mgw, | ||||
| 	int nr = atoi(argv[0]); | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
|  | ||||
| 	pool_member = pool_member_by_nr(nr); | ||||
| 	pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr); | ||||
| 	if (!pool_member) { | ||||
| 		pool_member = talloc_zero(global_mgcp_client_pool, struct mgcp_client_pool_member); | ||||
| 		pool_member = mgcp_client_pool_member_alloc(global_mgcp_client_pool, nr); | ||||
| 		OSMO_ASSERT(pool_member); | ||||
| 		mgcp_client_conf_init(&pool_member->conf); | ||||
| 		pool_member->nr = nr; | ||||
| 		llist_add_tail(&pool_member->list, &global_mgcp_client_pool->pool); | ||||
| 	} | ||||
|  | ||||
| 	vty->index = &pool_member->conf; | ||||
| @@ -382,7 +579,7 @@ DEFUN_ATTR(cfg_no_mgw, | ||||
| 	int nr = atoi(argv[0]); | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
|  | ||||
| 	pool_member = pool_member_by_nr(nr); | ||||
| 	pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr); | ||||
| 	if (!pool_member) { | ||||
| 		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| @@ -395,12 +592,7 @@ DEFUN_ATTR(cfg_no_mgw, | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	llist_del(&pool_member->list); | ||||
| 	if (pool_member->client) { | ||||
| 		mgcp_client_disconnect(pool_member->client); | ||||
| 		talloc_free(pool_member->client); | ||||
| 	} | ||||
| 	talloc_free(pool_member); | ||||
| 	mgcp_client_pool_member_free(pool_member); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| @@ -412,7 +604,7 @@ DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd, | ||||
| 	int nr = atoi(argv[0]); | ||||
| 	struct mgcp_client_pool_member *pool_member = NULL; | ||||
|  | ||||
| 	pool_member = pool_member_by_nr(nr); | ||||
| 	pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr); | ||||
| 	if (!pool_member) { | ||||
| 		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| @@ -425,32 +617,10 @@ DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd, | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	/* Get rid of a possibly existing old MGCP client instance first */ | ||||
| 	if (pool_member->client) { | ||||
| 		mgcp_client_disconnect(pool_member->client); | ||||
| 		talloc_free(pool_member->client); | ||||
| 	} | ||||
|  | ||||
| 	/* Create a new MGCP client instance with the current config */ | ||||
| 	pool_member->client = mgcp_client_init(pool_member, &pool_member->conf); | ||||
| 	if (!pool_member->client) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s initalization failed\n", | ||||
| 		     mgcp_client_pool_member_name(pool_member)); | ||||
| 		vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s", | ||||
| 			mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */ | ||||
| 	pool_member->client->pool = global_mgcp_client_pool; | ||||
|  | ||||
| 	/* Connect client */ | ||||
| 	if (mgcp_client_connect(pool_member->client)) { | ||||
| 	if (mgcp_client_pool_member_reinit_client(pool_member) < 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s connect failed at (%s:%u)\n", | ||||
| 		     mgcp_client_pool_member_name(pool_member), pool_member->conf.remote_addr, | ||||
| 		     pool_member->conf.remote_port); | ||||
| 		talloc_free(pool_member->client); | ||||
| 		pool_member->client = NULL; | ||||
| 		vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s", | ||||
| 			mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| @@ -466,7 +636,7 @@ DEFUN_ATTR(mgw_block, mgw_block_cmd, | ||||
| 	int nr = atoi(argv[0]); | ||||
| 	struct mgcp_client_pool_member *pool_member = NULL; | ||||
|  | ||||
| 	pool_member = pool_member_by_nr(nr); | ||||
| 	pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr); | ||||
| 	if (!pool_member) { | ||||
| 		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| @@ -483,7 +653,7 @@ DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd, | ||||
| 	int nr = atoi(argv[0]); | ||||
| 	struct mgcp_client_pool_member *pool_member = NULL; | ||||
|  | ||||
| 	pool_member = pool_member_by_nr(nr); | ||||
| 	pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr); | ||||
| 	if (!pool_member) { | ||||
| 		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| @@ -493,22 +663,27 @@ DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd, | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(mgw_show, mgw_snow_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n") | ||||
| DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n") | ||||
| { | ||||
| 	vty_out(vty, "%% MGW-Pool:%s", VTY_NEWLINE); | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
|  | ||||
| 	if (llist_empty(&global_mgcp_client_pool->pool) && global_mgcp_client_pool->mgcp_client_single) { | ||||
| 	if (llist_empty(&global_mgcp_client_pool->member_list) && global_mgcp_client_pool->mgcp_client_single) { | ||||
| 		vty_out(vty, "%%  (pool is empty, single MGCP client will be used)%s", VTY_NEWLINE); | ||||
| 		return CMD_SUCCESS; | ||||
| 	} else if (llist_empty(&global_mgcp_client_pool->pool)) { | ||||
| 	} else if (llist_empty(&global_mgcp_client_pool->member_list)) { | ||||
| 		vty_out(vty, "%%  (pool is empty)%s", VTY_NEWLINE); | ||||
| 		return CMD_SUCCESS; | ||||
| 	} | ||||
|  | ||||
| 	llist_for_each_entry(pool_member, &global_mgcp_client_pool->pool, list) { | ||||
| 	llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) { | ||||
| 		const struct mgcp_client *cli = pool_member->client; | ||||
| 		vty_out(vty, "%%  MGW %s%s", mgcp_client_pool_member_name(pool_member), VTY_NEWLINE); | ||||
| 		vty_out(vty, "%%   mgcp-client:   %s%s", pool_member->client ? "connected" : "disconnected", | ||||
| 		vty_out(vty, "%%   MGCP link:     %s,%s%s", | ||||
| 			cli && cli->wq.bfd.fd != -1 ? "connected" : "disconnected", | ||||
| 			cli && cli->conn_up ? | ||||
| 				((cli->actual.keepalive.timeout_sec > 0) ? "UP" : "MAYBE") : | ||||
| 				"DOWN", | ||||
| 			VTY_NEWLINE); | ||||
| 		vty_out(vty, "%%   service:       %s%s", pool_member->blocked ? "blocked" : "unblocked", VTY_NEWLINE); | ||||
| 		vty_out(vty, "%%   ongoing calls: %u%s", pool_member->refcount, VTY_NEWLINE); | ||||
| @@ -520,7 +695,8 @@ DEFUN(mgw_show, mgw_snow_cmd, "show mgw-pool", SHOW_STR "Display information abo | ||||
|  *  (called once at startup by the application process). | ||||
|  *  \param[in] parent_node identifier of the parent node on which the mgw node appears. | ||||
|  *  \param[in] mgw_node identifier that should be used with the newly installed MGW node. | ||||
|  *  \param[in] indent indentation string to match the indentation in the VTY config | ||||
|  *  \param[in] indent indentation string to match the indentation in the VTY config. | ||||
| 	       If NULL, it must be passed explicitly each time mgcp_client_pool_config_write() is called. | ||||
|  *  \param[in] pool user provided memory to store the configured MGCP client (MGW) pool. */ | ||||
| void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool) | ||||
| { | ||||
| @@ -528,11 +704,12 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent | ||||
| 	OSMO_ASSERT(pool); | ||||
|  | ||||
| 	/* Never allow this function to be called twice on the same pool */ | ||||
| 	OSMO_ASSERT(!pool->vty_indent); | ||||
| 	OSMO_ASSERT(!pool->vty_node); | ||||
|  | ||||
| 	pool->vty_indent = talloc_strdup(pool, indent); | ||||
| 	OSMO_ASSERT(pool->vty_indent); | ||||
| 	if (indent) { | ||||
| 		pool->vty_indent = talloc_strdup(pool, indent); | ||||
| 		OSMO_ASSERT(pool->vty_indent); | ||||
| 	} | ||||
| 	pool->vty_node = talloc_zero(pool, struct cmd_node); | ||||
| 	OSMO_ASSERT(pool->vty_node); | ||||
| 	pool->vty_node->node = mgw_node; | ||||
| @@ -542,8 +719,19 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent | ||||
| 	install_lib_element(parent_node, &cfg_mgw_cmd); | ||||
| 	install_lib_element(parent_node, &cfg_no_mgw_cmd); | ||||
|  | ||||
| 	/* Note: config_write_pool is deprecated and user is expected to | ||||
| 	 * manually call mgcp_client_pool_config_write() when printing the VTY | ||||
| 	 * config */ | ||||
| 	install_node(pool->vty_node, config_write_pool); | ||||
| 	vty_init_common(pool, mgw_node); | ||||
| 	install_lib_element(mgw_node, &cfg_mgw_local_ip_cmd); | ||||
| 	install_lib_element(mgw_node, &cfg_mgw_local_port_cmd); | ||||
| 	install_lib_element(mgw_node, &cfg_mgw_remote_ip_cmd); | ||||
| 	install_lib_element(mgw_node, &cfg_mgw_remote_port_cmd); | ||||
| 	install_lib_element(mgw_node, &cfg_mgw_rtp_bts_base_port_cmd); | ||||
| 	install_lib_element(mgw_node, &cfg_mgw_endpoint_domain_name_cmd); | ||||
| 	install_lib_element(mgw_node, &cfg_mgw_reset_ep_name_cmd); | ||||
| 	install_lib_element(mgw_node, &cfg_mgw_no_reset_ep_name_cmd); | ||||
|  | ||||
| 	install_element(mgw_node, &cfg_description_cmd); | ||||
|  | ||||
| @@ -551,7 +739,7 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent | ||||
| 	install_lib_element(ENABLE_NODE, &mgw_block_cmd); | ||||
| 	install_lib_element(ENABLE_NODE, &mgw_unblock_cmd); | ||||
|  | ||||
| 	install_lib_element_ve(&mgw_snow_cmd); | ||||
| 	install_lib_element_ve(&mgw_show_cmd); | ||||
|  | ||||
| 	global_mgcp_client_pool = pool; | ||||
| } | ||||
|   | ||||
| @@ -16,12 +16,6 @@ AM_CFLAGS = \ | ||||
| 	$(NULL) | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOGSM_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	$(LIBOSMOABIS_LIBS) \ | ||||
| 	$(LIBOSMOTRAU_LIBS) \ | ||||
| 	$(COVERAGE_LDFLAGS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
| @@ -45,7 +39,6 @@ libosmo_mgcp_a_SOURCES = \ | ||||
| 	mgcp_stat.c \ | ||||
| 	mgcp_endp.c \ | ||||
| 	mgcp_trunk.c \ | ||||
| 	mgcp_ctrl.c \ | ||||
| 	mgcp_ratectr.c \ | ||||
| 	mgcp_e1.c \ | ||||
| 	mgcp_iuup.c \ | ||||
|   | ||||
| @@ -276,75 +276,6 @@ error: | ||||
| 	return -EINVAL; | ||||
| } | ||||
|  | ||||
| /* Check if the given codec is applicable on the specified endpoint | ||||
|  * Helper function for mgcp_codec_decide() */ | ||||
| static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	/* A codec name must be set, if not, this might mean that the codec | ||||
| 	 * (payload type) that was assigned is unknown to us so we must stop | ||||
| 	 * here. */ | ||||
| 	if (!strlen(codec->subtype_name)) | ||||
| 		return false; | ||||
|  | ||||
| 	/* FIXME: implement meaningful checks to make sure that the given codec | ||||
| 	 * is compatible with the given endpoint */ | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /*! Decide for one suitable codec | ||||
|  *  \param[in] conn related rtp-connection. | ||||
|  *  \returns 0 on success, -EINVAL on failure. */ | ||||
| int mgcp_codec_decide(struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	struct mgcp_rtp_end *rtp; | ||||
| 	unsigned int i; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	bool codec_assigned = false; | ||||
|  | ||||
| 	endp = conn->conn->endp; | ||||
| 	rtp = &conn->end; | ||||
|  | ||||
| 	/* This function works on the results the SDP/LCO parser has extracted | ||||
| 	 * from the MGCP message. The goal is to select a suitable codec for | ||||
| 	 * the given connection. When transcoding is available, the first codec | ||||
| 	 * from the codec list is taken without further checking. When | ||||
| 	 * transcoding is not available, then the choice must be made more | ||||
| 	 * carefully. Each codec in the list is checked until one is found that | ||||
| 	 * is rated compatible. The rating is done by the helper function | ||||
| 	 * is_codec_compatible(), which does the actual checking. */ | ||||
| 	for (i = 0; i < rtp->codecs_assigned; i++) { | ||||
| 		/* When no transcoding is available, avoid codecs that would | ||||
| 		 * require transcoding. */ | ||||
| 		if (endp->trunk->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) { | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n", | ||||
| 			     rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		rtp->codec = &rtp->codecs[i]; | ||||
| 		codec_assigned = true; | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	/* FIXME: To the reviewes: This is problematic. I do not get why we | ||||
| 	 * need to reset the packet_duration_ms depending on the codec | ||||
| 	 * selection. I thought it were all 20ms? Is this to address some | ||||
| 	 * cornercase. (This piece of code was in the code path before, | ||||
| 	 * together with the note: "TODO/XXX: Store this per codec and derive | ||||
| 	 * it on use" */ | ||||
| 	if (codec_assigned) { | ||||
| 		if (rtp->maximum_packet_time >= 0 | ||||
| 		    && rtp->maximum_packet_time * rtp->codec->frame_duration_den > | ||||
| 		    rtp->codec->frame_duration_num * 1500) | ||||
| 			rtp->packet_duration_ms = 0; | ||||
|  | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return -EINVAL; | ||||
| } | ||||
|  | ||||
| /* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode. | ||||
|  * See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB | ||||
|  * Media Type Registration": | ||||
| @@ -364,10 +295,15 @@ bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec) | ||||
| 	return codec->param.amr_octet_aligned; | ||||
| } | ||||
|  | ||||
| /* Compare two codecs, all parameters must match up, except for the payload type | ||||
|  * number. */ | ||||
| /* Compare two codecs, all parameters must match up */ | ||||
| static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b) | ||||
| { | ||||
| 	/* All codec properties must match up, except the payload type number. Even though standardisd payload numbers | ||||
| 	 * exist for certain situations, the call agent may still assign them freely. Hence we must not insist on equal | ||||
| 	 * payload type numbers. Also the audio_name is not checked since it is already parsed into subtype_name, rate, | ||||
| 	 * and channels, which are checked. */ | ||||
| 	if (strcmp(codec_a->subtype_name, codec_b->subtype_name)) | ||||
| 		return false; | ||||
| 	if (codec_a->rate != codec_b->rate) | ||||
| 		return false; | ||||
| 	if (codec_a->channels != codec_b->channels) | ||||
| @@ -376,61 +312,163 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c | ||||
| 		return false; | ||||
| 	if (codec_a->frame_duration_den != codec_b->frame_duration_den) | ||||
| 		return false; | ||||
| 	if (strcmp(codec_a->subtype_name, codec_b->subtype_name)) | ||||
| 		return false; | ||||
|  | ||||
| 	/* Note: AMR allows to set the RTP payload format to octet-aligned or bandwith-efficient (octet-aligned=0) | ||||
| 	 * via SDP. This difference concerns payload format only, but not the actual codec. It is not a difference | ||||
| 	 * within the meaning of this function. */ | ||||
| 	/* AMR payload may be formatted in two different payload formats, it is still the same codec but since the | ||||
| 	 * formatting of the payload is different, conversation is required, so we must treat it as a different | ||||
| 	 * codec here. */ | ||||
| 	if (strcmp(codec_a->subtype_name, "AMR") == 0) { | ||||
| 		if (mgcp_codec_amr_is_octet_aligned(codec_a) != mgcp_codec_amr_is_octet_aligned(codec_b)) | ||||
| 			return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /*! Translate a given payload type number that belongs to the packet of a | ||||
|  *  source connection to the equivalent payload type number that matches the | ||||
|  *  configuration of a destination connection. | ||||
|  *  \param[in] conn_src related source rtp-connection. | ||||
|  *  \param[in] conn_dst related destination rtp-connection. | ||||
|  *  \param[in] payload_type number from the source packet or source connection. | ||||
|  *  \returns translated payload type number on success, -EINVAL on failure. */ | ||||
| int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type) | ||||
| /* Compare two codecs, all parameters must match up, except parameters related to payload formatting (not checked). */ | ||||
| static bool codecs_convertible(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b) | ||||
| { | ||||
| 	struct mgcp_rtp_end *rtp_src; | ||||
| 	struct mgcp_rtp_end *rtp_dst; | ||||
| 	struct mgcp_rtp_codec *codec_src = NULL; | ||||
| 	struct mgcp_rtp_codec *codec_dst = NULL; | ||||
| 	/* OsmoMGW currently has no ability to transcode from one codec to another. However OsmoMGW is still able to | ||||
| 	 * translate between different payload formats as long as the encoded voice data itself does not change. | ||||
| 	 * Therefore we must insist on equal codecs but still allow different payload formatting. */ | ||||
|  | ||||
| 	/* In 3G IuUP, AMR may be encapsulated in IuFP, this means even though the codec name and negotiated rate is | ||||
| 	 * different, the formatting can still be converted by OsmoMGW. Therefore we won't insist on equal | ||||
| 	 * subtype_name and rate if we detect IuFP and AMR is used on the same tandem. */ | ||||
| 	if (strcmp(codec_a->subtype_name, "AMR") == 0 && strcmp(codec_b->subtype_name, "VND.3GPP.IUFP") == 0) | ||||
| 		goto iufp; | ||||
| 	if (strcmp(codec_a->subtype_name, "VND.3GPP.IUFP") == 0 && strcmp(codec_b->subtype_name, "AMR") == 0) | ||||
| 		goto iufp; | ||||
|  | ||||
| 	if (strcmp(codec_a->subtype_name, codec_b->subtype_name)) | ||||
| 		return false; | ||||
| 	if (codec_a->rate != codec_b->rate) | ||||
| 		return false; | ||||
| iufp: | ||||
| 	if (codec_a->channels != codec_b->channels) | ||||
| 		return false; | ||||
| 	if (codec_a->frame_duration_num != codec_b->frame_duration_num) | ||||
| 		return false; | ||||
| 	if (codec_a->frame_duration_den != codec_b->frame_duration_den) | ||||
| 		return false; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| struct mgcp_rtp_codec *mgcp_codec_find_same(struct mgcp_conn_rtp *conn, struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	struct mgcp_rtp_end *rtp_end; | ||||
| 	unsigned int i; | ||||
| 	unsigned int codecs_assigned; | ||||
|  | ||||
| 	rtp_src = &conn_src->end; | ||||
| 	rtp_dst = &conn_dst->end; | ||||
| 	rtp_end = &conn->end; | ||||
|  | ||||
| 	/* Find the codec information that is used on the source side */ | ||||
| 	codecs_assigned = rtp_src->codecs_assigned; | ||||
| 	/* Use the codec information from the source and try to find the equivalent of it on the destination side. In | ||||
| 	 * the first run we will look for an exact match. */ | ||||
| 	codecs_assigned = rtp_end->codecs_assigned; | ||||
| 	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS); | ||||
| 	for (i = 0; i < codecs_assigned; i++) { | ||||
| 		if (payload_type == rtp_src->codecs[i].payload_type) { | ||||
| 			codec_src = &rtp_src->codecs[i]; | ||||
| 		if (codecs_same(codec, &rtp_end->codecs[i])) { | ||||
| 			return &rtp_end->codecs[i]; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!codec_src) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	/* Use the codec information from the source and try to find the | ||||
| 	 * equivalent of it on the destination side */ | ||||
| 	codecs_assigned = rtp_dst->codecs_assigned; | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /* For a given codec, find a convertible codec in the given connection. */ | ||||
| static struct mgcp_rtp_codec *codec_find_convertible(struct mgcp_conn_rtp *conn, struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	struct mgcp_rtp_end *rtp_end; | ||||
| 	unsigned int i; | ||||
| 	unsigned int codecs_assigned; | ||||
| 	struct mgcp_rtp_codec *codec_convertible = NULL; | ||||
|  | ||||
| 	rtp_end = &conn->end; | ||||
|  | ||||
| 	/* Use the codec information from the source and try to find the equivalent of it on the destination side. In | ||||
| 	 * the first run we will look for an exact match. */ | ||||
| 	codec_convertible = mgcp_codec_find_same(conn, codec); | ||||
| 	if (codec_convertible) | ||||
| 		return codec_convertible; | ||||
|  | ||||
| 	/* In case we weren't able to find an exact match, we will try to find a match that is the same codec, but the | ||||
| 	 * payload format may be different. This alternative will require a frame format conversion (i.e. AMR bwe->oe) */ | ||||
| 	codecs_assigned = rtp_end->codecs_assigned; | ||||
| 	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS); | ||||
| 	for (i = 0; i < codecs_assigned; i++) { | ||||
| 		if (codecs_same(codec_src, &rtp_dst->codecs[i])) { | ||||
| 			codec_dst = &rtp_dst->codecs[i]; | ||||
| 		if (codecs_convertible(codec, &rtp_end->codecs[i])) { | ||||
| 			codec_convertible = &rtp_end->codecs[i]; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!codec_dst) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	return codec_dst->payload_type; | ||||
| 	return codec_convertible; | ||||
| } | ||||
|  | ||||
| /*! Decide for one suitable codec on both of the given connections. In case a destination connection is not available, | ||||
|  *  a tentative decision is made. | ||||
|  *  \param[inout] conn_src related rtp-connection. | ||||
|  *  \param[inout] conn_dst related destination rtp-connection (NULL if not present). | ||||
|  *  \returns 0 on success, -EINVAL on failure. */ | ||||
| int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst) | ||||
| { | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	/* In case no destination connection is available (yet), or in case the destination connection exists but has | ||||
| 	 * no codecs assigned, we are forced to make a simple tentative decision: | ||||
| 	 * We just use the first codec of the source connection (conn_src) */ | ||||
| 	OSMO_ASSERT(conn_src->end.codecs_assigned <= MGCP_MAX_CODECS); | ||||
| 	if (!conn_dst || conn_dst->end.codecs_assigned == 0) { | ||||
| 		if (conn_src->end.codecs_assigned >= 1) { | ||||
| 			conn_src->end.codec = &conn_src->end.codecs[0]; | ||||
| 			return 0; | ||||
| 		} else | ||||
| 			return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/* Compare all codecs of the source connection (conn_src) to the codecs of the destination connection (conn_dst). In case | ||||
| 	 * of a match set this codec on both connections. This would be an ideal selection since no codec conversion would be | ||||
| 	 * required. */ | ||||
| 	for (i = 0; i < conn_src->end.codecs_assigned; i++) { | ||||
| 		struct mgcp_rtp_codec *codec_conn_dst = mgcp_codec_find_same(conn_dst, &conn_src->end.codecs[i]); | ||||
| 		if (codec_conn_dst) { | ||||
| 			/* We found the a codec that is exactly the same (same codec, same payload format etc.) on both | ||||
| 			 * sides. We now set this codec on both connections. */ | ||||
| 			conn_dst->end.codec = codec_conn_dst; | ||||
| 			conn_src->end.codec = mgcp_codec_find_same(conn_src, codec_conn_dst); | ||||
| 			OSMO_ASSERT(conn_src->end.codec); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* In case we could not find a codec that is exactly the same, let's at least try to find a codec that we are able | ||||
| 	 * to convert. */ | ||||
| 	for (i = 0; i < conn_src->end.codecs_assigned; i++) { | ||||
| 		struct mgcp_rtp_codec *codec_conn_dst = codec_find_convertible(conn_dst, &conn_src->end.codecs[i]); | ||||
| 		if (codec_conn_dst) { | ||||
| 			/* We found the a codec that we are able to convert on both sides. We now set this codec on both | ||||
| 			 * connections. */ | ||||
| 			conn_dst->end.codec = codec_conn_dst; | ||||
| 			conn_src->end.codec = codec_find_convertible(conn_src, codec_conn_dst); | ||||
| 			OSMO_ASSERT(conn_src->end.codec); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return -EINVAL; | ||||
| } | ||||
|  | ||||
| /* Check if the codec has a specific AMR mode (octet-aligned or bandwith-efficient) set. */ | ||||
| bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	if (codec->param_present == false) | ||||
| 		return false; | ||||
| 	if (!codec->param.amr_octet_aligned_present) | ||||
| 		return false; | ||||
| 	if (strcmp(codec->subtype_name, "AMR") != 0) | ||||
| 		return false; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /* Find the payload type number configured for a specific codec by SDP. | ||||
| @@ -440,8 +478,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp | ||||
|  * \param subtype_name  SDP codec name without parameters (e.g. "AMR"). | ||||
|  * \param match_nr  Index for the match found, first being match_nr == 0. Iterate all matches by calling multiple times | ||||
|  *                  with incrementing match_nr. | ||||
|  * \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found. | ||||
|  */ | ||||
|  * \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found. */ | ||||
| const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn, | ||||
| 								const char *subtype_name, unsigned int match_nr) | ||||
| { | ||||
| @@ -457,3 +494,26 @@ const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /*! Lookup a codec that is assigned to a connection by its payload type number. | ||||
|  *  \param[in] conn related rtp-connection. | ||||
|  *  \param[in] payload_type number of the codec to look up. | ||||
|  *  \returns pointer to codec struct on success, NULL on failure. */ | ||||
| struct mgcp_rtp_codec *mgcp_codec_from_pt(struct mgcp_conn_rtp *conn, int payload_type) | ||||
| { | ||||
| 	struct mgcp_rtp_end *rtp_end = &conn->end; | ||||
| 	unsigned int codecs_assigned = rtp_end->codecs_assigned; | ||||
| 	struct mgcp_rtp_codec *codec = NULL; | ||||
| 	size_t i; | ||||
|  | ||||
| 	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS); | ||||
|  | ||||
| 	for (i = 0; i < codecs_assigned; i++) { | ||||
| 		if (payload_type == rtp_end->codecs[i].payload_type) { | ||||
| 			codec = &rtp_end->codecs[i]; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return codec; | ||||
| } | ||||
|   | ||||
| @@ -37,7 +37,7 @@ | ||||
| #include <osmocom/core/timer.h> | ||||
| #include <ctype.h> | ||||
|  | ||||
| const static struct rate_ctr_group_desc rate_ctr_group_desc = { | ||||
| static const struct rate_ctr_group_desc rate_ctr_group_desc = { | ||||
| 	.group_name_prefix = "conn_rtp", | ||||
| 	.group_description = "rtp connection statistics", | ||||
| 	.class_id = 1, | ||||
| @@ -95,15 +95,21 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn * | ||||
| 	static atomic_uint rate_ctr_index = 0; | ||||
|  | ||||
| 	conn_rtp->type = MGCP_RTP_DEFAULT; | ||||
| 	conn_rtp->osmux.cid_allocated = false; | ||||
| 	conn_rtp->osmux.cid = 0; | ||||
|  | ||||
| 	/* Osmux specific defaults, only used if conn is later on Osmux-enabled: */ | ||||
| 	conn_rtp->osmux.state = OSMUX_STATE_DISABLED; | ||||
| 	conn_rtp->osmux.local_cid_allocated = false; | ||||
| 	conn_rtp->osmux.local_cid = 0; | ||||
| 	conn_rtp->osmux.remote_cid_present = false; | ||||
| 	conn_rtp->osmux.remote_cid = 0; | ||||
|  | ||||
| 	/* backpointer to the generic part of the connection */ | ||||
| 	conn->u.rtp.conn = conn; | ||||
|  | ||||
| 	end->rtp.fd = -1; | ||||
| 	end->rtcp.fd = -1; | ||||
| 	end->rtp_port = end->rtcp_port = 0; | ||||
| 	memset(&end->addr, 0, sizeof(end->addr)); | ||||
| 	end->rtcp_port = 0; | ||||
| 	talloc_free(end->fmtp_extra); | ||||
| 	end->fmtp_extra = NULL; | ||||
|  | ||||
| @@ -113,12 +119,12 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn * | ||||
| 	end->output_enabled = false; | ||||
| 	end->maximum_packet_time = -1; | ||||
|  | ||||
| 	conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++); | ||||
| 	if (!conn_rtp->rate_ctr_group) | ||||
| 	conn_rtp->ctrg = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++); | ||||
| 	if (!conn_rtp->ctrg) | ||||
| 		return -1; | ||||
|  | ||||
| 	conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, IN_STREAM_ERR_TSTMP_CTR); | ||||
| 	conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, OUT_STREAM_ERR_TSTMP_CTR); | ||||
| 	conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, IN_STREAM_ERR_TSTMP_CTR); | ||||
| 	conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, OUT_STREAM_ERR_TSTMP_CTR); | ||||
|  | ||||
| 	/* Make sure codec table is reset */ | ||||
| 	mgcp_codec_reset_all(conn_rtp); | ||||
| @@ -134,7 +140,7 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp) | ||||
| 	if (mgcp_conn_rtp_is_iuup(conn_rtp)) | ||||
| 		mgcp_conn_iuup_cleanup(conn_rtp); | ||||
| 	mgcp_free_rtp_port(&conn_rtp->end); | ||||
| 	rate_ctr_group_free(conn_rtp->rate_ctr_group); | ||||
| 	rate_ctr_group_free(conn_rtp->ctrg); | ||||
| 	mgcp_codec_reset_all(conn_rtp); | ||||
| } | ||||
|  | ||||
| @@ -167,8 +173,8 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, | ||||
| 	struct mgcp_conn *conn; | ||||
| 	int rc; | ||||
|  | ||||
| 	/* Do not allow more then two connections */ | ||||
| 	if (llist_count(&endp->conns) >= endp->type->max_conns) | ||||
| 	/* Do not allow more than the maximum number of connections */ | ||||
| 	if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns) | ||||
| 		return NULL; | ||||
|  | ||||
| 	/* Create new connection and add it to the list */ | ||||
| @@ -263,7 +269,7 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, | ||||
| static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp) | ||||
| { | ||||
| 	struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats; | ||||
| 	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group; | ||||
| 	struct rate_ctr_group *conn_stats = conn_rtp->ctrg; | ||||
|  | ||||
| 	if (all_stats == NULL || conn_stats == NULL) | ||||
| 		return; | ||||
| @@ -360,14 +366,38 @@ char *mgcp_conn_dump(struct mgcp_conn *conn) | ||||
|  | ||||
| 	switch (conn->type) { | ||||
| 	case MGCP_CONN_TYPE_RTP: | ||||
| 		/* Dump RTP connection */ | ||||
| 		snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, " | ||||
| 			 "rtp:%u rtcp:%u)", | ||||
| 			 conn->name, | ||||
| 			 conn->id, | ||||
| 			 osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf), | ||||
| 			 ntohs(conn->u.rtp.end.rtp_port), | ||||
| 			 ntohs(conn->u.rtp.end.rtcp_port)); | ||||
| 		switch (conn->u.rtp.type) { | ||||
| 		case MGCP_RTP_DEFAULT: | ||||
| 			/* Dump RTP connection */ | ||||
| 			snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, " | ||||
| 				"rtp:%u rtcp:%u)", | ||||
| 				conn->name, conn->id, | ||||
| 				osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf), | ||||
| 				osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa), | ||||
| 				ntohs(conn->u.rtp.end.rtcp_port)); | ||||
| 			break; | ||||
| 		case MGCP_RTP_OSMUX: | ||||
| 			snprintf(str, sizeof(str), "(%s/osmux, id:0x%s, ip:%s, " | ||||
| 				"port:%u CID:%u)", | ||||
| 				conn->name, conn->id, | ||||
| 				osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf), | ||||
| 				osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa), | ||||
| 				conn->u.rtp.osmux.local_cid); | ||||
| 			break; | ||||
| 		case MGCP_RTP_IUUP: | ||||
| 			snprintf(str, sizeof(str), "(%s/iuup, id:0x%s, ip:%s, " | ||||
| 				"port:%u)", | ||||
| 				conn->name, conn->id, | ||||
| 				osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf), | ||||
| 				osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa)); | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* Should not happen, we should be able to dump | ||||
| 			 * every possible connection type. */ | ||||
| 			snprintf(str, sizeof(str), "(unknown conn_rtp connection type %u)", | ||||
| 				 conn->u.rtp.type); | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
|  | ||||
| 	default: | ||||
|   | ||||
| @@ -1,36 +0,0 @@ | ||||
| /* | ||||
|  * (C) 2020 by Harald Welte <laforge@gnumonks.org> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <osmocom/ctrl/control_if.h> | ||||
| #include <osmocom/mgcp/mgcp.h> | ||||
|  | ||||
|  | ||||
| static int mgw_ctrl_node_lookup(void *data, vector vline, int *node_type, | ||||
| 				void **node_data, int *i) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg, | ||||
| 					     const char *bind_addr, uint16_t port) | ||||
| { | ||||
| 	return ctrl_interface_setup_dynip2(cfg, bind_addr, port, mgw_ctrl_node_lookup, | ||||
| 					   _LAST_CTRL_NODE); | ||||
| } | ||||
| @@ -222,7 +222,7 @@ static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_dat | ||||
| 	osmo_i460_mux_enqueue(endp->e1.schan, msg); | ||||
| } | ||||
|  | ||||
| /* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */ | ||||
| /* called by I.460 de-multiplexer; feed output of I.460 demux into TRAU frame sync */ | ||||
| static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits, | ||||
| 				  unsigned int num_bits) | ||||
| { | ||||
| @@ -309,8 +309,8 @@ skip: | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| /* Function to handle outgoing E1 traffic */ | ||||
| static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk) | ||||
| /* handle outgoing E1 traffic */ | ||||
| static void e1_send_ts_frame(struct e1inp_ts *ts, struct mgcp_trunk *trunk) | ||||
| { | ||||
| 	struct msgb *msg = msgb_alloc_c(trunk, E1_TS_BYTES, "E1-TX-timeslot-bytes"); | ||||
| 	uint8_t *ptr; | ||||
| @@ -359,19 +359,17 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg) | ||||
| 	osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(msg)); | ||||
|  | ||||
| 	/* Trigger sending of pending E1 traffic */ | ||||
| 	e1_send(ts, trunk); | ||||
| 	e1_send_ts_frame(ts, trunk); | ||||
|  | ||||
| 	/* e1inp_rx_ts() does not free() msgb */ | ||||
| 	/* e1inp_rx_ts(), the caller of this callback does not free() msgb. */ | ||||
| 	msgb_free(msg); | ||||
| } | ||||
|  | ||||
| static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr) | ||||
| static int e1_open(struct mgcp_trunk *trunk, uint8_t ts_nr) | ||||
| { | ||||
| 	/*! Each timeslot needs only to be configured once. The Timeslot then | ||||
| 	 *  stays open and permanently receives data. It is then up to the | ||||
| 	 *  I.460 demultiplexer to add/remove subchannels as needed. It is | ||||
| 	 *  allowed to call this function multiple times since we check if the | ||||
| 	 *  timeslot is already configured. */ | ||||
| 	/*! One E1 timeslot may serve multiple I.460 subslots. The timeslot is opened as soon as an I.460 subslot is | ||||
| 	 *  opened and will stay open until the last I.460 subslot is closed (see e1_close below). This function must | ||||
| 	 *  be called any time a new I.460 subslot is opened in order to maintain constancy of the ts_usecount counter. */ | ||||
|  | ||||
| 	struct e1inp_line *e1_line; | ||||
| 	int rc; | ||||
| @@ -379,12 +377,14 @@ static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr) | ||||
| 	OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS); | ||||
| 	cfg = trunk->cfg; | ||||
|  | ||||
| 	if (trunk->e1.ts_in_use[ts_nr - 1]) { | ||||
| 		LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up, skipping...\n", ts_nr); | ||||
| 	if (trunk->e1.ts_usecount[ts_nr - 1] > 0) { | ||||
| 		LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up and in use by %u subslot(s), using it as it is...\n", | ||||
| 			  ts_nr, trunk->e1.ts_usecount[ts_nr - 1]); | ||||
| 		trunk->e1.ts_usecount[ts_nr - 1]++; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Get E1 line */ | ||||
| 	/* Find E1 line */ | ||||
| 	e1_line = e1inp_line_find(trunk->e1.vty_line_nr); | ||||
| 	if (!e1_line) { | ||||
| 		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n", | ||||
| @@ -401,12 +401,61 @@ static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr) | ||||
| 	} | ||||
| 	rc = e1inp_line_update(e1_line); | ||||
| 	if (rc < 0) { | ||||
| 		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 timeslot %u.\n", ts_nr); | ||||
| 		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", ts_nr); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u set up successfully.\n", ts_nr); | ||||
| 	trunk->e1.ts_in_use[ts_nr - 1] = true; | ||||
| 	trunk->e1.ts_usecount[ts_nr - 1]++; | ||||
| 	OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 1); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int e1_close(struct mgcp_trunk *trunk, uint8_t ts_nr) | ||||
| { | ||||
| 	/* See also comment above (e1_open). This function must be called any time an I.460 subslot is closed */ | ||||
|  | ||||
| 	struct e1inp_line *e1_line; | ||||
| 	int rc; | ||||
|  | ||||
| 	OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS); | ||||
| 	cfg = trunk->cfg; | ||||
|  | ||||
| 	if (trunk->e1.ts_usecount[ts_nr - 1] > 1) { | ||||
| 		trunk->e1.ts_usecount[ts_nr - 1]--; | ||||
| 		LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u still in use by %u other subslot(s), leaving it open...\n", | ||||
| 			  ts_nr, trunk->e1.ts_usecount[ts_nr - 1]); | ||||
| 		return 0; | ||||
| 	} else if (trunk->e1.ts_usecount[ts_nr - 1] == 0) { | ||||
| 		/* This should not be as it means we close the timeslot too often. */ | ||||
| 		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "E1 timeslot %u already closed, leaving it as it is...\n", ts_nr); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/* Find E1 line */ | ||||
| 	e1_line = e1inp_line_find(trunk->e1.vty_line_nr); | ||||
| 	if (!e1_line) { | ||||
| 		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n", | ||||
| 			  trunk->e1.vty_line_nr); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/* Release E1 timeslot */ | ||||
| 	rc = e1inp_ts_config_none(&e1_line->ts[ts_nr - 1], e1_line); | ||||
| 	if (rc < 0) { | ||||
| 		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to disable E1 timeslot %u.\n", ts_nr); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	rc = e1inp_line_update(e1_line); | ||||
| 	if (rc < 0) { | ||||
| 		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", trunk->e1.vty_line_nr); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u closed.\n", ts_nr); | ||||
| 	trunk->e1.ts_usecount[ts_nr - 1]--; | ||||
| 	OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 0); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
| @@ -496,7 +545,7 @@ static bool tf_type_is_amr(enum osmo_trau_frame_type ft) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* !Equip E1 endpoint with I.460 mux resources. | ||||
| /*! Equip E1 endpoint with I.460 mux and E1 timeslot resources. | ||||
|  *  \param[in] endp endpoint to equip | ||||
|  *  \param[in] ts E1 timeslot number. | ||||
|  *  \param[in] ss E1 subslot number. | ||||
| @@ -517,7 +566,7 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8 | ||||
| 	endp->e1.last_amr_ft = AMR_4_75; | ||||
|  | ||||
| 	/* Set up E1 line / timeslot */ | ||||
| 	rc = e1_init(endp->trunk, ts); | ||||
| 	rc = e1_open(endp->trunk, ts); | ||||
| 	if (rc != 0) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| @@ -604,9 +653,15 @@ void mgcp_e1_endp_update(struct mgcp_endpoint *endp) | ||||
| } | ||||
|  | ||||
| /*! Remove E1 resources from endpoint | ||||
|  *  \param[in] endp endpoint to release. */ | ||||
| void mgcp_e1_endp_release(struct mgcp_endpoint *endp) | ||||
|  *  \param[in] endp endpoint to release. | ||||
|  *  \param[in] ts E1 timeslot number. */ | ||||
| void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts) | ||||
| { | ||||
| 	/* Guard against multiple calls. In case we don't see a subchannel anymore we can safely assume that all work | ||||
| 	 * is done. */ | ||||
| 	if (!(endp->e1.schan || endp->e1.trau_rtp_st || endp->e1.trau_sync_fi)) | ||||
| 		return; | ||||
|  | ||||
| 	LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n"); | ||||
|  | ||||
| 	if (endp->e1.schan) | ||||
| @@ -615,8 +670,10 @@ void mgcp_e1_endp_release(struct mgcp_endpoint *endp) | ||||
| 		talloc_free(endp->e1.trau_rtp_st); | ||||
| 	if (endp->e1.trau_sync_fi) | ||||
| 		osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL); | ||||
|  | ||||
| 	memset(&endp->e1, 0, sizeof(endp->e1)); | ||||
|  | ||||
| 	/* Close E1 timeslot */ | ||||
| 	e1_close(endp->trunk, ts); | ||||
| } | ||||
|  | ||||
| /*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit. | ||||
|   | ||||
| @@ -38,7 +38,6 @@ | ||||
| const struct mgcp_endpoint_typeset ep_typeset = { | ||||
| 	/* Specify endpoint properties for RTP endpoint */ | ||||
| 	.rtp = { | ||||
| 		.max_conns = 2, | ||||
| 		.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb, | ||||
| 		.cleanup_cb = mgcp_cleanup_rtp_bridge_cb, | ||||
| 	}, | ||||
| @@ -110,36 +109,6 @@ struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, | ||||
| 	return endp; | ||||
| } | ||||
|  | ||||
| /*! release endpoint, all open connections are closed. | ||||
|  *  \param[in] endp endpoint to release */ | ||||
| void mgcp_endp_release(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n"); | ||||
|  | ||||
| 	/* Normally this function should only be called when | ||||
| 	 * all connections have been removed already. In case | ||||
| 	 * that there are still connections open (e.g. when | ||||
| 	 * RSIP is executed), free them all at once. */ | ||||
| 	mgcp_conn_free_all(endp); | ||||
|  | ||||
| 	/* We must only decrement the stat item when the endpoint as actually | ||||
| 	 * claimed. An endpoint is claimed when a call-id is set */ | ||||
| 	if (endp->callid) | ||||
| 		osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common, | ||||
| 								 TRUNK_STAT_ENDPOINTS_USED), 1); | ||||
|  | ||||
| 	/* Reset endpoint parameters and states */ | ||||
| 	talloc_free(endp->callid); | ||||
| 	endp->callid = NULL; | ||||
| 	talloc_free(endp->local_options.string); | ||||
| 	endp->local_options.string = NULL; | ||||
| 	talloc_free(endp->local_options.codec); | ||||
| 	endp->local_options.codec = NULL; | ||||
|  | ||||
| 	if (endp->trunk->trunk_type == MGCP_TRUNK_E1) | ||||
| 		mgcp_e1_endp_release(endp); | ||||
| } | ||||
|  | ||||
| /* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or | ||||
|  * "ds/e1-") and write the epname without the prefix back to the memory | ||||
|  * pointed at by epname. (per trunk the prefix is the same for all endpoints, | ||||
| @@ -187,7 +156,6 @@ static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk) | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
|  /*! Convert all characters in epname to lowercase and strip trunk prefix and | ||||
|  * endpoint name suffix (domain name) from epname. The result is written to | ||||
|  * to the memory pointed at by epname_stripped. The expected size of the | ||||
| @@ -260,6 +228,17 @@ bool mgcp_endp_is_wildcarded(const char *epname) | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| /*! Check if the given epname refers to a "null" endpoint. | ||||
|  *  \param[in] epname endpoint name to check | ||||
|  *  \returns true if epname refers to "null"" endpoint, else false. */ | ||||
| bool mgcp_endp_is_null(const char *epname) | ||||
| { | ||||
| 	if (strncasecmp(epname, "null@", 5) == 0) | ||||
| 		return true; | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| /*! Find an endpoint by its name on a specified trunk. | ||||
|  *  \param[out] cause pointer to store cause code, can be NULL. | ||||
|  *  \param[in] epname endpoint name to lookup. | ||||
| @@ -675,3 +654,36 @@ void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn) | ||||
| 	if (llist_empty(&endp->conns)) | ||||
| 		mgcp_endp_release(endp); | ||||
| } | ||||
|  | ||||
| /*! release endpoint, all open connections are closed. | ||||
|  *  \param[in] endp endpoint to release */ | ||||
| void mgcp_endp_release(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n"); | ||||
|  | ||||
| 	/* Normally this function should only be called when | ||||
| 	 * all connections have been removed already. In case | ||||
| 	 * that there are still connections open (e.g. when | ||||
| 	 * RSIP is executed), free them all at once. */ | ||||
| 	mgcp_conn_free_all(endp); | ||||
|  | ||||
| 	/* We must only decrement the stat item when the endpoint as actually | ||||
| 	 * claimed. An endpoint is claimed when a call-id is set */ | ||||
| 	if (endp->callid) | ||||
| 		osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common, | ||||
| 								 TRUNK_STAT_ENDPOINTS_USED), 1); | ||||
|  | ||||
| 	/* Reset endpoint parameters and states */ | ||||
| 	talloc_free(endp->callid); | ||||
| 	endp->callid = NULL; | ||||
| 	talloc_free(endp->local_options.string); | ||||
| 	endp->local_options.string = NULL; | ||||
| 	talloc_free(endp->local_options.codec); | ||||
| 	endp->local_options.codec = NULL; | ||||
|  | ||||
| 	if (endp->trunk->trunk_type == MGCP_TRUNK_E1) { | ||||
| 		uint8_t ts = e1_ts_nr_from_epname(endp->name); | ||||
| 		mgcp_e1_endp_release(endp, ts); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -98,7 +98,11 @@ static int8_t _conn_iuup_amr_ft_2_rfci(struct mgcp_conn_rtp *conn_rtp, uint8_t f | ||||
| 	uint8_t rfci_cnt = 0; | ||||
| 	unsigned match_bytes = (unsigned)osmo_amr_bytes(ft); | ||||
| 	struct osmo_iuup_rnl_prim *irp = conn_rtp->iuup.init_ind; | ||||
| 	OSMO_ASSERT(irp); | ||||
| 	if (!irp) { | ||||
| 		/* No IuUP Initialization has occured on the IuUP side yet. Return error and drop the RTP data, until | ||||
| 		 * the IuUP Initialization has configured the link. */ | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* TODO: cache this somehow */ | ||||
| 	for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) { | ||||
| @@ -270,9 +274,9 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg | ||||
| 		return ft; | ||||
| 	} | ||||
| 	msgb_pull_to_l3(msg); | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR: ft %d, len %d\n", ft, msgb_l3len(msg)); | ||||
|  | ||||
| 	if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.codec)) { | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR OA: ft %d, len %d\n", ft, msgb_length(msg)); | ||||
| 		amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr)); | ||||
| 		amr_hdr->cmr = 15; /* no change */ | ||||
| 		amr_hdr->f = 0; | ||||
| @@ -291,6 +295,7 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg | ||||
| 		amr_data[1] |= ((ft & 0x1) << 7) | (((!fqc) & 0x1) << 6); | ||||
| 		amr_length = (osmo_amr_bits(ft) + 10 + 7) / 8; | ||||
| 		msgb_trim(msg, amr_length); | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR BE: ft %d, len %zd\n", ft, amr_length); | ||||
| 	} | ||||
| 	rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(*rtp_hdr)); | ||||
| 	*rtp_hdr = (struct rtp_hdr){ | ||||
| @@ -342,8 +347,7 @@ static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osm | ||||
| 		return bridge_iuup_to_iuup_peer(conn_rtp_src, conn_rtp_dst, irp); | ||||
| 	case MGCP_RTP_DEFAULT: | ||||
| 		return bridge_iuup_to_rtp_peer(conn_rtp_src, conn_rtp_dst, irp); | ||||
| 	case MGCP_OSMUX_BSC: | ||||
| 	case MGCP_OSMUX_BSC_NAT: | ||||
| 	case MGCP_RTP_OSMUX: | ||||
| 	default: | ||||
| 		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR, | ||||
| 			 "Forward of IuUP payload to RTP connection type %u not supported!\n", | ||||
| @@ -490,7 +494,7 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg, | ||||
| 			 "rtp_port:%u rtcp_port:%u\n", | ||||
| 			 dest_name, | ||||
| 			 osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf), | ||||
| 			 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port) | ||||
| 			 osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port) | ||||
| 			); | ||||
| 		return 0; | ||||
| 	} | ||||
| @@ -506,14 +510,13 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg, | ||||
| 	LOGPENDP(endp, DRTP, LOGL_DEBUG, | ||||
| 		 "process/send IuUP to %s %s rtp_port:%u rtcp_port:%u\n", | ||||
| 		 dest_name, osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf), | ||||
| 		 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)); | ||||
| 		 osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)); | ||||
|  | ||||
| 	/* Forward a copy of the RTP data to a debug ip/port */ | ||||
| 	forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out, | ||||
| 		     msg); | ||||
|  | ||||
| 	len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port, | ||||
| 			    (char *)hdr, buflen); | ||||
| 	len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, (char *)hdr, buflen); | ||||
|  | ||||
| 	if (len <= 0) | ||||
| 		return len; | ||||
| @@ -618,9 +621,8 @@ int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg) | ||||
| 		force_output_enabled = true; | ||||
| 		/* Fill in the peer address so that we can send Init-ACK back: */ | ||||
| 		prev_rem_addr = conn_rtp_src->end.addr; | ||||
| 		prev_rem_rtp_port = conn_rtp_src->end.rtp_port; | ||||
| 		prev_rem_rtp_port = osmo_sockaddr_port(&conn_rtp_src->end.addr.u.sa); | ||||
| 		conn_rtp_src->end.addr = *mc->from_addr; | ||||
| 		conn_rtp_src->end.rtp_port = htons(osmo_sockaddr_port(&mc->from_addr->u.sa)); | ||||
| 	} | ||||
|  | ||||
| 	rc = _conn_iuup_rtp_pl_up(conn_rtp_src, msg); | ||||
| @@ -628,7 +630,7 @@ int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg) | ||||
| 	if (force_output_enabled) { | ||||
| 		conn_rtp_src->end.output_enabled = prev_output_enabled; | ||||
| 		conn_rtp_src->end.addr = prev_rem_addr; | ||||
| 		conn_rtp_src->end.rtp_port = prev_rem_rtp_port; | ||||
| 		osmo_sockaddr_set_port(&conn_rtp_src->end.addr.u.sa, prev_rem_rtp_port); | ||||
| 	} | ||||
|  | ||||
| 	return rc; | ||||
| @@ -670,7 +672,7 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn | ||||
| 				     "Bridge RTP=>IuUP: too short for AMR OA hdr (%u)\n", msgb_length(msg)); | ||||
| 			goto free_ret; | ||||
| 		} | ||||
| 		if (amr_hdr->ft >= AMR_FT_MAX) { | ||||
| 		if (!osmo_amr_ft_valid(amr_hdr->ft)) { | ||||
| 			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR OA ft=%u\n", amr_hdr->ft); | ||||
| 			goto free_ret; | ||||
| 		} | ||||
| @@ -678,19 +680,22 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn | ||||
| 			LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR OA ft=%u\n", amr_hdr->ft); | ||||
| 			goto free_ret; | ||||
| 		} | ||||
| 		irp->u.data.fqc = amr_hdr->q; | ||||
| 		irp->u.data.fqc = amr_hdr->q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD; | ||||
| 		irp->u.data.rfci = rfci; | ||||
| 		msgb_pull(msg, 2); | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR OA -> IuUP: ft %d -> rfci %d len %d\n", | ||||
| 		     amr_hdr->ft, rfci, msgb_length(msg)); | ||||
| 	} else { | ||||
| 		uint8_t *amr_bwe_hdr = (uint8_t *) msgb_data(msg); | ||||
| 		int8_t ft; | ||||
| 		uint8_t q; | ||||
| 		if (msgb_length(msg) < 2) { | ||||
| 			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, | ||||
| 				     "Bridge RTP=>IuUP: too short for AMR BE hdr (%u)\n", msgb_length(msg)); | ||||
| 			goto free_ret; | ||||
| 		} | ||||
| 		ft = ((amr_bwe_hdr[0] & 0x07) << 1) | ((amr_bwe_hdr[1] & 0x80) >> 7); | ||||
| 		if (ft >= AMR_FT_MAX) { | ||||
| 		if (!osmo_amr_ft_valid(ft)) { | ||||
| 			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR BE ft=%u\n", ft); | ||||
| 			goto free_ret; | ||||
| 		} | ||||
| @@ -698,7 +703,8 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn | ||||
| 			LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR BE ft=%u\n", ft); | ||||
| 			goto free_ret; | ||||
| 		} | ||||
| 		irp->u.data.fqc = ((amr_bwe_hdr[1] & 0x40) >> 6); | ||||
| 		q = amr_bwe_hdr[1] & 0x40; | ||||
| 		irp->u.data.fqc = q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD; | ||||
| 		irp->u.data.rfci = rfci; | ||||
| 		rc = iuup_length = osmo_amr_bwe_to_iuup(msgb_data(msg), msgb_length(msg)); | ||||
| 		if (rc < 0) { | ||||
| @@ -706,6 +712,8 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn | ||||
| 			return rc; | ||||
| 		} | ||||
| 		msgb_trim(msg, iuup_length); | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR BE -> IuUP: ft %d -> rfci %d len %d\n", | ||||
| 		     ft, rfci, msgb_length(msg)); | ||||
| 	} | ||||
|  | ||||
| 	irp->oph.msg->l3h = msgb_put(irp->oph.msg, msgb_length(msg)); | ||||
|   | ||||
| @@ -75,7 +75,7 @@ void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble) | ||||
| } | ||||
|  | ||||
| /*! Parse connection mode. | ||||
|  *  \param[in] mode as string (recvonly, sendrecv, sendonly or loopback) | ||||
|  *  \param[in] mode as string (recvonly, sendrecv, sendonly confecho or loopback) | ||||
|  *  \param[in] endp pointer to endpoint (only used for log output) | ||||
|  *  \param[out] associated connection to be modified accordingly | ||||
|  *  \returns 0 on success, -1 on error */ | ||||
| @@ -100,6 +100,8 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp, | ||||
| 		conn->mode = MGCP_CONN_RECV_SEND; | ||||
| 	else if (strcasecmp(mode, "sendonly") == 0) | ||||
| 		conn->mode = MGCP_CONN_SEND_ONLY; | ||||
| 	else if (strcasecmp(mode, "confecho") == 0) | ||||
| 		conn->mode = MGCP_CONN_CONFECHO; | ||||
| 	else if (strcasecmp(mode, "loopback") == 0) | ||||
| 		conn->mode = MGCP_CONN_LOOPBACK; | ||||
| 	else { | ||||
| @@ -202,7 +204,7 @@ int mgcp_parse_osmux_cid(const char *line) | ||||
| 		     osmux_cid, OSMUX_CID_MAX); | ||||
| 		return -2; | ||||
| 	} | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid); | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "MGCP client offered Osmux CID %u\n", osmux_cid); | ||||
|  | ||||
| 	return osmux_cid; | ||||
| } | ||||
|   | ||||
| @@ -54,15 +54,10 @@ | ||||
| #define RTP_MAX_DROPOUT		3000 | ||||
| #define RTP_MAX_MISORDER	100 | ||||
|  | ||||
| enum rtp_proto { | ||||
| 	MGCP_PROTO_RTP, | ||||
| 	MGCP_PROTO_RTCP, | ||||
| }; | ||||
|  | ||||
| void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, | ||||
| 				 int id, int inc) | ||||
| { | ||||
| 	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group; | ||||
| 	struct rate_ctr_group *conn_stats = conn_rtp->ctrg; | ||||
| 	struct rate_ctr_group *mgw_stats = endp->trunk->ratectr.all_rtp_conn_stats; | ||||
|  | ||||
| 	/* add to both the per-connection and the global stats */ | ||||
| @@ -70,49 +65,68 @@ void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint * | ||||
| 	rate_ctr_add(rate_ctr_group_get_ctr(mgw_stats, id), inc); | ||||
| } | ||||
|  | ||||
| static void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id) | ||||
| void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id) | ||||
| { | ||||
| 	rtpconn_rate_ctr_add(conn_rtp, endp, id, 1); | ||||
| } | ||||
|  | ||||
| static int rx_rtp(struct msgb *msg); | ||||
|  | ||||
| static bool addr_is_any(const struct osmo_sockaddr *osa) | ||||
| { | ||||
| 	if (osa->u.sa.sa_family == AF_INET6) { | ||||
| 		struct in6_addr ip6_any = IN6ADDR_ANY_INIT; | ||||
| 		return memcmp(&osa->u.sin6.sin6_addr, | ||||
| 				   &ip6_any, sizeof(ip6_any)) == 0; | ||||
| 	} else { | ||||
| 		return osa->u.sin.sin_addr.s_addr == 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end) | ||||
| { | ||||
| 	return rtp_end->rtp_port && !addr_is_any(&rtp_end->addr); | ||||
| 	return (osmo_sockaddr_port(&rtp_end->addr.u.sa) != 0) && | ||||
| 	       (osmo_sockaddr_is_any(&rtp_end->addr) == 0); | ||||
| } | ||||
|  | ||||
| /*! 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. | ||||
|  * \ returns 0 on success, -1 if no local address could be provided. | ||||
|  * | ||||
|  *  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) | ||||
| int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn) | ||||
| { | ||||
|  | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	const struct mgcp_endpoint *endp = conn->conn->endp; | ||||
| 	const struct mgcp_config *cfg = endp->trunk->cfg; | ||||
| 	char ipbuf[INET6_ADDRSTRLEN]; | ||||
| 	int rc; | ||||
| 	endp = conn->conn->endp; | ||||
| 	bool rem_addr_set = !addr_is_any(&conn->end.addr); | ||||
| 	char *bind_addr; | ||||
| 	bool rem_addr_set = osmo_sockaddr_is_any(&conn->end.addr) == 0; | ||||
| 	const char *bind_addr; | ||||
|  | ||||
| 	/* Osmux: No smart IP addresses allocation is supported yet. Simply | ||||
| 	 * return the one set in VTY config: */ | ||||
| 	if (mgcp_conn_rtp_is_osmux(conn)) { | ||||
| 		if (rem_addr_set) { | ||||
| 			/* Match IP version with what was requested from remote: */ | ||||
| 			bind_addr = conn->end.addr.u.sa.sa_family == AF_INET6 ? | ||||
| 				    cfg->osmux.local_addr_v6 : | ||||
| 				    cfg->osmux.local_addr_v4; | ||||
| 		} else { | ||||
| 			/* Choose any of the bind addresses, preferring v6 over v4 if available: */ | ||||
| 			bind_addr = cfg->osmux.local_addr_v6; | ||||
| 			if (!bind_addr) | ||||
| 				bind_addr = cfg->osmux.local_addr_v4; | ||||
| 		} | ||||
| 		if (!bind_addr) { | ||||
| 			LOGPCONN(conn->conn, DOSMUX, LOGL_ERROR, | ||||
| 				"Unable to locate local Osmux address, check your configuration! v4=%u v6=%u remote_known=%s\n", | ||||
| 				!!cfg->osmux.local_addr_v4, | ||||
| 				!!cfg->osmux.local_addr_v6, | ||||
| 				rem_addr_set ? osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf) : "no"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		LOGPCONN(conn->conn, DOSMUX, LOGL_DEBUG, | ||||
| 			 "Using configured osmux bind ip as local bind ip %s\n", | ||||
| 			 bind_addr); | ||||
| 		osmo_strlcpy(addr, bind_addr, INET6_ADDRSTRLEN); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Try probing the local IP-Address */ | ||||
| 	if (endp->trunk->cfg->net_ports.bind_addr_probe && rem_addr_set) { | ||||
| 	if (cfg->net_ports.bind_addr_probe && rem_addr_set) { | ||||
| 		rc = osmo_sock_local_ip(addr, osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf)); | ||||
| 		if (rc < 0) | ||||
| 			LOGPCONN(conn->conn, DRTP, LOGL_ERROR, | ||||
| @@ -121,22 +135,22 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn) | ||||
| 			LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, | ||||
| 				 "selected local rtp bind ip %s by probing using remote ip %s\n", | ||||
| 				 addr, osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf)); | ||||
| 			return; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Select from preconfigured IP-Addresses. We don't have bind_addr for Osmux (yet?). */ | ||||
| 	/* Select from preconfigured IP-Addresses. */ | ||||
| 	if (rem_addr_set) { | ||||
| 		/* Check there is a bind IP for the RTP traffic configured, | ||||
| 		 * if so, use that IP-Address */ | ||||
| 		bind_addr = conn->end.addr.u.sa.sa_family == AF_INET6 ? | ||||
| 				endp->trunk->cfg->net_ports.bind_addr_v6 : | ||||
| 				endp->trunk->cfg->net_ports.bind_addr_v4; | ||||
| 				cfg->net_ports.bind_addr_v6 : | ||||
| 				cfg->net_ports.bind_addr_v4; | ||||
| 	} else { | ||||
| 		/* Choose any of the bind addresses, preferring v6 over v4 */ | ||||
| 		bind_addr = endp->trunk->cfg->net_ports.bind_addr_v6; | ||||
| 		bind_addr = cfg->net_ports.bind_addr_v6; | ||||
| 		if (!strlen(bind_addr)) | ||||
| 			bind_addr = endp->trunk->cfg->net_ports.bind_addr_v4; | ||||
| 			bind_addr = cfg->net_ports.bind_addr_v4; | ||||
| 	} | ||||
| 	if (strlen(bind_addr)) { | ||||
| 		LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, | ||||
| @@ -146,11 +160,12 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn) | ||||
| 		/* No specific bind IP is configured for the RTP traffic, so | ||||
| 		 * assume the IP where we listen for incoming MGCP messages | ||||
| 		 * as bind IP */ | ||||
| 		bind_addr = endp->trunk->cfg->source_addr; | ||||
| 		bind_addr = cfg->source_addr; | ||||
| 		LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, | ||||
| 			"using mgcp bind ip as local rtp bind ip: %s\n", bind_addr); | ||||
| 	} | ||||
| 	osmo_strlcpy(addr, bind_addr, INET6_ADDRSTRLEN); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* This does not need to be a precision timestamp and | ||||
| @@ -477,28 +492,24 @@ void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, | ||||
|  | ||||
| /* There may be different payload type numbers negotiated for two connections. | ||||
|  * Patch the payload type of an RTP packet so that it uses the payload type | ||||
|  * that is valid for the destination connection (conn_dst) */ | ||||
| static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src, | ||||
| 			 struct mgcp_conn_rtp *conn_dst, struct msgb *msg) | ||||
|  * of the codec that is set for the destination connection (conn_dst) */ | ||||
| static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_dst, struct msgb *msg) | ||||
| { | ||||
| 	struct rtp_hdr *rtp_hdr; | ||||
| 	uint8_t pt_in; | ||||
| 	int pt_out; | ||||
|  | ||||
| 	if (msgb_length(msg) < sizeof(struct rtp_hdr)) { | ||||
| 		LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n", | ||||
| 		LOG_CONN_RTP(conn_dst, LOGL_NOTICE, "RTP packet too short (%u < %zu)\n", | ||||
| 			     msgb_length(msg), sizeof(struct rtp_hdr)); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	rtp_hdr = (struct rtp_hdr *)msgb_data(msg); | ||||
|  | ||||
| 	pt_in = rtp_hdr->payload_type; | ||||
| 	pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in); | ||||
| 	if (pt_out < 0) | ||||
| 	if (!conn_dst->end.codec) { | ||||
| 		LOG_CONN_RTP(conn_dst, LOGL_NOTICE, "no codec set on destination connection!\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	rtp_hdr->payload_type = (uint8_t) conn_dst->end.codec->payload_type; | ||||
|  | ||||
| 	rtp_hdr->payload_type = (uint8_t) pt_out; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -676,7 +687,7 @@ static int rfc5993_hr_convert(struct mgcp_endpoint *endp, struct msgb *msg) | ||||
| { | ||||
| 	struct rtp_hdr *rtp_hdr; | ||||
| 	if (msgb_length(msg) < sizeof(struct rtp_hdr)) { | ||||
| 		LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n", | ||||
| 		LOGPENDP(endp, DRTP, LOGL_ERROR, "RTP packet too short (%d < %zu)\n", | ||||
| 			 msgb_length(msg), sizeof(struct rtp_hdr)); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| @@ -702,11 +713,18 @@ static int rfc5993_hr_convert(struct mgcp_endpoint *endp, struct msgb *msg) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* For AMR RTP two framing modes are defined RFC3267. There is a bandwith | ||||
| /*! Convert msg to AMR RTP framing mode specified by target_is_oa. | ||||
|  *  \param[in] endp MGCP Endpoint where this message belongs to (used for logging purposes) | ||||
|  *  \param[in] msg Message containing an AMR RTP payload (in octet-aligned or bandwidth-efficient format). | ||||
|  *  \param[in] target_is_oa the target framing mode that msg will contain after this function succeeds. | ||||
|  *  \returns The size of the new RTP AMR content on success, negative on error. | ||||
|  * | ||||
|  * For AMR RTP two framing modes are defined RFC3267. There is a bandwidth | ||||
|  * efficient encoding scheme where all fields are packed together one after | ||||
|  * another and an octet aligned mode where all fields are aligned to octet | ||||
|  * boundaries. This function is used to convert between the two modes */ | ||||
| static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, | ||||
|  * boundaries. This function is used to convert between the two modes. | ||||
|  */ | ||||
| int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, | ||||
| 			      bool target_is_oa) | ||||
| { | ||||
| 	/* NOTE: the msgb has an allocated length of RTP_BUF_SIZE, so there is | ||||
| @@ -715,6 +733,7 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, | ||||
| 	struct rtp_hdr *rtp_hdr; | ||||
| 	unsigned int payload_len; | ||||
| 	int rc; | ||||
| 	bool orig_is_oa; | ||||
|  | ||||
| 	if (msgb_length(msg) < sizeof(struct rtp_hdr)) { | ||||
| 		LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n", msgb_length(msg), sizeof(struct rtp_hdr)); | ||||
| @@ -722,10 +741,10 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, | ||||
| 	} | ||||
|  | ||||
| 	rtp_hdr = (struct rtp_hdr *)msgb_data(msg); | ||||
|  | ||||
| 	payload_len = msgb_length(msg) - sizeof(struct rtp_hdr); | ||||
| 	orig_is_oa = osmo_amr_is_oa(rtp_hdr->data, payload_len); | ||||
|  | ||||
| 	if (osmo_amr_is_oa(rtp_hdr->data, payload_len)) { | ||||
| 	if (orig_is_oa) { | ||||
| 		if (!target_is_oa) | ||||
| 			/* Input data is oa an target format is bwe | ||||
| 			 * ==> convert */ | ||||
| @@ -747,27 +766,16 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, | ||||
| 	} | ||||
| 	if (rc < 0) { | ||||
| 		LOGPENDP(endp, DRTP, LOGL_ERROR, | ||||
| 			 "AMR RTP packet conversion failed\n"); | ||||
| 			 "RTP AMR packet conversion %s->%s failed: %s\n", | ||||
| 			 orig_is_oa ? "OA" : "BWE", | ||||
| 			 target_is_oa ? "OA" : "BWE", | ||||
| 			 osmo_hexdump(rtp_hdr->data, payload_len)); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	return msgb_trim(msg, rc + sizeof(struct rtp_hdr)); | ||||
| } | ||||
|  | ||||
| /* Check if a conversion between octet-aligned and bandwith-efficient mode is | ||||
|  * indicated. */ | ||||
| static bool amr_oa_bwe_convert_indicated(struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	if (codec->param_present == false) | ||||
| 		return false; | ||||
| 	if (!codec->param.amr_octet_aligned_present) | ||||
| 		return false; | ||||
| 	if (strcmp(codec->subtype_name, "AMR") != 0) | ||||
| 		return false; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Return whether an RTP packet with AMR payload is in octet-aligned mode. | ||||
|  * Return 0 if in bandwidth-efficient mode, 1 for octet-aligned mode, and negative if the RTP data is invalid. */ | ||||
| static int amr_oa_check(char *data, int len) | ||||
| @@ -826,7 +834,7 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad | ||||
| { | ||||
| 	char ipbuf[INET6_ADDRSTRLEN]; | ||||
|  | ||||
| 	if (addr_is_any(&conn->end.addr)) { | ||||
| 	if (osmo_sockaddr_is_any(&conn->end.addr) != 0) { | ||||
| 		switch (conn->conn->mode) { | ||||
| 		case MGCP_CONN_LOOPBACK: | ||||
| 			/* HACK: for IuUP, we want to reply with an IuUP Initialization ACK upon the first RTP | ||||
| @@ -875,14 +883,14 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad | ||||
| 	 * the same as the remote port where we transmit outgoing RTP traffic | ||||
| 	 * to (set by MDCX). We use this to check the origin of the data for | ||||
| 	 * plausibility. */ | ||||
| 	if (ntohs(conn->end.rtp_port) != osmo_sockaddr_port(&addr->u.sa) && | ||||
| 	if (osmo_sockaddr_port(&conn->end.addr.u.sa) != osmo_sockaddr_port(&addr->u.sa) && | ||||
| 	    ntohs(conn->end.rtcp_port) != osmo_sockaddr_port(&addr->u.sa)) { | ||||
| 		LOGPCONN(conn->conn, DRTP, LOGL_ERROR, | ||||
| 			 "data from wrong source port: %d, ", | ||||
| 			 osmo_sockaddr_port(&addr->u.sa)); | ||||
| 		LOGPC(DRTP, LOGL_ERROR, | ||||
| 		      "expected: %d for RTP or %d for RTCP\n", | ||||
| 		      ntohs(conn->end.rtp_port), ntohs(conn->end.rtcp_port)); | ||||
| 		      osmo_sockaddr_port(&conn->end.addr.u.sa), ntohs(conn->end.rtcp_port)); | ||||
| 		LOGPCONN(conn->conn, DRTP, LOGL_ERROR, "packet tossed\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| @@ -895,28 +903,29 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad | ||||
| static int check_rtp_destin(struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	char ipbuf[INET6_ADDRSTRLEN]; | ||||
| 	bool ip_is_any = addr_is_any(&conn->end.addr); | ||||
| 	bool ip_is_any = osmo_sockaddr_is_any(&conn->end.addr) != 0; | ||||
| 	uint16_t port = osmo_sockaddr_port(&conn->end.addr.u.sa); | ||||
|  | ||||
| 	/* Note: it is legal to create a connection but never setting a port | ||||
| 	 * and IP-address for outgoing data. */ | ||||
| 	if (ip_is_any && conn->end.rtp_port == 0) { | ||||
| 	if (ip_is_any && port == 0) { | ||||
| 		LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, | ||||
| 			 "destination IP-address and rtp port is not (yet) known (%s:%u)\n", | ||||
| 			 osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), conn->end.rtp_port); | ||||
| 			 osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), port); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (ip_is_any) { | ||||
| 		LOGPCONN(conn->conn, DRTP, LOGL_ERROR, | ||||
| 			 "destination IP-address is invalid (%s:%u)\n", | ||||
| 			 osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), conn->end.rtp_port); | ||||
| 			 osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), port); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (conn->end.rtp_port == 0) { | ||||
| 	if (port == 0) { | ||||
| 		LOGPCONN(conn->conn, DRTP, LOGL_ERROR, | ||||
| 			 "destination rtp port is invalid (%s:%u)\n", | ||||
| 			 osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), conn->end.rtp_port); | ||||
| 			 osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), port); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| @@ -980,9 +989,16 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Send RTP data. Possible options are standard RTP packet | ||||
|  * transmission or trsmission via an osmux connection */ | ||||
| static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg) | ||||
| /*! Dispatch msg bridged from the sister conn in the endpoint. | ||||
|  *  \param[in] conn_dst The destination conn that should handle and transmit the content to | ||||
|  *			its peer outside MGW. | ||||
|  *  \param[in] msg msgb containing an RTP pkt received by the sister conn in the endpoint, | ||||
|  *  \returns bytes sent, -1 on error. | ||||
|  * | ||||
|  * Possible options are standard RTP packet transmission, transmission | ||||
|  * via IuUP or transmission via an osmux connection. | ||||
|  */ | ||||
| static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg) | ||||
| { | ||||
| 	struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg); | ||||
| 	enum rtp_proto proto = mc->proto; | ||||
| @@ -1007,12 +1023,11 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg) | ||||
| 			 "using mgcp_send() to forward data directly\n"); | ||||
| 		return mgcp_send(endp, proto == MGCP_PROTO_RTP, | ||||
| 				 mc->from_addr, msg, conn_src, conn_dst); | ||||
| 	case MGCP_OSMUX_BSC_NAT: | ||||
| 	case MGCP_OSMUX_BSC: | ||||
| 	case MGCP_RTP_OSMUX: | ||||
| 		LOGPENDP(endp, DRTP, LOGL_DEBUG, | ||||
| 			 "endpoint type is MGCP_OSMUX_BSC_NAT, " | ||||
| 			 "endpoint type is MGCP_RTP_OSMUX, " | ||||
| 			 "using osmux_xfrm_to_osmux() to forward data through OSMUX\n"); | ||||
| 		return osmux_xfrm_to_osmux((char*)msgb_data(msg), msgb_length(msg), conn_dst); | ||||
| 		return conn_osmux_send_rtp(conn_dst, msg); | ||||
| 	case MGCP_RTP_IUUP: | ||||
| 		if (proto == MGCP_PROTO_RTP) { | ||||
| 			LOGPENDP(endp, DRTP, LOGL_DEBUG, | ||||
| @@ -1039,26 +1054,22 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg) | ||||
| /*! send udp packet. | ||||
|  *  \param[in] fd associated file descriptor. | ||||
|  *  \param[in] addr destination ip-address. | ||||
|  *  \param[in] port destination UDP port (network byte order). | ||||
|  *  \param[in] buf buffer that holds the data to be send. | ||||
|  *  \param[in] len length of the data to be sent. | ||||
|  *  \returns bytes sent, -1 on error. */ | ||||
| int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, const char *buf, int len) | ||||
| int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len) | ||||
| { | ||||
| 	char ipbuf[INET6_ADDRSTRLEN]; | ||||
| 	size_t addr_len; | ||||
| 	bool is_ipv6 =  addr->u.sa.sa_family == AF_INET6; | ||||
|  | ||||
| 	LOGP(DRTP, LOGL_DEBUG, | ||||
| 	     "sending %i bytes length packet to %s:%u ...\n", len, | ||||
| 	     osmo_sockaddr_ntop(&addr->u.sa, ipbuf), | ||||
| 	     ntohs(port)); | ||||
| 	     osmo_sockaddr_port(&addr->u.sa)); | ||||
|  | ||||
| 	if (is_ipv6) { | ||||
| 		addr->u.sin6.sin6_port = port; | ||||
| 	if (addr->u.sa.sa_family == AF_INET6) { | ||||
| 		addr_len = sizeof(addr->u.sin6); | ||||
| 	} else { | ||||
| 		addr->u.sin.sin_port = port; | ||||
| 		addr_len = sizeof(addr->u.sin); | ||||
| 	} | ||||
|  | ||||
| @@ -1073,6 +1084,7 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	int rc; | ||||
| 	int was_rtcp = 0; | ||||
| 	struct osmo_sockaddr rtcp_addr; | ||||
|  | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	OSMO_ASSERT(conn); | ||||
| @@ -1089,7 +1101,7 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) | ||||
| 	if (mgcp_conn_rtp_is_iuup(conn)) | ||||
| 		rc = mgcp_conn_iuup_send_dummy(conn); | ||||
| 	else | ||||
| 		rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr, conn->end.rtp_port, | ||||
| 		rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr, | ||||
| 				   rtp_dummy_payload, sizeof(rtp_dummy_payload)); | ||||
|  | ||||
| 	if (rc == -1) | ||||
| @@ -1099,8 +1111,10 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) | ||||
| 		return rc; | ||||
|  | ||||
| 	was_rtcp = 1; | ||||
| 	rc = mgcp_udp_send(conn->end.rtcp.fd, &conn->end.addr, | ||||
| 			   conn->end.rtcp_port, rtp_dummy_payload, sizeof(rtp_dummy_payload)); | ||||
| 	rtcp_addr = conn->end.addr; | ||||
| 	osmo_sockaddr_set_port(&rtcp_addr.u.sa, ntohs(conn->end.rtcp_port)); | ||||
| 	rc = mgcp_udp_send(conn->end.rtcp.fd, &rtcp_addr, | ||||
| 			   rtp_dummy_payload, sizeof(rtp_dummy_payload)); | ||||
|  | ||||
| 	if (rc >= 0) | ||||
| 		return rc; | ||||
| @@ -1116,12 +1130,11 @@ failed: | ||||
| /*! Send RTP/RTCP data to a specified destination connection. | ||||
|  *  \param[in] endp associated endpoint (for configuration, logging). | ||||
|  *  \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP. | ||||
|  *  \param[in] spoofed source address (set to NULL to disable). | ||||
|  *  \param[in] buf buffer that contains the RTP/RTCP data. | ||||
|  *  \param[in] len length of the buffer that contains the RTP/RTCP data. | ||||
|  *  \param[in] addr spoofed source address (set to NULL to disable). | ||||
|  *  \param[in] msg message buffer that contains the RTP/RTCP data. | ||||
|  *  \param[in] conn_src associated source connection. | ||||
|  *  \param[in] conn_dst associated destination connection. | ||||
|  *  \returns 0 on success, -1 on ERROR. */ | ||||
|  *  \returns 0 on success, negative on ERROR. */ | ||||
| int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr, | ||||
| 	      struct msgb *msg, struct mgcp_conn_rtp *conn_src, | ||||
| 	      struct mgcp_conn_rtp *conn_dst) | ||||
| @@ -1146,19 +1159,14 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr | ||||
| 	else | ||||
| 		LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering RTCP packet...\n"); | ||||
|  | ||||
| 	/* FIXME: It is legal that the payload type on the egress connection is | ||||
| 	 * different from the payload type that has been negotiated on the | ||||
| 	 * ingress connection. Essentially the codecs are the same so we can | ||||
| 	 * match them and patch the payload type. However, if we can not find | ||||
| 	 * the codec pendant (everything ist equal except the PT), we are of | ||||
| 	 * course unable to patch the payload type. A situation like this | ||||
| 	 * should not occur if transcoding is consequently avoided. Until | ||||
| 	 * we have transcoding support in osmo-mgw we can not resolve this. */ | ||||
| 	if (is_rtp && conn_dst->type != MGCP_RTP_IUUP) { | ||||
| 		rc = mgcp_patch_pt(conn_src, conn_dst, msg); | ||||
| 		if (rc < 0) { | ||||
| 			LOGPENDP(endp, DRTP, LOGL_DEBUG, | ||||
| 				 "can not patch PT because no suitable egress codec was found.\n"); | ||||
| 	/* Patch the payload type number: translate from conn_src to conn_dst. | ||||
| 	 * Do not patch for IuUP, where the correct payload type number is already set in bridge_iuup_to_rtp_peer(): | ||||
| 	 * IuUP -> AMR: calls this function, skip patching if conn_src is IuUP. | ||||
| 	 * {AMR or IuUP} -> IuUP: calls mgcp_udp_send() directly, skipping this function: No need to examine dst. */ | ||||
| 	if (is_rtp && !mgcp_conn_rtp_is_iuup(conn_src)) { | ||||
| 		if (mgcp_patch_pt(conn_dst, msg) < 0) { | ||||
| 			LOGPENDP(endp, DRTP, LOGL_NOTICE, "unable to patch payload type RTP packet, discarding...\n"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -1180,7 +1188,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr | ||||
| 			 "rtp_port:%u rtcp_port:%u\n", | ||||
| 			 dest_name, | ||||
| 			 osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf), | ||||
| 			 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port) | ||||
| 			 osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port) | ||||
| 		    ); | ||||
| 	} else if (is_rtp) { | ||||
| 		int cont; | ||||
| @@ -1203,12 +1211,13 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr | ||||
|  | ||||
| 			if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) { | ||||
| 				/* the iuup code will correctly transform to the correct AMR mode */ | ||||
| 			} else if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) { | ||||
| 			} else if (mgcp_codec_amr_align_mode_is_indicated(conn_dst->end.codec)) { | ||||
| 				rc = amr_oa_bwe_convert(endp, msg, | ||||
| 							conn_dst->end.codec->param.amr_octet_aligned); | ||||
| 				if (rc < 0) { | ||||
| 					LOGPENDP(endp, DRTP, LOGL_ERROR, | ||||
| 						 "Error in AMR octet-aligned <-> bandwidth-efficient mode conversion\n"); | ||||
| 						 "Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n", | ||||
| 						 conn_dst->end.codec->param.amr_octet_aligned ? "octet-aligned" : "bandwidth-efficient"); | ||||
| 					break; | ||||
| 				} | ||||
| 			} else if (rtp_end->rfc5993_hr_convert && | ||||
| @@ -1225,14 +1234,14 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr | ||||
| 				 "rtp_port:%u rtcp_port:%u\n", | ||||
| 				 dest_name, | ||||
| 				 osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf), | ||||
| 				 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port) | ||||
| 				 osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port) | ||||
| 				); | ||||
|  | ||||
| 			/* Forward a copy of the RTP data to a debug ip/port */ | ||||
| 			forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out, | ||||
| 				     msg); | ||||
|  | ||||
| 			len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port, | ||||
| 			len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, | ||||
| 					    (char *)msgb_data(msg), msgb_length(msg)); | ||||
|  | ||||
| 			if (len <= 0) | ||||
| @@ -1247,15 +1256,17 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr | ||||
| 		} while (buflen > 0); | ||||
| 		return nbytes; | ||||
| 	} else if (!trunk->omit_rtcp) { | ||||
| 		struct osmo_sockaddr rtcp_addr = rtp_end->addr; | ||||
| 		osmo_sockaddr_set_port(&rtcp_addr.u.sa, rtp_end->rtcp_port); | ||||
| 		LOGPENDP(endp, DRTP, LOGL_DEBUG, | ||||
| 			 "send to %s %s rtp_port:%u rtcp_port:%u\n", | ||||
| 			 dest_name, osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf), | ||||
| 			 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port) | ||||
| 			 dest_name, osmo_sockaddr_ntop(&rtcp_addr.u.sa, ipbuf), | ||||
| 			 osmo_sockaddr_port(&rtp_end->addr.u.sa), | ||||
| 			 osmo_sockaddr_port(&rtcp_addr.u.sa) | ||||
| 			); | ||||
|  | ||||
| 		len = mgcp_udp_send(rtp_end->rtcp.fd, | ||||
| 				    &rtp_end->addr, | ||||
| 				    rtp_end->rtcp_port, (char *)msgb_data(msg), msgb_length(msg)); | ||||
| 		len = mgcp_udp_send(rtp_end->rtcp.fd, &rtcp_addr, | ||||
| 				    (char *)msgb_data(msg), msgb_length(msg)); | ||||
|  | ||||
| 		rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR); | ||||
| 		rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len); | ||||
| @@ -1267,21 +1278,22 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! dispatch incoming RTP packet to opposite RTP connection. | ||||
|  *  \param[in] proto protocol (MGCP_CONN_TYPE_RTP or MGCP_CONN_TYPE_RTCP). | ||||
|  *  \param[in] addr socket address where the RTP packet has been received from. | ||||
|  *  \param[in] buf buffer that hold the RTP payload. | ||||
|  *  \param[in] buf_size size data length of buf. | ||||
|  *  \param[in] conn originating connection. | ||||
|  *  \returns 0 on success, -1 on ERROR. */ | ||||
| /*! Dispatch incoming RTP packet to opposite RTP connection. | ||||
|  * \param[in] msg Message buffer to bridge, coming from source connection. | ||||
|  *            msg shall contain "struct osmo_rtp_msg_ctx *" attached in | ||||
|  *            "OSMO_RTP_MSG_CTX(msg)". | ||||
|  *  \returns 0 on success, -1 on ERROR. | ||||
|  */ | ||||
| int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg) | ||||
| { | ||||
| 	struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg); | ||||
| 	struct mgcp_conn_rtp *conn_src = mc->conn_src; | ||||
| 	struct mgcp_conn *conn = conn_src->conn; | ||||
| 	struct mgcp_conn *conn_dst; | ||||
| 	struct mgcp_endpoint *endp = conn->endp; | ||||
| 	struct osmo_sockaddr *from_addr = mc->from_addr; | ||||
| 	char ipbuf[INET6_ADDRSTRLEN]; | ||||
| 	int rc = 0; | ||||
|  | ||||
| 	/*! NOTE: This callback function implements the endpoint specific | ||||
| 	 *  dispatch behaviour of an rtp bridge/proxy endpoint. It is assumed | ||||
| @@ -1302,57 +1314,46 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg) | ||||
| 		 * 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->u.rtp.end.rtp_port == 0) { | ||||
| 		if (osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa) == 0) { | ||||
| 			memcpy(&conn->u.rtp.end.addr, from_addr, | ||||
| 			       sizeof(conn->u.rtp.end.addr)); | ||||
| 			switch (from_addr->u.sa.sa_family) { | ||||
| 			case AF_INET: | ||||
| 				conn->u.rtp.end.rtp_port = from_addr->u.sin.sin_port; | ||||
| 				break; | ||||
| 			case AF_INET6: | ||||
| 				conn->u.rtp.end.rtp_port = from_addr->u.sin6.sin6_port; | ||||
| 				break; | ||||
| 			default: | ||||
| 				OSMO_ASSERT(false); | ||||
| 			} | ||||
| 			LOG_CONN_RTP(conn_src, LOGL_NOTICE, | ||||
| 				     "loopback mode: implicitly using source address (%s:%u) as destination address\n", | ||||
| 				     osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf), | ||||
| 				     conn->u.rtp.end.rtp_port); | ||||
| 				     osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa)); | ||||
| 		} | ||||
| 		return mgcp_send_rtp(conn_src, msg); | ||||
| 		return mgcp_conn_rtp_dispatch_rtp(conn_src, msg); | ||||
| 	} | ||||
|  | ||||
| 	/* Find a destination connection. */ | ||||
| 	/* NOTE: This code path runs every time an RTP packet is received. The | ||||
| 	 * function mgcp_find_dst_conn() we use to determine the detination | ||||
| 	 * connection will iterate the connection list inside the endpoint. | ||||
| 	 * Since list iterations are quite costly, we will figure out the | ||||
| 	 * destination only once and use the optional private data pointer of | ||||
| 	 * the connection to cache the destination connection pointer. */ | ||||
| 	if (!conn->priv) { | ||||
| 		conn_dst = mgcp_find_dst_conn(conn); | ||||
| 		conn->priv = conn_dst; | ||||
| 	} else { | ||||
| 		conn_dst = (struct mgcp_conn *)conn->priv; | ||||
| 	/* If the mode does not allow receiving RTP, we are done. */ | ||||
| 	switch (conn->mode) { | ||||
| 	case MGCP_CONN_RECV_ONLY: | ||||
| 	case MGCP_CONN_RECV_SEND: | ||||
| 	case MGCP_CONN_CONFECHO: | ||||
| 		break; | ||||
| 	default: | ||||
| 		return rc; | ||||
| 	} | ||||
|  | ||||
| 	/* There is no destination conn, stop here */ | ||||
| 	if (!conn_dst) { | ||||
| 		LOGPCONN(conn, DRTP, LOGL_DEBUG, | ||||
| 			 "no connection to forward an incoming RTP packet to\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/* If the mode is "confecho", send RTP back to the sender. */ | ||||
| 	if (conn->mode == MGCP_CONN_CONFECHO) | ||||
| 		rc = mgcp_conn_rtp_dispatch_rtp(conn_src, msg); | ||||
|  | ||||
| 	/* The destination conn is not an RTP connection */ | ||||
| 	if (conn_dst->type != MGCP_CONN_TYPE_RTP) { | ||||
| 		LOGPCONN(conn, DRTP, LOGL_ERROR, | ||||
| 			 "unable to find suitable destination conn\n"); | ||||
| 		return -1; | ||||
| 	/* Dispatch RTP packet to all other connection(s) that send audio. */ | ||||
| 	llist_for_each_entry(conn_dst, &endp->conns, entry) { | ||||
| 		if (conn_dst == conn) | ||||
| 			continue; | ||||
| 		switch (conn_dst->mode) { | ||||
| 		case MGCP_CONN_SEND_ONLY: | ||||
| 		case MGCP_CONN_RECV_SEND: | ||||
| 		case MGCP_CONN_CONFECHO: | ||||
| 			rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg); | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Dispatch RTP packet to destination RTP connection */ | ||||
| 	return mgcp_send_rtp(&conn_dst->u.rtp, msg); | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /*! dispatch incoming RTP packet to E1 subslot, handle RTCP packets locally. | ||||
| @@ -1377,25 +1378,15 @@ int mgcp_dispatch_e1_bridge_cb(struct msgb *msg) | ||||
| 		 * 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->u.rtp.end.rtp_port == 0) { | ||||
| 		if (osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa) == 0) { | ||||
| 			memcpy(&conn->u.rtp.end.addr, from_addr, | ||||
| 			       sizeof(conn->u.rtp.end.addr)); | ||||
| 			switch (from_addr->u.sa.sa_family) { | ||||
| 			case AF_INET: | ||||
| 				conn->u.rtp.end.rtp_port = from_addr->u.sin.sin_port; | ||||
| 				break; | ||||
| 			case AF_INET6: | ||||
| 				conn->u.rtp.end.rtp_port = from_addr->u.sin6.sin6_port; | ||||
| 				break; | ||||
| 			default: | ||||
| 				OSMO_ASSERT(false); | ||||
| 			} | ||||
| 			LOG_CONN_RTP(conn_src, LOGL_NOTICE, | ||||
| 				     "loopback mode: implicitly using source address (%s:%u) as destination address\n", | ||||
| 				     osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf), | ||||
| 				     conn->u.rtp.end.rtp_port); | ||||
| 				     osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa)); | ||||
| 		} | ||||
| 		return mgcp_send_rtp(conn_src, msg); | ||||
| 		return mgcp_conn_rtp_dispatch_rtp(conn_src, msg); | ||||
| 	} | ||||
|  | ||||
| 	/* Forward to E1 */ | ||||
| @@ -1504,8 +1495,8 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) | ||||
| 					sizeof(struct sockaddr_in))); | ||||
|  | ||||
| 	/* Increment RX statistics */ | ||||
| 	rate_ctr_inc(rate_ctr_group_get_ctr(conn_src->rate_ctr_group, RTP_PACKETS_RX_CTR)); | ||||
| 	rate_ctr_add(rate_ctr_group_get_ctr(conn_src->rate_ctr_group, RTP_OCTETS_RX_CTR), msgb_length(msg)); | ||||
| 	rate_ctr_inc(rate_ctr_group_get_ctr(conn_src->ctrg, RTP_PACKETS_RX_CTR)); | ||||
| 	rate_ctr_add(rate_ctr_group_get_ctr(conn_src->ctrg, RTP_OCTETS_RX_CTR), msgb_length(msg)); | ||||
| 	/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */ | ||||
|  | ||||
| 	/* Forward a copy of the RTP data to a debug ip/port */ | ||||
| @@ -1529,25 +1520,29 @@ static int rx_rtp(struct msgb *msg) | ||||
|  | ||||
| 	LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx_rtp(%u bytes)\n", msgb_length(msg)); | ||||
|  | ||||
| 	mgcp_conn_watchdog_kick(conn_src->conn); | ||||
|  | ||||
| 	/* If AMR is configured for the ingress connection a conversion of the | ||||
| 	 * framing mode (octet-aligned vs. bandwith-efficient is explicitly | ||||
| 	 * define, then we check if the incoming payload matches that | ||||
| 	 * expectation. */ | ||||
| 	if (mc->proto == MGCP_PROTO_RTP && | ||||
| 	    amr_oa_bwe_convert_indicated(conn_src->end.codec)) { | ||||
| 		int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg)); | ||||
| 		if (oa < 0) | ||||
| 			return -1; | ||||
| 		if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) | ||||
| 			return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if the origin of the RTP packet seems plausible */ | ||||
| 	if (!trunk->rtp_accept_all && check_rtp_origin(conn_src, from_addr)) | ||||
| 		return -1; | ||||
|  | ||||
| 	/* Handle AMR frame format conversion (octet-aligned vs. bandwith-efficient) */ | ||||
| 	if (mc->proto == MGCP_PROTO_RTP && | ||||
| 	    mgcp_codec_amr_align_mode_is_indicated(conn_src->end.codec)) { | ||||
| 		/* Make sure that the incoming AMR frame format matches the frame format that the call agent has | ||||
| 		 * communicated via SDP when the connection was created/modfied. */ | ||||
| 		int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg)); | ||||
| 		if (oa < 0) | ||||
| 			return -1; | ||||
| 		if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) { | ||||
| 			LOG_CONN_RTP(conn_src, LOGL_NOTICE, | ||||
| 				     "rx_rtp(%u bytes): Expected RTP AMR octet-aligned=%u but got octet-aligned=%u." | ||||
| 				     " check the config of your call-agent!\n", | ||||
| 				     msgb_length(msg), conn_src->end.codec->param.amr_octet_aligned, oa); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	mgcp_conn_watchdog_kick(conn_src->conn); | ||||
|  | ||||
| 	/* Execute endpoint specific implementation that handles the | ||||
| 	 * dispatching of the RTP data */ | ||||
| 	return conn->endp->type->dispatch_rtp_cb(msg); | ||||
| @@ -1668,14 +1663,14 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port, | ||||
| void mgcp_free_rtp_port(struct mgcp_rtp_end *end) | ||||
| { | ||||
| 	if (end->rtp.fd != -1) { | ||||
| 		osmo_fd_unregister(&end->rtp); | ||||
| 		close(end->rtp.fd); | ||||
| 		end->rtp.fd = -1; | ||||
| 		osmo_fd_unregister(&end->rtp); | ||||
| 	} | ||||
|  | ||||
| 	if (end->rtcp.fd != -1) { | ||||
| 		osmo_fd_unregister(&end->rtcp); | ||||
| 		close(end->rtcp.fd); | ||||
| 		end->rtcp.fd = -1; | ||||
| 		osmo_fd_unregister(&end->rtcp); | ||||
| 	} | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -47,6 +47,7 @@ | ||||
| #include <osmocom/mgcp/mgcp_codec.h> | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
| #include <osmocom/mgcp/mgcp_iuup.h> | ||||
| #include <osmocom/mgcp/debug.h> | ||||
|  | ||||
| /* Contains the last successfully resolved endpoint name. This variable is used | ||||
|  * for the unit-tests to verify that the endpoint was correctly resolved. */ | ||||
| @@ -85,6 +86,9 @@ struct mgcp_request_data { | ||||
| 	/* set to true when the request has been classified as wildcarded */ | ||||
| 	bool wildcarded; | ||||
|  | ||||
| 	/* Set to true when the request is targeted at the "null" endpoint */ | ||||
| 	bool null_endp; | ||||
|  | ||||
| 	/* contains cause code in case of problems during endp/trunk resolution */ | ||||
| 	int mgcp_cause; | ||||
| }; | ||||
| @@ -150,8 +154,7 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp, | ||||
|  | ||||
| 	switch (conn->type) { | ||||
| 	case MGCP_RTP_DEFAULT: | ||||
| 	case MGCP_OSMUX_BSC: | ||||
| 	case MGCP_OSMUX_BSC_NAT: | ||||
| 	case MGCP_RTP_OSMUX: | ||||
| 	case MGCP_RTP_IUUP: | ||||
| 		break; | ||||
| 	default: | ||||
| @@ -301,7 +304,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, | ||||
|  | ||||
| 	/* Attach optional OSMUX parameters */ | ||||
| 	if (mgcp_conn_rtp_is_osmux(conn)) { | ||||
| 		rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid); | ||||
| 		rc = msgb_printf(sdp, MGCP_X_OSMO_OSMUX_HEADER " %u\r\n", conn->osmux.local_cid); | ||||
| 		if (rc < 0) | ||||
| 			goto error; | ||||
| 	} | ||||
| @@ -324,8 +327,13 @@ error: | ||||
|  * osmux connection, send the dummy packet via OSMUX */ | ||||
| static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	if (conn->osmux.state != OSMUX_STATE_DISABLED) | ||||
| 		osmux_send_dummy(endp, conn); | ||||
| 	/* Avoid sending dummy packet if the remote address was not yet | ||||
| 	 * configured through CRCX/MDCX: */ | ||||
| 	if (!mgcp_rtp_end_remote_addr_available(&conn->end)) | ||||
| 		return; | ||||
|  | ||||
| 	if (mgcp_conn_rtp_is_osmux(conn)) | ||||
| 		osmux_send_dummy(conn); | ||||
| 	else | ||||
| 		mgcp_send_dummy(endp, conn); | ||||
| } | ||||
| @@ -385,7 +393,10 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) | ||||
| 	/* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */ | ||||
| 	rq.pdata = &pdata; | ||||
| 	rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname); | ||||
| 	rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg); | ||||
| 	if (!rq.wildcarded) | ||||
| 		rq.null_endp = mgcp_endp_is_null(pdata.epname); | ||||
| 	if (!rq.null_endp) | ||||
| 		rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg); | ||||
| 	rq.mgcp_cause = rc; | ||||
| 	if (!rq.endp) { | ||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT)); | ||||
| @@ -402,14 +413,14 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) | ||||
| 				     rq.name, pdata.epname); | ||||
| 				return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans); | ||||
| 			} | ||||
| 		} else { | ||||
| 		} else if (!rq.null_endp) { | ||||
| 			/* If the endpoint name suggests that the request refers to a specific endpoint, then the | ||||
| 			 * request cannot be handled and we must stop early. */ | ||||
| 			LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 			     "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name, | ||||
| 			     pdata.epname, -rq.mgcp_cause); | ||||
| 			return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans); | ||||
| 		} | ||||
| 		} /* else: Handle special "null" endpoint below (with rq.endp=NULL, rq.trunk=NULL) */ | ||||
| 	} else { | ||||
| 		osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name)); | ||||
| 		rq.trunk = rq.endp->trunk; | ||||
| @@ -455,6 +466,11 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) | ||||
| static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq) | ||||
| { | ||||
| 	LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n"); | ||||
|  | ||||
| 	/* Auditing "null" endpoint is allowed for keepalive purposes. There's no rq->endp nor rq->trunk in this case. */ | ||||
| 	if (rq->null_endp) | ||||
| 		return create_ok_response(rq->pdata->cfg, NULL, 200, "AUEP", rq->pdata->trans); | ||||
|  | ||||
| 	if (!rq->endp || !mgcp_endp_avail(rq->endp)) { | ||||
| 		LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR, "AUEP: selected endpoint not available!\n"); | ||||
| 		return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdata->trans); | ||||
| @@ -656,8 +672,8 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco, | ||||
| 		case 'a': | ||||
| 			/* FIXME: LCO also supports the negotiation of more than one codec. | ||||
| 			 * (e.g. a:PCMU;G726-32) But this implementation only supports a single | ||||
| 			 * codec only. */ | ||||
| 			if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) { | ||||
| 			 * codec only. Ignoring all but the first codec. */ | ||||
| 			if (sscanf(lco_id + 1, ":%16[^,;]", codec) == 1) { | ||||
| 				talloc_free(lco->codec); | ||||
| 				/* MGCP header is case insensive, and we'll need | ||||
| 				   codec in uppercase when using it later: */ | ||||
| @@ -709,7 +725,7 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, | ||||
|  | ||||
| 	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, | ||||
| 		 "Configuring RTP endpoint: local port %d%s%s\n", | ||||
| 		 ntohs(rtp->rtp_port), | ||||
| 		 osmo_sockaddr_port(&rtp->addr.u.sa), | ||||
| 		 rtp->force_aligned_timing ? ", force constant timing" : "", | ||||
| 		 rtp->force_constant_ssrc ? ", force constant ssrc" : ""); | ||||
| } | ||||
| @@ -740,12 +756,12 @@ uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp, | ||||
|  */ | ||||
| static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line) | ||||
| { | ||||
| 	if (!endp->trunk->cfg->osmux_init) { | ||||
| 		if (osmux_init(OSMUX_ROLE_BSC, endp->trunk->cfg) < 0) { | ||||
| 			LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n"); | ||||
| 	if (!endp->trunk->cfg->osmux.initialized) { | ||||
| 		if (osmux_init(endp->trunk) < 0) { | ||||
| 			LOGPENDP(endp, DOSMUX, LOGL_ERROR, "Cannot init OSMUX\n"); | ||||
| 			return -3; | ||||
| 		} | ||||
| 		LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n"); | ||||
| 		LOGPENDP(endp, DOSMUX, LOGL_NOTICE, "OSMUX socket has been set up\n"); | ||||
| 	} | ||||
|  | ||||
| 	return mgcp_parse_osmux_cid(line); | ||||
| @@ -756,6 +772,9 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn, | ||||
| 			     struct mgcp_request_data *rq, int have_sdp, bool crcx) | ||||
| { | ||||
| 	struct mgcp_endpoint *endp = rq->endp; | ||||
| 	struct mgcp_conn *conn_dst; | ||||
| 	struct mgcp_conn_rtp *conn_dst_rtp; | ||||
|  | ||||
| 	int rc; | ||||
| 	char *cmd; | ||||
|  | ||||
| @@ -797,8 +816,15 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn, | ||||
| 			goto error; | ||||
| 	} | ||||
|  | ||||
| 	/* Try to find an destination RTP connection that we can include in the codec decision. */ | ||||
| 	conn_dst = mgcp_find_dst_conn(conn->conn); | ||||
| 	if (conn_dst && conn_dst->type == MGCP_CONN_TYPE_RTP) | ||||
| 		conn_dst_rtp = &conn_dst->u.rtp; | ||||
| 	else | ||||
| 		conn_dst_rtp = NULL; | ||||
|  | ||||
| 	/* Make codec decision */ | ||||
| 	if (mgcp_codec_decide(conn) != 0) | ||||
| 	if (mgcp_codec_decide(conn, conn_dst_rtp) != 0) | ||||
| 		goto error; | ||||
|  | ||||
| 	return 0; | ||||
| @@ -840,13 +866,13 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq) | ||||
| 	struct mgcp_parse_data *pdata = rq->pdata; | ||||
| 	struct mgcp_trunk *trunk = rq->trunk; | ||||
| 	struct mgcp_endpoint *endp = rq->endp; | ||||
| 	struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group; | ||||
| 	struct rate_ctr_group *rate_ctrs; | ||||
| 	int error_code = 400; | ||||
| 	const char *local_options = NULL; | ||||
| 	const char *callid = NULL; | ||||
| 	const char *mode = NULL; | ||||
| 	char *line; | ||||
| 	int have_sdp = 0, osmux_cid = -2; | ||||
| 	int have_sdp = 0, remote_osmux_cid = -2; | ||||
| 	struct mgcp_conn_rtp *conn = NULL; | ||||
| 	struct mgcp_conn *_conn = NULL; | ||||
| 	char conn_name[512]; | ||||
| @@ -854,6 +880,14 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq) | ||||
|  | ||||
| 	LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n"); | ||||
|  | ||||
| 	if (rq->null_endp) { | ||||
| 		/* trunk not available so rate_ctr aren't available either. */ | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "CRCX: Not allowed in 'null' endpoint!\n"); | ||||
| 		return create_err_response(pdata->cfg, NULL, 502, "CRCX", pdata->trans); | ||||
| 	} | ||||
|  | ||||
| 	rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group; | ||||
|  | ||||
| 	/* we must have a free ep */ | ||||
| 	if (!endp) { | ||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL)); | ||||
| @@ -893,9 +927,9 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq) | ||||
| 		case 'X': | ||||
| 			if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) { | ||||
| 				/* If osmux is disabled, just skip setting it up */ | ||||
| 				if (!rq->endp->trunk->cfg->osmux) | ||||
| 				if (rq->endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF) | ||||
| 					break; | ||||
| 				osmux_cid = mgcp_osmux_setup(endp, line); | ||||
| 				remote_osmux_cid = mgcp_osmux_setup(endp, line); | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| @@ -933,7 +967,7 @@ mgcp_header_done: | ||||
| 	} | ||||
|  | ||||
| 	/* Check if we are able to accept the creation of another connection */ | ||||
| 	if (llist_count(&endp->conns) >= endp->type->max_conns) { | ||||
| 	if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns) { | ||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||
| 			"CRCX: endpoint full, max. %i connections allowed!\n", | ||||
| 			endp->type->max_conns); | ||||
| @@ -998,16 +1032,17 @@ mgcp_header_done: | ||||
| 		goto error2; | ||||
| 	} | ||||
|  | ||||
| 	/* Annotate Osmux circuit ID and set it to negotiating state until this | ||||
| 	 * is fully set up from the dummy load. */ | ||||
| 	conn->osmux.state = OSMUX_STATE_DISABLED; | ||||
| 	if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */ | ||||
| 		conn->osmux.state = OSMUX_STATE_ACTIVATING; | ||||
| 		if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) { | ||||
| 	/* If X-Osmux (remote CID) was received (-1 is wilcard), alloc next avail CID as local CID */ | ||||
| 	if (remote_osmux_cid >= -1) { | ||||
| 		if (osmux_init_conn(conn) < 0) { | ||||
| 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX)); | ||||
| 			goto error2; | ||||
| 		} | ||||
| 	} else if (endp->trunk->cfg->osmux == OSMUX_USAGE_ONLY) { | ||||
| 		if (remote_osmux_cid >= 0) { | ||||
| 			conn->osmux.remote_cid_present = true; | ||||
| 			conn->osmux.remote_cid = remote_osmux_cid; | ||||
| 		} | ||||
| 	} else if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_ONLY) { | ||||
| 		LOGPCONN(_conn, DLMGCP, LOGL_ERROR, | ||||
| 			 "CRCX: osmux only and no osmux offered\n"); | ||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX)); | ||||
| @@ -1020,7 +1055,7 @@ mgcp_header_done: | ||||
| 					  &endp->local_options, local_options); | ||||
| 		if (rc != 0) { | ||||
| 			LOGPCONN(_conn, DLMGCP, LOGL_ERROR, | ||||
| 				 "CRCX: inavlid local connection options!\n"); | ||||
| 				 "CRCX: invalid local connection options!\n"); | ||||
| 			error_code = rc; | ||||
| 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS)); | ||||
| 			goto error2; | ||||
| @@ -1054,7 +1089,7 @@ mgcp_header_done: | ||||
| 	/* check connection mode setting */ | ||||
| 	if (conn->conn->mode != MGCP_CONN_LOOPBACK | ||||
| 	    && conn->conn->mode != MGCP_CONN_RECV_ONLY | ||||
| 	    && conn->end.rtp_port == 0) { | ||||
| 	    && osmo_sockaddr_port(&conn->end.addr.u.sa) == 0) { | ||||
| 		LOGPCONN(_conn, DLMGCP, LOGL_ERROR, | ||||
| 			 "CRCX: selected connection mode type requires an opposite end!\n"); | ||||
| 		error_code = 527; | ||||
| @@ -1064,7 +1099,10 @@ mgcp_header_done: | ||||
|  | ||||
| 	/* Find a local address for conn based on policy and initial SDP remote | ||||
| 	   information, then find a free port for it */ | ||||
| 	mgcp_get_local_addr(conn->end.local_addr, conn); | ||||
| 	if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) { | ||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT)); | ||||
| 		goto error2; | ||||
| 	} | ||||
| 	if (allocate_port(endp, conn) != 0) { | ||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT)); | ||||
| 		goto error2; | ||||
| @@ -1077,13 +1115,20 @@ mgcp_header_done: | ||||
| 		goto error2; | ||||
| 	} | ||||
|  | ||||
| 	/* Notify Osmux conn that CRCX was received */ | ||||
| 	if (mgcp_conn_rtp_is_osmux(conn)) { | ||||
| 		if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) { | ||||
| 			LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n"); | ||||
| 			goto error2; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, | ||||
| 		 "CRCX: Creating connection: port: %u\n", conn->end.local_port); | ||||
|  | ||||
| 	/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */ | ||||
| 	OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE); | ||||
| 	if (conn->conn->mode & MGCP_CONN_RECV_ONLY && | ||||
| 	    mgcp_rtp_end_remote_addr_available(&conn->end) && | ||||
| 	    trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER) | ||||
| 		send_dummy(endp, conn); | ||||
|  | ||||
| @@ -1108,7 +1153,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq) | ||||
| 	struct mgcp_parse_data *pdata = rq->pdata; | ||||
| 	struct mgcp_trunk *trunk = rq->trunk; | ||||
| 	struct mgcp_endpoint *endp = rq->endp; | ||||
| 	struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group; | ||||
| 	struct rate_ctr_group *rate_ctrs; | ||||
| 	char new_local_addr[INET6_ADDRSTRLEN]; | ||||
| 	int error_code = 500; | ||||
| 	int silent = 0; | ||||
| @@ -1118,11 +1163,19 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq) | ||||
| 	const char *mode = NULL; | ||||
| 	struct mgcp_conn_rtp *conn = NULL; | ||||
|         const char *conn_id = NULL; | ||||
| 	int osmux_cid = -2; | ||||
| 	int remote_osmux_cid = -2; | ||||
| 	int rc; | ||||
|  | ||||
| 	LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n"); | ||||
|  | ||||
| 	if (rq->null_endp) { | ||||
| 		/* trunk not available so rate_ctr aren't available either. */ | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "MDCX: Not allowed in 'null' endpoint!\n"); | ||||
| 		return create_err_response(pdata->cfg, NULL, 502, "MDCX", pdata->trans); | ||||
| 	} | ||||
|  | ||||
| 	rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group; | ||||
|  | ||||
| 	/* Prohibit wildcarded requests */ | ||||
| 	if (rq->wildcarded) { | ||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||
| @@ -1174,9 +1227,9 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq) | ||||
| 		case 'X': | ||||
| 			if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) { | ||||
| 				/* If osmux is disabled, just skip setting it up */ | ||||
| 				if (!endp->trunk->cfg->osmux) | ||||
| 				if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF) | ||||
| 					break; | ||||
| 				osmux_cid = mgcp_osmux_setup(endp, line); | ||||
| 				remote_osmux_cid = mgcp_osmux_setup(endp, line); | ||||
| 				break; | ||||
| 			} | ||||
| 			/* Ignore unknown X-headers */ | ||||
| @@ -1240,6 +1293,10 @@ mgcp_header_done: | ||||
| 		error_code = rc; | ||||
| 		goto error3; | ||||
| 	} | ||||
| 	/* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */ | ||||
| 	/* TODO: "codec" probably needs to be moved from endp to conn */ | ||||
| 	if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) | ||||
| 		rc = mgcp_conn_iuup_init(conn); | ||||
|  | ||||
| 	/* check connection mode setting */ | ||||
| 	if (conn->conn->mode != MGCP_CONN_LOOPBACK | ||||
| @@ -1253,30 +1310,39 @@ mgcp_header_done: | ||||
| 	} | ||||
|  | ||||
| 	if (mgcp_conn_rtp_is_osmux(conn)) { | ||||
| 		OSMO_ASSERT(conn->osmux.cid_allocated); | ||||
| 		if (osmux_cid < -1) { | ||||
| 		OSMO_ASSERT(conn->osmux.local_cid_allocated); | ||||
| 		if (remote_osmux_cid < -1) { | ||||
| 			LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, | ||||
| 				 "MDCX: Failed to parse Osmux CID!\n"); | ||||
| 			goto error3; | ||||
| 		} else if (osmux_cid == -1) { | ||||
| 		} else if (remote_osmux_cid == -1) { | ||||
| 			LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, | ||||
| 				 "MDCX: wilcard in MDCX is not supported!\n"); | ||||
| 			goto error3; | ||||
| 		} else if (osmux_cid != (int) conn->osmux.cid) { | ||||
| 			LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, | ||||
| 				 "MDCX: changing already allocated CID is not supported!\n"); | ||||
| 			goto error3; | ||||
| 		} else if (conn->osmux.remote_cid_present && | ||||
| 				remote_osmux_cid != conn->osmux.remote_cid) { | ||||
| 				LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, | ||||
| 					"MDCX: changing already allocated CID is not supported!\n"); | ||||
| 				goto error3; | ||||
| 		} else { | ||||
| 			conn->osmux.remote_cid_present = true; | ||||
| 			conn->osmux.remote_cid = remote_osmux_cid; | ||||
| 			if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) { | ||||
| 				LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, | ||||
| 					"MDCX: Osmux handling failed!\n"); | ||||
| 				goto error3; | ||||
| 			} | ||||
| 		} | ||||
| 		/* TODO: In the future (when we have recvCID!=sendCID), we need to | ||||
| 		   tell Osmux code that osmux_cid is to be used as sendCID for | ||||
| 		   that conn. */ | ||||
| 	} | ||||
|  | ||||
| 	/* MDCX may have provided a new remote address, which means we may need | ||||
| 	   to update our announced IP addr and re-bind our local end. This can | ||||
| 	   happen for instance if MGW initially provided an IPv4 during CRCX | ||||
| 	   ACK, and now MDCX tells us the remote has an IPv6 address. */ | ||||
| 	mgcp_get_local_addr(new_local_addr, conn); | ||||
| 	if (mgcp_get_local_addr(new_local_addr, conn) < 0) { | ||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT)); | ||||
| 		goto error3; | ||||
| 	} | ||||
| 	if (strcmp(new_local_addr, conn->end.local_addr)) { | ||||
| 		osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr)); | ||||
| 		mgcp_free_rtp_port(&conn->end); | ||||
| @@ -1300,7 +1366,6 @@ mgcp_header_done: | ||||
| 	/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */ | ||||
| 	OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE); | ||||
| 	if (conn->conn->mode & MGCP_CONN_RECV_ONLY && | ||||
| 	    mgcp_rtp_end_remote_addr_available(&conn->end) && | ||||
| 	    trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER) | ||||
| 		send_dummy(endp, conn); | ||||
|  | ||||
| @@ -1326,7 +1391,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq) | ||||
| 	struct mgcp_parse_data *pdata = rq->pdata; | ||||
| 	struct mgcp_trunk *trunk = rq->trunk; | ||||
| 	struct mgcp_endpoint *endp = rq->endp; | ||||
| 	struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group; | ||||
| 	struct rate_ctr_group *rate_ctrs; | ||||
| 	int error_code = 400; | ||||
| 	int silent = 0; | ||||
| 	char *line; | ||||
| @@ -1336,10 +1401,18 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq) | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	/* NOTE: In this handler we can not take it for granted that the endp | ||||
| 	 * pointer will be populated, however a trunk is always guaranteed. */ | ||||
| 	 * pointer will be populated, however a trunk is always guaranteed (except for 'null' endp). | ||||
| 	 */ | ||||
|  | ||||
| 	LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n"); | ||||
|  | ||||
| 	if (rq->null_endp) { | ||||
| 		/* trunk not available so rate_ctr aren't available either. */ | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "DLCX: Not allowed in 'null' endpoint!\n"); | ||||
| 		return create_err_response(pdata->cfg, NULL, 502, "DLCX", pdata->trans); | ||||
| 	} | ||||
|  | ||||
| 	rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group; | ||||
| 	if (endp && !mgcp_endp_avail(endp)) { | ||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL)); | ||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||
| @@ -1490,6 +1563,12 @@ static struct msgb *handle_rsip(struct mgcp_request_data *rq) | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n"); | ||||
|  | ||||
| 	if (rq->null_endp) { | ||||
| 		/* trunk not available so rate_ctr aren't available either. */ | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "RSIP: Not allowed in 'null' endpoint!\n"); | ||||
| 		return create_err_response(rq->pdata->cfg, NULL, 502, "RSIP", rq->pdata->trans); | ||||
| 	} | ||||
|  | ||||
| 	if (rq->pdata->cfg->reset_cb) | ||||
| 		rq->pdata->cfg->reset_cb(rq->endp->trunk); | ||||
| 	return NULL; | ||||
| @@ -1515,6 +1594,12 @@ static struct msgb *handle_noti_req(struct mgcp_request_data *rq) | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n"); | ||||
|  | ||||
| 	if (rq->null_endp) { | ||||
| 		/* trunk not available so rate_ctr aren't available either. */ | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "RQNT: Not allowed in 'null' endpoint!\n"); | ||||
| 		return create_err_response(rq->pdata->cfg, NULL, 502, "RQNT", rq->pdata->trans); | ||||
| 	} | ||||
|  | ||||
| 	for_each_line(line, rq->pdata->save) { | ||||
| 		switch (toupper(line[0])) { | ||||
| 		case 'S': | ||||
| @@ -1569,8 +1654,7 @@ static void mgcp_keepalive_timer_cb(void *_trunk) | ||||
| 		struct mgcp_endpoint *endp = trunk->endpoints[i]; | ||||
| 		llist_for_each_entry(conn, &endp->conns, entry) { | ||||
| 			if (conn->type == MGCP_CONN_TYPE_RTP && | ||||
| 			    conn->mode == MGCP_CONN_RECV_ONLY && | ||||
| 			     mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end)) | ||||
| 			    conn->mode == MGCP_CONN_RECV_ONLY) | ||||
| 				send_dummy(endp, &conn->u.rtp); | ||||
| 		} | ||||
| 	} | ||||
| @@ -1624,7 +1708,6 @@ struct mgcp_config *mgcp_config_alloc(void) | ||||
|  | ||||
| 	cfg->source_port = 2427; | ||||
| 	osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr)); | ||||
| 	osmo_strlcpy(cfg->osmux_addr, "0.0.0.0", sizeof(cfg->osmux_addr)); | ||||
|  | ||||
| 	cfg->rtp_processing_cb = &mgcp_rtp_processing_default; | ||||
| 	cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default; | ||||
|   | ||||
| @@ -43,7 +43,7 @@ static const struct rate_ctr_desc mgcp_general_ctr_desc[] = { | ||||
| 	    { "mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints." }, | ||||
| }; | ||||
|  | ||||
| const static struct rate_ctr_group_desc mgcp_general_ctr_group_desc = { | ||||
| static const struct rate_ctr_group_desc mgcp_general_ctr_group_desc = { | ||||
| 	.group_name_prefix = "mgcp", | ||||
| 	.group_description = "mgcp general statistics", | ||||
| 	.class_id = OSMO_STATS_CLASS_GLOBAL, | ||||
| @@ -71,7 +71,7 @@ static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = { | ||||
| 	[MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." }, | ||||
| }; | ||||
|  | ||||
| const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = { | ||||
| static const struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = { | ||||
| 	.group_name_prefix = "crcx", | ||||
| 	.group_description = "crxc statistics", | ||||
| 	.class_id = OSMO_STATS_CLASS_GLOBAL, | ||||
| @@ -97,7 +97,7 @@ static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = { | ||||
| 	[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." }, | ||||
| }; | ||||
|  | ||||
| const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = { | ||||
| static const struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = { | ||||
| 	.group_name_prefix = "mdcx", | ||||
| 	.group_description = "mdcx statistics", | ||||
| 	.class_id = OSMO_STATS_CLASS_GLOBAL, | ||||
| @@ -116,7 +116,7 @@ static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = { | ||||
| 	[MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." }, | ||||
| }; | ||||
|  | ||||
| const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = { | ||||
| static const struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = { | ||||
| 	.group_name_prefix = "dlcx", | ||||
| 	.group_description = "dlcx statistics", | ||||
| 	.class_id = OSMO_STATS_CLASS_GLOBAL, | ||||
| @@ -130,7 +130,7 @@ static const struct rate_ctr_desc e1_rate_ctr_desc[] = { | ||||
| 	[E1_I460_TRAU_MUX_EMPTY_CTR] = { "e1:i460", "Outbound I.460 MUX queue empty." } | ||||
| }; | ||||
|  | ||||
| const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = { | ||||
| static const struct rate_ctr_group_desc e1_rate_ctr_group_desc = { | ||||
| 	.group_name_prefix = "e1", | ||||
| 	.group_description = "e1 statistics", | ||||
| 	.class_id = OSMO_STATS_CLASS_GLOBAL, | ||||
| @@ -138,7 +138,7 @@ const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = { | ||||
| 	.ctr_desc = e1_rate_ctr_desc | ||||
| }; | ||||
|  | ||||
| const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = { | ||||
| static const struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = { | ||||
| 	.group_name_prefix = "all_rtp_conn", | ||||
| 	.group_description = "aggregated statistics for all rtp connections", | ||||
| 	.class_id = 1, | ||||
| @@ -146,6 +146,14 @@ const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = { | ||||
| 	.ctr_desc = all_rtp_conn_rate_ctr_desc | ||||
| }; | ||||
|  | ||||
| static const struct rate_ctr_group_desc all_osmux_conn_rate_ctr_group_desc = { | ||||
| 	.group_name_prefix = "all_osmux_conn", | ||||
| 	.group_description = "aggregated statistics for all osmux connections", | ||||
| 	.class_id = 1, | ||||
| 	.num_ctr = ARRAY_SIZE(all_osmux_conn_rate_ctr_desc), | ||||
| 	.ctr_desc = all_osmux_conn_rate_ctr_desc | ||||
| }; | ||||
|  | ||||
| /*! allocate global rate counters | ||||
|  *  (called once at startup). | ||||
|  *  \param[in] cfg mgw configuration for which the rate counters are allocated. | ||||
| @@ -191,6 +199,7 @@ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk) | ||||
| 	static atomic_uint mdcx_rate_ctr_index = 0; | ||||
| 	static atomic_uint dlcx_rate_ctr_index = 0; | ||||
| 	static atomic_uint all_rtp_conn_rate_ctr_index = 0; | ||||
| 	static atomic_uint all_osmux_conn_rate_ctr_index = 0; | ||||
| 	char ctr_name[256]; | ||||
|  | ||||
| 	if (ratectr->mgcp_crcx_ctr_group == NULL) { | ||||
| @@ -229,6 +238,15 @@ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk) | ||||
| 			 trunk->trunk_nr); | ||||
| 		rate_ctr_group_set_name(ratectr->all_rtp_conn_stats, ctr_name); | ||||
| 	} | ||||
| 	if (ratectr->all_osmux_conn_stats == NULL) { | ||||
| 		ratectr->all_osmux_conn_stats = rate_ctr_group_alloc(trunk, &all_osmux_conn_rate_ctr_group_desc, | ||||
| 								   all_osmux_conn_rate_ctr_index++); | ||||
| 		if (!ratectr->all_osmux_conn_stats) | ||||
| 			return -EINVAL; | ||||
| 		snprintf(ctr_name, sizeof(ctr_name), "%s-%u:osmux_conn", mgcp_trunk_type_strs_str(trunk->trunk_type), | ||||
| 			 trunk->trunk_nr); | ||||
| 		rate_ctr_group_set_name(ratectr->all_osmux_conn_stats, ctr_name); | ||||
| 	} | ||||
|  | ||||
| 	/* E1 specific */ | ||||
| 	if (trunk->trunk_type == MGCP_TRUNK_E1 && ratectr->e1_stats == NULL) { | ||||
| @@ -265,6 +283,10 @@ void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk) | ||||
| 		rate_ctr_group_free(ratectr->all_rtp_conn_stats); | ||||
| 		ratectr->all_rtp_conn_stats = NULL; | ||||
| 	} | ||||
| 	if (ratectr->all_osmux_conn_stats) { | ||||
| 		rate_ctr_group_free(ratectr->all_osmux_conn_stats); | ||||
| 		ratectr->all_osmux_conn_stats = NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* E1 specific */ | ||||
| 	if (ratectr->e1_stats) { | ||||
|   | ||||
| @@ -239,13 +239,12 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp | ||||
| 		if (delimiter == ';' || delimiter == ',') | ||||
| 			str_ptr[strlen(str_ptr) - 1] = '\0'; | ||||
|  | ||||
| 		/* AMR octet aligned parameter */ | ||||
| 		/* AMR octet aligned parameter (see also RFC 3267, section 8.3) */ | ||||
| 		if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) { | ||||
| 			fmtp_param->param.amr_octet_aligned_present = true; | ||||
| 			fmtp_param->param.amr_octet_aligned = false; | ||||
| 			if (amr_octet_aligned == 1) | ||||
| 				fmtp_param->param.amr_octet_aligned = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		param_str = strtok(NULL, " "); | ||||
| @@ -384,7 +383,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp, | ||||
| 		case 'm': | ||||
| 			rc = sscanf(line, "m=audio %d RTP/AVP", &port); | ||||
| 			if (rc == 1) { | ||||
| 				rtp->rtp_port = htons(port); | ||||
| 				osmo_sockaddr_set_port(&rtp->addr.u.sa, port); | ||||
| 				rtp->rtcp_port = htons(port + 1); | ||||
| 			} | ||||
|  | ||||
| @@ -432,7 +431,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp, | ||||
|  | ||||
| 	LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE, | ||||
| 	     "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:", | ||||
| 	     ntohs(rtp->rtp_port), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf), | ||||
| 	     osmo_sockaddr_port(&rtp->addr.u.sa), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf), | ||||
| 	     rtp->packet_duration_ms); | ||||
| 	if (codecs_used == 0) | ||||
| 		LOGPC(DLMGCP, LOGL_NOTICE, "none"); | ||||
| @@ -601,7 +600,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp, | ||||
|  | ||||
| 		payload_types[0] = payload_type; | ||||
| 		if (mgcp_conn_rtp_is_osmux(conn)) | ||||
| 			local_port = endp->trunk->cfg->osmux_port; | ||||
| 			local_port = endp->trunk->cfg->osmux.local_port; | ||||
| 		else | ||||
| 			local_port = conn->end.local_port; | ||||
| 		rc = add_audio(sdp, payload_types, 1, local_port); | ||||
|   | ||||
| @@ -39,7 +39,7 @@ __attribute__((no_sanitize("undefined"))) | ||||
| void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss) | ||||
| { | ||||
| 	struct mgcp_rtp_state *state = &conn->state; | ||||
| 	struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR); | ||||
| 	struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR); | ||||
|  | ||||
| 	*expected = state->stats.cycles + state->stats.max_seq; | ||||
| 	*expected = *expected - state->stats.base_seq + 1; | ||||
| @@ -80,10 +80,10 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len, | ||||
| 	int ploss; | ||||
| 	int nchars; | ||||
|  | ||||
| 	struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR); | ||||
| 	struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR); | ||||
| 	struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR); | ||||
| 	struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR); | ||||
| 	struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR); | ||||
| 	struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR); | ||||
| 	struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR); | ||||
| 	struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR); | ||||
|  | ||||
| 	calc_loss(conn, &expected, &ploss); | ||||
| 	jitter = calc_jitter(&conn->state); | ||||
| @@ -99,7 +99,7 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len, | ||||
| 	str += nchars; | ||||
| 	str_len -= nchars; | ||||
|  | ||||
| 	if (conn->conn->endp->trunk->cfg->osmux != OSMUX_USAGE_OFF) { | ||||
| 	if (conn->conn->endp->trunk->cfg->osmux.usage != OSMUX_USAGE_OFF) { | ||||
| 		/* Error Counter */ | ||||
| 		nchars = snprintf(str, str_len, | ||||
| 				  "\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64, | ||||
| @@ -112,9 +112,12 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len, | ||||
| 		str_len -= nchars; | ||||
|  | ||||
| 		if (conn->osmux.state == OSMUX_STATE_ENABLED) { | ||||
| 			struct rate_ctr *osmux_chunks_rx, *osmux_octets_rx; | ||||
| 			osmux_chunks_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_CHUNKS_RX_CTR); | ||||
| 			osmux_octets_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_OCTETS_RX_CTR); | ||||
| 			snprintf(str, str_len, | ||||
| 				 "\r\nX-Osmux-ST: CR=%u, BR=%u", | ||||
| 				 conn->osmux.stats.chunks, conn->osmux.stats.octets); | ||||
| 				 "\r\nX-Osmux-ST: CR=%" PRIu64 ", BR=%" PRIu64, | ||||
| 				 osmux_chunks_rx->current, osmux_octets_rx->current); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -120,8 +120,6 @@ static int config_write_mgcp(struct vty *vty) | ||||
| 		trunk->audio_send_name ? "" : "no ", VTY_NEWLINE); | ||||
| 	vty_out(vty, " number endpoints %u%s", | ||||
| 		trunk->v.vty_number_endpoints, VTY_NEWLINE); | ||||
| 	vty_out(vty, " %sallow-transcoding%s", | ||||
| 		trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE); | ||||
| 	if (strlen(g_cfg->call_agent_addr)) | ||||
| 		vty_out(vty, " call-agent ip %s%s", g_cfg->call_agent_addr, | ||||
| 			VTY_NEWLINE); | ||||
| @@ -129,7 +127,7 @@ static int config_write_mgcp(struct vty *vty) | ||||
| 		vty_out(vty, " rtp force-ptime %d%s", g_cfg->force_ptime, | ||||
| 			VTY_NEWLINE); | ||||
|  | ||||
| 	switch (g_cfg->osmux) { | ||||
| 	switch (g_cfg->osmux.usage) { | ||||
| 	case OSMUX_USAGE_ON: | ||||
| 		vty_out(vty, " osmux on%s", VTY_NEWLINE); | ||||
| 		break; | ||||
| @@ -141,17 +139,23 @@ static int config_write_mgcp(struct vty *vty) | ||||
| 		vty_out(vty, " osmux off%s", VTY_NEWLINE); | ||||
| 		break; | ||||
| 	} | ||||
| 	if (g_cfg->osmux) { | ||||
| 		vty_out(vty, " osmux bind-ip %s%s", | ||||
| 			g_cfg->osmux_addr, VTY_NEWLINE); | ||||
| 	if (g_cfg->osmux.usage != OSMUX_USAGE_OFF) { | ||||
| 		if (g_cfg->osmux.local_addr_v4) | ||||
| 			vty_out(vty, " osmux bind-ip %s%s", | ||||
| 				g_cfg->osmux.local_addr_v4, VTY_NEWLINE); | ||||
| 		if (g_cfg->osmux.local_addr_v6) | ||||
| 			vty_out(vty, " osmux bind-ip-v6 %s%s", | ||||
| 				g_cfg->osmux.local_addr_v6, VTY_NEWLINE); | ||||
| 		vty_out(vty, " osmux batch-factor %d%s", | ||||
| 			g_cfg->osmux_batch, VTY_NEWLINE); | ||||
| 			g_cfg->osmux.batch_factor, VTY_NEWLINE); | ||||
| 		vty_out(vty, " osmux batch-size %u%s", | ||||
| 			g_cfg->osmux_batch_size, VTY_NEWLINE); | ||||
| 			g_cfg->osmux.batch_size, VTY_NEWLINE); | ||||
| 		vty_out(vty, " osmux port %u%s", | ||||
| 			g_cfg->osmux_port, VTY_NEWLINE); | ||||
| 			g_cfg->osmux.local_port, VTY_NEWLINE); | ||||
| 		vty_out(vty, " osmux dummy %s%s", | ||||
| 			g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE); | ||||
| 			g_cfg->osmux.dummy_padding ? "on" : "off", VTY_NEWLINE); | ||||
| 		vty_out(vty, " osmux peer-behind-nat %s%s", | ||||
| 			g_cfg->osmux.peer_behind_nat ? "on" : "off", VTY_NEWLINE); | ||||
| 	} | ||||
|  | ||||
| 	if (g_cfg->conn_timeout) | ||||
| @@ -169,11 +173,11 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn) | ||||
| 	struct rate_ctr *rx_packets, *rx_bytes; | ||||
| 	struct rate_ctr *dropped_packets; | ||||
|  | ||||
| 	tx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR); | ||||
| 	tx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR); | ||||
| 	rx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR); | ||||
| 	rx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR); | ||||
| 	dropped_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_DROPPED_PACKETS_CTR); | ||||
| 	tx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR); | ||||
| 	tx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR); | ||||
| 	rx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR); | ||||
| 	rx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR); | ||||
| 	dropped_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_DROPPED_PACKETS_CTR); | ||||
|  | ||||
| 	vty_out(vty, | ||||
| 		"   Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s" | ||||
| @@ -189,7 +193,7 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn) | ||||
| 		rx_packets->current, rx_bytes->current, VTY_NEWLINE, | ||||
| 		state->in_stream.err_ts_ctr->current, | ||||
| 		state->out_stream.err_ts_ctr->current, | ||||
| 	        VTY_NEWLINE, | ||||
| 		VTY_NEWLINE, | ||||
| 		dropped_packets->current, VTY_NEWLINE, | ||||
| 		codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE, | ||||
| 		codec->frame_duration_num, codec->frame_duration_den, | ||||
| @@ -197,6 +201,30 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn) | ||||
| 		VTY_NEWLINE, end->fmtp_extra, codec->audio_name, | ||||
| 		codec->subtype_name, VTY_NEWLINE, end->output_enabled, | ||||
| 		end->force_output_ptime, VTY_NEWLINE); | ||||
| 	if (mgcp_conn_rtp_is_osmux(conn)) { | ||||
| 		struct rate_ctr *rx_chunks, *rx_octets, *rtp_tx, *rtp_tx_dropped, *octets_tx; | ||||
| 		rx_chunks = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_CHUNKS_RX_CTR); | ||||
| 		rx_octets = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_OCTETS_RX_CTR); | ||||
| 		rtp_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_CTR); | ||||
| 		rtp_tx_dropped =  rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_DROPPED_CTR); | ||||
| 		octets_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_AMR_OCTETS_TX_CTR); | ||||
| 		vty_out(vty, | ||||
| 		"   Osmux:%s" | ||||
| 		"    State: %s%s" | ||||
| 		"    Local CID: %d%s" | ||||
| 		"    Remote CID: %d%s" | ||||
| 		"    Chunks received: %" PRIu64 " (%" PRIu64 " bytes total)%s" | ||||
| 		"    RTP Packets encoded (Tx): %" PRIu64 " (%" PRIu64 " AMR octets total)%s" | ||||
| 		"    AMR payloads Dropped (Tx): %" PRIu64 "%s", | ||||
| 		VTY_NEWLINE, osmux_state_str(conn->osmux.state), VTY_NEWLINE, | ||||
| 		conn->osmux.local_cid_allocated ? conn->osmux.local_cid : -1, VTY_NEWLINE, | ||||
| 		conn->osmux.remote_cid_present ? conn->osmux.remote_cid : -1, VTY_NEWLINE, | ||||
| 		rx_chunks->current, rx_octets->current, VTY_NEWLINE, | ||||
| 		rtp_tx->current, octets_tx->current, VTY_NEWLINE, | ||||
| 		rtp_tx_dropped->current, VTY_NEWLINE | ||||
| 		); | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, | ||||
| @@ -289,6 +317,14 @@ static void dump_ratectr_trunk(struct vty *vty, struct mgcp_trunk *trunk) | ||||
| 					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d", | ||||
| 					   ratectr->all_rtp_conn_stats); | ||||
| 	} | ||||
| 	if (ratectr->all_osmux_conn_stats) { | ||||
| 		vty_out(vty, "   %s:%s", | ||||
| 			ratectr->all_osmux_conn_stats->desc->group_description, | ||||
| 			VTY_NEWLINE); | ||||
| 		vty_out_rate_ctr_group_fmt(vty, | ||||
| 					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d", | ||||
| 					   ratectr->all_osmux_conn_stats); | ||||
| 	} | ||||
|  | ||||
| 	if (ratectr->e1_stats && trunk->trunk_type == MGCP_TRUNK_E1) { | ||||
| 		vty_out(vty, "   %s:%s", | ||||
| @@ -342,7 +378,7 @@ static int mgcp_show(struct vty *vty, int argc, const char **argv, | ||||
| 	llist_for_each_entry(trunk, &g_cfg->trunks, entry) | ||||
| 		dump_trunk(vty, trunk, show_stats, active_only); | ||||
|  | ||||
| 	if (g_cfg->osmux) | ||||
| 	if (g_cfg->osmux.usage != OSMUX_USAGE_OFF) | ||||
| 		vty_out(vty, "Osmux used CID: %d%s", osmux_cid_pool_count_used(), | ||||
| 			VTY_NEWLINE); | ||||
|  | ||||
| @@ -475,6 +511,7 @@ DEFUN_DEPRECATED(cfg_mgcp_bind_early, | ||||
| 		 BIND_STR | ||||
| 		 "Bind local ports on start up\n" "Bind on demand\n" "Bind on startup\n") | ||||
| { | ||||
| 	vty_out(vty, "%% Deprecated 'bind early (0|1)' config no longer has any effect%s", VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -662,25 +699,19 @@ DEFUN_USRATTR(cfg_mgcp_sdp_fmtp_extra, | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN_USRATTR(cfg_mgcp_allow_transcoding, | ||||
| 	      cfg_mgcp_allow_transcoding_cmd, | ||||
| 	      X(MGW_CMD_ATTR_NEWCONN), | ||||
| 	      "allow-transcoding", "Allow transcoding\n") | ||||
| DEFUN_DEPRECATED(cfg_mgcp_allow_transcoding, | ||||
| 		  cfg_mgcp_allow_transcoding_cmd, | ||||
| 		  "allow-transcoding", "Allow transcoding\n") | ||||
| { | ||||
| 	struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID); | ||||
| 	OSMO_ASSERT(trunk); | ||||
| 	trunk->no_audio_transcoding = 0; | ||||
| 	vty_out(vty, "%% Deprecated 'allow-transcoding' config no longer has any effect%s", VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN_USRATTR(cfg_mgcp_no_allow_transcoding, | ||||
| 	      cfg_mgcp_no_allow_transcoding_cmd, | ||||
| 	      X(MGW_CMD_ATTR_NEWCONN), | ||||
| 	      "no allow-transcoding", NO_STR "Allow transcoding\n") | ||||
| DEFUN_DEPRECATED(cfg_mgcp_no_allow_transcoding, | ||||
| 		 cfg_mgcp_no_allow_transcoding_cmd, | ||||
| 		 "no allow-transcoding", NO_STR "Allow transcoding\n") | ||||
| { | ||||
| 	struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID); | ||||
| 	OSMO_ASSERT(trunk); | ||||
| 	trunk->no_audio_transcoding = 1; | ||||
| 	vty_out(vty, "%% Deprecated 'no allow-transcoding' config no longer has any effect%s", VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -691,6 +722,7 @@ DEFUN_DEPRECATED(cfg_mgcp_sdp_payload_number, | ||||
|       "sdp audio-payload number <0-255>", | ||||
|       SDP_STR AUDIO_STR "Number\n" "Payload number\n") | ||||
| { | ||||
| 	vty_out(vty, "%% Deprecated 'sdp audio-payload number <0-255>' config no longer has any effect%s", VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -704,6 +736,7 @@ DEFUN_DEPRECATED(cfg_mgcp_sdp_payload_name, | ||||
|       "sdp audio-payload name NAME", | ||||
|       SDP_STR AUDIO_STR "Name\n" "Payload name\n") | ||||
| { | ||||
| 	vty_out(vty, "%% Deprecated 'sdp audio-payload name NAME' config no longer has any effect%s", VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -764,6 +797,7 @@ DEFUN_DEPRECATED(cfg_mgcp_loop, | ||||
| 		 "loop (0|1)", | ||||
| 		 "Loop audio for all endpoints on main trunk\n" "Don't Loop\n" "Loop\n") | ||||
| { | ||||
| 	vty_out(vty, "%% Deprecated 'loop (0|1)' config no longer has any effect%s", VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1031,8 +1065,6 @@ static int config_write_trunk(struct vty *vty) | ||||
| 		if (trunk->audio_fmtp_extra) | ||||
| 			vty_out(vty, "   sdp audio fmtp-extra %s%s", | ||||
| 				trunk->audio_fmtp_extra, VTY_NEWLINE); | ||||
| 		vty_out(vty, "  %sallow-transcoding%s", | ||||
| 			trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE); | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| @@ -1060,6 +1092,7 @@ DEFUN_DEPRECATED(cfg_trunk_payload_number, | ||||
|       "sdp audio-payload number <0-255>", | ||||
|       SDP_STR AUDIO_STR "Number\n" "Payload Number\n") | ||||
| { | ||||
| 	vty_out(vty, "%% Deprecated 'sdp audio-payload number <0-255>' config no longer has any effect%s", VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1072,6 +1105,7 @@ DEFUN_DEPRECATED(cfg_trunk_payload_name, | ||||
|       "sdp audio-payload name NAME", | ||||
|       SDP_STR AUDIO_STR "Payload\n" "Payload Name\n") | ||||
| { | ||||
| 	vty_out(vty, "%% Deprecated 'sdp audio-payload name NAME' config no longer has any effect%s", VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1084,6 +1118,7 @@ DEFUN_DEPRECATED(cfg_trunk_loop, | ||||
| 		 "loop (0|1)", | ||||
| 		 "Loop audio for all endpoints on this trunk\n" "Don't Loop\n" "Loop\n") | ||||
| { | ||||
| 	vty_out(vty, "%% Deprecated 'loop (0|1)' config no longer has any effect%s", VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1281,23 +1316,19 @@ DEFUN_ATTR(cfg_trunk_no_rtp_keepalive, | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN_USRATTR(cfg_trunk_allow_transcoding, | ||||
| 	      cfg_trunk_allow_transcoding_cmd, | ||||
| 	      X(MGW_CMD_ATTR_NEWCONN), | ||||
| 	      "allow-transcoding", "Allow transcoding\n") | ||||
| DEFUN_DEPRECATED(cfg_trunk_allow_transcoding, | ||||
| 		 cfg_trunk_allow_transcoding_cmd, | ||||
| 		 "allow-transcoding", "Allow transcoding\n") | ||||
| { | ||||
| 	struct mgcp_trunk *trunk = vty->index; | ||||
| 	trunk->no_audio_transcoding = 0; | ||||
| 	vty_out(vty, "%% Deprecated 'allow-transcoding' config no longer has any effect%s", VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN_USRATTR(cfg_trunk_no_allow_transcoding, | ||||
| 	      cfg_trunk_no_allow_transcoding_cmd, | ||||
| 	      X(MGW_CMD_ATTR_NEWCONN), | ||||
| 	      "no allow-transcoding", NO_STR "Allow transcoding\n") | ||||
| DEFUN_DEPRECATED(cfg_trunk_no_allow_transcoding, | ||||
| 		 cfg_trunk_no_allow_transcoding_cmd, | ||||
| 		 "no allow-transcoding", NO_STR "Allow transcoding\n") | ||||
| { | ||||
| 	struct mgcp_trunk *trunk = vty->index; | ||||
| 	trunk->no_audio_transcoding = 1; | ||||
| 	vty_out(vty, "%% Deprecated 'no allow-transcoding' config no longer has any effect%s", VTY_NEWLINE); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1539,12 +1570,12 @@ DEFUN(cfg_mgcp_osmux, | ||||
| 	OSMO_ASSERT(trunk); | ||||
|  | ||||
| 	if (strcmp(argv[0], "off") == 0) { | ||||
| 		g_cfg->osmux = OSMUX_USAGE_OFF; | ||||
| 		g_cfg->osmux.usage = OSMUX_USAGE_OFF; | ||||
| 		return CMD_SUCCESS; | ||||
| 	} else if (strcmp(argv[0], "on") == 0) | ||||
| 		g_cfg->osmux = OSMUX_USAGE_ON; | ||||
| 		g_cfg->osmux.usage = OSMUX_USAGE_ON; | ||||
| 	else if (strcmp(argv[0], "only") == 0) | ||||
| 		g_cfg->osmux = OSMUX_USAGE_ONLY; | ||||
| 		g_cfg->osmux.usage = OSMUX_USAGE_ONLY; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
|  | ||||
| @@ -1552,12 +1583,29 @@ DEFUN(cfg_mgcp_osmux, | ||||
|  | ||||
| DEFUN(cfg_mgcp_osmux_ip, | ||||
|       cfg_mgcp_osmux_ip_cmd, | ||||
|       "osmux bind-ip " VTY_IPV46_CMD, | ||||
|       "osmux bind-ip " VTY_IPV4_CMD, | ||||
|       OSMUX_STR IP_STR | ||||
|       "IPv4 Address to bind to\n") | ||||
| { | ||||
| 	osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v4, argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgcp_osmux_ip_v6, | ||||
|       cfg_mgcp_osmux_ip_v6_cmd, | ||||
|       "osmux bind-ip-v6 " VTY_IPV6_CMD, | ||||
|       OSMUX_STR IP_STR | ||||
|       "IPv4 Address to bind to\n" | ||||
|       "IPv6 Address to bind to\n") | ||||
| { | ||||
| 	osmo_strlcpy(g_cfg->osmux_addr, argv[0], sizeof(g_cfg->osmux_addr)); | ||||
| 	osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v6, argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgcp_osmux_port, | ||||
|       cfg_mgcp_osmux_port_cmd, | ||||
|       "osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n") | ||||
| { | ||||
| 	g_cfg->osmux.local_port = atoi(argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1566,7 +1614,7 @@ DEFUN(cfg_mgcp_osmux_batch_factor, | ||||
|       "osmux batch-factor <1-8>", | ||||
|       OSMUX_STR "Batching factor\n" "Number of messages in the batch\n") | ||||
| { | ||||
| 	g_cfg->osmux_batch = atoi(argv[0]); | ||||
| 	g_cfg->osmux.batch_factor = atoi(argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1575,15 +1623,7 @@ DEFUN(cfg_mgcp_osmux_batch_size, | ||||
|       "osmux batch-size <1-65535>", | ||||
|       OSMUX_STR "batch size\n" "Batch size in bytes\n") | ||||
| { | ||||
| 	g_cfg->osmux_batch_size = atoi(argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgcp_osmux_port, | ||||
|       cfg_mgcp_osmux_port_cmd, | ||||
|       "osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n") | ||||
| { | ||||
| 	g_cfg->osmux_port = atoi(argv[0]); | ||||
| 	g_cfg->osmux.batch_size = atoi(argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1594,9 +1634,24 @@ DEFUN(cfg_mgcp_osmux_dummy, | ||||
|       "Disable dummy padding\n") | ||||
| { | ||||
| 	if (strcmp(argv[0], "on") == 0) | ||||
| 		g_cfg->osmux_dummy = 1; | ||||
| 		g_cfg->osmux.dummy_padding = true; | ||||
| 	else if (strcmp(argv[0], "off") == 0) | ||||
| 		g_cfg->osmux_dummy = 0; | ||||
| 		g_cfg->osmux.dummy_padding = false; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_mgcp_osmux_peer_behind_nat, | ||||
|       cfg_mgcp_osmux_peer_behind_nat_cmd, | ||||
|       "osmux peer-behind-nat (on|off)", | ||||
|       OSMUX_STR "Define whether peer is behind NAT\n" | ||||
|       "Peer is behind NAT\n" | ||||
|       "Peer is NOT behind NAT\n") | ||||
| { | ||||
| 	if (strcmp(argv[0], "on") == 0) | ||||
| 		g_cfg->osmux.peer_behind_nat = true; | ||||
| 	else if (strcmp(argv[0], "off") == 0) | ||||
| 		g_cfg->osmux.peer_behind_nat = false; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| @@ -1686,10 +1741,12 @@ int mgcp_vty_init(void) | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_name_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_v6_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_size_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_osmux_peer_behind_nat_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd); | ||||
| 	install_element(MGCP_NODE, &cfg_mgcp_domain_cmd); | ||||
| @@ -1734,9 +1791,9 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, | ||||
| 	int rc; | ||||
| 	struct mgcp_trunk *trunk; | ||||
|  | ||||
| 	cfg->osmux_port = OSMUX_PORT; | ||||
| 	cfg->osmux_batch = 4; | ||||
| 	cfg->osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX; | ||||
| 	cfg->osmux.local_port = OSMUX_DEFAULT_PORT; | ||||
| 	cfg->osmux.batch_factor = 4; | ||||
| 	cfg->osmux.batch_size = OSMUX_BATCH_DEFAULT_MAX; | ||||
|  | ||||
| 	g_cfg = cfg; | ||||
| 	rc = vty_read_config_file(config_file, NULL); | ||||
|   | ||||
| @@ -41,7 +41,6 @@ | ||||
| #include <osmocom/mgcp/debug.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
| #include <osmocom/mgcp/mgcp_trunk.h> | ||||
| #include <osmocom/mgcp/mgcp_ctrl.h> | ||||
|  | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/core/msgb.h> | ||||
| @@ -51,7 +50,7 @@ | ||||
| #include <osmocom/core/rate_ctr.h> | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/core/socket.h> | ||||
|  | ||||
| #include <osmocom/ctrl/control_if.h> | ||||
| #include <osmocom/ctrl/control_vty.h> | ||||
|  | ||||
| #include <osmocom/vty/telnet_interface.h> | ||||
| @@ -83,7 +82,7 @@ static int daemonize = 0; | ||||
|  | ||||
| const char *osmomgw_copyright = | ||||
| 	"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n" | ||||
| 	"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n" | ||||
| 	"Copyright (C) 2017-2022 by sysmocom s.f.m.c. GmbH\r\n" | ||||
| 	"Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n" | ||||
| 	"Philipp Maier\r\n\r\n" | ||||
| 	"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n" | ||||
| @@ -95,7 +94,7 @@ static char *config_file = "osmo-mgw.cfg"; | ||||
| /* used by msgb and mgcp */ | ||||
| void *tall_mgw_ctx = NULL; | ||||
|  | ||||
| static void print_help() | ||||
| static void print_help(void) | ||||
| { | ||||
| 	printf("Some useful options:\n"); | ||||
| 	printf(" -h --help is printing this text.\n"); | ||||
| @@ -295,13 +294,22 @@ static const struct log_info_cat log_categories[] = { | ||||
| 		  .name = "DRTP", | ||||
| 		  .description = "RTP stream handling", | ||||
| 		  .color = "\033[1;30m", | ||||
| 		  .enabled = 1,.loglevel = LOGL_NOTICE, | ||||
| 		  .enabled = 1, | ||||
| 		  .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
| 	[DE1] = { | ||||
| 		  .name = "DE1", | ||||
| 		  .description = "E1 line handling", | ||||
| 		  .color = "\033[1;31m", | ||||
| 		  .enabled = 1,.loglevel = LOGL_NOTICE, | ||||
| 		  .enabled = 1, | ||||
| 		  .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
| 	[DOSMUX] = { | ||||
| 		  .name = "DOSMUX", | ||||
| 		  .description = "Osmux (Osmocom RTP multiplexing)", | ||||
| 		  .color = "\033[1;32m", | ||||
| 		  .enabled = 1, | ||||
| 		  .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| @@ -352,12 +360,11 @@ int main(int argc, char **argv) | ||||
| 		return rc; | ||||
|  | ||||
| 	/* start telnet after reading config for vty_get_bind_addr() */ | ||||
| 	rc = telnet_init_dynif(tall_mgw_ctx, NULL, | ||||
| 			       vty_get_bind_addr(), OSMO_VTY_PORT_MGW); | ||||
| 	rc = telnet_init_default(tall_mgw_ctx, NULL, OSMO_VTY_PORT_MGW); | ||||
| 	if (rc < 0) | ||||
| 		return rc; | ||||
|  | ||||
| 	cfg->ctrl = mgw_ctrl_interface_setup(cfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW); | ||||
| 	cfg->ctrl = ctrl_interface_setup(cfg, OSMO_CTRL_PORT_MGW, NULL); | ||||
| 	if (!cfg->ctrl) { | ||||
| 		fprintf(stderr, "Failed to init the control interface on %s:%u. Exiting\n", | ||||
| 			ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW); | ||||
|   | ||||
| @@ -34,11 +34,11 @@ DISTCLEANFILES = \ | ||||
| 	$(NULL) | ||||
|  | ||||
| if ENABLE_EXT_TESTS | ||||
| python-tests: $(BUILT_SOURCES) | ||||
| python-tests: $(top_builddir)/src/osmo-mgw/osmo-mgw | ||||
| 	osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v | ||||
| 	osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v | ||||
| else | ||||
| python-tests: $(BUILT_SOURCES) | ||||
| python-tests: | ||||
| 	echo "Not running python-based tests (determined at configure-time)" | ||||
| endif | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,7 @@ AM_CFLAGS = \ | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	$(COVERAGE_LDFLAGS) \ | ||||
| 	-no-install \ | ||||
| 	$(NULL) | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
|   | ||||
| @@ -77,6 +77,8 @@ static void test_strline(void) | ||||
| #define AUEP1_RET "500 158663169 FAIL\r\n" | ||||
| #define AUEP2	"AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0\r\n" | ||||
| #define AUEP2_RET "500 18983213 FAIL\r\n" | ||||
| #define AUEP_NULL "AUEP 18983215 null@mgw MGCP 1.0\r\n" | ||||
| #define AUEP_NULL_RET "200 18983215 OK\r\n" | ||||
| #define EMPTY	"\r\n" | ||||
| #define EMPTY_RET NULL | ||||
| #define SHORT	"CRCX \r\n" | ||||
| @@ -274,6 +276,12 @@ static void test_strline(void) | ||||
|  | ||||
| #define MDCX_TOO_LONG_CI_RET "510 18983224 FAIL\r\n" | ||||
|  | ||||
| #define MDCX_NULL \ | ||||
| 	"MDCX 9 null@mgw MGCP 1.0\r\n" \ | ||||
| 	"I: %s\n" | ||||
|  | ||||
| #define MDCX_NULL_RET "502 9 FAIL\r\n" | ||||
|  | ||||
| #define SHORT2	"CRCX 1" | ||||
| #define SHORT2_RET "510 000000 FAIL\r\n" | ||||
| #define SHORT3	"CRCX 1 1@mgw" | ||||
| @@ -392,6 +400,13 @@ static void test_strline(void) | ||||
|  #define DLCX_RET_OSMUX DLCX_RET \ | ||||
| 	"X-Osmo-CP: EC TI=0, TO=0\r\n" | ||||
|  | ||||
| #define DLCX_NULL \ | ||||
| 	"DLCX 8 null@mgw MGCP 1.0\r\n" \ | ||||
| 	"I: %s\r\n" \ | ||||
| 	"C: 2\r\n" | ||||
|  | ||||
| #define DLCX_NULL_RET "502 8 FAIL\r\n" | ||||
|  | ||||
| #define RQNT \ | ||||
| 	"RQNT 186908780 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"X: B244F267488\r\n" \ | ||||
| @@ -405,6 +420,13 @@ static void test_strline(void) | ||||
| #define RQNT1_RET "200 186908780 OK\r\n" | ||||
| #define RQNT2_RET "200 186908781 OK\r\n" | ||||
|  | ||||
| #define RQNT_NULL \ | ||||
| 	"RQNT 186908782 null@mgw MGCP 1.0\r\n" \ | ||||
| 	"X: B244F267488\r\n" \ | ||||
| 	"S: D/9\r\n" | ||||
|  | ||||
| #define RQNT_NULL_RET "502 186908782 FAIL\r\n" | ||||
|  | ||||
| #define PTYPE_IGNORE 0		/* == default initializer */ | ||||
| #define PTYPE_NONE 128 | ||||
| #define PTYPE_NYI  PTYPE_NONE | ||||
| @@ -545,6 +567,20 @@ static void test_strline(void) | ||||
| 	"m=audio 16008 RTP/AVP 0\r\n" \ | ||||
| 	"a=ptime:20\r\n" | ||||
|  | ||||
| #define CRCX_NULL \ | ||||
| 	"CRCX 2 null@mgw MGCP 1.0\r\n" \ | ||||
| 	"m: recvonly\r\n" \ | ||||
| 	"C: 2\r\n" \ | ||||
| 	"L: p:20\r\n" \ | ||||
| 	"\r\n" \ | ||||
| 	"v=0\r\n" \ | ||||
| 	"c=IN IP4 123.12.12.123\r\n" \ | ||||
| 	"m=audio 5904 RTP/AVP 97\r\n" \ | ||||
| 	"a=rtpmap:97 GSM-EFR/8000\r\n" \ | ||||
| 	"a=ptime:40\r\n" | ||||
|  | ||||
| #define CRCX_NULL_RET "502 2 FAIL\r\n" | ||||
|  | ||||
| struct mgcp_test { | ||||
| 	const char *name; | ||||
| 	const char *req; | ||||
| @@ -586,6 +622,11 @@ static const struct mgcp_test tests[] = { | ||||
| 	{"CRCX", CRCX_X_OSMO_IGN, CRCX_X_OSMO_IGN_RET, 97}, | ||||
| 	{"MDCX_TOO_LONG_CI", MDCX_TOO_LONG_CI, MDCX_TOO_LONG_CI_RET}, | ||||
| 	{"CRCX", CRCX_AMR_WITH_FMTP, CRCX_AMR_WITH_FMTP_RET}, | ||||
| 	{"AUEP_NULL", AUEP_NULL, AUEP_NULL_RET}, | ||||
| 	{"CRCX_NULL", CRCX_NULL, CRCX_NULL_RET}, | ||||
| 	{"MDCX_NULL", MDCX_NULL, MDCX_NULL_RET}, | ||||
| 	{"DLCX_NULL", DLCX_NULL, DLCX_NULL_RET}, | ||||
| 	{"RQNT_NULL", RQNT_NULL, RQNT_NULL_RET}, | ||||
| }; | ||||
|  | ||||
| static const struct mgcp_test retransmit[] = { | ||||
| @@ -838,7 +879,7 @@ static void test_messages(void) | ||||
| 				if (endp->local_options.pkt_period_min || | ||||
| 				    endp->local_options.pkt_period_max) | ||||
| 					printf | ||||
| 					    ("Requested packetetization period: " | ||||
| 					    ("Requested packetization period: " | ||||
| 					     "%d-%d\n", | ||||
| 					     endp->local_options.pkt_period_min, | ||||
| 					     endp-> | ||||
| @@ -1071,7 +1112,7 @@ static void test_packet_loss_calc(void) | ||||
| 				    "test-connection"); | ||||
| 		conn = mgcp_conn_get_rtp(&endp, _conn->id); | ||||
| 		state = &conn->state; | ||||
| 		packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR); | ||||
| 		packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR); | ||||
|  | ||||
| 		state->stats.initialized = 1; | ||||
| 		state->stats.base_seq = pl_test_dat[i].base_seq; | ||||
| @@ -1467,7 +1508,6 @@ static void test_multilple_codec(void) | ||||
| 	/* Allocate 5@mgw and let osmo-mgw pick a codec from the list */ | ||||
| 	last_endpoint[0] = '\0'; | ||||
| 	inp = create_msg(CRCX_MULT_GSM_EXACT, NULL); | ||||
| 	trunk->no_audio_transcoding = 1; | ||||
| 	resp = mgcp_handle_message(cfg, inp); | ||||
| 	OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, | ||||
| 					      sizeof(conn_id)) == 0); | ||||
| @@ -1492,7 +1532,7 @@ static void test_multilple_codec(void) | ||||
| 	conn = mgcp_conn_get_rtp(endp, conn_id); | ||||
| 	OSMO_ASSERT(conn); | ||||
| 	OSMO_ASSERT(conn->end.codec->payload_type == 3); | ||||
| 	OSMO_ASSERT(conn->end.rtp_port == htons(16434)); | ||||
| 	OSMO_ASSERT(osmo_sockaddr_port(&conn->end.addr.u.sa) == 16434); | ||||
| 	memset(&addr, 0, sizeof(addr)); | ||||
| 	inet_aton("8.8.8.8", &addr); | ||||
| 	OSMO_ASSERT(conn->end.addr.u.sa.sa_family == AF_INET); | ||||
| @@ -1511,7 +1551,6 @@ static void test_multilple_codec(void) | ||||
|  | ||||
| 	last_endpoint[0] = '\0'; | ||||
| 	inp = create_msg(CRCX_MULT_GSM_EXACT, NULL); | ||||
| 	trunk->no_audio_transcoding = 0; | ||||
| 	resp = mgcp_handle_message(cfg, inp); | ||||
| 	OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, | ||||
| 					      sizeof(conn_id)) == 0); | ||||
| @@ -1632,7 +1671,8 @@ static void test_osmux_cid(void) | ||||
|  | ||||
| 	for (i = 0; i < 256; ++i) { | ||||
| 		id = osmux_cid_pool_get_next(); | ||||
| 		OSMO_ASSERT(id == i); | ||||
| 		/* We called osmux_cid_pool_get_next() above, so next CID is i+1. */ | ||||
| 		OSMO_ASSERT(id == ((i + 1) & 0xff)); | ||||
| 		OSMO_ASSERT(osmux_cid_pool_count_used() == i + 1); | ||||
| 	} | ||||
|  | ||||
| @@ -1768,26 +1808,25 @@ static const struct mgcp_codec_param amr_param_octet_aligned_unset = { | ||||
| 	.amr_octet_aligned_present = false, | ||||
| }; | ||||
|  | ||||
| struct testcase_mgcp_codec_pt_translate_codec { | ||||
| struct testcase_mgcp_codec_decide_codec { | ||||
| 	int payload_type; | ||||
| 	const char *audio_name; | ||||
| 	const struct mgcp_codec_param *param; | ||||
| 	int expect_rc; | ||||
| }; | ||||
|  | ||||
| struct testcase_mgcp_codec_pt_translate_expect { | ||||
| 	bool end; | ||||
| struct testcase_mgcp_codec_decide_expect { | ||||
| 	int payload_type_map[2]; | ||||
| }; | ||||
|  | ||||
| struct testcase_mgcp_codec_pt_translate { | ||||
| struct testcase_mgcp_codec_decide { | ||||
| 	const char *descr; | ||||
| 	/* two conns on an endpoint, each with N configured codecs */ | ||||
| 	struct testcase_mgcp_codec_pt_translate_codec codecs[2][10]; | ||||
| 	struct testcase_mgcp_codec_pt_translate_expect expect[32]; | ||||
| 	struct testcase_mgcp_codec_decide_codec codecs[2][10]; | ||||
| 	struct testcase_mgcp_codec_decide_expect expect[2]; | ||||
| }; | ||||
|  | ||||
| static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translate_cases[] = { | ||||
| static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_cases[] = { | ||||
| 	{ | ||||
| 		.descr = "same order, but differing payload type numbers", | ||||
| 		.codecs = { | ||||
| @@ -1804,10 +1843,7 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {112, 96}, }, | ||||
| 			{ .payload_type_map = {0, 0}, }, | ||||
| 			{ .payload_type_map = {111, 97} }, | ||||
| 			{ .payload_type_map = {123, -EINVAL} }, | ||||
| 			{ .end = true }, | ||||
| 			{ .payload_type_map = {112, 96}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| @@ -1825,11 +1861,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {112, 96}, }, | ||||
| 			{ .payload_type_map = {0, 0}, }, | ||||
| 			{ .payload_type_map = {111, 97} }, | ||||
| 			{ .payload_type_map = {123, -EINVAL} }, | ||||
| 			{ .end = true }, | ||||
| 			{ .payload_type_map = {111, 97}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| @@ -1847,10 +1880,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {96, 97}, }, | ||||
| 			{ .payload_type_map = {97, 96}, }, | ||||
| 			{ .payload_type_map = {0, 0}, }, | ||||
| 			{ .end = true }, | ||||
| 			{ .payload_type_map = {96, 97}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| @@ -1866,10 +1897,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {112, -EINVAL}, }, | ||||
| 			{ .payload_type_map = {0, -EINVAL}, }, | ||||
| 			{ .payload_type_map = {111, -EINVAL} }, | ||||
| 			{ .end = true }, | ||||
| 			{ .payload_type_map = {-EINVAL, -EINVAL}, }, | ||||
| 			{ .payload_type_map = {-EINVAL, 96}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| @@ -1886,28 +1915,60 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {112, -EINVAL}, }, | ||||
| 			{ .payload_type_map = {0, -EINVAL}, }, | ||||
| 			{ .payload_type_map = {111, -EINVAL} }, | ||||
| 			{ .end = true }, | ||||
| 			{ .payload_type_map = {-EINVAL, -EINVAL}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.descr = "test AMR with differing octet-aligned settings", | ||||
| 		.descr = "test AMR with differing octet-aligned settings (both <-> both)", | ||||
| 		.codecs = { | ||||
| 			{ | ||||
| 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||
| 				{ 112, "AMR/8000", &amr_param_octet_aligned_false, }, | ||||
| 			}, | ||||
| 			{ | ||||
| 				{ 122, "AMR/8000", &amr_param_octet_aligned_false, }, | ||||
| 				{ 121, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {111, 121}, }, | ||||
| 			{ .payload_type_map = {112, 122} }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.descr = "test AMR with differing octet-aligned settings (oa <-> both)", | ||||
| 		.codecs = { | ||||
| 			{ | ||||
| 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||
| 			}, | ||||
| 			{ | ||||
| 				{ 122, "AMR/8000", &amr_param_octet_aligned_false, }, | ||||
| 				{ 121, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {111, 122}, }, | ||||
| 			{ .end = true }, | ||||
| 			{ .payload_type_map = {111, 121}, }, | ||||
| 			{ .payload_type_map = {111, 121}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.descr = "test AMR with missing octet-aligned settings (defaults to 0)", | ||||
| 		.descr = "test AMR with differing octet-aligned settings (bwe <-> both)", | ||||
| 		.codecs = { | ||||
| 			{ | ||||
| 				{ 112, "AMR/8000", &amr_param_octet_aligned_false, }, | ||||
| 			}, | ||||
| 			{ | ||||
| 				{ 122, "AMR/8000", &amr_param_octet_aligned_false, }, | ||||
| 				{ 121, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {112, 122}, }, | ||||
| 			{ .payload_type_map = {112, 122}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.descr = "test AMR with missing octet-aligned settings (oa <-> unset)", | ||||
| 		.codecs = { | ||||
| 			{ | ||||
| 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||
| @@ -1918,22 +1979,54 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {111, 122}, }, | ||||
| 			{ .end = true }, | ||||
| 			{ .payload_type_map = {111, 122}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.descr = "test AMR with NULL param (defaults to 0)", | ||||
| 		.descr = "test AMR with missing octet-aligned settings (bwe <-> unset)", | ||||
| 		.codecs = { | ||||
| 			{ | ||||
| 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||
| 				{ 111, "AMR/8000", &amr_param_octet_aligned_false, }, | ||||
| 			}, | ||||
| 			{ | ||||
| 				{ 122, "AMR/8000", &amr_param_octet_aligned_unset, }, | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {111, 122}, }, | ||||
| 			{ .payload_type_map = {111, 122}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.descr = "test AMR with NULL param (oa <-> null)", | ||||
| 		.codecs = { | ||||
| 			{ | ||||
| 				{ 112, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||
| 			}, | ||||
| 			{ | ||||
| 				{ 122, "AMR/8000", NULL, }, | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {111, 122}, }, | ||||
| 			{ .end = true }, | ||||
| 			/* Note: Both 111, anbd 112 will translate to 122. The translation from 112 */ | ||||
| 			{ .payload_type_map = {112, 122} }, | ||||
| 			{ .payload_type_map = {112, 122}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.descr = "test AMR with NULL param (bwe <-> null)", | ||||
| 		.codecs = { | ||||
| 			{ | ||||
| 				{ 112, "AMR/8000", &amr_param_octet_aligned_false, }, | ||||
| 			}, | ||||
| 			{ | ||||
| 				{ 122, "AMR/8000", NULL, }, | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			/* Note: Both 111, anbd 112 will translate to 122. The translation from 112 */ | ||||
| 			{ .payload_type_map = {112, 122} }, | ||||
| 			{ .payload_type_map = {112, 122}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| @@ -1951,11 +2044,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {112, 96}, }, | ||||
| 			{ .payload_type_map = {0, 0}, }, | ||||
| 			{ .payload_type_map = {111, 97} }, | ||||
| 			{ .payload_type_map = {123, -EINVAL} }, | ||||
| 			{ .end = true }, | ||||
| 			{ .payload_type_map = {111, 97}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| @@ -1973,11 +2063,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {112, 96}, }, | ||||
| 			{ .payload_type_map = {0, 0}, }, | ||||
| 			{ .payload_type_map = {111, 97} }, | ||||
| 			{ .payload_type_map = {123, -EINVAL} }, | ||||
| 			{ .end = true }, | ||||
| 			{ .payload_type_map = {111, 97}, }, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| @@ -1995,45 +2082,97 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {111, 121}, }, | ||||
| 			{ .payload_type_map = {112, -EINVAL} }, | ||||
| 			{ .payload_type_map = {113, -EINVAL} }, | ||||
| 			{ .end = true }, | ||||
| 			{ .payload_type_map = {111, 121} }, | ||||
| 		}, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| static void test_mgcp_codec_pt_translate(void) | ||||
| static bool codec_decision(struct mgcp_conn_rtp *conn, unsigned int index_conn_src, unsigned int index_conn_dst, | ||||
| 			   const struct testcase_mgcp_codec_decide_expect *expect) | ||||
| { | ||||
| 	bool ok = true; | ||||
| 	int payload_type_conn_src; | ||||
| 	int payload_type_conn_dst; | ||||
|  | ||||
| 	printf(" - mgcp_codec_decide(&conn[%u], &conn[%u]):\n", index_conn_src, index_conn_dst); | ||||
| 	if (mgcp_codec_decide(&conn[index_conn_src], &conn[index_conn_dst]) != 0) { | ||||
| 		if (expect->payload_type_map[index_conn_src] == -EINVAL | ||||
| 		    && expect->payload_type_map[index_conn_dst] == -EINVAL) | ||||
| 			printf("    codec decision failed (expected)!\n"); | ||||
| 		else { | ||||
| 			printf("    ERROR: codec decision failed!\n"); | ||||
| 			ok = false; | ||||
| 		} | ||||
| 	} else { | ||||
| 		printf("    Codec decision result:\n"); | ||||
| 		if (conn[index_conn_src].end.codec) { | ||||
| 			payload_type_conn_src = conn[index_conn_src].end.codec->payload_type; | ||||
| 			printf("    conn[%u]: codec:%s, pt:%d\n", | ||||
| 			       index_conn_src, conn[index_conn_src].end.codec->subtype_name, payload_type_conn_src); | ||||
| 		} else { | ||||
| 			payload_type_conn_src = -EINVAL; | ||||
| 			printf("    conn[%u]: codec:none, pt:none\n", index_conn_src); | ||||
| 		} | ||||
|  | ||||
| 		if (conn[index_conn_dst].end.codec) { | ||||
| 			payload_type_conn_dst = conn[index_conn_dst].end.codec->payload_type; | ||||
| 			printf("    conn[%u]: codec:%s, pt:%d\n", | ||||
| 			       index_conn_dst, conn[index_conn_dst].end.codec->subtype_name, | ||||
| 			       payload_type_conn_dst); | ||||
| 		} else { | ||||
| 			payload_type_conn_dst = -EINVAL; | ||||
| 			printf("    conn[%u]: codec:none, pt:none\n", index_conn_dst); | ||||
| 		} | ||||
|  | ||||
| 		if (payload_type_conn_src != expect->payload_type_map[index_conn_src]) { | ||||
| 			printf("    ERROR: conn[%u] unexpected codec decision, expected pt=%d, got pt=%d\n", | ||||
| 			       index_conn_src, expect->payload_type_map[index_conn_src], payload_type_conn_src); | ||||
| 			ok = false; | ||||
| 		} | ||||
|  | ||||
| 		if (payload_type_conn_dst != expect->payload_type_map[index_conn_dst]) { | ||||
| 			printf("    ERROR: conn[%u] unexpected codec decision, expected pt=%d, got pt=%d\n", | ||||
| 			       index_conn_dst, expect->payload_type_map[index_conn_dst], | ||||
| 			       payload_type_conn_dst); | ||||
| 			ok = false; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ok; | ||||
| } | ||||
|  | ||||
| static void test_mgcp_codec_decide(void) | ||||
| { | ||||
| 	int i; | ||||
| 	bool ok = true; | ||||
| 	printf("\nTesting mgcp_codec_pt_translate()\n"); | ||||
| 	printf("\nTesting mgcp_codec_find_convertible()\n"); | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_pt_translate_cases); i++) { | ||||
| 		const struct testcase_mgcp_codec_pt_translate *t = &test_mgcp_codec_pt_translate_cases[i]; | ||||
| 		struct mgcp_conn_rtp conn[2] = {}; | ||||
| 	for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_find_convertible_cases); i++) { | ||||
| 		const struct testcase_mgcp_codec_decide *t = &test_mgcp_codec_find_convertible_cases[i]; | ||||
| 		struct mgcp_conn_rtp conn[2] = { }; | ||||
| 		int rc; | ||||
| 		int conn_i; | ||||
| 		int c; | ||||
|  | ||||
| 		printf("#%d: %s\n", i, t->descr); | ||||
|  | ||||
| 		/* Build testvector (add codecs to conn, set properties etc... */ | ||||
| 		for (conn_i = 0; conn_i < 2; conn_i++) { | ||||
| 			printf(" - add codecs on conn%d:\n", conn_i); | ||||
| 			for (c = 0; c < ARRAY_SIZE(t->codecs[conn_i]); c++) { | ||||
| 				const struct testcase_mgcp_codec_pt_translate_codec *codec = &t->codecs[conn_i][c]; | ||||
| 				const struct testcase_mgcp_codec_decide_codec *codec = &t->codecs[conn_i][c]; | ||||
| 				if (!codec->audio_name) | ||||
| 					break; | ||||
|  | ||||
| 				rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name, codec->param); | ||||
| 				rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name, | ||||
| 						    codec->param); | ||||
|  | ||||
| 				printf("   %2d: %3d %s%s  -> rc=%d\n", c, codec->payload_type, codec->audio_name, | ||||
| 				       codec->param ? | ||||
| 					       (codec->param->amr_octet_aligned_present? | ||||
| 							(codec->param->amr_octet_aligned ? | ||||
| 								" octet-aligned=1" : " octet-aligned=0") | ||||
| 							: " octet-aligned=unset") | ||||
| 						: "", | ||||
| 						rc); | ||||
| 				       (codec->param->amr_octet_aligned_present ? | ||||
| 					(codec->param->amr_octet_aligned ? " octet-aligned=1" : " octet-aligned=0") | ||||
| 					: " octet-aligned=unset") | ||||
| 				       : "", rc); | ||||
| 				if (rc != codec->expect_rc) { | ||||
| 					printf("     ERROR: expected rc=%d\n", codec->expect_rc); | ||||
| 					ok = false; | ||||
| @@ -2043,42 +2182,23 @@ static void test_mgcp_codec_pt_translate(void) | ||||
| 				printf("    (none)\n"); | ||||
| 		} | ||||
|  | ||||
| 		for (c = 0; c < ARRAY_SIZE(t->expect); c++) { | ||||
| 			const struct testcase_mgcp_codec_pt_translate_expect *expect = &t->expect[c]; | ||||
| 			int result; | ||||
| 		/* Run codec decision and check expectation */ | ||||
| 		if (!codec_decision(conn, 0, 1, &t->expect[0])) | ||||
| 			ok = false; | ||||
|  | ||||
| 			if (expect->end) | ||||
| 				break; | ||||
| 		if (!codec_decision(conn, 1, 0, &t->expect[1])) | ||||
| 			ok = false; | ||||
|  | ||||
| 			result = mgcp_codec_pt_translate(&conn[0], &conn[1], expect->payload_type_map[0]); | ||||
| 			printf(" - mgcp_codec_pt_translate(conn0, conn1, %d) -> %d\n", | ||||
| 			       expect->payload_type_map[0], result); | ||||
| 			if (result != expect->payload_type_map[1]) { | ||||
| 				printf("     ERROR: expected -> %d\n", expect->payload_type_map[1]); | ||||
| 				ok = false; | ||||
| 			} | ||||
|  | ||||
| 			/* If the expected result is an error, don't do reverse map test */ | ||||
| 			if (expect->payload_type_map[1] < 0) | ||||
| 				continue; | ||||
|  | ||||
| 			result = mgcp_codec_pt_translate(&conn[1], &conn[0], expect->payload_type_map[1]); | ||||
| 			printf(" - mgcp_codec_pt_translate(conn1, conn0, %d) -> %d\n", | ||||
| 			       expect->payload_type_map[1], result); | ||||
| 			if (result != expect->payload_type_map[0]) { | ||||
| 				printf("     ERROR: expected -> %d\n", expect->payload_type_map[0]); | ||||
| 				ok = false; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for (conn_i = 0; conn_i < 2; conn_i++) | ||||
| 			mgcp_codec_reset_all(&conn[conn_i]); | ||||
| 		if (ok) | ||||
| 			printf(" ===> SUCCESS: codec decision as expected!\n"); | ||||
| 		else | ||||
| 			printf(" ===> FAIL: unexpected codec decision!\n"); | ||||
| 	} | ||||
|  | ||||
| 	OSMO_ASSERT(ok); | ||||
| } | ||||
|  | ||||
| void test_conn_id_matching() | ||||
| void test_conn_id_matching(void) | ||||
| { | ||||
| 	struct mgcp_endpoint endp = {}; | ||||
| 	struct mgcp_conn *conn; | ||||
| @@ -2118,7 +2238,7 @@ void test_conn_id_matching() | ||||
| 	talloc_free(conn); | ||||
| } | ||||
|  | ||||
| void test_e1_trunk_nr_from_epname() | ||||
| void test_e1_trunk_nr_from_epname(void) | ||||
| { | ||||
| 	unsigned int trunk_nr; | ||||
| 	int rc; | ||||
| @@ -2168,7 +2288,7 @@ void test_e1_trunk_nr_from_epname() | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| void test_mgcp_is_rtp_dummy_payload() | ||||
| void test_mgcp_is_rtp_dummy_payload(void) | ||||
| { | ||||
| 	/* realistic rtp packet */ | ||||
| 	static const char rtp_payload[] = | ||||
| @@ -2232,7 +2352,7 @@ int main(int argc, char **argv) | ||||
| 	test_osmux_cid(); | ||||
| 	test_get_lco_identifier(); | ||||
| 	test_check_local_cx_options(ctx); | ||||
| 	test_mgcp_codec_pt_translate(); | ||||
| 	test_mgcp_codec_decide(); | ||||
| 	test_conn_id_matching(); | ||||
| 	test_e1_trunk_nr_from_epname(); | ||||
| 	test_mgcp_is_rtp_dummy_payload(); | ||||
|   | ||||
| @@ -525,6 +525,80 @@ Response matches our expectations. | ||||
| L: p:20 | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 111 | ||||
| a=rtpmap:111 AMR/8000/1 | ||||
| a=ptime:20 | ||||
| a=fmtp:111 mode-change-capability=2; octet-align=1 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
| Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| Testing AUEP_NULL | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| AUEP 18983215 null@mgw MGCP 1.0 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing CRCX_NULL | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| CRCX 2 null@mgw MGCP 1.0 | ||||
| m: recvonly | ||||
| C: 2 | ||||
| L: p:20 | ||||
|  | ||||
| v=0 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 97 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX_NULL | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 9 null@mgw MGCP 1.0 | ||||
| I: %s | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing DLCX_NULL | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| DLCX 8 null@mgw MGCP 1.0 | ||||
| I: %s | ||||
| C: 2 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing RQNT_NULL | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| @@ -1276,7 +1350,7 @@ p:10, a:PCMU -> p:10, a:PCMU | ||||
| Response matches our expectations. | ||||
| Testing get_lco_identifier() | ||||
| p:10, a:PCMU -> p:10, a:PCMU | ||||
| p:10, a:PCMU -> p:10, a:PCMU | ||||
| p:10, a:PCMU -> p:10, a:PCMU | ||||
| 'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU' | ||||
| 'XXXX,p:10,a:PCMU' -> 'p:10,a:PCMU' | ||||
| '10,a:PCMU' -> 'a:PCMU' | ||||
| @@ -1286,13 +1360,15 @@ Testing mgcp_codec_pt_translate() | ||||
| ', a:PCMU' -> 'a:PCMU' | ||||
| ' a:PCMU' -> 'a:PCMU' | ||||
| '' -> '(null)' | ||||
| p10, aPCMU -> (null) | ||||
| '10,a :PCMU' -> '(null)' | ||||
|  | ||||
| Testing mgcp_codec_pt_translate() | ||||
| #0: same order, but differing payload type numbers | ||||
|  - add codecs on conn0: | ||||
|     0: 112 AMR/8000/1 octet-aligned=1  -> rc=0 | ||||
| p10, aPCMU -> (null) | ||||
| '10,a :PCMU' -> '(null)' | ||||
|  | ||||
| Testing mgcp_codec_find_convertible() | ||||
| #0: same order, but differing payload type numbers | ||||
|  - add codecs on conn0: | ||||
|     0: 112 AMR/8000/1 octet-aligned=1  -> rc=0 | ||||
|     1:   0 PCMU/8000/1  -> rc=0 | ||||
|     2: 111 GSM-HR-08/8000/1  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|     0:  96 AMR/8000/1 octet-aligned=1  -> rc=0 | ||||
|     1:   0 PCMU/8000/1  -> rc=0 | ||||
| @@ -1302,13 +1378,15 @@ Testing mgcp_codec_pt_translate() | ||||
|     conn[0]: codec:AMR, pt:112 | ||||
|     conn[1]: codec:AMR, pt:96 | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|  - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97 | ||||
|  - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22 | ||||
| #1: different order and different payload type numbers | ||||
|  - add codecs on conn0: | ||||
|     0:   0 PCMU/8000/1  -> rc=0 | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:AMR, pt:96 | ||||
|     conn[0]: codec:AMR, pt:112 | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #1: different order and different payload type numbers | ||||
|  - add codecs on conn0: | ||||
|     0:   0 PCMU/8000/1  -> rc=0 | ||||
|     1: 111 GSM-HR-08/8000/1  -> rc=0 | ||||
|     2: 112 AMR/8000/1 octet-aligned=1  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|     0:  97 GSM-HR-08/8000/1  -> rc=0 | ||||
|     1:   0 PCMU/8000/1  -> rc=0 | ||||
| @@ -1318,12 +1396,15 @@ Testing mgcp_codec_pt_translate() | ||||
|     conn[0]: codec:PCMU, pt:0 | ||||
|     conn[1]: codec:PCMU, pt:0 | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|  - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97 | ||||
|  - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22 | ||||
| #2: both sides have the same payload_type numbers assigned to differing codecs | ||||
|  - add codecs on conn0: | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:GSM-HR-08, pt:97 | ||||
|     conn[0]: codec:GSM-HR-08, pt:111 | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #2: both sides have the same payload_type numbers assigned to differing codecs | ||||
|  - add codecs on conn0: | ||||
|     0:   0 PCMU/8000/1  -> rc=0 | ||||
|     1:  96 GSM-HR-08/8000/1  -> rc=0 | ||||
|     2:  97 AMR/8000/1 octet-aligned=1  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|     0:  97 GSM-HR-08/8000/1  -> rc=0 | ||||
|     1:   0 PCMU/8000/1  -> rc=0 | ||||
| @@ -1331,9 +1412,13 @@ Testing mgcp_codec_pt_translate() | ||||
|  - mgcp_codec_decide(&conn[0], &conn[1]): | ||||
|     Codec decision result: | ||||
|     conn[0]: codec:PCMU, pt:0 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 96) -> 97 | ||||
|  - mgcp_codec_pt_translate(conn1, conn0, 97) -> 96 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 97) -> 96 | ||||
|     conn[1]: codec:PCMU, pt:0 | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:GSM-HR-08, pt:97 | ||||
|     conn[0]: codec:GSM-HR-08, pt:96 | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #3: conn0 has no codecs | ||||
|  - add codecs on conn0: | ||||
|     (none) | ||||
|  - add codecs on conn1: | ||||
| @@ -1341,31 +1426,116 @@ Testing mgcp_codec_pt_translate() | ||||
|     1:   0 PCMU/8000/1  -> rc=0 | ||||
|     2:  97 GSM-HR-08/8000/1  -> rc=0 | ||||
|  - mgcp_codec_decide(&conn[0], &conn[1]): | ||||
|     0:  96 AMR/8000/1 octet-aligned=1  -> rc=0 | ||||
|     1:   0 PCMU/8000/1  -> rc=0 | ||||
|     2:  97 GSM-HR-08/8000/1  -> rc=0 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22 | ||||
|     codec decision failed (expected)! | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:AMR, pt:96 | ||||
|     conn[0]: codec:none, pt:none | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #4: conn1 has no codecs | ||||
|  - add codecs on conn0: | ||||
|     0: 112 AMR/8000/1 octet-aligned=1  -> rc=0 | ||||
|     1:   0 PCMU/8000/1  -> rc=0 | ||||
|     2: 111 GSM-HR-08/8000/1  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|     (none) | ||||
|  - mgcp_codec_decide(&conn[0], &conn[1]): | ||||
|     Codec decision result: | ||||
|     conn[0]: codec:AMR, pt:112 | ||||
|     conn[1]: codec:none, pt:none | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|     codec decision failed (expected)! | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #5: test AMR with differing octet-aligned settings (both <-> both) | ||||
|  - add codecs on conn0: | ||||
|     0: 111 AMR/8000 octet-aligned=1  -> rc=0 | ||||
|     1: 112 AMR/8000 octet-aligned=0  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|     0: 122 AMR/8000 octet-aligned=0  -> rc=0 | ||||
|     1: 121 AMR/8000 octet-aligned=1  -> rc=0 | ||||
|  - mgcp_codec_decide(&conn[0], &conn[1]): | ||||
|     0: 112 AMR/8000/1 octet-aligned=1  -> rc=0 | ||||
|     1:   0 PCMU/8000/1  -> rc=0 | ||||
|     2: 111 GSM-HR-08/8000/1  -> rc=0 | ||||
|     Codec decision result: | ||||
|     conn[0]: codec:AMR, pt:111 | ||||
|     conn[1]: codec:AMR, pt:121 | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:AMR, pt:122 | ||||
|     conn[0]: codec:AMR, pt:112 | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #6: test AMR with differing octet-aligned settings (oa <-> both) | ||||
|  - add codecs on conn0: | ||||
|     0: 111 AMR/8000 octet-aligned=1  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|     0: 122 AMR/8000 octet-aligned=0  -> rc=0 | ||||
|     1: 121 AMR/8000 octet-aligned=1  -> rc=0 | ||||
|  - mgcp_codec_decide(&conn[0], &conn[1]): | ||||
|     Codec decision result: | ||||
|     conn[0]: codec:AMR, pt:111 | ||||
|     conn[1]: codec:AMR, pt:121 | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:AMR, pt:121 | ||||
|     conn[0]: codec:AMR, pt:111 | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #7: test AMR with differing octet-aligned settings (bwe <-> both) | ||||
|  - add codecs on conn0: | ||||
|     0: 112 AMR/8000 octet-aligned=0  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|     0: 122 AMR/8000 octet-aligned=0  -> rc=0 | ||||
|     1: 121 AMR/8000 octet-aligned=1  -> rc=0 | ||||
|  - mgcp_codec_decide(&conn[0], &conn[1]): | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22 | ||||
| #5: test AMR with differing octet-aligned settings | ||||
|  - add codecs on conn0: | ||||
|     Codec decision result: | ||||
|     conn[0]: codec:AMR, pt:112 | ||||
|     conn[1]: codec:AMR, pt:122 | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:AMR, pt:122 | ||||
|     conn[0]: codec:AMR, pt:112 | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #8: test AMR with missing octet-aligned settings (oa <-> unset) | ||||
|  - add codecs on conn0: | ||||
|     0: 111 AMR/8000 octet-aligned=1  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|  - add codecs on conn1: | ||||
|     0: 122 AMR/8000 octet-aligned=unset  -> rc=0 | ||||
|  - mgcp_codec_decide(&conn[0], &conn[1]): | ||||
|     Codec decision result: | ||||
|     conn[0]: codec:AMR, pt:111 | ||||
|     conn[1]: codec:AMR, pt:122 | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:AMR, pt:122 | ||||
|     conn[0]: codec:AMR, pt:111 | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #9: test AMR with missing octet-aligned settings (bwe <-> unset) | ||||
|  - add codecs on conn0: | ||||
|     0: 111 AMR/8000 octet-aligned=0  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|     0: 122 AMR/8000 octet-aligned=unset  -> rc=0 | ||||
|  - mgcp_codec_decide(&conn[0], &conn[1]): | ||||
|  - mgcp_codec_pt_translate(conn1, conn0, 122) -> 111 | ||||
| #6: test AMR with missing octet-aligned settings (defaults to 0) | ||||
|  - add codecs on conn0: | ||||
|     Codec decision result: | ||||
|     conn[0]: codec:AMR, pt:111 | ||||
|     conn[1]: codec:AMR, pt:122 | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:AMR, pt:122 | ||||
|     conn[0]: codec:AMR, pt:111 | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #10: test AMR with NULL param (oa <-> null) | ||||
|  - add codecs on conn0: | ||||
|     0: 112 AMR/8000 octet-aligned=1  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|     0: 122 AMR/8000  -> rc=0 | ||||
|  - mgcp_codec_decide(&conn[0], &conn[1]): | ||||
|     Codec decision result: | ||||
|     conn[0]: codec:AMR, pt:112 | ||||
|     conn[1]: codec:AMR, pt:122 | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:AMR, pt:122 | ||||
|     conn[0]: codec:AMR, pt:112 | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #11: test AMR with NULL param (bwe <-> null) | ||||
|  - add codecs on conn0: | ||||
|     0: 112 AMR/8000 octet-aligned=0  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|     0: 122 AMR/8000  -> rc=0 | ||||
| @@ -1374,14 +1544,16 @@ Testing mgcp_codec_pt_translate() | ||||
|     conn[0]: codec:AMR, pt:112 | ||||
|     conn[1]: codec:AMR, pt:122 | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|  - add codecs on conn1: | ||||
|     0: 122 AMR/8000  -> rc=0 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 111) -> 122 | ||||
|  - mgcp_codec_pt_translate(conn1, conn0, 122) -> 111 | ||||
| #8: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit | ||||
|  - add codecs on conn0: | ||||
|     0:   0 PCMU/8000/1  -> rc=0 | ||||
|     1: 111 GSM-HR-08/8000/1  -> rc=0 | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:AMR, pt:122 | ||||
|     conn[0]: codec:AMR, pt:112 | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #12: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit | ||||
|  - add codecs on conn0: | ||||
|     0:   0 PCMU/8000/1  -> rc=0 | ||||
|     1: 111 GSM-HR-08/8000/1  -> rc=0 | ||||
|     2: 112 AMR/8000/1 octet-aligned=1  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|     0:  97 GSM-HR-08/8000  -> rc=0 | ||||
|     1:   0 PCMU/8000  -> rc=0 | ||||
|     2:  96 AMR/8000 octet-aligned=1  -> rc=0 | ||||
| @@ -1390,14 +1562,16 @@ Testing mgcp_codec_pt_translate() | ||||
|     conn[0]: codec:PCMU, pt:0 | ||||
|     conn[1]: codec:PCMU, pt:0 | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|  - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97 | ||||
|  - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22 | ||||
| #9: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit | ||||
|  - add codecs on conn0: | ||||
|     0:   0 PCMU/8000/1  -> rc=0 | ||||
|     1: 111 GSM-HR-08/8000/1  -> rc=0 | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:GSM-HR-08, pt:97 | ||||
|     conn[0]: codec:GSM-HR-08, pt:111 | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #13: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit | ||||
|  - add codecs on conn0: | ||||
|     0:   0 PCMU/8000/1  -> rc=0 | ||||
|     1: 111 GSM-HR-08/8000/1  -> rc=0 | ||||
|     2: 112 AMR/8000/1 octet-aligned=1  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|     0:  97 GSM-HR-08  -> rc=0 | ||||
|     1:   0 PCMU  -> rc=0 | ||||
|     2:  96 AMR octet-aligned=1  -> rc=0 | ||||
| @@ -1405,10 +1579,15 @@ Testing mgcp_codec_pt_translate() | ||||
|     Codec decision result: | ||||
|     conn[0]: codec:PCMU, pt:0 | ||||
|     conn[1]: codec:PCMU, pt:0 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0 | ||||
|  - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97 | ||||
|  - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111 | ||||
|  - mgcp_codec_decide(&conn[1], &conn[0]): | ||||
|     Codec decision result: | ||||
|     conn[1]: codec:GSM-HR-08, pt:97 | ||||
|     conn[0]: codec:GSM-HR-08, pt:111 | ||||
|  ===> SUCCESS: codec decision as expected! | ||||
| #14: test whether channel number matching is waterproof | ||||
|  - add codecs on conn0: | ||||
|     0: 111 GSM-HR-08/8000  -> rc=0 | ||||
|     1: 112 GSM-HR-08/8000/2  -> rc=-22 | ||||
|     2: 113 GSM-HR-08/8000/3  -> rc=-22 | ||||
|  - add codecs on conn1: | ||||
|     0: 122 GSM-HR-08/8000/2  -> rc=-22 | ||||
|   | ||||
| @@ -15,6 +15,7 @@ AM_CFLAGS = \ | ||||
|  | ||||
| AM_LDFLAGS = \ | ||||
| 	$(COVERAGE_LDFLAGS) \ | ||||
| 	-no-install \ | ||||
| 	$(NULL) | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
|   | ||||
| @@ -73,7 +73,7 @@ static struct msgb *from_str(const char *str) | ||||
| 	return msg; | ||||
| } | ||||
|  | ||||
| static struct mgcp_client_conf conf; | ||||
| static struct mgcp_client_conf *conf; | ||||
| struct mgcp_client *mgcp = NULL; | ||||
|  | ||||
| static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment, | ||||
| @@ -162,7 +162,7 @@ void test_mgcp_msg(void) | ||||
|  | ||||
| 	if (mgcp) | ||||
| 		talloc_free(mgcp); | ||||
| 	mgcp = mgcp_client_init(ctx, &conf); | ||||
| 	mgcp = mgcp_client_init(ctx, conf); | ||||
|  | ||||
| 	printf("\n"); | ||||
|  | ||||
| @@ -315,7 +315,7 @@ void test_mgcp_msg(void) | ||||
| 	msgb_free(msg); | ||||
| } | ||||
|  | ||||
| void test_mgcp_client_cancel() | ||||
| void test_mgcp_client_cancel(void) | ||||
| { | ||||
| 	mgcp_trans_id_t trans_id; | ||||
| 	struct msgb *msg; | ||||
| @@ -339,7 +339,7 @@ void test_mgcp_client_cancel() | ||||
|  | ||||
| 	if (mgcp) | ||||
| 		talloc_free(mgcp); | ||||
| 	mgcp = mgcp_client_init(ctx, &conf); | ||||
| 	mgcp = mgcp_client_init(ctx, conf); | ||||
|  | ||||
| 	msg = mgcp_msg_gen(mgcp, &mgcp_msg); | ||||
| 	trans_id = mgcp_msg_trans_id(msg); | ||||
| @@ -487,7 +487,7 @@ static struct sdp_section_start_test sdp_section_start_tests[] = { | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| void test_sdp_section_start() | ||||
| void test_sdp_section_start(void) | ||||
| { | ||||
| 	int i; | ||||
| 	int failures = 0; | ||||
| @@ -630,7 +630,7 @@ void test_mgcp_client_e1_epname(void) | ||||
|  | ||||
| 	if (mgcp) | ||||
| 		talloc_free(mgcp); | ||||
| 	mgcp = mgcp_client_init(ctx, &conf); | ||||
| 	mgcp = mgcp_client_init(ctx, conf); | ||||
|  | ||||
| 	/* Valid endpoint names */ | ||||
| 	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 1, 15, 64, 0); | ||||
| @@ -697,7 +697,7 @@ int main(int argc, char **argv) | ||||
|  | ||||
| 	log_set_category_filter(osmo_stderr_target, DLMGCP, 1, LOGL_DEBUG); | ||||
|  | ||||
| 	mgcp_client_conf_init(&conf); | ||||
| 	conf = mgcp_client_conf_alloc(ctx); | ||||
|  | ||||
| 	test_mgcp_msg(); | ||||
| 	test_mgcp_client_cancel(); | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw' | ||||
| DLMGCP MGW(mgw) Message buffer to small, can not generate MGCP message (SDP) | ||||
| DLMGCP MGW(mgw) Message buffer too small, can not generate MGCP message (SDP) | ||||
| DLMGCP MGW(mgw) Failed to add SDP, can not generate MGCP message | ||||
|  | ||||
| test_mgcp_client_cancel(): | ||||
| DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw' | ||||
| @@ -10,6 +11,7 @@ DLMGCP MGW(mgw) Cannot cancel, no such transaction: 1 | ||||
| - cancel succeeds | ||||
| DLMGCP MGW(mgw) Canceled transaction 1 | ||||
| - late response gets discarded | ||||
| DLMGCP MGW(mgw) MGCP link to MGW now considered UP | ||||
| DLMGCP MGW(mgw) MGCP client: Rx 200 1 OK | ||||
| DLMGCP MGW(mgw) Cannot find matching MGCP transaction for trans_id 1 | ||||
| - canceling again does nothing | ||||
|   | ||||
		Reference in New Issue
	
	Block a user