mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-03 21:43:32 +00:00 
			
		
		
		
	Compare commits
	
		
			255 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					6ccdaa243f | ||
| 
						 | 
					ace0b946ff | ||
| 
						 | 
					a34036c296 | ||
| 
						 | 
					9fec391d6e | ||
| 
						 | 
					a835fa85d7 | ||
| 
						 | 
					2c66c15227 | ||
| 
						 | 
					92a73cd637 | ||
| 
						 | 
					6e2795bfbf | ||
| 
						 | 
					276b595361 | ||
| 
						 | 
					3c293b418b | ||
| 
						 | 
					41425e9b85 | ||
| 
						 | 
					31682a3274 | ||
| 
						 | 
					870f94b493 | ||
| 
						 | 
					242e9ada91 | ||
| 
						 | 
					9b444fd417 | ||
| 
						 | 
					e9473f610e | ||
| 
						 | 
					53002b3a00 | ||
| 
						 | 
					a098b65252 | ||
| 
						 | 
					78b0055e5e | ||
| 
						 | 
					648bec3982 | ||
| 
						 | 
					c2a8f59560 | ||
| 
						 | 
					38404969df | ||
| 
						 | 
					19c430feba | ||
| 
						 | 
					3ba409558e | ||
| 
						 | 
					60be627557 | ||
| 
						 | 
					8cfe7df3f1 | ||
| 
						 | 
					ba94b6d7be | ||
| 
						 | 
					30e01355f6 | ||
| 
						 | 
					06624e13d4 | ||
| 
						 | 
					19539866e5 | ||
| 
						 | 
					1dc2dcebbf | ||
| 
						 | 
					6049a63d1d | ||
| 
						 | 
					3ef8e76534 | ||
| 
						 | 
					fbf07f3f69 | ||
| 
						 | 
					de133113a0 | ||
| 
						 | 
					6d0a59a1bf | ||
| 
						 | 
					2491401932 | ||
| 
						 | 
					fbbe8f2f98 | ||
| 
						 | 
					71d42e778a | ||
| 
						 | 
					8a2a1b22fe | ||
| 
						 | 
					a790f0c082 | ||
| 
						 | 
					0ab152b2c6 | ||
| 
						 | 
					2741d6bb0e | ||
| 
						 | 
					74d0e5c318 | ||
| 
						 | 
					c4ef4a21c6 | ||
| 
						 | 
					ee2f33bf9a | ||
| 
						 | 
					531470a0ca | ||
| 
						 | 
					9dc73593a3 | ||
| 
						 | 
					ae6858bddf | ||
| 
						 | 
					99d4d368e8 | ||
| 
						 | 
					8667d5169d | ||
| 
						 | 
					1add5a53cb | ||
| 
						 | 
					729bf3e45a | ||
| 
						 | 
					2f34b53b5b | ||
| 
						 | 
					ad79f9eb99 | ||
| 
						 | 
					9e494e67c7 | ||
| 
						 | 
					5322473d04 | ||
| 
						 | 
					c57ad7ff9a | ||
| 
						 | 
					c59b5c533d | ||
| 
						 | 
					0653cc8a7c | ||
| 
						 | 
					246233d0d4 | ||
| 
						 | 
					a910a81b7c | ||
| 
						 | 
					c8acee2234 | ||
| 
						 | 
					55863e42c1 | ||
| 
						 | 
					6177cae2a2 | ||
| 
						 | 
					6af3ccf65d | ||
| 
						 | 
					03cb5f3397 | ||
| 
						 | 
					113141d4f9 | ||
| 
						 | 
					993ea6be7a | ||
| 
						 | 
					889fe7f203 | ||
| 
						 | 
					efd09d0b77 | ||
| 
						 | 
					ffbc618a13 | ||
| 
						 | 
					563ffc51b5 | ||
| 
						 | 
					51b42ff2e1 | ||
| 
						 | 
					7a755be4c2 | ||
| 
						 | 
					06ea49159e | ||
| 
						 | 
					869b21c869 | ||
| 
						 | 
					37a808c5a5 | ||
| 
						 | 
					24b8c0ce92 | ||
| 
						 | 
					fe67e094ad | ||
| 
						 | 
					fbcf39976e | ||
| 
						 | 
					4863591c23 | ||
| 
						 | 
					827da2c867 | ||
| 
						 | 
					efe1571b7b | ||
| 
						 | 
					6521fc0695 | ||
| 
						 | 
					f5531845bc | ||
| 
						 | 
					13fae78b32 | ||
| 
						 | 
					b7d395d83a | ||
| 
						 | 
					6fbbeec064 | ||
| 
						 | 
					0ffa3bdc45 | ||
| 
						 | 
					ca8639dc05 | ||
| 
						 | 
					8d6a193c1a | ||
| 
						 | 
					58a1ba85c7 | ||
| 
						 | 
					b0c05aa3a9 | ||
| 
						 | 
					0996a1e4ae | ||
| 
						 | 
					7e9ddc9904 | ||
| 
						 | 
					04bbb9de3e | ||
| 
						 | 
					bea56788cb | ||
| 
						 | 
					48bcc2ee12 | ||
| 
						 | 
					080935a8c7 | ||
| 
						 | 
					a466f57007 | ||
| 
						 | 
					41f77d8018 | ||
| 
						 | 
					e674345e29 | ||
| 
						 | 
					3f8139c55f | ||
| 
						 | 
					98c09b3f30 | ||
| 
						 | 
					7462b95829 | ||
| 
						 | 
					7a64182f9a | ||
| 
						 | 
					d19de2ee80 | ||
| 
						 | 
					08eb9352ab | ||
| 
						 | 
					0b79d21c7f | ||
| 
						 | 
					bce5f29265 | ||
| 
						 | 
					3d5a2dd19f | ||
| 
						 | 
					7f90ddb519 | ||
| 
						 | 
					c66ab2c4c3 | ||
| 
						 | 
					f53796c1fe | ||
| 
						 | 
					14b27a8893 | ||
| 
						 | 
					21be42abed | ||
| 
						 | 
					2d681fd84c | ||
| 
						 | 
					c39b1bffec | ||
| 
						 | 
					62612e8575 | ||
| 
						 | 
					af932ce3bc | ||
| 
						 | 
					265b0a8045 | ||
| 
						 | 
					74390c521e | ||
| 
						 | 
					abfb858f0c | ||
| 
						 | 
					6500d72aaf | ||
| 
						 | 
					d6877eb8f9 | ||
| 
						 | 
					ebb9bf3f12 | ||
| 
						 | 
					61cf9bb5f1 | ||
| 
						 | 
					a7152e055a | ||
| 
						 | 
					63866009e2 | ||
| 
						 | 
					dab89af070 | ||
| 
						 | 
					eebbf2b1fc | ||
| 
						 | 
					e885bc5c24 | ||
| 
						 | 
					173dc129fc | ||
| 
						 | 
					3abced8d64 | ||
| 
						 | 
					a48ff4a738 | ||
| 
						 | 
					6c92f9d83e | ||
| 
						 | 
					9852e22101 | ||
| 
						 | 
					b141cccbfb | ||
| 
						 | 
					ec45068972 | ||
| 
						 | 
					832bcdf631 | ||
| 
						 | 
					73f9c02f49 | ||
| 
						 | 
					6a25a61142 | ||
| 
						 | 
					1dbbed169a | ||
| 
						 | 
					a8f27abe12 | ||
| 
						 | 
					ca2aec0235 | ||
| 
						 | 
					e827831514 | ||
| 
						 | 
					923d60bb12 | ||
| 
						 | 
					f2bf8dc8c8 | ||
| 
						 | 
					055ded74de | ||
| 
						 | 
					3ff71284fa | ||
| 
						 | 
					cc0b97e197 | ||
| 
						 | 
					8c69e29820 | ||
| 
						 | 
					843d9038ce | ||
| 
						 | 
					d071a30238 | ||
| 
						 | 
					3ab8ca4d84 | ||
| 
						 | 
					23f4048b57 | ||
| 
						 | 
					401b740ccd | ||
| 
						 | 
					a468b0f57f | ||
| 
						 | 
					b0cfa7272e | ||
| 
						 | 
					683e05f60b | ||
| 
						 | 
					16b637bf1b | ||
| 
						 | 
					2698540c1e | ||
| 
						 | 
					d2f5e69d3e | ||
| 
						 | 
					ce64f18587 | ||
| 
						 | 
					667fa59b0c | ||
| 
						 | 
					5a6220f43b | ||
| 
						 | 
					7c6dd3c2c3 | ||
| 
						 | 
					740af6ed44 | ||
| 
						 | 
					782d607962 | ||
| 
						 | 
					50e52e45f9 | ||
| 
						 | 
					a2d10216ea | ||
| 
						 | 
					f54eb96338 | ||
| 
						 | 
					a2b1c5e6f6 | ||
| 
						 | 
					c5c1430a1c | ||
| 
						 | 
					f7de9aea7b | ||
| 
						 | 
					199c5e965e | ||
| 
						 | 
					06a49fc04b | ||
| 
						 | 
					6349bd422a | ||
| 
						 | 
					796a4a1325 | ||
| 
						 | 
					2cf6652ba2 | ||
| 
						 | 
					9b508f6b53 | ||
| 
						 | 
					6e26c70f46 | ||
| 
						 | 
					9a34592c09 | ||
| 
						 | 
					7eb6f2cb56 | ||
| 
						 | 
					83fd8a5692 | ||
| 
						 | 
					17058484d1 | ||
| 
						 | 
					166077ea48 | ||
| 
						 | 
					0c6c3c1da6 | ||
| 
						 | 
					fe9a1fe03b | ||
| 
						 | 
					1e4a34d45e | ||
| 
						 | 
					189f29e939 | ||
| 
						 | 
					d2ce444008 | ||
| 
						 | 
					ef9420adf5 | ||
| 
						 | 
					cb96e05a91 | ||
| 
						 | 
					f6387dc766 | ||
| 
						 | 
					1b1d7ed98f | ||
| 
						 | 
					df7d97e4b4 | ||
| 
						 | 
					85978dadab | ||
| 
						 | 
					9aaaab6b3b | ||
| 
						 | 
					c1ad553d86 | ||
| 
						 | 
					c1bf4694e7 | ||
| 
						 | 
					1442c5a99e | ||
| 
						 | 
					dac2ca7e21 | ||
| 
						 | 
					375252279c | ||
| 
						 | 
					295570c631 | ||
| 
						 | 
					a93c6e9263 | ||
| 
						 | 
					30907dc9d8 | ||
| 
						 | 
					14f8a08f44 | ||
| 
						 | 
					c63f15a9a7 | ||
| 
						 | 
					ca538fc5eb | ||
| 
						 | 
					6be2c49538 | ||
| 
						 | 
					91088c305f | ||
| 
						 | 
					900cd6518a | ||
| 
						 | 
					fa810e8ccd | ||
| 
						 | 
					497611ae84 | ||
| 
						 | 
					2b89617aad | ||
| 
						 | 
					b542b0457b | ||
| 
						 | 
					120568651a | ||
| 
						 | 
					f1d301a9a6 | ||
| 
						 | 
					c9a6280c94 | ||
| 
						 | 
					b5583cde41 | ||
| 
						 | 
					6c1cd63a57 | ||
| 
						 | 
					0d7ba56f5c | ||
| 
						 | 
					3ac604e3ad | ||
| 
						 | 
					9fb8ddf00e | ||
| 
						 | 
					182ca3bad4 | ||
| 
						 | 
					f027f17dcb | ||
| 
						 | 
					ac772d8b0c | ||
| 
						 | 
					9d9f44ac71 | ||
| 
						 | 
					84274e4e9b | ||
| 
						 | 
					c5479fe086 | ||
| 
						 | 
					538d2c53d9 | ||
| 
						 | 
					8de58e79b8 | ||
| 
						 | 
					5e8d7995d1 | ||
| 
						 | 
					3239f6212e | ||
| 
						 | 
					209eb9f103 | ||
| 
						 | 
					ef6304e4a1 | ||
| 
						 | 
					21cdbfc196 | ||
| 
						 | 
					fc8067348b | ||
| 
						 | 
					9f11dc5616 | ||
| 
						 | 
					ca0818c760 | ||
| 
						 | 
					c12bfb7ffc | ||
| 
						 | 
					228e591589 | ||
| 
						 | 
					58128258b0 | ||
| 
						 | 
					e7ae69a7be | ||
| 
						 | 
					7e37ce6008 | ||
| 
						 | 
					dbe09dd515 | ||
| 
						 | 
					217d31d62f | ||
| 
						 | 
					47642f245d | ||
| 
						 | 
					cb760bdebe | ||
| 
						 | 
					e3f8bca424 | ||
| 
						 | 
					9fc8a02196 | ||
| 
						 | 
					e36b775eae | ||
| 
						 | 
					622dd61764 | 
							
								
								
									
										40
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -2,20 +2,13 @@ debian/*.log
 | 
			
		||||
*.o
 | 
			
		||||
*.lo
 | 
			
		||||
*.a
 | 
			
		||||
*.la
 | 
			
		||||
.deps
 | 
			
		||||
Makefile
 | 
			
		||||
Makefile.in
 | 
			
		||||
bscconfig.h
 | 
			
		||||
bscconfig.h.in
 | 
			
		||||
openbsc.pc
 | 
			
		||||
src/osmo-nitb/osmo-nitb
 | 
			
		||||
src/osmo-bsc_mgcp/osmo-bsc_mgcp
 | 
			
		||||
src/osmo-bsc/osmo-bsc
 | 
			
		||||
src/utils/meas_vis
 | 
			
		||||
src/utils/meas_json
 | 
			
		||||
src/utils/osmo-meas-pcap2db
 | 
			
		||||
src/utils/osmo-meas-udp2db
 | 
			
		||||
src/utils/smpp_mirror
 | 
			
		||||
src/osmo-mgw/osmo-mgw
 | 
			
		||||
*.*~
 | 
			
		||||
*.sw?
 | 
			
		||||
.libs
 | 
			
		||||
@@ -23,6 +16,7 @@ src/utils/smpp_mirror
 | 
			
		||||
*.gcda
 | 
			
		||||
*.gcno
 | 
			
		||||
*.pc
 | 
			
		||||
*~
 | 
			
		||||
 | 
			
		||||
#configure
 | 
			
		||||
aclocal.m4
 | 
			
		||||
@@ -46,32 +40,9 @@ m4/*.m4
 | 
			
		||||
.version
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# apps and app data
 | 
			
		||||
hlr.sqlite3
 | 
			
		||||
src/utils/bs11_config
 | 
			
		||||
src/ipaccess/ipaccess-config
 | 
			
		||||
src/ipaccess/abisip-find
 | 
			
		||||
src/ipaccess/ipaccess-firmware
 | 
			
		||||
src/ipaccess/ipaccess-proxy
 | 
			
		||||
src/utils/isdnsync
 | 
			
		||||
src/nat/bsc_nat
 | 
			
		||||
src/gprs/osmo-sgsn
 | 
			
		||||
src/gprs/osmo-gbproxy
 | 
			
		||||
src/gprs/osmo-gtphub
 | 
			
		||||
src/osmo-bsc_nat/osmo-bsc_nat
 | 
			
		||||
src/libcommon/gsup_test_client
 | 
			
		||||
src/osmo-msc/osmo-msc
 | 
			
		||||
 | 
			
		||||
#tests
 | 
			
		||||
tests/testsuite.dir
 | 
			
		||||
tests/*/*_test
 | 
			
		||||
# ignore compiled binaries like msc_vlr_test_foo; do not ignore
 | 
			
		||||
# msc_vlr_test_foo.{c,ok,err}, but do still ignore the corresponding .o object
 | 
			
		||||
# files:
 | 
			
		||||
tests/msc_vlr/msc_vlr_test_*
 | 
			
		||||
!tests/msc_vlr/msc_vlr_test_*.*
 | 
			
		||||
tests/msc_vlr/msc_vlr_test_*.o
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
tests/atconfig
 | 
			
		||||
tests/atlocal
 | 
			
		||||
@@ -79,10 +50,7 @@ tests/package.m4
 | 
			
		||||
tests/testsuite
 | 
			
		||||
tests/testsuite.log
 | 
			
		||||
 | 
			
		||||
gsn_restart
 | 
			
		||||
src/openbsc.cfg*
 | 
			
		||||
writtenconfig/
 | 
			
		||||
gtphub_restart_count
 | 
			
		||||
 | 
			
		||||
# manuals
 | 
			
		||||
doc/manuals/*.html
 | 
			
		||||
@@ -94,3 +62,5 @@ doc/manuals/generated/
 | 
			
		||||
doc/manuals/osmomsc-usermanual.xml
 | 
			
		||||
doc/manuals/common
 | 
			
		||||
doc/manuals/build
 | 
			
		||||
 | 
			
		||||
contrib/osmo-mgw.spec
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								Makefile.am
									
									
									
									
									
								
							@@ -9,10 +9,10 @@ AM_CPPFLAGS = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	doc \
 | 
			
		||||
	include \
 | 
			
		||||
	src \
 | 
			
		||||
	tests \
 | 
			
		||||
	doc \
 | 
			
		||||
	contrib \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
@@ -22,7 +22,13 @@ pkgconfig_DATA = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
BUILT_SOURCES = $(top_srcdir)/.version
 | 
			
		||||
EXTRA_DIST = git-version-gen osmoappdesc.py .version
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	     .version \
 | 
			
		||||
	     contrib/osmo-mgw.spec.in \
 | 
			
		||||
	     debian \
 | 
			
		||||
	     git-version-gen \
 | 
			
		||||
	     osmoappdesc.py \
 | 
			
		||||
	     $(NULL)
 | 
			
		||||
 | 
			
		||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
 | 
			
		||||
	--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								configure.ac
									
									
									
									
									
								
							@@ -9,6 +9,8 @@ AC_CONFIG_AUX_DIR([.])
 | 
			
		||||
AM_INIT_AUTOMAKE([dist-bzip2])
 | 
			
		||||
AC_CONFIG_TESTDIR(tests)
 | 
			
		||||
 | 
			
		||||
CFLAGS="$CFLAGS -std=gnu11"
 | 
			
		||||
 | 
			
		||||
dnl kernel style compile messages
 | 
			
		||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 | 
			
		||||
 | 
			
		||||
@@ -22,6 +24,11 @@ AC_PROG_CC
 | 
			
		||||
AC_PROG_INSTALL
 | 
			
		||||
LT_INIT
 | 
			
		||||
 | 
			
		||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
 | 
			
		||||
AS_CASE(["$LD"],[*clang*],
 | 
			
		||||
  [AS_CASE(["${host_os}"],
 | 
			
		||||
     [*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
 | 
			
		||||
 | 
			
		||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
 | 
			
		||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
 | 
			
		||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
 | 
			
		||||
@@ -38,11 +45,17 @@ dnl checks for libraries
 | 
			
		||||
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
 | 
			
		||||
AC_SUBST(LIBRARY_DL)
 | 
			
		||||
 | 
			
		||||
AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
 | 
			
		||||
AC_SUBST(LIBRARY_DLSYM)
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.4.0)
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.1.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.1.0)
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(sanitize,
 | 
			
		||||
	[AS_HELP_STRING(
 | 
			
		||||
@@ -76,21 +89,10 @@ fi
 | 
			
		||||
 | 
			
		||||
dnl Checks for typedefs, structures and compiler characteristics
 | 
			
		||||
 | 
			
		||||
# The following test is taken from WebKit's webkit.m4
 | 
			
		||||
saved_CFLAGS="$CFLAGS"
 | 
			
		||||
CFLAGS="$CFLAGS -fvisibility=hidden "
 | 
			
		||||
AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
 | 
			
		||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])],
 | 
			
		||||
      [ AC_MSG_RESULT([yes])
 | 
			
		||||
        SYMBOL_VISIBILITY="-fvisibility=hidden"],
 | 
			
		||||
        AC_MSG_RESULT([no]))
 | 
			
		||||
CFLAGS="$saved_CFLAGS"
 | 
			
		||||
AC_SUBST(SYMBOL_VISIBILITY)
 | 
			
		||||
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Werror=implicit], [CFLAGS="$CFLAGS -Werror=implicit"])
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Werror=maybe-uninitialized], [CFLAGS="$CFLAGS -Werror=maybe-uninitialized"])
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Werror=memset-transposed-args], [CFLAGS="$CFLAGS -Werror=memset-transposed-args"])
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Werror=null-dereference], [CFLAGS="$CFLAGS -Werror=null-dereference"])
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Wnull-dereference], [CFLAGS="$CFLAGS -Wnull-dereference"])
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Werror=sizeof-array-argument], [CFLAGS="$CFLAGS -Werror=sizeof-array-argument"])
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Werror=sizeof-pointer-memaccess], [CFLAGS="$CFLAGS -Werror=sizeof-pointer-memaccess"])
 | 
			
		||||
 | 
			
		||||
@@ -211,4 +213,5 @@ AC_OUTPUT(
 | 
			
		||||
    doc/manuals/Makefile
 | 
			
		||||
    contrib/Makefile
 | 
			
		||||
    contrib/systemd/Makefile
 | 
			
		||||
    contrib/osmo-mgw.spec
 | 
			
		||||
    Makefile)
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,6 @@ osmo-build-dep.sh libosmo-netif
 | 
			
		||||
# Additional configure options and depends
 | 
			
		||||
CONFIG=""
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ]; then
 | 
			
		||||
	osmo-build-dep.sh osmo-gsm-manuals
 | 
			
		||||
	CONFIG="--enable-manuals"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
@@ -56,11 +55,12 @@ LD_LIBRARY_PATH="$inst/lib" $MAKE check \
 | 
			
		||||
  || cat-testlogs.sh
 | 
			
		||||
LD_LIBRARY_PATH="$inst/lib" \
 | 
			
		||||
  DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests $CONFIG" \
 | 
			
		||||
  $MAKE distcheck \
 | 
			
		||||
  $MAKE $PARALLEL_MAKE distcheck \
 | 
			
		||||
  || cat-testlogs.sh
 | 
			
		||||
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
 | 
			
		||||
	make -C "$base/doc/manuals" publish
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
$MAKE $PARALLEL_MAKE maintainer-clean
 | 
			
		||||
osmo-clean-workspace.sh
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										137
									
								
								contrib/osmo-mgw.spec.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								contrib/osmo-mgw.spec.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
#
 | 
			
		||||
# spec file for package osmo-mgw
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
 | 
			
		||||
#
 | 
			
		||||
# All modifications and additions to the file contributed by third parties
 | 
			
		||||
# remain the property of their copyright owners, unless otherwise agreed
 | 
			
		||||
# upon. The license for this file, and modifications and additions to the
 | 
			
		||||
# file, is the same license as for the pristine package itself (unless the
 | 
			
		||||
# license for the pristine package is not an Open Source License, in which
 | 
			
		||||
# case the license is the MIT License). An "Open Source License" is a
 | 
			
		||||
# license that conforms to the Open Source Definition (Version 1.9)
 | 
			
		||||
# published by the Open Source Initiative.
 | 
			
		||||
 | 
			
		||||
# Please submit bugfixes or comments via http://bugs.opensuse.org/
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
Name:           osmo-mgw
 | 
			
		||||
Version:        @VERSION@
 | 
			
		||||
Release:        0
 | 
			
		||||
Summary:        Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
 | 
			
		||||
License:        AGPL-3.0-or-later AND GPL-2.0-or-later
 | 
			
		||||
Group:          Hardware/Mobile
 | 
			
		||||
URL:            https://osmocom.org/projects/osmo-mgw
 | 
			
		||||
Source:         %{name}-%{version}.tar.xz
 | 
			
		||||
BuildRequires:  automake >= 1.9
 | 
			
		||||
BuildRequires:  libtool >= 2
 | 
			
		||||
BuildRequires:  pkgconfig >= 0.20
 | 
			
		||||
%if 0%{?suse_version}
 | 
			
		||||
BuildRequires:  systemd-rpm-macros
 | 
			
		||||
%endif
 | 
			
		||||
BuildRequires:  pkgconfig(libosmo-netif) >= 1.1.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocore) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoctrl) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmogsm) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmovty) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocoding) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoabis) >= 1.1.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmotrau) >= 1.1.0
 | 
			
		||||
%{?systemd_requires}
 | 
			
		||||
 | 
			
		||||
%description
 | 
			
		||||
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
 | 
			
		||||
 | 
			
		||||
%package -n libosmo-mgcp-client8
 | 
			
		||||
Summary:        Osmocom's Media Gateway Control Protocol client library
 | 
			
		||||
Group:          System/Libraries
 | 
			
		||||
 | 
			
		||||
%description -n libosmo-mgcp-client8
 | 
			
		||||
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-client8 = %{version}
 | 
			
		||||
 | 
			
		||||
%description -n libosmo-mgcp-client-devel
 | 
			
		||||
Osmocom's Media Gateway Control Protocol client librarary.
 | 
			
		||||
 | 
			
		||||
This subpackage contains libraries and header files for developing
 | 
			
		||||
applications that want to make use of libosmo-mgcp-client.
 | 
			
		||||
 | 
			
		||||
%package -n libosmo-mgcp-devel
 | 
			
		||||
Summary:        Development files for Osmocom's Media Gateway server library
 | 
			
		||||
Group:          Development/Libraries/C and C++
 | 
			
		||||
 | 
			
		||||
%description -n libosmo-mgcp-devel
 | 
			
		||||
Osmocom's Media Gateway Control Protocol server library.
 | 
			
		||||
 | 
			
		||||
This subpackage contains libraries and header files for developing
 | 
			
		||||
applications that want to make use of libosmo-mgcp.
 | 
			
		||||
 | 
			
		||||
%prep
 | 
			
		||||
%setup -q
 | 
			
		||||
 | 
			
		||||
%build
 | 
			
		||||
echo "%{version}" >.tarball-version
 | 
			
		||||
autoreconf -fi
 | 
			
		||||
%configure \
 | 
			
		||||
  --disable-static \
 | 
			
		||||
  --docdir=%{_docdir}/%{name} \
 | 
			
		||||
  --with-systemdsystemunitdir=%{_unitdir}
 | 
			
		||||
 | 
			
		||||
make %{?_smp_mflags}
 | 
			
		||||
 | 
			
		||||
%install
 | 
			
		||||
%make_install
 | 
			
		||||
find %{buildroot} -type f -name "*.la" -delete -print
 | 
			
		||||
 | 
			
		||||
%check
 | 
			
		||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
 | 
			
		||||
 | 
			
		||||
%post   -n libosmo-mgcp-client8 -p /sbin/ldconfig
 | 
			
		||||
%postun -n libosmo-mgcp-client8 -p /sbin/ldconfig
 | 
			
		||||
 | 
			
		||||
%if 0%{?suse_version}
 | 
			
		||||
%preun
 | 
			
		||||
%service_del_preun osmo-mgw.service
 | 
			
		||||
 | 
			
		||||
%postun
 | 
			
		||||
%service_del_postun osmo-mgw.service
 | 
			
		||||
 | 
			
		||||
%pre
 | 
			
		||||
%service_add_pre osmo-mgw.service
 | 
			
		||||
 | 
			
		||||
%post
 | 
			
		||||
%service_add_post osmo-mgw.service
 | 
			
		||||
%endif
 | 
			
		||||
 | 
			
		||||
%files
 | 
			
		||||
%license COPYING
 | 
			
		||||
%doc AUTHORS README
 | 
			
		||||
%dir %{_docdir}/%{name}/examples
 | 
			
		||||
%dir %{_docdir}/%{name}/examples/osmo-mgw
 | 
			
		||||
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw.cfg
 | 
			
		||||
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
 | 
			
		||||
%{_bindir}/osmo-mgw
 | 
			
		||||
%{_unitdir}/osmo-mgw.service
 | 
			
		||||
%dir %{_sysconfdir}/osmocom
 | 
			
		||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg
 | 
			
		||||
 | 
			
		||||
%files -n libosmo-mgcp-client8
 | 
			
		||||
%{_libdir}/libosmo-mgcp-client.so.6*
 | 
			
		||||
 | 
			
		||||
%files -n libosmo-mgcp-client-devel
 | 
			
		||||
%{_libdir}/libosmo-mgcp-client.so
 | 
			
		||||
%{_libdir}/pkgconfig/libosmo-mgcp-client.pc
 | 
			
		||||
%dir %{_includedir}/osmocom
 | 
			
		||||
%dir %{_includedir}/osmocom/mgcp_client
 | 
			
		||||
%{_includedir}/osmocom/mgcp_client/*.h
 | 
			
		||||
 | 
			
		||||
%files -n libosmo-mgcp-devel
 | 
			
		||||
%dir %{_includedir}/osmocom
 | 
			
		||||
%dir %{_includedir}/osmocom/mgcp
 | 
			
		||||
%{_includedir}/osmocom/mgcp/*.h
 | 
			
		||||
 | 
			
		||||
%changelog
 | 
			
		||||
							
								
								
									
										308
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										308
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,311 @@
 | 
			
		||||
osmo-mgw (1.8.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * Fix number of endpoints of default trunk
 | 
			
		||||
  * Add CTRL interface to osmo-mgw
 | 
			
		||||
  * Update per-trunk global packet/byte counters in real-time
 | 
			
		||||
  * remove accidential TODO-RELEASE entry
 | 
			
		||||
  * mgcp_protocol: Avoid code duplication between virtual + other trunks
 | 
			
		||||
  * osmo-mgw.spec.in: Use %config(noreplace) to retain current config file
 | 
			
		||||
  * mgcp_client_init(): consider "talloc returns NULL" case
 | 
			
		||||
  * mgcp_client_pending_add(): Consider "talloc returns NULL" case
 | 
			
		||||
  * libosmo-mgcp-client: fix memleak in case if no response is received
 | 
			
		||||
  * debian/control + SPEC: Add missing build dependency to libosmo-abis
 | 
			
		||||
  * osmo-mgw.spec.in: Fix dependency to libosmoabis
 | 
			
		||||
  * osmo-mgw.spec.in: Add missing dependency to libosmotrau
 | 
			
		||||
  * Add example osmo-mgw configuration file for Abis/E1
 | 
			
		||||
  * use osmo_fd_setup() whenever applicable
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * fix vty dump_trunk: start from zero, do not omit first CONN
 | 
			
		||||
  * allow larger MGCP client wqueue: 10 -> 1024
 | 
			
		||||
  * add osmo-mgw --vty-ref-xml: dump VTY ref XML to stdout
 | 
			
		||||
  * manuals: generate vty reference xml at build time
 | 
			
		||||
  * refactor: use msgb to receive, pass and send RTP packets
 | 
			
		||||
  * change timer T2427001 to X2427
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * doc: do not bind osmo-mgw to random ip-address
 | 
			
		||||
  * cosmetic: remove excess newlines
 | 
			
		||||
  * mgcp: remove unused callback pointer
 | 
			
		||||
  * mgcp: find better locations for LOGPCONN and LOGPENDP
 | 
			
		||||
  * vty: fix unreachable code (error msg on trunk alloc fail)
 | 
			
		||||
  * mgcp_vty: fix indentation
 | 
			
		||||
  * osmo-mgw: rename struct mgcp_trunk_config and symbol tcfg
 | 
			
		||||
  * mgcp_vty: fix indentation in VTY config write
 | 
			
		||||
  * osmo-mgw: refactor endpoint and trunk handling
 | 
			
		||||
  * mgcp_trunk: remove audio_name and audio_payload
 | 
			
		||||
  * ratectr: move rate counter definitions into mgcp_ratectr.h
 | 
			
		||||
  * cosmetic: fix doxygen
 | 
			
		||||
  * cosmetic: fix doxygen
 | 
			
		||||
  * cosmetic: fix doxygen for mgcp_cleanup_rtp_bridge_cb()
 | 
			
		||||
  * trunk: get rid of virt_trunk pointer
 | 
			
		||||
  * cosmetic: remove excess space
 | 
			
		||||
  * endp: move endpoint name generation into mgcp_endp.c
 | 
			
		||||
  * endp: add name generator function for E1 endpoints
 | 
			
		||||
  * mgcp_client: add docstring for mgcp_client_rtpbridge_wildcard()
 | 
			
		||||
  * mgcp_trunk: fix docstring for mgcp_trunk_alloc()
 | 
			
		||||
  * mgcp_osmux: remove unused define constants
 | 
			
		||||
  * mgcp_trunk: move enum mgcp_trunk_type to mgcp_trunk.h
 | 
			
		||||
  * mgcp_trunk: use enum type for trunk type variable
 | 
			
		||||
  * trunk: parse E1 trunk number
 | 
			
		||||
  * endp: add typeset for e1-endpoints
 | 
			
		||||
  * mgcp_conn: move struct mgcp_conn mgcp_conn.h
 | 
			
		||||
  * mgcp_internal: remove forward declaration struct mgcp_endpoint_type
 | 
			
		||||
  * endp: add E1 endpoint interlocking
 | 
			
		||||
  * endp: require domain name also for E1 endpoints
 | 
			
		||||
  * mgcp_trunk: pick trunk by number and type
 | 
			
		||||
  * mgcp_client: add function to generate e1-endpoint names
 | 
			
		||||
  * mgcp_endp: use define constant to define max number of E1 subslots
 | 
			
		||||
  * mgcp_endp.c: cosmetic: fix sourcecode formatting
 | 
			
		||||
  * mgcp_trunk: remove double check
 | 
			
		||||
  * mgcp_test: do not access endpoint array elements directly
 | 
			
		||||
  * mgcp_vty: fix endpoint number configuration
 | 
			
		||||
  * mgcp_test: remove trunk2 from unit-test
 | 
			
		||||
  * mgcp_trunk: use talloc_zero_array instead of _talloc_zero_array
 | 
			
		||||
  * mgcp_protocol: remove unused variable
 | 
			
		||||
  * mgcp_e1: finish E1 support, add E1 support from libosmoabis
 | 
			
		||||
  * get rid of mgcp_internal.h
 | 
			
		||||
  * mgcp_ratectr: fix comments in header file
 | 
			
		||||
  * mgcp_e1: use return value of e1inp_line_update()
 | 
			
		||||
  * mgcp_protocol: log when endpoint is unavailable
 | 
			
		||||
  * cosmetic: add missing new-line
 | 
			
		||||
  * mgcp_trunk: drop "trunk 0" limitation
 | 
			
		||||
  * mgcp_e1: make E1 ts initalization more debugable
 | 
			
		||||
  * mgcp_e1: remove unused struct member trunk->e1.line
 | 
			
		||||
  * mgcp_endp: use NUM_E1_TS from e1_input.h
 | 
			
		||||
  * mgcp_trunk: increase default number of virtual endpoints
 | 
			
		||||
  * cosmetic: mgcp_client_fsm: change error message.
 | 
			
		||||
  * mgcp_vty: remove remains of loopback functionality
 | 
			
		||||
  * mgcp_vty: deprecate bind early command
 | 
			
		||||
  * mgcp_e1: do not expose function  mgcp_e1_init()
 | 
			
		||||
  * mgcp_vty: add user attributes to configuration commands
 | 
			
		||||
  * overview: update section limitations.
 | 
			
		||||
  * overview: fix graph "OsmoMGW used with OsmoBSC"
 | 
			
		||||
  * overview: fix graph "OsmoMGW used with OsmoMSC"
 | 
			
		||||
  * overview: add graph to show E1 integration
 | 
			
		||||
  * configuration: remove hint towards trunk 0 limit
 | 
			
		||||
  * configuration: drop note about lackin E1 support
 | 
			
		||||
  * configuration: add note that changes to trunks need a restart
 | 
			
		||||
  * mgcp_vty: fix config write for trunk 0
 | 
			
		||||
  * mgcp_vty: add missing VTY commands for E1 trunks
 | 
			
		||||
  * mgcp_trunk: get rid of magic numbers for E1 slots
 | 
			
		||||
  * configuration: add section about E1 trunks
 | 
			
		||||
  * usermanual: add chapter about mgcp endpoints
 | 
			
		||||
  * mgcp_client: get rid of magic numbers for E1 slots
 | 
			
		||||
 | 
			
		||||
  [ Eric ]
 | 
			
		||||
  * tests: dlopen does not imply availability of dlsym..
 | 
			
		||||
  * configure.ac: fix libtool issue with clang and sanitizer
 | 
			
		||||
 | 
			
		||||
  [ Alexander Chemeris ]
 | 
			
		||||
  * vty: Prepend VTY output of counters for better visual separation.
 | 
			
		||||
  * counters: Implement more useful counters.
 | 
			
		||||
  * mgcp_network: Fix a typo in the comment bahviour -> behaviour
 | 
			
		||||
  * rtp_bridge: Demote a chatty ERROR log message to DEBUG level.
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * Use OSMO_FD_* instead of deprecated BSC_FD_*
 | 
			
		||||
  * Support setting rt-prio and cpu-affinity mask through VTY
 | 
			
		||||
  * cosmetic: Rename main talloc ctx
 | 
			
		||||
  * mgcp-client: Support IPv6 in osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr() implementation
 | 
			
		||||
  * mgcp-client: Fix trailing whitespace in mgcp_client_fsm.h
 | 
			
		||||
  * mgcp_client: Use INET6_ADDRSTRLEN to store addresses in str format
 | 
			
		||||
  * mgcp_client: Allow submitting and parsing IPv6 addr in SDP
 | 
			
		||||
  * mgcp_client: Allow setting IPv6 addresses
 | 
			
		||||
  * mgcp_client: Make MGCP_CLIENT_LOCAL_ADDR_DEFAULT IPv6 compatible
 | 
			
		||||
  * mgcp_client: Support validating IPv6 addresses in CRCX and MDCX commands
 | 
			
		||||
  * mgcp_client: Deprecate unused IPv4-only API
 | 
			
		||||
  * mgcp_client: copy back Connection Information from MDCX ACK
 | 
			
		||||
  * mgw: Fix mgcp_rtp_end field description comment
 | 
			
		||||
  * mgw: Initial IPv6 support
 | 
			
		||||
  * mgw: Introduce VTY cmd 'rtp bind-ip-v6' command
 | 
			
		||||
  * mgw: Find and store RTP conn local_addr once during CRCX handling
 | 
			
		||||
  * mgw: Announce and rebind new local address if change required during MDCX
 | 
			
		||||
  * mgw: osmux: Avoid sending packets on recvonly connection
 | 
			
		||||
  * mgw: Release endpoint after last conn times out
 | 
			
		||||
  * mgw: osmux: Fix conn watchdog timeout not updated
 | 
			
		||||
  * mgw: Don't be case-sensitive when parsing X-Osmo-IGN param
 | 
			
		||||
  * cosmetic: Fix typo in comment
 | 
			
		||||
  * mgw: Avoid logging notice message each time we receive nt param in LCO
 | 
			
		||||
  * mgw: Fix return value documentation for API mgcp_verify_call_id
 | 
			
		||||
  * cosmetic: Fix typo in comment
 | 
			
		||||
  * contrib/jenkins: Enable parallel make in make distcheck
 | 
			
		||||
  * .gitignore: Ignore new autofoo tmp files
 | 
			
		||||
  * tests: Replace deprecated API log_set_print_filename
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * contrib: import RPM spec
 | 
			
		||||
  * contrib: integrate RPM spec
 | 
			
		||||
  * Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
 | 
			
		||||
  * contrib/jenkins: don't build osmo-gsm-manuals
 | 
			
		||||
  * configure.ac: set -std=gnu11
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * libosmo-mgcp-client: fix use-after-free in mgcp_client_tx()
 | 
			
		||||
  * libosmo-mgcp-client: fix use-after-free in mgcp_msg_gen()
 | 
			
		||||
  * libosmo-mgcp: fix unsigned compared against 0 in mgcp_trunk_by_name()
 | 
			
		||||
  * libosmo-mgcp: fix unused extra argument to printf() in add_fmtp()
 | 
			
		||||
  * libosmo-mgcp: always check result of msgb_printf() in add_fmtp()
 | 
			
		||||
  * libosmo-mgcp-client: mgcp_client_tx(): return rc on error
 | 
			
		||||
  * debian/control: change maintainer to the Osmocom team / mailing list
 | 
			
		||||
  * vty: use install_lib_element() and install_lib_element_ve()
 | 
			
		||||
  * main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
 | 
			
		||||
  * fixup mgcp_trunk: increase default number of virtual endpoints
 | 
			
		||||
 | 
			
		||||
  [ Alexander Couzens ]
 | 
			
		||||
  * configure.ac: require libosmoabis + libosmotrau >= 1.0.0
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 23 Feb 2021 18:28:45 +0100
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.7.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * rename codecs_cmp() to codecs_same()
 | 
			
		||||
  * mgcp_codec: constify 'param' arg
 | 
			
		||||
  * fix crashes: don't assert on incoming RTP packet size
 | 
			
		||||
  * mgcp_send(): stop looping on conversion error
 | 
			
		||||
  * mgcp_codec: split codec_free() off of codec_init()
 | 
			
		||||
  * fix memleak: actually free strings in mgcp_codec_reset_all()
 | 
			
		||||
  * mgcp_test: extend / rewrite test_mgcp_codec_pt_translate()
 | 
			
		||||
  * test_mgcp_codec_pt_translate(): more tests
 | 
			
		||||
  * differentiate AMR octet-aligned=0 vs =1
 | 
			
		||||
  * ptmap: implicitly match  '/8000' and '/8000/1'
 | 
			
		||||
  * mgcp_codec: codec_set(): log about all possible errors
 | 
			
		||||
  * mgcp_codec_add: fix audio_name size check
 | 
			
		||||
  * explicitly free codecs in mgcp_rtp_conn_cleanup()
 | 
			
		||||
  * tweak mgcp_parse_audio_ptime_rtpmap()
 | 
			
		||||
  * SDP: store all ptmap entries
 | 
			
		||||
  * mgcp_client_fsm cleanup: Do not assert on DLCX failure
 | 
			
		||||
  * clear pending requests on MGCP failure
 | 
			
		||||
  * client: endp fsm: add notify struct, prep for cancel-notify
 | 
			
		||||
  * client: endp fsm: clear ci[] before dispatching DLCX success
 | 
			
		||||
  * client: endp fsm: allow cancelling a notify event
 | 
			
		||||
  * client: endp fsm: add osmo_mgcpc_ep_ci_ep()
 | 
			
		||||
  * accept MGCP without SDP
 | 
			
		||||
  * fix use-after-free: require new fsm deferred dealloc, check for term
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * mgcp_test: Correctly release all endpoints allocated
 | 
			
		||||
  * mgw: Allocate mgcp_conn instance under tcfg->endpoints
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * manual: Fix copy+paste error
 | 
			
		||||
  * mgcp_client: Check for osmo_fsm_register() error return value
 | 
			
		||||
  * Move fsm_mgcp_client regstration to __attribute__((contructor))
 | 
			
		||||
  * exit(2) on unsupported positional arguments on command line
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * osmoappdesc.py: switch to python 3
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Fri, 03 Jan 2020 13:35:09 +0100
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.6.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * Cosmetic: fix spaces/tabs in mgcp_requests[]
 | 
			
		||||
  * Inactive connection cleanup (disabled by default)
 | 
			
		||||
  * debian: create -doc subpackage with pdf manuals
 | 
			
		||||
  * vty: allow 0 as conn-timeout to disable it
 | 
			
		||||
  * vty: update desc of conn-timeout
 | 
			
		||||
  * manuals: update VTY documentation
 | 
			
		||||
  * "make dist" fix for: no rule to make mgcp_common.h
 | 
			
		||||
  * contrib/jenkins.sh: run "make maintainer-clean"
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * Add option to GSM HR frames to RFC5593 representation
 | 
			
		||||
  * mgcp_sdp: cosmetic: correct typo in comment
 | 
			
		||||
  * mgco_client: cosmetic: remove excess whitespace
 | 
			
		||||
  * mgcp_sdp: mgcp_sdp.c does not include own header file
 | 
			
		||||
  * mgcp_sdp: untangle parsing of a= parameters
 | 
			
		||||
  * mgcp_network: use mgcp_rtp_codec in downlink format callback
 | 
			
		||||
  * AMR: Add function to convert between bw-effient and octet aligned mode
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * log: don't spam with "can not patch PT" messages
 | 
			
		||||
  * mgcp client: allow setting conn mode for CRCX and MDCX
 | 
			
		||||
  * make codec_table public as osmo_mgcpc_codec_names
 | 
			
		||||
  * move MGW endpoint FSM from osmo-bsc to here
 | 
			
		||||
  * fix: multiple initial CRCX
 | 
			
		||||
  * constify map_codec_to_pt() ptmap arg
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * mgcp-client: whitespace cleanup in mgcp_client.c
 | 
			
		||||
  * mgcp-client: Sanitize implementation of mgcp_client_rtpbridge_wildcard
 | 
			
		||||
  * libosmo-mgcp: Use trunk type during endpoint allocation
 | 
			
		||||
  * cosmetic: Fix typos in comment
 | 
			
		||||
  * cosmetic: tests: mgcp_client_test: clean trailing whitespace
 | 
			
		||||
  * mgcp_msg: Log faulty line on Osmux parsing error
 | 
			
		||||
  * cosmetic: handle_modify_con: Fix indentation level
 | 
			
		||||
  * Introduce log fmt helpers LOGPENDP and LOGPCONN
 | 
			
		||||
  * create_response_with_sdp: Fix inclusion of X-Osmux
 | 
			
		||||
  * osmux: Cleanup of CID alloc pool APIs
 | 
			
		||||
  * mgcp_osmux.c: osmux_enable_endpoint: Fix incorrect return check
 | 
			
		||||
  * osmux: Log osmux socket during osmux_init
 | 
			
		||||
  * mgcp-cli: Change osmo_mgcpc_ep_fsm name to avoid collision with old osmo-bsc
 | 
			
		||||
  * osmux: Document func and return different rc upon osmux init failure
 | 
			
		||||
  * osmux: Fix reception of legacy dummy payloads
 | 
			
		||||
  * osmux: Use LOGPCONN in several log calls
 | 
			
		||||
  * cosmetic: mgcp_udp_send: Document port param is in network byte order
 | 
			
		||||
  * cosmetic: osmux: Document network byte order in port variable
 | 
			
		||||
  * vty: Allow enabling Osmux
 | 
			
		||||
  * osmux: Allocate CID during CRCX
 | 
			
		||||
  * osmux: Introduce mgcp_conn_rtp_is_osmux() helper
 | 
			
		||||
  * osmux: Mark conn_rtp->type as osmux during CRCX
 | 
			
		||||
  * mgcp-cli: Allow submitting X-Osmux on CRCX request
 | 
			
		||||
  * mgcp-cli: Parse X-Osmux on CRCX response
 | 
			
		||||
  * osmux: Handle Osmux MGCP extension in MDCX messages
 | 
			
		||||
  * mgcp-cli: Allow submitting X-Osmux on MDCX request
 | 
			
		||||
  * mgcp-cli: Parse X-Osmux on MDCX response
 | 
			
		||||
  * osmux: Drop unneeded OSMUX_STATE_NEGOTIATING
 | 
			
		||||
  * mgcp-cli: endpoint_fsm: Add API to retrieve Osmux CID from MGW
 | 
			
		||||
  * osmux: Provide correct local port during mgcp resp
 | 
			
		||||
  * osmux: Use remote port to send osmux frames
 | 
			
		||||
  * osmux: Improve logging around osmux enabling events
 | 
			
		||||
  * osmux: Delay osmux enable of conn until remote addr is configured by MDCX
 | 
			
		||||
  * osmux: Redo read/write osmux glue code to have data routed correctly
 | 
			
		||||
  * mgw, mgcp-li: Handle X-Osmux param name as case insensitive
 | 
			
		||||
  * osmux: Use DUMMY ft msg as per Osmux spec
 | 
			
		||||
  * osmux: Fix loopback for Osmux connections
 | 
			
		||||
  * osmux: Fix CID release for non-enabled connections
 | 
			
		||||
  * osmux: Fix hardcoded rtp payload_type 98 in osmux conn
 | 
			
		||||
  * mgcp-cli: Validate osmux cid value during mgcp_msg_gen
 | 
			
		||||
  * mgw: Support uppercase LCO options
 | 
			
		||||
  * mgw: Support lowercase header parameters
 | 
			
		||||
  * mgcp-cli: Support lowercase header parameters
 | 
			
		||||
  * mgw: Support receiving uppercase connection mode
 | 
			
		||||
  * mgw: Support receiving lowercase LCO codec
 | 
			
		||||
  * mgw: Make check of duplicated LCO fields case insensitive
 | 
			
		||||
  * mgw: Allow receiving lowercase MGCP header keyword
 | 
			
		||||
  * mgw: Allow receiving lowercase X-Osmo-Ign Callid field
 | 
			
		||||
  * mgw: Allow receiving uppercase noanswer keyword
 | 
			
		||||
  * doc: X-Osmo-IGN: small formatting and typo fixes
 | 
			
		||||
  * doc: Add Osmux documentation to OsmoMGW User Manual
 | 
			
		||||
  * Catch unsigned integer MGCP parsing errors with strtoul
 | 
			
		||||
  * Fix return variable of strtoul()
 | 
			
		||||
  * Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds
 | 
			
		||||
  * configure.ac: Require libosmo-netif 0.6.0
 | 
			
		||||
 | 
			
		||||
  [ Alexander Couzens ]
 | 
			
		||||
  * mgcp_internal: LOGPENDP: ensure *endp is not NULL
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * handle NULL return of rate_ctr_group_alloc()
 | 
			
		||||
  * update .gitignore
 | 
			
		||||
  * mgcp_sdp: Don't check if an unsigned int is below 0
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * configure.ac: drop useless check for -fvisibility=hidden
 | 
			
		||||
 | 
			
		||||
  [ Daniel Willmann ]
 | 
			
		||||
  * manuals: Add script to regenerate vty/counter documentation
 | 
			
		||||
  * manuals: Update vty/counter documentation
 | 
			
		||||
 | 
			
		||||
  [ Hoernchen ]
 | 
			
		||||
  * turn -Werror=null-dereference into a warning
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Wed, 07 Aug 2019 16:52:58 +0200
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.5.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -1,13 +1,15 @@
 | 
			
		||||
Source: osmo-mgw
 | 
			
		||||
Section: net
 | 
			
		||||
Priority: extra
 | 
			
		||||
Maintainer: Alexander Couzens <lynxis@fe80.eu>
 | 
			
		||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
 | 
			
		||||
Build-Depends: debhelper (>=9),
 | 
			
		||||
               dh-autoreconf,
 | 
			
		||||
               pkg-config,
 | 
			
		||||
               autotools-dev,
 | 
			
		||||
               libosmocore-dev,
 | 
			
		||||
               libosmo-netif-dev
 | 
			
		||||
               libosmocore-dev (>= 1.5.0),
 | 
			
		||||
               libosmo-netif-dev (>= 1.1.0),
 | 
			
		||||
               libosmo-abis-dev (>= 1.1.0),
 | 
			
		||||
               osmo-gsm-manuals-dev (>= 1.1.0)
 | 
			
		||||
Standards-Version: 3.9.8
 | 
			
		||||
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
 | 
			
		||||
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
 | 
			
		||||
@@ -19,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-client5
 | 
			
		||||
Package: libosmo-mgcp-client8
 | 
			
		||||
Section: libs
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
@@ -31,5 +33,14 @@ Package: libosmo-mgcp-client-dev
 | 
			
		||||
Section: libdevel
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
Depends: libosmo-mgcp-client5 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Depends: libosmo-mgcp-client8 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
 | 
			
		||||
 | 
			
		||||
Package: osmo-mgw-doc
 | 
			
		||||
Architecture: all
 | 
			
		||||
Section: doc
 | 
			
		||||
Priority: optional
 | 
			
		||||
Depends: ${misc:Depends}
 | 
			
		||||
Description: ${misc:Package} PDF documentation
 | 
			
		||||
 Various manuals: user manual, VTY reference manual and/or
 | 
			
		||||
 protocol/interface manuals.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/osmo-mgw-doc.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-mgw-doc.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
usr/share/doc/osmo-mgw-doc/*.pdf
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							@@ -2,3 +2,4 @@ etc/osmocom/osmo-mgw.cfg
 | 
			
		||||
lib/systemd/system/osmo-mgw.service
 | 
			
		||||
usr/bin/osmo-mgw
 | 
			
		||||
usr/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw.cfg
 | 
			
		||||
usr/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							@@ -30,6 +30,10 @@ override_dh_auto_test:
 | 
			
		||||
	dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
 | 
			
		||||
 | 
			
		||||
override_dh_auto_configure:
 | 
			
		||||
	dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system
 | 
			
		||||
	dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
 | 
			
		||||
 | 
			
		||||
# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
 | 
			
		||||
override_dh_compress:
 | 
			
		||||
	dh_compress -X.pdf
 | 
			
		||||
 | 
			
		||||
# See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								doc/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								doc/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
!
 | 
			
		||||
! MGCP configuration example
 | 
			
		||||
!
 | 
			
		||||
e1_input
 | 
			
		||||
 e1_line 0 driver dahdi
 | 
			
		||||
 e1_line 0 port 0
 | 
			
		||||
mgcp
 | 
			
		||||
  bind ip 127.0.0.1
 | 
			
		||||
  rtp port-range 4002 16000
 | 
			
		||||
  rtp bind-ip 127.0.0.1
 | 
			
		||||
  rtp ip-probing
 | 
			
		||||
  rtp ip-tos 184
 | 
			
		||||
  bind port 2427
 | 
			
		||||
  sdp audio payload number 98
 | 
			
		||||
  sdp audio payload name GSM
 | 
			
		||||
  number endpoints 512
 | 
			
		||||
  loop 0
 | 
			
		||||
  force-realloc 1
 | 
			
		||||
  rtcp-omit
 | 
			
		||||
  rtp-patch ssrc
 | 
			
		||||
  rtp-patch timestamp
 | 
			
		||||
  trunk 1
 | 
			
		||||
   rtp keep-alive once
 | 
			
		||||
   no rtp keep-alive
 | 
			
		||||
   line 0
 | 
			
		||||
@@ -4,13 +4,13 @@
 | 
			
		||||
mgcp
 | 
			
		||||
  bind ip 127.0.0.1
 | 
			
		||||
  rtp port-range 4002 16000
 | 
			
		||||
  rtp bind-ip 10.9.1.122
 | 
			
		||||
  rtp bind-ip 127.0.0.1
 | 
			
		||||
  rtp ip-probing
 | 
			
		||||
  rtp ip-tos 184
 | 
			
		||||
  bind port 2427
 | 
			
		||||
  sdp audio payload number 98
 | 
			
		||||
  sdp audio payload name GSM
 | 
			
		||||
  number endpoints 31
 | 
			
		||||
  number endpoints 512
 | 
			
		||||
  loop 0
 | 
			
		||||
  force-realloc 1
 | 
			
		||||
  rtcp-omit
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
EXTRA_DIST = osmomgw-usermanual.adoc \
 | 
			
		||||
    osmomgw-usermanual-docinfo.xml \
 | 
			
		||||
    osmomgw-vty-reference.xml \
 | 
			
		||||
    regen_doc.sh \
 | 
			
		||||
    chapters \
 | 
			
		||||
    vty
 | 
			
		||||
 | 
			
		||||
@@ -12,5 +13,11 @@ if BUILD_MANUALS
 | 
			
		||||
  VTY_REFERENCE = osmomgw-vty-reference.xml
 | 
			
		||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
 | 
			
		||||
 | 
			
		||||
  BUILT_REFERENCE_XML = $(builddir)/vty/mgw_vty_reference.xml
 | 
			
		||||
  $(builddir)/vty/mgw_vty_reference.xml: $(top_builddir)/src/osmo-mgw/osmo-mgw
 | 
			
		||||
	mkdir -p $(builddir)/vty
 | 
			
		||||
	$(top_builddir)/src/osmo-mgw/osmo-mgw --vty-ref-xml > $@
 | 
			
		||||
 | 
			
		||||
  OSMO_REPOSITORY = osmo-mgw
 | 
			
		||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
 | 
			
		||||
endif
 | 
			
		||||
 
 | 
			
		||||
@@ -23,14 +23,13 @@ OsmoMGW(config-mgcp)# local ip 127.0.0.1
 | 
			
		||||
 | 
			
		||||
=== Configuring the trunk
 | 
			
		||||
 | 
			
		||||
The first trunk (trunk 0) is considered a virtual trunk in OsmoMGW. All
 | 
			
		||||
The first trunk is considered a virtual trunk in OsmoMGW. All
 | 
			
		||||
endpoints of type "rtpbridge" are routed here. The virtual trunk is configured
 | 
			
		||||
in the config-mgcp context.
 | 
			
		||||
 | 
			
		||||
All other trunks are configured in the config-mgcp-trunk context, but the
 | 
			
		||||
commands used are identical. Right now trunks are considered only for ds/e1
 | 
			
		||||
type endpoints which are not yet implemented. Don't use trunks other than the
 | 
			
		||||
"virtual" trunk 0.
 | 
			
		||||
commands used are identical. Right now trunks are considered only for "ds/e1"
 | 
			
		||||
type endpoints.
 | 
			
		||||
 | 
			
		||||
.Example: MGCP trunk configuration
 | 
			
		||||
----
 | 
			
		||||
@@ -54,4 +53,72 @@ OsmoMGW(config-mgcp)# rtp-patch timestamp <3>
 | 
			
		||||
<2> Hide SSRC changes
 | 
			
		||||
<3> Ensure RTP timestamp is aligned with frame duration
 | 
			
		||||
 | 
			
		||||
NOTE: Changes to trunks that affect resource allocation, such as newly created
 | 
			
		||||
trunks or a change of the number of available endpoints, require a full restart
 | 
			
		||||
of osmo-mgw!
 | 
			
		||||
 | 
			
		||||
=== E1 trunk considerations
 | 
			
		||||
 | 
			
		||||
While the RTP bridge trunks are natively based on IP no special considerations
 | 
			
		||||
are required during setup. E1 trunks are mapped on a physical E1 line, which has
 | 
			
		||||
to be configured as shown below.
 | 
			
		||||
 | 
			
		||||
.Example: E1 line setup
 | 
			
		||||
----
 | 
			
		||||
OsmoMGW(config-e1_input)# e1_line 0 driver dahdi <1>
 | 
			
		||||
OsmoMGW(config-e1_input)# e1_line 0 port 2 <2>
 | 
			
		||||
----
 | 
			
		||||
<1> Name of the libosmo-abis driver implementation ("dahdi")
 | 
			
		||||
<2> Port number of the physical E1 port to use (2)
 | 
			
		||||
 | 
			
		||||
In osmo-mgw the e1_input node is used to configure the physical E1 line. The
 | 
			
		||||
line number will be used internally to identify the configured E1 line. The
 | 
			
		||||
port number is the physical E1 connector (sometimes called 'span') at the E1
 | 
			
		||||
hardware. Per trunk an individual E1 line will be needed. Beware that the E1
 | 
			
		||||
driver may also need configuration settings that are not discussed here.
 | 
			
		||||
 | 
			
		||||
.Example: E1 trunk setup
 | 
			
		||||
----
 | 
			
		||||
OsmoMGW(config-mgcp)# trunk 0 <1>
 | 
			
		||||
OsmoMGW(config-mgcp-trunk)# line 0 <2>
 | 
			
		||||
----
 | 
			
		||||
<1> Creation of a trunk (0)
 | 
			
		||||
<2> Reference to the E1 line to use (0)
 | 
			
		||||
 | 
			
		||||
The E1 trunk is created along with a number, typically starting at 0, but if
 | 
			
		||||
required any number from 0-64 is allowed. The E1 trunk configuration concerning
 | 
			
		||||
the IP related aspects is nearly identical to the configuration of the virtual
 | 
			
		||||
trunk. However, it is important that the user assigns one of the E1 line numbers
 | 
			
		||||
that were configured under the e1_input node.
 | 
			
		||||
 | 
			
		||||
.Example: A typical configuration with one E1 trunk
 | 
			
		||||
----
 | 
			
		||||
e1_input
 | 
			
		||||
 e1_line 0 driver dahdi
 | 
			
		||||
 e1_line 0 port 2
 | 
			
		||||
mgcp
 | 
			
		||||
 bind ip 127.0.0.1
 | 
			
		||||
 rtp net-range 6000 6011
 | 
			
		||||
 rtp net-bind-ip 192.168.100.130
 | 
			
		||||
 rtp ip-probing
 | 
			
		||||
 rtp ip-tos 184
 | 
			
		||||
 no rtp keep-alive
 | 
			
		||||
 bind port 2428
 | 
			
		||||
 number endpoints 30
 | 
			
		||||
 loop 0
 | 
			
		||||
 force-realloc 1
 | 
			
		||||
 osmux off
 | 
			
		||||
 rtp-patch rfc5993hr
 | 
			
		||||
 trunk 0
 | 
			
		||||
  rtp keep-alive once
 | 
			
		||||
  no rtp keep-alive
 | 
			
		||||
  line 0
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
NOTE: One E1 trunk always covers a whole E1 line. All subslots (I.640) will be mapped
 | 
			
		||||
to individual MGCP endpoints. As long as the endpoints remain unused the
 | 
			
		||||
underlying E1 timeslot is not used.
 | 
			
		||||
 | 
			
		||||
NOTE: The E1 trunk implementation also works with T1 lines, however since T1 has
 | 
			
		||||
24 instead of 31 usable timeslots only the endpoints that fall into that 1-24 timeslot
 | 
			
		||||
range will be useable.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,79 @@
 | 
			
		||||
 | 
			
		||||
// autogenerated by show asciidoc counters
 | 
			
		||||
These counters and their description based on OsmoMGW 1.3.0.34-9cd52 (OsmoMGW).
 | 
			
		||||
These counters and their description based on OsmoMGW 1.5.0.64-189f (OsmoMGW).
 | 
			
		||||
 | 
			
		||||
=== Rate Counters
 | 
			
		||||
 | 
			
		||||
// generating tables for rate_ctr_group
 | 
			
		||||
// rate_ctr_group table aggregated statistics for all rtp connections
 | 
			
		||||
.all_rtp_conn - aggregated statistics for all rtp connections
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Name | Reference | Description
 | 
			
		||||
| all_rtp:err_tstmp_in | <<all_rtp_conn_all_rtp:err_tstmp_in>> | Total inbound rtp-stream timestamp errors.
 | 
			
		||||
| all_rtp:err_tstmp_out | <<all_rtp_conn_all_rtp:err_tstmp_out>> | Total outbound rtp-stream timestamp errors.
 | 
			
		||||
| all_rtp:packets_rx | <<all_rtp_conn_all_rtp:packets_rx>> | Total inbound rtp packets.
 | 
			
		||||
| all_rtp:octets_rx | <<all_rtp_conn_all_rtp:octets_rx>> | Total inbound rtp octets.
 | 
			
		||||
| all_rtp:packets_tx | <<all_rtp_conn_all_rtp:packets_tx>> | Total outbound rtp packets.
 | 
			
		||||
| all_rtp:octets_tx | <<all_rtp_conn_all_rtp:octets_tx>> | Total outbound rtp octets.
 | 
			
		||||
| all_rtp:dropped | <<all_rtp_conn_all_rtp:dropped>> | Total dropped rtp packets.
 | 
			
		||||
| all_rtp:num_closed_conns | <<all_rtp_conn_all_rtp:num_closed_conns>> | Total number of rtp connections closed.
 | 
			
		||||
|===
 | 
			
		||||
// rate_ctr_group table dlcx statistics
 | 
			
		||||
.dlcx - dlcx statistics
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Name | Reference | Description
 | 
			
		||||
| dlcx:success | <<dlcx_dlcx:success>> | DLCX command processed successfully.
 | 
			
		||||
| dlcx:wildcard | <<dlcx_dlcx:wildcard>> | wildcard names in DLCX commands are unsupported.
 | 
			
		||||
| dlcx:no_conn | <<dlcx_dlcx:no_conn>> | endpoint specified in DLCX command has no active connections.
 | 
			
		||||
| dlcx:callid | <<dlcx_dlcx:callid>> | CallId specified in DLCX command mismatches endpoint's CallId .
 | 
			
		||||
| dlcx:connid | <<dlcx_dlcx:connid>> | connection ID specified in DLCX command does not exist on endpoint.
 | 
			
		||||
| dlcx:unhandled_param | <<dlcx_dlcx:unhandled_param>> | unhandled parameter in DLCX command.
 | 
			
		||||
| dlcx:rejected | <<dlcx_dlcx:rejected>> | connection deletion rejected by policy.
 | 
			
		||||
| dlcx:deferred | <<dlcx_dlcx:deferred>> | connection deletion deferred by policy.
 | 
			
		||||
|===
 | 
			
		||||
// rate_ctr_group table mdcx statistics
 | 
			
		||||
.mdcx - mdcx statistics
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Name | Reference | Description
 | 
			
		||||
| mdcx:success | <<mdcx_mdcx:success>> | MDCX command processed successfully.
 | 
			
		||||
| mdcx:wildcard | <<mdcx_mdcx:wildcard>> | wildcard endpoint names in MDCX commands are unsupported.
 | 
			
		||||
| mdcx:no_conn | <<mdcx_mdcx:no_conn>> | endpoint specified in MDCX command has no active connections.
 | 
			
		||||
| mdcx:callid | <<mdcx_mdcx:callid>> | invalid CallId specified in MDCX command.
 | 
			
		||||
| mdcx:connid | <<mdcx_mdcx:connid>> | invalid connection ID specified in MDCX command.
 | 
			
		||||
| crcx:unhandled_param | <<mdcx_crcx:unhandled_param>> | unhandled parameter in MDCX command.
 | 
			
		||||
| mdcx:no_connid | <<mdcx_mdcx:no_connid>> | no connection ID specified in MDCX command.
 | 
			
		||||
| mdcx:conn_not_found | <<mdcx_mdcx:conn_not_found>> | connection specified in MDCX command does not exist.
 | 
			
		||||
| mdcx:invalid_mode | <<mdcx_mdcx:invalid_mode>> | invalid connection mode in MDCX command.
 | 
			
		||||
| mdcx:conn_opt | <<mdcx_mdcx:conn_opt>> | connection options invalid.
 | 
			
		||||
| mdcx:no_remote_conn_desc | <<mdcx_mdcx:no_remote_conn_desc>> | no opposite end specified for connection.
 | 
			
		||||
| mdcx:start_rtp_failure | <<mdcx_mdcx:start_rtp_failure>> | failure to start RTP processing.
 | 
			
		||||
| mdcx:conn_rejected | <<mdcx_mdcx:conn_rejected>> | connection rejected by policy.
 | 
			
		||||
| mdcx:conn_deferred | <<mdcx_mdcx:conn_deferred>> | connection deferred by policy.
 | 
			
		||||
|===
 | 
			
		||||
// rate_ctr_group table crxc statistics
 | 
			
		||||
.crcx - crxc statistics
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Name | Reference | Description
 | 
			
		||||
| crcx:success | <<crcx_crcx:success>> | CRCX command processed successfully.
 | 
			
		||||
| crcx:bad_action | <<crcx_crcx:bad_action>> | bad action in CRCX command.
 | 
			
		||||
| crcx:unhandled_param | <<crcx_crcx:unhandled_param>> | unhandled parameter in CRCX command.
 | 
			
		||||
| crcx:missing_callid | <<crcx_crcx:missing_callid>> | missing CallId in CRCX command.
 | 
			
		||||
| crcx:invalid_mode | <<crcx_crcx:invalid_mode>> | invalid connection mode in CRCX command.
 | 
			
		||||
| crcx:limit_exceeded | <<crcx_crcx:limit_exceeded>> | limit of concurrent connections was reached.
 | 
			
		||||
| crcx:unkown_callid | <<crcx_crcx:unkown_callid>> | unknown CallId in CRCX command.
 | 
			
		||||
| crcx:alloc_conn_fail | <<crcx_crcx:alloc_conn_fail>> | connection allocation failure.
 | 
			
		||||
| crcx:no_remote_conn_desc | <<crcx_crcx:no_remote_conn_desc>> | no opposite end specified for connection.
 | 
			
		||||
| crcx:start_rtp_failure | <<crcx_crcx:start_rtp_failure>> | failure to start RTP processing.
 | 
			
		||||
| crcx:conn_rejected | <<crcx_crcx:conn_rejected>> | connection rejected by policy.
 | 
			
		||||
| crcx:no_osmux | <<crcx_crcx:no_osmux>> | no osmux offered by peer.
 | 
			
		||||
| crcx:conn_opt | <<crcx_crcx:conn_opt>> | connection options invalid.
 | 
			
		||||
| crcx:codec_nego | <<crcx_crcx:codec_nego>> | codec negotiation failure.
 | 
			
		||||
| crcx:bind_port | <<crcx_crcx:bind_port>> | port bind failure.
 | 
			
		||||
|===
 | 
			
		||||
== Osmo Stat Items
 | 
			
		||||
 | 
			
		||||
// generating tables for osmo_stat_items
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										94
									
								
								doc/manuals/chapters/mgcp_endpoints.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								doc/manuals/chapters/mgcp_endpoints.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
== MGCP Endpoints
 | 
			
		||||
 | 
			
		||||
MGCP organizes the switching resources in so called endpoints. Each endpoint is
 | 
			
		||||
referenced by its unique identifier. While RFC 3435 specifies a naming scheme, the
 | 
			
		||||
actual identifier naming is subject to the implementation and configuration.
 | 
			
		||||
 | 
			
		||||
=== RTP proxy / RTP bridge endpoints
 | 
			
		||||
 | 
			
		||||
OsmoMGW implements a freely configurable number of `rtpbridge` endpoints. Those
 | 
			
		||||
endpoints are able to host two connections at a time to model the functionality
 | 
			
		||||
of a tandem switch.
 | 
			
		||||
 | 
			
		||||
RTP bridge endpoint identifiers are referenced by the string `rtpbridge/`, a
 | 
			
		||||
hexadecimal number without leading zeros and a domain name (configurable).
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
rtpbridge/<number>@<domain>
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
.Example: List of virtual endpoints
 | 
			
		||||
----
 | 
			
		||||
rtpbridge/1@mgw
 | 
			
		||||
rtpbridge/2@mgw
 | 
			
		||||
rtpbridge/3@mgw
 | 
			
		||||
rtpbridge/4@mgw
 | 
			
		||||
rtpbridge/5@mgw
 | 
			
		||||
rtpbridge/6@mgw
 | 
			
		||||
rtpbridge/7@mgw
 | 
			
		||||
rtpbridge/8@mgw
 | 
			
		||||
rtpbridge/9@mgw
 | 
			
		||||
rtpbridge/a@mgw
 | 
			
		||||
rtpbridge/b@mgw
 | 
			
		||||
rtpbridge/c@mgw
 | 
			
		||||
rtpbridge/d@mgw
 | 
			
		||||
rtpbridge/e@mgw
 | 
			
		||||
rtpbridge/f@mgw
 | 
			
		||||
rtpbridge/10@mgw
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
=== E1/T1 endpoints
 | 
			
		||||
 | 
			
		||||
OsmoMGW supports E1 subslot multiplexing as specified by I.460. All possible
 | 
			
		||||
subslot combinations are mapped on individual endpoints. The endpoint names
 | 
			
		||||
are prefixed with `ds/e1-` followed by the trunk number and the E1 timeslot.
 | 
			
		||||
The subslot is defined by a bit rate and a bit offset.
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
ds/e1-<trunk>/s-<timeslot>/su<bitrate>-<bitoffset>@<domain>
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
.Example: List of endpoints on E1 trunk 0 at E1 timeslot 2
 | 
			
		||||
----
 | 
			
		||||
ds/e1-0/s-2/su64-0@mgw
 | 
			
		||||
ds/e1-0/s-2/su32-0@mgw
 | 
			
		||||
ds/e1-0/s-2/su32-4@mgw
 | 
			
		||||
ds/e1-0/s-2/su16-0@mgw
 | 
			
		||||
ds/e1-0/s-2/su16-2@mgw
 | 
			
		||||
ds/e1-0/s-2/su16-4@mgw
 | 
			
		||||
ds/e1-0/s-2/su16-6@mgw
 | 
			
		||||
ds/e1-0/s-2/su8-0@mgw
 | 
			
		||||
ds/e1-0/s-2/su8-1@mgw
 | 
			
		||||
ds/e1-0/s-2/su8-2@mgw
 | 
			
		||||
ds/e1-0/s-2/su8-3@mgw
 | 
			
		||||
ds/e1-0/s-2/su8-4@mgw
 | 
			
		||||
ds/e1-0/s-2/su8-5@mgw
 | 
			
		||||
ds/e1-0/s-2/su8-6@mgw
 | 
			
		||||
ds/e1-0/s-2/su8-7@mgw
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
When creating connections on endpoints that reside in one E1 timeslot the call
 | 
			
		||||
agent must make sure that no overlapping endpoints are used. It is for example
 | 
			
		||||
not possible to use `ds/e1-0/s-2/su16-2@mgw` and `ds/e1-0/s-2/su8-3@mgw` at the
 | 
			
		||||
same time because they overlap.
 | 
			
		||||
 | 
			
		||||
.Subslot overlapping
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Bit offset  4+| Subslots
 | 
			
		||||
| 0 | 8k .2+| 16k .4+| 32k .8+| 64k
 | 
			
		||||
| 1 | 8k
 | 
			
		||||
| 2 | 8k .2+| 16k
 | 
			
		||||
| 3 | 8k
 | 
			
		||||
| 4 | 8k .2+| 16k .4+| 32k
 | 
			
		||||
| 5 | 8k
 | 
			
		||||
| 6 | 8k .2+| 16k
 | 
			
		||||
| 7 | 8k
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
NOTE: The current implementation (December 2020) only implements TRAU frame
 | 
			
		||||
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)
 | 
			
		||||
@@ -11,7 +11,7 @@ indicate collisions or configuration errors.
 | 
			
		||||
 | 
			
		||||
==== `X-Osmo-IGN` Format
 | 
			
		||||
 | 
			
		||||
The value part of X-Osmo-IGN must be one or more items separated by one or more
 | 
			
		||||
The value part of `X-Osmo-IGN` must be one or more items separated by one or more
 | 
			
		||||
spaces. Each item consists of one or more non-whitespace characters.
 | 
			
		||||
 | 
			
		||||
.Example: `X-Osmo-IGN` format with three ficticious items "X", "abc" and "123".
 | 
			
		||||
@@ -19,7 +19,7 @@ spaces. Each item consists of one or more non-whitespace characters.
 | 
			
		||||
X-Osmo-IGN: X abc 123
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
`X-Osmo-IGN` must be issued in the MGCP section (typically as its last item),
 | 
			
		||||
`X-Osmo-IGN` must be issued in the MGCP header section (typically as its last item),
 | 
			
		||||
before the SDP section starts.
 | 
			
		||||
 | 
			
		||||
==== Supported `X-Osmo-IGN` Items
 | 
			
		||||
@@ -66,3 +66,7 @@ m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
=== `X-Osmux`
 | 
			
		||||
 | 
			
		||||
See <<mgcp-extension-osmux>>
 | 
			
		||||
 
 | 
			
		||||
@@ -21,10 +21,10 @@ Protocol.
 | 
			
		||||
digraph G {
 | 
			
		||||
    rankdir = LR;
 | 
			
		||||
    OsmoBTS -> OsmoBSC [label="Abis/IP"];
 | 
			
		||||
    OsmoBSC -> OsmoMSC [label="3GPP AoIP"];
 | 
			
		||||
    OsmoBSC -> "core-network" [label="3GPP AoIP"];
 | 
			
		||||
    OsmoBSC -> OsmoMGW [label="MGCP"];
 | 
			
		||||
    OsmoBTS -> OsmoMGW [label="RTP",dir=both];
 | 
			
		||||
    OsmoMGW -> OsmoMSC [label="RTP",dir=both];
 | 
			
		||||
    OsmoMGW -> "core-network" [label="RTP",dir=both];
 | 
			
		||||
    {rank=same OsmoBSC OsmoMGW}
 | 
			
		||||
    OsmoMGW [color=red];
 | 
			
		||||
}
 | 
			
		||||
@@ -36,10 +36,9 @@ digraph G {
 | 
			
		||||
----
 | 
			
		||||
digraph G {
 | 
			
		||||
    rankdir = LR;
 | 
			
		||||
    BTS -> BSC [label="Abis"];
 | 
			
		||||
    BSC -> OsmoMSC [label="3GPP AoIP"];
 | 
			
		||||
    "2G BSS" -> OsmoMSC [label="3GPP AoIP"];
 | 
			
		||||
    OsmoMSC -> OsmoMGW [label="MGCP"];
 | 
			
		||||
    BSC -> OsmoMGW [label="RTP",dir=both];
 | 
			
		||||
    "2G BSS" -> OsmoMGW [label="RTP",dir=both];
 | 
			
		||||
    OsmoMSC -> OsmoSIP [label="MNCC"];
 | 
			
		||||
    OsmoSIP -> PBX [label="SIP Trunk"];
 | 
			
		||||
    OsmoMGW -> PBX [label="RTP",dir=both];
 | 
			
		||||
@@ -53,6 +52,22 @@ digraph G {
 | 
			
		||||
}
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
[[fig-bsc]]
 | 
			
		||||
.Integration of legacy E1 BTS in AoIP network
 | 
			
		||||
[graphviz]
 | 
			
		||||
----
 | 
			
		||||
digraph G {
 | 
			
		||||
    rankdir = LR;
 | 
			
		||||
    BTS -> OsmoBSC [label="Abis/E1"];
 | 
			
		||||
    OsmoBSC -> "core-network" [label="3GPP AoIP"];
 | 
			
		||||
    OsmoBSC -> OsmoMGW [label="MGCP"];
 | 
			
		||||
    BTS -> OsmoMGW [label="TRAU/E1",dir=both];
 | 
			
		||||
    OsmoMGW -> "core-network" [label="RTP",dir=both];
 | 
			
		||||
    {rank=same OsmoBSC OsmoMGW}
 | 
			
		||||
    OsmoMGW [color=red];
 | 
			
		||||
}
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
=== Software Components
 | 
			
		||||
 | 
			
		||||
OsmoMGW contains a variety of different software components, which we’ll
 | 
			
		||||
@@ -77,14 +92,14 @@ Transcoding is currently not supported in OsmoMGW.
 | 
			
		||||
 | 
			
		||||
=== Limitations
 | 
			
		||||
 | 
			
		||||
Osmux is not yet supported in OsmoMGW.
 | 
			
		||||
 | 
			
		||||
At the moment (July 2018), OsmoMGW only implements RTP proxy / RTP bridge
 | 
			
		||||
type endpoints, to each of which two RTP connections can be established.
 | 
			
		||||
We are planning to add endpoint types for:
 | 
			
		||||
At the moment (November 2020), OsmoMGW implements RTP proxy / RTP bridge
 | 
			
		||||
type endpoints and E1/T1 16k/8k sub-slots with TRAU frames for classic BTS
 | 
			
		||||
support. To the RTP proxy / RTP bridge endpoints two RTP connections can
 | 
			
		||||
be established, which then work as a tandem. E1/T1 endpoints support one
 | 
			
		||||
RTP connection at a time that is associated with a sub-slot on an E1 line.
 | 
			
		||||
We are planning to add further endpoint types for:
 | 
			
		||||
 | 
			
		||||
- classic E1/T1 timeslots (64kBps alaw/ulaw)
 | 
			
		||||
- classic E1/T1 16k sub-slots with TRAU frames for classic BTS support
 | 
			
		||||
- announcement/playout end-points
 | 
			
		||||
- conference endpoints
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ arguments:
 | 
			
		||||
*-h, --help*::
 | 
			
		||||
	Print a short help message about the supported options
 | 
			
		||||
*-V, --version*::
 | 
			
		||||
	Print the compile-time version number of the OsmoBTS program
 | 
			
		||||
	Print the compile-time version number of the program
 | 
			
		||||
*-D, --daemonize*::
 | 
			
		||||
	Fork the process as a daemon into background.
 | 
			
		||||
*-c, --config-file 'CONFIGFILE'*::
 | 
			
		||||
 
 | 
			
		||||
@@ -18,10 +18,16 @@ include::./common/chapters/logging.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/configuration.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/mgcp_endpoints.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/mgcp_extensions.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/osmux/osmux.adoc[]
 | 
			
		||||
 | 
			
		||||
//include::{srcdir}/chapters/counters.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/vty_cpu_sched.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/port_numbers.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/bibliography.adoc[]
 | 
			
		||||
@@ -29,5 +35,3 @@ include::./common/chapters/bibliography.adoc[]
 | 
			
		||||
include::./common/chapters/glossary.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/gfdl.adoc[]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								doc/manuals/regen_doc.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								doc/manuals/regen_doc.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
#!/bin/sh -x
 | 
			
		||||
 | 
			
		||||
if [ -z "$DOCKER_PLAYGROUND" ]; then
 | 
			
		||||
	echo "You need to set DOCKER_PLAYGROUND"
 | 
			
		||||
	exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
SCRIPT=$(realpath "$0")
 | 
			
		||||
MANUAL_DIR=$(dirname "$SCRIPT")
 | 
			
		||||
 | 
			
		||||
COMMIT=${COMMIT:-$(git log -1 --format=format:%H)}
 | 
			
		||||
 | 
			
		||||
cd "$DOCKER_PLAYGROUND/scripts" || exit 1
 | 
			
		||||
 | 
			
		||||
OSMO_MGW_BRANCH=$COMMIT ./regen_doc.sh osmo-mgw 4243 \
 | 
			
		||||
	"$MANUAL_DIR/chapters/counters_generated.adoc" \
 | 
			
		||||
	"$MANUAL_DIR/vty/mgw_vty_reference.xml"
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -4,10 +4,15 @@ SUBDIRS = \
 | 
			
		||||
 | 
			
		||||
nobase_include_HEADERS = \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client_fsm.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_common.h \
 | 
			
		||||
	osmocom/mgcp/mgcp.h \
 | 
			
		||||
	osmocom/mgcp/mgcp_common.h \
 | 
			
		||||
	osmocom/mgcp/mgcp_internal.h \
 | 
			
		||||
	osmocom/mgcp/osmux.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# This gets copied during make from osmocom/mgcp/mgcp_common.h. Therefore it is not included in the source tree and we
 | 
			
		||||
# don't need to distribute it (OS#4084).
 | 
			
		||||
nobase_nodist_include_HEADERS = \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_common.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,5 +6,11 @@ noinst_HEADERS = \
 | 
			
		||||
	mgcp_endp.h \
 | 
			
		||||
	mgcp_sdp.h \
 | 
			
		||||
	mgcp_codec.h \
 | 
			
		||||
	mgcp_ctrl.h \
 | 
			
		||||
	mgcp_trunk.h \
 | 
			
		||||
	debug.h \
 | 
			
		||||
	mgcp_ratectr.h \
 | 
			
		||||
	mgcp_e1.h \
 | 
			
		||||
	mgcp_network.h \
 | 
			
		||||
	mgcp_protocol.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@
 | 
			
		||||
/* Debug Areas of the code */
 | 
			
		||||
enum {
 | 
			
		||||
	DRTP,
 | 
			
		||||
	DE1,
 | 
			
		||||
	Debug_LastEntry,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
@@ -34,6 +35,8 @@
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
 | 
			
		||||
#include "mgcp_ratectr.h"
 | 
			
		||||
 | 
			
		||||
#define RTP_PORT_DEFAULT_RANGE_START 16002
 | 
			
		||||
#define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
 | 
			
		||||
 | 
			
		||||
@@ -42,7 +45,7 @@
 | 
			
		||||
 */
 | 
			
		||||
struct mgcp_endpoint;
 | 
			
		||||
struct mgcp_config;
 | 
			
		||||
struct mgcp_trunk_config;
 | 
			
		||||
struct mgcp_trunk;
 | 
			
		||||
struct mgcp_rtp_end;
 | 
			
		||||
 | 
			
		||||
#define MGCP_ENDP_CRCX 1
 | 
			
		||||
@@ -59,10 +62,9 @@ struct mgcp_rtp_end;
 | 
			
		||||
#define MGCP_POLICY_REJECT	5
 | 
			
		||||
#define MGCP_POLICY_DEFER	6
 | 
			
		||||
 | 
			
		||||
typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
 | 
			
		||||
typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
 | 
			
		||||
typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
 | 
			
		||||
typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
 | 
			
		||||
typedef int (*mgcp_change)(struct mgcp_endpoint *endp, int state);
 | 
			
		||||
typedef int (*mgcp_policy)(struct mgcp_endpoint *endp, int state, const char *transaction_id);
 | 
			
		||||
typedef int (*mgcp_reset)(struct mgcp_trunk *cfg);
 | 
			
		||||
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -81,10 +83,11 @@ typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
 | 
			
		||||
				     struct mgcp_conn_rtp *conn_dst,
 | 
			
		||||
				     struct mgcp_conn_rtp *conn_src);
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_codec;
 | 
			
		||||
 | 
			
		||||
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
 | 
			
		||||
				int *payload_type,
 | 
			
		||||
				const char**subtype_name,
 | 
			
		||||
				const char**fmtp_extra,
 | 
			
		||||
				const struct mgcp_rtp_codec **codec,
 | 
			
		||||
				const char **fmtp_extra,
 | 
			
		||||
				struct mgcp_conn_rtp *conn);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -92,7 +95,8 @@ typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
 | 
			
		||||
 */
 | 
			
		||||
struct mgcp_port_range {
 | 
			
		||||
	/* addr or NULL to fall-back to default */
 | 
			
		||||
	char *bind_addr;
 | 
			
		||||
	char *bind_addr_v4;
 | 
			
		||||
	char *bind_addr_v6;
 | 
			
		||||
 | 
			
		||||
	/* dynamically allocated */
 | 
			
		||||
	int range_start;
 | 
			
		||||
@@ -118,103 +122,6 @@ struct mgcp_port_range {
 | 
			
		||||
#define MGCP_KEEPALIVE_ONCE (-1)
 | 
			
		||||
#define MGCP_KEEPALIVE_NEVER 0
 | 
			
		||||
 | 
			
		||||
/* Global MCGP CRCX related rate counters */
 | 
			
		||||
enum {
 | 
			
		||||
	MGCP_CRCX_SUCCESS,
 | 
			
		||||
	MGCP_CRCX_FAIL_BAD_ACTION,
 | 
			
		||||
	MGCP_CRCX_FAIL_UNHANDLED_PARAM,
 | 
			
		||||
	MGCP_CRCX_FAIL_MISSING_CALLID,
 | 
			
		||||
	MGCP_CRCX_FAIL_INVALID_MODE,
 | 
			
		||||
	MGCP_CRCX_FAIL_LIMIT_EXCEEDED,
 | 
			
		||||
	MGCP_CRCX_FAIL_UNKNOWN_CALLID,
 | 
			
		||||
	MGCP_CRCX_FAIL_ALLOC_CONN,
 | 
			
		||||
	MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC,
 | 
			
		||||
	MGCP_CRCX_FAIL_START_RTP,
 | 
			
		||||
	MGCP_CRCX_FAIL_REJECTED_BY_POLICY,
 | 
			
		||||
	MGCP_CRCX_FAIL_NO_OSMUX,
 | 
			
		||||
	MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
 | 
			
		||||
	MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
 | 
			
		||||
	MGCP_CRCX_FAIL_BIND_PORT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Global MCGP MDCX related rate counters */
 | 
			
		||||
enum {
 | 
			
		||||
	MGCP_MDCX_SUCCESS,
 | 
			
		||||
	MGCP_MDCX_FAIL_WILDCARD,
 | 
			
		||||
	MGCP_MDCX_FAIL_NO_CONN,
 | 
			
		||||
	MGCP_MDCX_FAIL_INVALID_CALLID,
 | 
			
		||||
	MGCP_MDCX_FAIL_INVALID_CONNID,
 | 
			
		||||
	MGCP_MDCX_FAIL_UNHANDLED_PARAM,
 | 
			
		||||
	MGCP_MDCX_FAIL_NO_CONNID,
 | 
			
		||||
	MGCP_MDCX_FAIL_CONN_NOT_FOUND,
 | 
			
		||||
	MGCP_MDCX_FAIL_INVALID_MODE,
 | 
			
		||||
	MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
 | 
			
		||||
	MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
 | 
			
		||||
	MGCP_MDCX_FAIL_START_RTP,
 | 
			
		||||
	MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
 | 
			
		||||
	MGCP_MDCX_DEFERRED_BY_POLICY
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Global MCGP DLCX related rate counters */
 | 
			
		||||
enum {
 | 
			
		||||
	MGCP_DLCX_SUCCESS,
 | 
			
		||||
	MGCP_DLCX_FAIL_WILDCARD,
 | 
			
		||||
	MGCP_DLCX_FAIL_NO_CONN,
 | 
			
		||||
	MGCP_DLCX_FAIL_INVALID_CALLID,
 | 
			
		||||
	MGCP_DLCX_FAIL_INVALID_CONNID,
 | 
			
		||||
	MGCP_DLCX_FAIL_UNHANDLED_PARAM,
 | 
			
		||||
	MGCP_DLCX_FAIL_REJECTED_BY_POLICY,
 | 
			
		||||
	MGCP_DLCX_DEFERRED_BY_POLICY,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_trunk_config {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
	struct mgcp_config *cfg;
 | 
			
		||||
 | 
			
		||||
	int trunk_nr;
 | 
			
		||||
	int trunk_type;
 | 
			
		||||
 | 
			
		||||
	char *audio_fmtp_extra;
 | 
			
		||||
	char *audio_name;
 | 
			
		||||
	int audio_payload;
 | 
			
		||||
	int audio_send_ptime;
 | 
			
		||||
	int audio_send_name;
 | 
			
		||||
	int audio_loop;
 | 
			
		||||
 | 
			
		||||
	int no_audio_transcoding;
 | 
			
		||||
 | 
			
		||||
	int omit_rtcp;
 | 
			
		||||
	int keepalive_interval;
 | 
			
		||||
 | 
			
		||||
	/* RTP patching */
 | 
			
		||||
	int force_constant_ssrc; /* 0: don't, 1: once */
 | 
			
		||||
	int force_aligned_timing;
 | 
			
		||||
 | 
			
		||||
	/* spec handling */
 | 
			
		||||
	int force_realloc;
 | 
			
		||||
 | 
			
		||||
	/* timer */
 | 
			
		||||
	struct osmo_timer_list keepalive_timer;
 | 
			
		||||
 | 
			
		||||
	/* When set, incoming RTP packets are not filtered
 | 
			
		||||
	 * when ports and ip-address do not match (debug) */
 | 
			
		||||
	int rtp_accept_all;
 | 
			
		||||
 | 
			
		||||
	unsigned int number_endpoints;
 | 
			
		||||
	int vty_number_endpoints;
 | 
			
		||||
	struct mgcp_endpoint *endpoints;
 | 
			
		||||
 | 
			
		||||
	/* Rate counter group which contains stats for processed CRCX commands. */
 | 
			
		||||
	struct rate_ctr_group *mgcp_crcx_ctr_group;
 | 
			
		||||
	/* Rate counter group which contains stats for processed MDCX commands. */
 | 
			
		||||
	struct rate_ctr_group *mgcp_mdcx_ctr_group;
 | 
			
		||||
	/* Rate counter group which contains stats for processed DLCX commands. */
 | 
			
		||||
	struct rate_ctr_group *mgcp_dlcx_ctr_group;
 | 
			
		||||
	/* Rate counter group which aggregates stats of individual RTP connections. */
 | 
			
		||||
	struct rate_ctr_group *all_rtp_conn_stats;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum mgcp_role {
 | 
			
		||||
	MGCP_BSC = 0,
 | 
			
		||||
	MGCP_BSC_NAT,
 | 
			
		||||
@@ -242,14 +149,12 @@ struct mgcp_config {
 | 
			
		||||
	mgcp_change change_cb;
 | 
			
		||||
	mgcp_policy policy_cb;
 | 
			
		||||
	mgcp_reset reset_cb;
 | 
			
		||||
	mgcp_realloc realloc_cb;
 | 
			
		||||
	mgcp_rqnt rqnt_cb;
 | 
			
		||||
	void *data;
 | 
			
		||||
 | 
			
		||||
	uint32_t last_call_id;
 | 
			
		||||
 | 
			
		||||
	/* trunk handling */
 | 
			
		||||
	struct mgcp_trunk_config trunk;
 | 
			
		||||
	/* list holding the trunks */
 | 
			
		||||
	struct llist_head trunks;
 | 
			
		||||
 | 
			
		||||
	enum mgcp_role role;
 | 
			
		||||
@@ -274,6 +179,16 @@ struct mgcp_config {
 | 
			
		||||
	uint16_t osmux_dummy;
 | 
			
		||||
	/* domain name of the media gateway */
 | 
			
		||||
	char domain[255+1];
 | 
			
		||||
 | 
			
		||||
	/* time after which inactive connections (CIs) get closed */
 | 
			
		||||
	int conn_timeout;
 | 
			
		||||
 | 
			
		||||
	/* osmocom CTRL interface */
 | 
			
		||||
	struct ctrl_handle *ctrl;
 | 
			
		||||
 | 
			
		||||
	/* global rate counters to measure the MGWs overall performance and
 | 
			
		||||
	 * health */
 | 
			
		||||
	struct mgcp_ratectr_global ratectr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* config management */
 | 
			
		||||
@@ -281,8 +196,7 @@ struct mgcp_config *mgcp_config_alloc(void);
 | 
			
		||||
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
 | 
			
		||||
		      enum mgcp_role role);
 | 
			
		||||
int mgcp_vty_init(void);
 | 
			
		||||
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
 | 
			
		||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
 | 
			
		||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * format helper functions
 | 
			
		||||
@@ -290,9 +204,9 @@ void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
 | 
			
		||||
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
 | 
			
		||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp);
 | 
			
		||||
int mgcp_send_reset_all(struct mgcp_config *cfg);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port);
 | 
			
		||||
int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len);
 | 
			
		||||
int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, char *buf, int len);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,19 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
 | 
			
		||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
 | 
			
		||||
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
 | 
			
		||||
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE  8000
 | 
			
		||||
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
 | 
			
		||||
 | 
			
		||||
#define PTYPE_UNDEFINED (-1)
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
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);
 | 
			
		||||
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);
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,7 @@ enum mgcp_connection_mode {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:"
 | 
			
		||||
#define MGCP_X_OSMO_OSMUX_HEADER "X-Osmux:"
 | 
			
		||||
 | 
			
		||||
/* Values should be bitwise-OR-able */
 | 
			
		||||
enum mgcp_x_osmo_ign {
 | 
			
		||||
@@ -57,6 +58,12 @@ enum mgcp_x_osmo_ign {
 | 
			
		||||
	MGCP_X_OSMO_IGN_CALLID = 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Codec parameters (communicated via SDP/fmtp) */
 | 
			
		||||
struct mgcp_codec_param {
 | 
			
		||||
	bool amr_octet_aligned_present;
 | 
			
		||||
	bool amr_octet_aligned;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Ensure that the msg->l2h is NUL terminated. */
 | 
			
		||||
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
@@ -93,6 +100,10 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
 | 
			
		||||
/* A prefix to denote the virtual trunk (RTP on both ends) */
 | 
			
		||||
#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
 | 
			
		||||
 | 
			
		||||
/* A prefix to denote the e1 trunk
 | 
			
		||||
 * (see also RFC3435 section E.2) */
 | 
			
		||||
#define MGCP_ENDPOINT_PREFIX_E1_TRUNK "ds/e1-"
 | 
			
		||||
 | 
			
		||||
/* Maximal number of payload types / codecs that can be negotiated via SDP at
 | 
			
		||||
 * at once. */
 | 
			
		||||
#define MGCP_MAX_CODECS 10
 | 
			
		||||
 
 | 
			
		||||
@@ -23,11 +23,114 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_network.h>
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#define LOGPCONN(conn, cat, level, fmt, args...) \
 | 
			
		||||
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
 | 
			
		||||
         (conn)->id, \
 | 
			
		||||
         ## args)
 | 
			
		||||
 | 
			
		||||
#define LOG_CONN(conn, level, fmt, args...) \
 | 
			
		||||
	LOGP(DRTP, level, "(%s I:%s) " fmt, \
 | 
			
		||||
	     (conn)->endp ? (conn)->endp->name : "none", (conn)->id, ## args)
 | 
			
		||||
 | 
			
		||||
#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
 | 
			
		||||
	LOG_CONN((conn_rtp)->conn, level, fmt, ## args)
 | 
			
		||||
 | 
			
		||||
/* 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,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
 | 
			
		||||
 *  contains a useful connection description (currently only RTP) */
 | 
			
		||||
enum mgcp_conn_type {
 | 
			
		||||
	MGCP_CONN_TYPE_RTP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* MGCP connection (RTP) */
 | 
			
		||||
struct mgcp_conn_rtp {
 | 
			
		||||
 | 
			
		||||
	/* Backpointer to conn struct */
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	/* Specific connection type */
 | 
			
		||||
	enum mgcp_conn_rtp_type type;
 | 
			
		||||
 | 
			
		||||
	/* Port status */
 | 
			
		||||
	struct mgcp_rtp_end end;
 | 
			
		||||
 | 
			
		||||
	/* Sequence bits */
 | 
			
		||||
	struct mgcp_rtp_state state;
 | 
			
		||||
 | 
			
		||||
	/* taps for the rtp connection; one per direction */
 | 
			
		||||
	struct mgcp_rtp_tap tap_in;
 | 
			
		||||
	struct mgcp_rtp_tap tap_out;
 | 
			
		||||
 | 
			
		||||
	/* Osmux states (optional) */
 | 
			
		||||
	struct {
 | 
			
		||||
		/* Osmux state: disabled, activating, active */
 | 
			
		||||
		enum osmux_state state;
 | 
			
		||||
		/* Is cid holding valid data? is it allocated from pool? */
 | 
			
		||||
		bool cid_allocated;
 | 
			
		||||
		/* Allocated Osmux circuit ID for this conn */
 | 
			
		||||
		uint8_t cid;
 | 
			
		||||
		/* handle to batch messages */
 | 
			
		||||
		struct osmux_in_handle *in;
 | 
			
		||||
		/* handle to unbatch messages */
 | 
			
		||||
		struct osmux_out_handle out;
 | 
			
		||||
		/* statistics */
 | 
			
		||||
		struct {
 | 
			
		||||
			uint32_t chunks;
 | 
			
		||||
			uint32_t octets;
 | 
			
		||||
		} stats;
 | 
			
		||||
	} osmux;
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr_group *rate_ctr_group;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! MGCP connection (untyped) */
 | 
			
		||||
struct mgcp_conn {
 | 
			
		||||
	/*! list head */
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
	/*! Backpointer to the endpoint where the conn belongs to */
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
 | 
			
		||||
	/*! type of the connection (union) */
 | 
			
		||||
	enum mgcp_conn_type type;
 | 
			
		||||
 | 
			
		||||
	/*! mode of the connection */
 | 
			
		||||
	enum mgcp_connection_mode mode;
 | 
			
		||||
 | 
			
		||||
	/*! copy of the mode to restore the original setting (VTY) */
 | 
			
		||||
	enum mgcp_connection_mode mode_orig;
 | 
			
		||||
 | 
			
		||||
	/*! connection id to identify the connection */
 | 
			
		||||
	char id[MGCP_CONN_ID_MAXLEN];
 | 
			
		||||
 | 
			
		||||
	/*! human readable name (vty, logging) */
 | 
			
		||||
	char name[256];
 | 
			
		||||
 | 
			
		||||
	/*! activity tracker (for cleaning up inactive connections) */
 | 
			
		||||
	struct osmo_timer_list watchdog;
 | 
			
		||||
 | 
			
		||||
	/*! union with connection description */
 | 
			
		||||
	union {
 | 
			
		||||
		struct mgcp_conn_rtp rtp;
 | 
			
		||||
	} u;
 | 
			
		||||
 | 
			
		||||
	/*! pointer to optional private data */
 | 
			
		||||
	void *priv;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* RTP connection related counters */
 | 
			
		||||
enum {
 | 
			
		||||
	IN_STREAM_ERR_TSTMP_CTR,
 | 
			
		||||
@@ -68,6 +171,11 @@ 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."}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
				  enum mgcp_conn_type type, char *name);
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
 | 
			
		||||
@@ -78,3 +186,5 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
 | 
			
		||||
char *mgcp_conn_dump(struct mgcp_conn *conn);
 | 
			
		||||
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								include/osmocom/mgcp/mgcp_ctrl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								include/osmocom/mgcp/mgcp_ctrl.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (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);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										27
									
								
								include/osmocom/mgcp/mgcp_e1.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								include/osmocom/mgcp/mgcp_e1.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
/* A 64k timeslot on an E1 line can be subdevied into the following
 | 
			
		||||
 * subslot combinations:
 | 
			
		||||
 *
 | 
			
		||||
 * subslot:                                          offset:
 | 
			
		||||
 * [          ][          ][   16k    ][8k_subslot]  0
 | 
			
		||||
 * [          ][   32k    ][_subslot__][8k_subslot]  1
 | 
			
		||||
 * [          ][ subslot  ][   16k    ][8k_subslot]  2
 | 
			
		||||
 * [   64k    ][__________][_subslot__][8k_subslot]  3
 | 
			
		||||
 * [ timeslot ][          ][   16k    ][8k_subslot]  4
 | 
			
		||||
 * [          ][   32K    ][_subslot__][8k_subslot]  5
 | 
			
		||||
 * [          ][ subslot  ][   16k    ][8k_subslot]  6
 | 
			
		||||
 * [          ][          ][ subslot  ][8k_subslot]  7
 | 
			
		||||
 *
 | 
			
		||||
 * Since overlapping assignment of subslots is not possible there is a limited
 | 
			
		||||
 * set of subslot assignments possible. The e1_rates array lists the possible
 | 
			
		||||
 * assignments as depicted above. Also each subslot assignment comes along with
 | 
			
		||||
 * a bit offset in the E1 bitstream. The e1_offsets arrays lists the bit
 | 
			
		||||
 * offsets. */
 | 
			
		||||
static const uint8_t e1_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
 | 
			
		||||
static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg);
 | 
			
		||||
@@ -23,15 +23,35 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
struct sockaddr_in;
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/gsm/i460_mux.h>
 | 
			
		||||
 | 
			
		||||
struct sockaddr;
 | 
			
		||||
struct mgcp_conn;
 | 
			
		||||
struct mgcp_conn_rtp;
 | 
			
		||||
struct mgcp_endpoint;
 | 
			
		||||
 | 
			
		||||
/* Callback type for RTP dispatcher functions
 | 
			
		||||
   (e.g mgcp_dispatch_rtp_bridge_cb, see below) */
 | 
			
		||||
typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr,
 | 
			
		||||
				     char *buf, unsigned int buf_size,
 | 
			
		||||
				     struct mgcp_conn *conn);
 | 
			
		||||
/* Number of E1 subslots (different variants, not all useable at the same time) */
 | 
			
		||||
#define MGCP_ENDP_E1_SUBSLOTS 15
 | 
			
		||||
 | 
			
		||||
#define LOGPENDP(endp, cat, level, fmt, args...) \
 | 
			
		||||
LOGP(cat, level, "endpoint:%s " fmt, \
 | 
			
		||||
     endp ? endp->name : "none", \
 | 
			
		||||
     ## args)
 | 
			
		||||
 | 
			
		||||
struct osmo_rtp_msg_ctx {
 | 
			
		||||
	int proto;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_src;
 | 
			
		||||
	struct osmo_sockaddr *from_addr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define OSMO_RTP_MSG_CTX(MSGB) ((struct osmo_rtp_msg_ctx*)(MSGB)->cb)
 | 
			
		||||
 | 
			
		||||
osmo_static_assert(sizeof(((struct msgb*)0)->cb) >= sizeof(struct osmo_rtp_msg_ctx), osmo_rtp_msg_ctx_fits_in_msgb_cb);
 | 
			
		||||
 | 
			
		||||
/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
 | 
			
		||||
 * The OSMO_RTP_MSG_CTX() should be set appropriately on the msg. */
 | 
			
		||||
typedef int (*mgcp_dispatch_rtp_cb) (struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
/* Callback type for endpoint specific cleanup actions. This function
 | 
			
		||||
 * is automatically executed when a connection is freed (see mgcp_conn_free()
 | 
			
		||||
@@ -55,6 +75,7 @@ struct mgcp_endpoint_type {
 | 
			
		||||
/*! MGCP endpoint typeset */
 | 
			
		||||
struct mgcp_endpoint_typeset {
 | 
			
		||||
	struct mgcp_endpoint_type rtp;
 | 
			
		||||
	struct mgcp_endpoint_type e1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
 | 
			
		||||
@@ -63,6 +84,9 @@ extern const struct mgcp_endpoint_typeset ep_typeset;
 | 
			
		||||
/*! MGCP endpoint model */
 | 
			
		||||
struct mgcp_endpoint {
 | 
			
		||||
 | 
			
		||||
	/*! Unique endpoint name, used for addressing via MGCP */
 | 
			
		||||
	char *name;
 | 
			
		||||
 | 
			
		||||
	/*! Call identifier string (as supplied by the call agant) */
 | 
			
		||||
	char *callid;
 | 
			
		||||
 | 
			
		||||
@@ -75,8 +99,8 @@ struct mgcp_endpoint {
 | 
			
		||||
	/*! Backpointer to the MGW configuration */
 | 
			
		||||
	struct mgcp_config *cfg;
 | 
			
		||||
 | 
			
		||||
	/*! Backpointer to the Trunk specific configuration */
 | 
			
		||||
	struct mgcp_trunk_config *tcfg;
 | 
			
		||||
	/*! Backpointer to the trunk this endpoint belongs to */
 | 
			
		||||
	struct mgcp_trunk *trunk;
 | 
			
		||||
 | 
			
		||||
	/*! Endpoint properties (see above) */
 | 
			
		||||
	const struct mgcp_endpoint_type *type;
 | 
			
		||||
@@ -93,10 +117,27 @@ struct mgcp_endpoint {
 | 
			
		||||
 | 
			
		||||
	/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
 | 
			
		||||
	uint32_t x_osmo_ign;
 | 
			
		||||
 | 
			
		||||
	/* E1 specific */
 | 
			
		||||
	struct {
 | 
			
		||||
		struct osmo_i460_schan_desc scd;
 | 
			
		||||
		struct osmo_i460_subchan *schan;
 | 
			
		||||
		struct osmo_fsm_inst *trau_sync_fi;
 | 
			
		||||
		struct osmo_trau2rtp_state *trau_rtp_st;
 | 
			
		||||
		uint8_t last_amr_ft;
 | 
			
		||||
		struct mgcp_rtp_codec *last_codec;
 | 
			
		||||
	} e1;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! Extract endpoint number for a given endpoint */
 | 
			
		||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
 | 
			
		||||
					      const struct mgcp_trunk *trunk);
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
 | 
			
		||||
					struct mgcp_config *cfg);
 | 
			
		||||
bool mgcp_endp_avail(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
 | 
			
		||||
void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,330 +0,0 @@
 | 
			
		||||
/* MGCP Private Data */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2012 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/counter.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
 | 
			
		||||
#define CI_UNUSED 0
 | 
			
		||||
 | 
			
		||||
/* FIXME: This this is only needed to compile the currently
 | 
			
		||||
 * broken OSMUX support. Remove when fixed */
 | 
			
		||||
#define CONN_ID_BTS "0"
 | 
			
		||||
#define CONN_ID_NET "1"
 | 
			
		||||
 | 
			
		||||
enum mgcp_trunk_type {
 | 
			
		||||
	MGCP_TRUNK_VIRTUAL,
 | 
			
		||||
	MGCP_TRUNK_E1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_stream_state {
 | 
			
		||||
	uint32_t ssrc;
 | 
			
		||||
	uint16_t last_seq;
 | 
			
		||||
	uint32_t last_timestamp;
 | 
			
		||||
	struct rate_ctr *err_ts_ctr;
 | 
			
		||||
	int32_t last_tsdelta;
 | 
			
		||||
	uint32_t last_arrival_time;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_state {
 | 
			
		||||
	/* has this state structure been initialized? */
 | 
			
		||||
	int initialized;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		/* are we patching the SSRC value? */
 | 
			
		||||
		int patch_ssrc;
 | 
			
		||||
		/* original SSRC (to which we shall patch any different SSRC) */
 | 
			
		||||
		uint32_t orig_ssrc;
 | 
			
		||||
		/* offset to apply on the sequence number */
 | 
			
		||||
		int seq_offset;
 | 
			
		||||
		/* offset to apply on the timestamp number */
 | 
			
		||||
		int32_t timestamp_offset;
 | 
			
		||||
	} patch;
 | 
			
		||||
 | 
			
		||||
	/* duration of a packet (FIXME: in which unit?) */
 | 
			
		||||
	uint32_t packet_duration;
 | 
			
		||||
 | 
			
		||||
	struct mgcp_rtp_stream_state in_stream;
 | 
			
		||||
	struct mgcp_rtp_stream_state out_stream;
 | 
			
		||||
 | 
			
		||||
	/* jitter and packet loss calculation */
 | 
			
		||||
	struct {
 | 
			
		||||
		int initialized;
 | 
			
		||||
		uint16_t base_seq;
 | 
			
		||||
		uint16_t max_seq;
 | 
			
		||||
		uint32_t ssrc;
 | 
			
		||||
		uint32_t jitter;
 | 
			
		||||
		int32_t transit;
 | 
			
		||||
		int cycles;
 | 
			
		||||
	} stats;
 | 
			
		||||
 | 
			
		||||
	bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_codec {
 | 
			
		||||
	uint32_t rate;
 | 
			
		||||
	int channels;
 | 
			
		||||
	uint32_t frame_duration_num;
 | 
			
		||||
	uint32_t frame_duration_den;
 | 
			
		||||
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	char *audio_name;
 | 
			
		||||
	char *subtype_name;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
 | 
			
		||||
struct mgcp_rtp_end {
 | 
			
		||||
	/* local IP address of the RTP socket */
 | 
			
		||||
	struct in_addr addr;
 | 
			
		||||
 | 
			
		||||
	/* in network byte order */
 | 
			
		||||
	int rtp_port, rtcp_port;
 | 
			
		||||
 | 
			
		||||
	/* currently selected audio codec */
 | 
			
		||||
	struct mgcp_rtp_codec *codec;
 | 
			
		||||
 | 
			
		||||
	/* array with assigned audio codecs to choose from (SDP) */
 | 
			
		||||
	struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
 | 
			
		||||
 | 
			
		||||
	/* number of assigned audio codecs (SDP) */
 | 
			
		||||
	unsigned int codecs_assigned;
 | 
			
		||||
 | 
			
		||||
	/* per endpoint data */
 | 
			
		||||
	int  frames_per_packet;
 | 
			
		||||
	uint32_t packet_duration_ms;
 | 
			
		||||
	int maximum_packet_time; /* -1: not set */
 | 
			
		||||
	char *fmtp_extra;
 | 
			
		||||
	/* are we transmitting packets (1) or dropping (0) outbound packets */
 | 
			
		||||
	int output_enabled;
 | 
			
		||||
	/* FIXME: This parameter can be set + printed, but is nowhere used! */
 | 
			
		||||
	int force_output_ptime;
 | 
			
		||||
 | 
			
		||||
	/* RTP patching */
 | 
			
		||||
	int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
 | 
			
		||||
	/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
 | 
			
		||||
	int force_aligned_timing;
 | 
			
		||||
 | 
			
		||||
	/* Each end has a separate socket for RTP and RTCP */
 | 
			
		||||
	struct osmo_fd rtp;
 | 
			
		||||
	struct osmo_fd rtcp;
 | 
			
		||||
 | 
			
		||||
	/* local UDP port number of the RTP socket; RTCP is +1 */
 | 
			
		||||
	int local_port;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_tap {
 | 
			
		||||
	/* is this tap active (1) or not (0) */
 | 
			
		||||
	int enabled;
 | 
			
		||||
	/* IP/port to which we're forwarding the tapped data */
 | 
			
		||||
	struct sockaddr_in forward;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_lco {
 | 
			
		||||
	char *string;
 | 
			
		||||
	char *codec;
 | 
			
		||||
	int pkt_period_min; /* time in ms */
 | 
			
		||||
	int pkt_period_max; /* time in ms */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
 | 
			
		||||
enum mgcp_conn_rtp_type {
 | 
			
		||||
	MGCP_RTP_DEFAULT	= 0,
 | 
			
		||||
	MGCP_OSMUX_BSC,
 | 
			
		||||
	MGCP_OSMUX_BSC_NAT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
struct mgcp_conn;
 | 
			
		||||
 | 
			
		||||
/* MGCP connection (RTP) */
 | 
			
		||||
struct mgcp_conn_rtp {
 | 
			
		||||
 | 
			
		||||
	/* Backpointer to conn struct */
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	/* Specific connection type */
 | 
			
		||||
	enum mgcp_conn_rtp_type type;
 | 
			
		||||
 | 
			
		||||
	/* Port status */
 | 
			
		||||
	struct mgcp_rtp_end end;
 | 
			
		||||
 | 
			
		||||
	/* Sequence bits */
 | 
			
		||||
	struct mgcp_rtp_state state;
 | 
			
		||||
 | 
			
		||||
	/* taps for the rtp connection; one per direction */
 | 
			
		||||
	struct mgcp_rtp_tap tap_in;
 | 
			
		||||
	struct mgcp_rtp_tap tap_out;
 | 
			
		||||
 | 
			
		||||
	/* Osmux states (optional) */
 | 
			
		||||
	struct {
 | 
			
		||||
		/* Osmux state: disabled, activating, active */
 | 
			
		||||
		enum osmux_state state;
 | 
			
		||||
		/* Allocated Osmux circuit ID for this endpoint */
 | 
			
		||||
		int allocated_cid;
 | 
			
		||||
		/* Used Osmux circuit ID for this endpoint */
 | 
			
		||||
		uint8_t cid;
 | 
			
		||||
		/* handle to batch messages */
 | 
			
		||||
		struct osmux_in_handle *in;
 | 
			
		||||
		/* handle to unbatch messages */
 | 
			
		||||
		struct osmux_out_handle out;
 | 
			
		||||
		/* statistics */
 | 
			
		||||
		struct {
 | 
			
		||||
			uint32_t chunks;
 | 
			
		||||
			uint32_t octets;
 | 
			
		||||
		} stats;
 | 
			
		||||
	} osmux;
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr_group *rate_ctr_group;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
 | 
			
		||||
 *  contains a useful connection description (currently only RTP) */
 | 
			
		||||
enum mgcp_conn_type {
 | 
			
		||||
	MGCP_CONN_TYPE_RTP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! MGCP connection (untyped) */
 | 
			
		||||
struct mgcp_conn {
 | 
			
		||||
	/*! list head */
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
	/*! Backpointer to the endpoint where the conn belongs to */
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
 | 
			
		||||
	/*! type of the connection (union) */
 | 
			
		||||
	enum mgcp_conn_type type;
 | 
			
		||||
 | 
			
		||||
	/*! mode of the connection */
 | 
			
		||||
	enum mgcp_connection_mode mode;
 | 
			
		||||
 | 
			
		||||
	/*! copy of the mode to restore the original setting (VTY) */
 | 
			
		||||
	enum mgcp_connection_mode mode_orig;
 | 
			
		||||
 | 
			
		||||
	/*! connection id to identify the connection */
 | 
			
		||||
	char id[MGCP_CONN_ID_MAXLEN];
 | 
			
		||||
 | 
			
		||||
	/*! human readable name (vty, logging) */
 | 
			
		||||
	char name[256];
 | 
			
		||||
 | 
			
		||||
	/*! union with connection description */
 | 
			
		||||
	union {
 | 
			
		||||
		struct mgcp_conn_rtp rtp;
 | 
			
		||||
	} u;
 | 
			
		||||
 | 
			
		||||
	/*! pointer to optional private data */
 | 
			
		||||
	void *priv;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
 | 
			
		||||
struct mgcp_endpoint_type;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Internal structure while parsing a request
 | 
			
		||||
 */
 | 
			
		||||
struct mgcp_parse_data {
 | 
			
		||||
	struct mgcp_config *cfg;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	char *trans;
 | 
			
		||||
	char *save;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
			
		||||
	      char *buf, int rc, struct mgcp_conn_rtp *conn_src,
 | 
			
		||||
	      struct mgcp_conn_rtp *conn_dst);
 | 
			
		||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
 | 
			
		||||
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
 | 
			
		||||
				unsigned int buf_size, struct mgcp_conn *conn);
 | 
			
		||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
 | 
			
		||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
 | 
			
		||||
			   struct mgcp_conn_rtp *conn);
 | 
			
		||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
 | 
			
		||||
 | 
			
		||||
/* For transcoding we need to manage an in and an output that are connected */
 | 
			
		||||
static inline int endp_back_channel(int endpoint)
 | 
			
		||||
{
 | 
			
		||||
	return endpoint + 60;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
 | 
			
		||||
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
 | 
			
		||||
 | 
			
		||||
char *get_lco_identifier(const char *options);
 | 
			
		||||
int check_local_cx_options(void *ctx, const char *options);
 | 
			
		||||
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
 | 
			
		||||
			 struct mgcp_rtp_end *rtp);
 | 
			
		||||
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
 | 
			
		||||
				  struct mgcp_rtp_end *rtp);
 | 
			
		||||
 | 
			
		||||
/* payload processing default functions */
 | 
			
		||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
 | 
			
		||||
				char *data, int *len, int buf_size);
 | 
			
		||||
 | 
			
		||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
 | 
			
		||||
				      struct mgcp_conn_rtp *conn_dst,
 | 
			
		||||
				      struct mgcp_conn_rtp *conn_src);
 | 
			
		||||
 | 
			
		||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
 | 
			
		||||
					  int *payload_type,
 | 
			
		||||
					  const char**audio_name,
 | 
			
		||||
					  const char**fmtp_extra,
 | 
			
		||||
					  struct mgcp_conn_rtp *conn);
 | 
			
		||||
 | 
			
		||||
/* internal RTP Annex A counting */
 | 
			
		||||
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
 | 
			
		||||
			const uint16_t seq, const int32_t transit,
 | 
			
		||||
			const uint32_t ssrc);
 | 
			
		||||
 | 
			
		||||
int mgcp_set_ip_tos(int fd, int tos);
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	MGCP_DEST_NET = 0,
 | 
			
		||||
	MGCP_DEST_BTS,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define MGCP_DUMMY_LOAD 0x23
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * SDP related information
 | 
			
		||||
 */
 | 
			
		||||
/* Assume audio frame length of 20ms */
 | 
			
		||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
 | 
			
		||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
 | 
			
		||||
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
 | 
			
		||||
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE  8000
 | 
			
		||||
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
 | 
			
		||||
 | 
			
		||||
#define PTYPE_UNDEFINED (-1)
 | 
			
		||||
 | 
			
		||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
 | 
			
		||||
							
								
								
									
										169
									
								
								include/osmocom/mgcp/mgcp_network.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								include/osmocom/mgcp/mgcp_network.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,169 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
 | 
			
		||||
#define MGCP_DUMMY_LOAD 0x23
 | 
			
		||||
#define RTP_BUF_SIZE	4096
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_stream_state {
 | 
			
		||||
	uint32_t ssrc;
 | 
			
		||||
	uint16_t last_seq;
 | 
			
		||||
	uint32_t last_timestamp;
 | 
			
		||||
	struct rate_ctr *err_ts_ctr;
 | 
			
		||||
	int32_t last_tsdelta;
 | 
			
		||||
	uint32_t last_arrival_time;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_state {
 | 
			
		||||
	/* has this state structure been initialized? */
 | 
			
		||||
	int initialized;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		/* are we patching the SSRC value? */
 | 
			
		||||
		int patch_ssrc;
 | 
			
		||||
		/* original SSRC (to which we shall patch any different SSRC) */
 | 
			
		||||
		uint32_t orig_ssrc;
 | 
			
		||||
		/* offset to apply on the sequence number */
 | 
			
		||||
		int seq_offset;
 | 
			
		||||
		/* offset to apply on the timestamp number */
 | 
			
		||||
		int32_t timestamp_offset;
 | 
			
		||||
	} patch;
 | 
			
		||||
 | 
			
		||||
	/* duration of a packet (FIXME: in which unit?) */
 | 
			
		||||
	uint32_t packet_duration;
 | 
			
		||||
 | 
			
		||||
	/* Note: These states are not continuously updated, they serve as an
 | 
			
		||||
	 * information source to patch certain values in the RTP header. Do
 | 
			
		||||
	 * not use this state if constantly updated data about the RTP stream
 | 
			
		||||
	 * is needed. (see also mgcp_patch_and_count() */
 | 
			
		||||
	struct mgcp_rtp_stream_state in_stream;
 | 
			
		||||
	struct mgcp_rtp_stream_state out_stream;
 | 
			
		||||
 | 
			
		||||
	/* jitter and packet loss calculation */
 | 
			
		||||
	struct {
 | 
			
		||||
		int initialized;
 | 
			
		||||
		uint16_t base_seq;
 | 
			
		||||
		uint16_t max_seq;
 | 
			
		||||
		uint32_t ssrc;
 | 
			
		||||
		uint32_t jitter;
 | 
			
		||||
		int32_t transit;
 | 
			
		||||
		int cycles;
 | 
			
		||||
	} stats;
 | 
			
		||||
 | 
			
		||||
	/* Alternative values for RTP tx, in case no sufficient header
 | 
			
		||||
	 * information is available so the header needs to be generated
 | 
			
		||||
	 * locally (when just forwarding packets, the header of incoming
 | 
			
		||||
	 * data is just re-used) */
 | 
			
		||||
	uint16_t alt_rtp_tx_sequence;
 | 
			
		||||
	uint32_t alt_rtp_tx_ssrc;
 | 
			
		||||
 | 
			
		||||
	bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_codec {
 | 
			
		||||
	uint32_t rate;
 | 
			
		||||
	int channels;
 | 
			
		||||
	uint32_t frame_duration_num;
 | 
			
		||||
	uint32_t frame_duration_den;
 | 
			
		||||
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	char *audio_name;
 | 
			
		||||
	char *subtype_name;
 | 
			
		||||
 | 
			
		||||
	bool param_present;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
 | 
			
		||||
struct mgcp_rtp_end {
 | 
			
		||||
	/* remote IP address of the RTP socket */
 | 
			
		||||
	struct osmo_sockaddr addr;
 | 
			
		||||
 | 
			
		||||
	/* in network byte order */
 | 
			
		||||
	int rtp_port, rtcp_port;
 | 
			
		||||
 | 
			
		||||
	/* currently selected audio codec */
 | 
			
		||||
	struct mgcp_rtp_codec *codec;
 | 
			
		||||
 | 
			
		||||
	/* array with assigned audio codecs to choose from (SDP) */
 | 
			
		||||
	struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
 | 
			
		||||
 | 
			
		||||
	/* number of assigned audio codecs (SDP) */
 | 
			
		||||
	unsigned int codecs_assigned;
 | 
			
		||||
 | 
			
		||||
	/* per endpoint data */
 | 
			
		||||
	int  frames_per_packet;
 | 
			
		||||
	uint32_t packet_duration_ms;
 | 
			
		||||
	int maximum_packet_time; /* -1: not set */
 | 
			
		||||
	char *fmtp_extra;
 | 
			
		||||
	/* are we transmitting packets (1) or dropping (0) outbound packets */
 | 
			
		||||
	int output_enabled;
 | 
			
		||||
	/* FIXME: This parameter can be set + printed, but is nowhere used! */
 | 
			
		||||
	int force_output_ptime;
 | 
			
		||||
 | 
			
		||||
	/* RTP patching */
 | 
			
		||||
	int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
 | 
			
		||||
	/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
 | 
			
		||||
	int force_aligned_timing;
 | 
			
		||||
	bool rfc5993_hr_convert;
 | 
			
		||||
 | 
			
		||||
	/* Each end has a separate socket for RTP and RTCP */
 | 
			
		||||
	struct osmo_fd rtp;
 | 
			
		||||
	struct osmo_fd rtcp;
 | 
			
		||||
 | 
			
		||||
	/* local UDP port number of the RTP socket; RTCP is +1 */
 | 
			
		||||
	int local_port;
 | 
			
		||||
	/* where the endpoint RTP connection binds to, set during CRCX and
 | 
			
		||||
	 * possibly updated during MDCX */
 | 
			
		||||
	char local_addr[INET6_ADDRSTRLEN];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_tap {
 | 
			
		||||
	/* is this tap active (1) or not (0) */
 | 
			
		||||
	int enabled;
 | 
			
		||||
	/* IP/port to which we're forwarding the tapped data */
 | 
			
		||||
	struct osmo_sockaddr forward;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn;
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
 | 
			
		||||
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
 | 
			
		||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
 | 
			
		||||
int mgcp_dispatch_e1_bridge_cb(struct msgb *msg);
 | 
			
		||||
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
 | 
			
		||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
 | 
			
		||||
			   struct mgcp_conn_rtp *conn);
 | 
			
		||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
 | 
			
		||||
void mgcp_patch_and_count(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_set_ip_tos(int fd, int tos);
 | 
			
		||||
 | 
			
		||||
/* payload processing default functions */
 | 
			
		||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
 | 
			
		||||
				char *data, int *len, int buf_size);
 | 
			
		||||
 | 
			
		||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
 | 
			
		||||
				      struct mgcp_conn_rtp *conn_dst,
 | 
			
		||||
				      struct mgcp_conn_rtp *conn_src);
 | 
			
		||||
 | 
			
		||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
 | 
			
		||||
					  const struct mgcp_rtp_codec **codec,
 | 
			
		||||
					  const char **fmtp_extra,
 | 
			
		||||
					  struct mgcp_conn_rtp *conn);
 | 
			
		||||
 | 
			
		||||
/* internal RTP Annex A counting */
 | 
			
		||||
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
 | 
			
		||||
			const uint16_t seq, const int32_t transit,
 | 
			
		||||
			const uint32_t ssrc);
 | 
			
		||||
							
								
								
									
										27
									
								
								include/osmocom/mgcp/mgcp_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								include/osmocom/mgcp/mgcp_protocol.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
/* Internal structure while parsing a request */
 | 
			
		||||
struct mgcp_parse_data {
 | 
			
		||||
	struct mgcp_config *cfg;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	char *trans;
 | 
			
		||||
	char *save;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Local connection options */
 | 
			
		||||
struct mgcp_lco {
 | 
			
		||||
	char *string;
 | 
			
		||||
	char *codec;
 | 
			
		||||
	int pkt_period_min; /* time in ms */
 | 
			
		||||
	int pkt_period_max; /* time in ms */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
char *get_lco_identifier(const char *options);
 | 
			
		||||
int check_local_cx_options(void *ctx, const char *options);
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_end;
 | 
			
		||||
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
 | 
			
		||||
			 struct mgcp_rtp_end *rtp);
 | 
			
		||||
 | 
			
		||||
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
 | 
			
		||||
				  struct mgcp_rtp_end *rtp);
 | 
			
		||||
							
								
								
									
										95
									
								
								include/osmocom/mgcp/mgcp_ratectr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								include/osmocom/mgcp/mgcp_ratectr.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Global MCGP general rate counters */
 | 
			
		||||
enum {
 | 
			
		||||
	MGCP_GENERAL_RX_MSGS_TOTAL,
 | 
			
		||||
	MGCP_GENERAL_RX_MSGS_RETRANSMITTED,
 | 
			
		||||
	MGCP_GENERAL_RX_MSGS_HANDLED,
 | 
			
		||||
	MGCP_GENERAL_RX_MSGS_UNHANDLED,
 | 
			
		||||
	MGCP_GENERAL_RX_FAIL_MSG_PARSE,
 | 
			
		||||
	MGCP_GENERAL_RX_FAIL_NO_ENDPOINT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Trunk-global MCGP CRCX related rate counters */
 | 
			
		||||
enum {
 | 
			
		||||
	MGCP_CRCX_SUCCESS,
 | 
			
		||||
	MGCP_CRCX_FAIL_BAD_ACTION,
 | 
			
		||||
	MGCP_CRCX_FAIL_UNHANDLED_PARAM,
 | 
			
		||||
	MGCP_CRCX_FAIL_MISSING_CALLID,
 | 
			
		||||
	MGCP_CRCX_FAIL_INVALID_MODE,
 | 
			
		||||
	MGCP_CRCX_FAIL_LIMIT_EXCEEDED,
 | 
			
		||||
	MGCP_CRCX_FAIL_UNKNOWN_CALLID,
 | 
			
		||||
	MGCP_CRCX_FAIL_ALLOC_CONN,
 | 
			
		||||
	MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC,
 | 
			
		||||
	MGCP_CRCX_FAIL_START_RTP,
 | 
			
		||||
	MGCP_CRCX_FAIL_REJECTED_BY_POLICY,
 | 
			
		||||
	MGCP_CRCX_FAIL_NO_OSMUX,
 | 
			
		||||
	MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
 | 
			
		||||
	MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
 | 
			
		||||
	MGCP_CRCX_FAIL_BIND_PORT,
 | 
			
		||||
	MGCP_CRCX_FAIL_AVAIL,
 | 
			
		||||
	MGCP_CRCX_FAIL_CLAIM,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Trunk-global MCGP MDCX related rate counters */
 | 
			
		||||
enum {
 | 
			
		||||
	MGCP_MDCX_SUCCESS,
 | 
			
		||||
	MGCP_MDCX_FAIL_WILDCARD,
 | 
			
		||||
	MGCP_MDCX_FAIL_NO_CONN,
 | 
			
		||||
	MGCP_MDCX_FAIL_INVALID_CALLID,
 | 
			
		||||
	MGCP_MDCX_FAIL_INVALID_CONNID,
 | 
			
		||||
	MGCP_MDCX_FAIL_UNHANDLED_PARAM,
 | 
			
		||||
	MGCP_MDCX_FAIL_NO_CONNID,
 | 
			
		||||
	MGCP_MDCX_FAIL_CONN_NOT_FOUND,
 | 
			
		||||
	MGCP_MDCX_FAIL_INVALID_MODE,
 | 
			
		||||
	MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
 | 
			
		||||
	MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
 | 
			
		||||
	MGCP_MDCX_FAIL_START_RTP,
 | 
			
		||||
	MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
 | 
			
		||||
	MGCP_MDCX_DEFERRED_BY_POLICY,
 | 
			
		||||
	MGCP_MDCX_FAIL_AVAIL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Trunk-global MCGP DLCX related rate counters */
 | 
			
		||||
enum {
 | 
			
		||||
	MGCP_DLCX_SUCCESS,
 | 
			
		||||
	MGCP_DLCX_FAIL_WILDCARD,
 | 
			
		||||
	MGCP_DLCX_FAIL_NO_CONN,
 | 
			
		||||
	MGCP_DLCX_FAIL_INVALID_CALLID,
 | 
			
		||||
	MGCP_DLCX_FAIL_INVALID_CONNID,
 | 
			
		||||
	MGCP_DLCX_FAIL_UNHANDLED_PARAM,
 | 
			
		||||
	MGCP_DLCX_FAIL_REJECTED_BY_POLICY,
 | 
			
		||||
	MGCP_DLCX_DEFERRED_BY_POLICY,
 | 
			
		||||
	MGCP_DLCX_FAIL_AVAIL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Trunk-global E1 related counters */
 | 
			
		||||
enum {
 | 
			
		||||
        E1_I460_TRAU_RX_FAIL_CTR,
 | 
			
		||||
        E1_I460_TRAU_TX_FAIL_CTR,
 | 
			
		||||
        E1_I460_TRAU_MUX_EMPTY_CTR,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* NOTE: When adding counters, also the dump_ratectr_* routines in vty.c must be updated. */
 | 
			
		||||
 | 
			
		||||
struct mgcp_ratectr_global {
 | 
			
		||||
	/* Rate counter group which contains stats for generic MGCP events. */
 | 
			
		||||
	struct rate_ctr_group *mgcp_general_ctr_group;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_ratectr_trunk {
 | 
			
		||||
	/* Rate counter group which contains stats for processed CRCX commands. */
 | 
			
		||||
	struct rate_ctr_group *mgcp_crcx_ctr_group;
 | 
			
		||||
	/* Rate counter group which contains stats for processed MDCX commands. */
 | 
			
		||||
	struct rate_ctr_group *mgcp_mdcx_ctr_group;
 | 
			
		||||
	/* Rate counter group which contains stats for processed DLCX commands. */
 | 
			
		||||
	struct rate_ctr_group *mgcp_dlcx_ctr_group;
 | 
			
		||||
	/* Rate counter group which aggregates stats of individual RTP connections. */
 | 
			
		||||
	struct rate_ctr_group *all_rtp_conn_stats;
 | 
			
		||||
	/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
 | 
			
		||||
	struct rate_ctr_group *e1_stats;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr);
 | 
			
		||||
int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr);
 | 
			
		||||
@@ -24,7 +24,6 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
void mgcp_format_stats(char *str, size_t str_len,  struct mgcp_conn *conn);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										82
									
								
								include/osmocom/mgcp/mgcp_trunk.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								include/osmocom/mgcp/mgcp_trunk.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/i460_mux.h>
 | 
			
		||||
#include <osmocom/abis/e1_input.h>
 | 
			
		||||
 | 
			
		||||
#define LOGPTRUNK(trunk, cat, level, fmt, args...) \
 | 
			
		||||
LOGP(cat, level, "trunk:%u " fmt, \
 | 
			
		||||
     trunk ? trunk->trunk_nr : 0, \
 | 
			
		||||
     ## args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
enum mgcp_trunk_type {
 | 
			
		||||
	MGCP_TRUNK_VIRTUAL,
 | 
			
		||||
	MGCP_TRUNK_E1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_trunk {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
	struct mgcp_config *cfg;
 | 
			
		||||
 | 
			
		||||
	int trunk_nr;
 | 
			
		||||
	enum mgcp_trunk_type trunk_type;
 | 
			
		||||
 | 
			
		||||
	char *audio_fmtp_extra;
 | 
			
		||||
	int audio_send_ptime;
 | 
			
		||||
	int audio_send_name;
 | 
			
		||||
 | 
			
		||||
	int no_audio_transcoding;
 | 
			
		||||
 | 
			
		||||
	int omit_rtcp;
 | 
			
		||||
	int keepalive_interval;
 | 
			
		||||
 | 
			
		||||
	/* RTP patching */
 | 
			
		||||
	int force_constant_ssrc; /* 0: don't, 1: once */
 | 
			
		||||
	int force_aligned_timing;
 | 
			
		||||
	bool rfc5993_hr_convert;
 | 
			
		||||
 | 
			
		||||
	/* spec handling */
 | 
			
		||||
	int force_realloc;
 | 
			
		||||
 | 
			
		||||
	/* timer */
 | 
			
		||||
	struct osmo_timer_list keepalive_timer;
 | 
			
		||||
 | 
			
		||||
	/* When set, incoming RTP packets are not filtered
 | 
			
		||||
	 * when ports and ip-address do not match (debug) */
 | 
			
		||||
	int rtp_accept_all;
 | 
			
		||||
 | 
			
		||||
	unsigned int number_endpoints;
 | 
			
		||||
	struct mgcp_endpoint **endpoints;
 | 
			
		||||
 | 
			
		||||
	/* global rate counters to measure the trunks overall performance and health */
 | 
			
		||||
	struct mgcp_ratectr_trunk ratectr;
 | 
			
		||||
 | 
			
		||||
	union {
 | 
			
		||||
		/* Virtual trunk specific */
 | 
			
		||||
		struct {
 | 
			
		||||
			unsigned int vty_number_endpoints;
 | 
			
		||||
		} v;
 | 
			
		||||
		/* E1 specific */
 | 
			
		||||
		struct {
 | 
			
		||||
			unsigned int vty_line_nr;
 | 
			
		||||
			bool ts_in_use[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
 | 
			
		||||
			 * are available for traffic. */
 | 
			
		||||
		} e1;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
 | 
			
		||||
int mgcp_trunk_equip(struct mgcp_trunk *trunk);
 | 
			
		||||
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
 | 
			
		||||
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname);
 | 
			
		||||
int e1_trunk_nr_from_epname(const char *epname);
 | 
			
		||||
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num);
 | 
			
		||||
 | 
			
		||||
/* The virtual trunk is always created on trunk id 0 for historical reasons,
 | 
			
		||||
 * use this define constant as ID when allocating a virtual trunk. Other
 | 
			
		||||
 * trunks may be assigned with arbritrary id numbers */
 | 
			
		||||
#define MGCP_VIRT_TRUNK_ID 0
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/netif/osmux.h>
 | 
			
		||||
struct mgcp_conn_rtp;
 | 
			
		||||
@@ -13,19 +14,21 @@ enum {
 | 
			
		||||
 | 
			
		||||
int osmux_init(int role, struct mgcp_config *cfg);
 | 
			
		||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
 | 
			
		||||
		      struct in_addr *addr, uint16_t port);
 | 
			
		||||
void osmux_disable_conn(struct mgcp_conn_rtp *conn);
 | 
			
		||||
void osmux_allocate_cid(struct mgcp_conn_rtp *conn);
 | 
			
		||||
void osmux_release_cid(struct mgcp_conn_rtp *conn);
 | 
			
		||||
		      struct osmo_sockaddr *addr, uint16_t port);
 | 
			
		||||
void conn_osmux_disable(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid);
 | 
			
		||||
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
 | 
			
		||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
 | 
			
		||||
int osmux_get_cid(void);
 | 
			
		||||
void osmux_put_cid(uint8_t osmux_cid);
 | 
			
		||||
int osmux_used_cid(void);
 | 
			
		||||
 | 
			
		||||
void osmux_cid_pool_get(uint8_t osmux_cid);
 | 
			
		||||
int osmux_cid_pool_get_next(void);
 | 
			
		||||
void osmux_cid_pool_put(uint8_t osmux_cid);
 | 
			
		||||
bool osmux_cid_pool_allocated(uint8_t osmux_cid);
 | 
			
		||||
int osmux_cid_pool_count_used(void);
 | 
			
		||||
 | 
			
		||||
enum osmux_state {
 | 
			
		||||
	OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
 | 
			
		||||
	OSMUX_STATE_NEGOTIATING,  /* Osmux was locally requested in MGCP CRCX */
 | 
			
		||||
	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 */
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -6,3 +6,7 @@ enum mgcp_vty_node {
 | 
			
		||||
	MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
 | 
			
		||||
	TRUNK_NODE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum mgw_vty_cmd_attr {
 | 
			
		||||
	MGW_CMD_ATTR_NEWCONN = 0,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_common.h>
 | 
			
		||||
 | 
			
		||||
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT NULL /* INADDR(6)_ANY */
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
 | 
			
		||||
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
 | 
			
		||||
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
 | 
			
		||||
@@ -47,6 +47,10 @@ enum mgcp_codecs {
 | 
			
		||||
 * this is an internal assumption that is made to avoid lookup tables.
 | 
			
		||||
 * The API-User should not rely on this coincidence! */
 | 
			
		||||
 | 
			
		||||
extern const struct value_string osmo_mgcpc_codec_names[];
 | 
			
		||||
static inline const char *osmo_mgcpc_codec_name(enum mgcp_codecs val)
 | 
			
		||||
{ return get_value_string(osmo_mgcpc_codec_names, val); }
 | 
			
		||||
 | 
			
		||||
/*! Structure to build a payload type map to allow the defiition custom payload
 | 
			
		||||
 *  types. */
 | 
			
		||||
struct ptmap {
 | 
			
		||||
@@ -63,13 +67,15 @@ struct mgcp_response_head {
 | 
			
		||||
	char comment[MGCP_COMMENT_MAXLEN];
 | 
			
		||||
	char conn_id[MGCP_CONN_ID_MAXLEN];
 | 
			
		||||
	char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	bool x_osmo_osmux_use;
 | 
			
		||||
	uint8_t x_osmo_osmux_cid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_response {
 | 
			
		||||
	char *body;
 | 
			
		||||
	struct mgcp_response_head head;
 | 
			
		||||
	uint16_t audio_port;
 | 
			
		||||
	char audio_ip[INET_ADDRSTRLEN];
 | 
			
		||||
	char audio_ip[INET6_ADDRSTRLEN];
 | 
			
		||||
	unsigned int ptime;
 | 
			
		||||
	enum mgcp_codecs codecs[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int codecs_len;
 | 
			
		||||
@@ -91,6 +97,7 @@ enum mgcp_verb {
 | 
			
		||||
#define MGCP_MSG_PRESENCE_AUDIO_IP	0x0008
 | 
			
		||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT	0x0010
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CONN_MODE	0x0020
 | 
			
		||||
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
 | 
			
		||||
#define MGCP_MSG_PRESENCE_X_OSMO_IGN	0x8000
 | 
			
		||||
 | 
			
		||||
struct mgcp_msg {
 | 
			
		||||
@@ -109,6 +116,10 @@ struct mgcp_msg {
 | 
			
		||||
	struct ptmap ptmap[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int ptmap_len;
 | 
			
		||||
	uint32_t x_osmo_ign;
 | 
			
		||||
	bool x_osmo_osmux_use;
 | 
			
		||||
	int x_osmo_osmux_cid; /* -1 is wildcard */
 | 
			
		||||
	bool param_present;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
 | 
			
		||||
@@ -122,10 +133,12 @@ int mgcp_client_connect(struct mgcp_client *mgcp);
 | 
			
		||||
 | 
			
		||||
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp);
 | 
			
		||||
uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp);
 | 
			
		||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp);
 | 
			
		||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp) OSMO_DEPRECATED("deprecated, returns 0");
 | 
			
		||||
 | 
			
		||||
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp);
 | 
			
		||||
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
 | 
			
		||||
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
 | 
			
		||||
				  uint8_t rate, uint8_t offset);
 | 
			
		||||
 | 
			
		||||
/* Invoked when an MGCP response is received or sending failed.  When the
 | 
			
		||||
 * response is passed as NULL, this indicates failure during transmission. */
 | 
			
		||||
@@ -148,7 +161,7 @@ static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum mgcp_codecs map_str_to_codec(const char *str);
 | 
			
		||||
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
 | 
			
		||||
unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
 | 
			
		||||
			     enum mgcp_codecs codec);
 | 
			
		||||
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
 | 
			
		||||
				 unsigned int pt);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										51
									
								
								include/osmocom/mgcp_client/mgcp_client_endpoint_fsm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								include/osmocom/mgcp_client/mgcp_client_endpoint_fsm.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
/* FSM to manage multiple connections of an MGW endpoint */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
 | 
			
		||||
 | 
			
		||||
#define LOG_MGCPC_EP(ep, level, fmt, args...) do { \
 | 
			
		||||
	LOGPFSML(ep->fi, level, "%s " fmt, \
 | 
			
		||||
		 osmo_mgcpc_ep_name(ep), ## args); \
 | 
			
		||||
	} while(0)
 | 
			
		||||
 | 
			
		||||
struct osmo_mgcpc_ep;
 | 
			
		||||
struct osmo_mgcpc_ep_ci;
 | 
			
		||||
struct osmo_tdef;
 | 
			
		||||
 | 
			
		||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_alloc(struct osmo_fsm_inst *parent, uint32_t parent_term_event,
 | 
			
		||||
					  struct mgcp_client *mgcp_client,
 | 
			
		||||
					  const struct osmo_tdef *T_defs,
 | 
			
		||||
					  const char *fsm_id,
 | 
			
		||||
					  const char *endpoint_str_fmt, ...);
 | 
			
		||||
 | 
			
		||||
struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_add(struct osmo_mgcpc_ep *ep, const char *label_fmt, ...);
 | 
			
		||||
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci);
 | 
			
		||||
bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest);
 | 
			
		||||
bool osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(const struct osmo_mgcpc_ep_ci *ci, uint8_t* cid);
 | 
			
		||||
 | 
			
		||||
void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
 | 
			
		||||
			      enum mgcp_verb verb, const struct mgcp_conn_peer *verb_info,
 | 
			
		||||
			      struct osmo_fsm_inst *notify,
 | 
			
		||||
			      uint32_t event_success, uint32_t event_failure,
 | 
			
		||||
			      void *notify_data);
 | 
			
		||||
 | 
			
		||||
void osmo_mgcpc_ep_cancel_notify(struct osmo_mgcpc_ep *ep, struct osmo_fsm_inst *notify);
 | 
			
		||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_ci_ep(struct osmo_mgcpc_ep_ci *ci);
 | 
			
		||||
 | 
			
		||||
/*! Dispatch a DLCX for the given connection.
 | 
			
		||||
 * \param ci  Connection identifier as obtained from osmo_mgcpc_ep_ci_add().
 | 
			
		||||
 */
 | 
			
		||||
static inline void osmo_mgcpc_ep_ci_dlcx(struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	osmo_mgcpc_ep_ci_request(ci, MGCP_VERB_DLCX, NULL, NULL, 0, 0, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep);
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep);
 | 
			
		||||
const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci);
 | 
			
		||||
const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci);
 | 
			
		||||
 | 
			
		||||
extern const struct value_string osmo_mgcp_verb_names[];
 | 
			
		||||
static inline const char *osmo_mgcp_verb_name(enum mgcp_verb val)
 | 
			
		||||
{ return get_value_string(osmo_mgcp_verb_names, val); }
 | 
			
		||||
@@ -15,7 +15,7 @@
 | 
			
		||||
 *  sure it is correct. */
 | 
			
		||||
struct mgcp_conn_peer {
 | 
			
		||||
	/*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
 | 
			
		||||
	char addr[INET_ADDRSTRLEN];
 | 
			
		||||
	char addr[INET6_ADDRSTRLEN];
 | 
			
		||||
 | 
			
		||||
	/*! RTP connection IP-Port (optional)  */
 | 
			
		||||
	uint16_t port;
 | 
			
		||||
@@ -47,6 +47,20 @@ struct mgcp_conn_peer {
 | 
			
		||||
	 * known to issue incoherent or unknown CallIDs / to issue CRCX commands with a different domain
 | 
			
		||||
	 * name than the BSC. An OsmoMGW will then ignore these and not fail on mismatches. */
 | 
			
		||||
	uint32_t x_osmo_ign;
 | 
			
		||||
 | 
			
		||||
	/*! send 'X-Osmux: %d' header (or "*" as wildcard). */
 | 
			
		||||
	bool x_osmo_osmux_use;
 | 
			
		||||
	/*! -1 means send wildcard. */
 | 
			
		||||
	int x_osmo_osmux_cid;
 | 
			
		||||
 | 
			
		||||
	/*! If left MGCP_CONN_NONE, use MGCP_CONN_RECV_ONLY or MGCP_CONN_RECV_SEND, depending on whether an audio RTP
 | 
			
		||||
	 * address is set. If != MGCP_CONN_NONE, force this conn mode. */
 | 
			
		||||
	enum mgcp_connection_mode conn_mode;
 | 
			
		||||
 | 
			
		||||
	/*! If the codec requires additional format parameters (fmtp), those cann be set here, see also
 | 
			
		||||
	 * mgcp_common.h */
 | 
			
		||||
	bool param_present;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
 | 
			
		||||
@@ -55,3 +69,5 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
 | 
			
		||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
 | 
			
		||||
 | 
			
		||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@
 | 
			
		||||
 | 
			
		||||
struct mgcp_client {
 | 
			
		||||
	struct mgcp_client_conf actual;
 | 
			
		||||
	uint32_t remote_addr;
 | 
			
		||||
	struct osmo_wqueue wq;
 | 
			
		||||
	mgcp_trans_id_t next_trans_id;
 | 
			
		||||
	struct llist_head responses_pending;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOABIS_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
@@ -20,7 +21,7 @@ AM_LDFLAGS = \
 | 
			
		||||
 | 
			
		||||
# This is not at all related to the release version, but a range of supported
 | 
			
		||||
# API versions. Read TODO_RELEASE in the source tree's root!
 | 
			
		||||
MGCP_CLIENT_LIBVERSION=5:0:0
 | 
			
		||||
MGCP_CLIENT_LIBVERSION=8:0:0
 | 
			
		||||
 | 
			
		||||
lib_LTLIBRARIES = \
 | 
			
		||||
	libosmo-mgcp-client.la \
 | 
			
		||||
@@ -30,6 +31,7 @@ libosmo_mgcp_client_la_SOURCES = \
 | 
			
		||||
	mgcp_client.c \
 | 
			
		||||
	mgcp_client_vty.c \
 | 
			
		||||
	mgcp_client_fsm.c \
 | 
			
		||||
	mgcp_client_endpoint_fsm.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)
 | 
			
		||||
 
 | 
			
		||||
@@ -25,19 +25,29 @@
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/byteswap.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/sockaddr_str.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/abis/e1_input.h>
 | 
			
		||||
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
 | 
			
		||||
#ifndef OSMUX_CID_MAX
 | 
			
		||||
#define OSMUX_CID_MAX 255 /* FIXME: use OSMUX_CID_MAX from libosmo-netif? */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Codec descripton for dynamic payload types (SDP) */
 | 
			
		||||
static const struct value_string codec_table[] = {
 | 
			
		||||
const struct value_string osmo_mgcpc_codec_names[] = {
 | 
			
		||||
	{ CODEC_PCMU_8000_1, "PCMU/8000/1" },
 | 
			
		||||
	{ CODEC_GSM_8000_1, "GSM/8000/1" },
 | 
			
		||||
	{ CODEC_PCMA_8000_1, "PCMA/8000/1" },
 | 
			
		||||
@@ -81,19 +91,19 @@ enum mgcp_codecs map_str_to_codec(const char *str)
 | 
			
		||||
 | 
			
		||||
	osmo_strlcpy(str_buf, extract_codec_name(str), sizeof(str_buf));
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(codec_table); i++) {
 | 
			
		||||
		codec_name = extract_codec_name(codec_table[i].str);
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(osmo_mgcpc_codec_names); i++) {
 | 
			
		||||
		codec_name = extract_codec_name(osmo_mgcpc_codec_names[i].str);
 | 
			
		||||
		if (!codec_name)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (strcmp(codec_name, str_buf) == 0)
 | 
			
		||||
			return codec_table[i].value;
 | 
			
		||||
			return osmo_mgcpc_codec_names[i].value;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check the ptmap for illegal mappings */
 | 
			
		||||
static int check_ptmap(struct ptmap *ptmap)
 | 
			
		||||
static int check_ptmap(const struct ptmap *ptmap)
 | 
			
		||||
{
 | 
			
		||||
	/* Check if there are mappings that leave the IANA assigned dynamic
 | 
			
		||||
	 * payload type range. Under normal conditions such mappings should
 | 
			
		||||
@@ -122,7 +132,7 @@ error:
 | 
			
		||||
 *  \ptmap[in] ptmap_len length of the payload type map.
 | 
			
		||||
 *  \ptmap[in] codec the codec for which the payload type should be looked up.
 | 
			
		||||
 *  \returns assigned payload type */
 | 
			
		||||
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
 | 
			
		||||
unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
 | 
			
		||||
			     enum mgcp_codecs codec)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
@@ -260,7 +270,8 @@ static bool mgcp_line_is_valid(const char *line)
 | 
			
		||||
static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
 | 
			
		||||
{
 | 
			
		||||
	char *pt_str;
 | 
			
		||||
	unsigned int pt;
 | 
			
		||||
	char *pt_end;
 | 
			
		||||
	unsigned long int pt;
 | 
			
		||||
	unsigned int count = 0;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
@@ -284,7 +295,14 @@ static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
 | 
			
		||||
		pt_str = strtok(NULL, " ");
 | 
			
		||||
		if (!pt_str)
 | 
			
		||||
			break;
 | 
			
		||||
		pt = atoi(pt_str);
 | 
			
		||||
		errno = 0;
 | 
			
		||||
		pt = strtoul(pt_str, &pt_end, 0);
 | 
			
		||||
		if ((errno == ERANGE && pt == ULONG_MAX) || (errno && !pt) ||
 | 
			
		||||
		    pt_str == pt_end)
 | 
			
		||||
			goto response_parse_failure_pt;
 | 
			
		||||
 | 
			
		||||
		if (pt >> 7) /* PT is 7 bit field, higher values not allowed */
 | 
			
		||||
			goto response_parse_failure_pt;
 | 
			
		||||
 | 
			
		||||
		/* Do not allow duplicate payload types */
 | 
			
		||||
		for (i = 0; i < count; i++)
 | 
			
		||||
@@ -322,69 +340,70 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
 | 
			
		||||
{
 | 
			
		||||
	unsigned int pt;
 | 
			
		||||
	char codec_resp[64];
 | 
			
		||||
	unsigned int codec;
 | 
			
		||||
	enum mgcp_codecs codec;
 | 
			
		||||
 | 
			
		||||
#define A_PTIME "a=ptime:"
 | 
			
		||||
#define A_RTPMAP "a=rtpmap:"
 | 
			
		||||
 | 
			
		||||
	if (strstr(line, "ptime")) {
 | 
			
		||||
		if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
 | 
			
		||||
			goto response_parse_failure_ptime;
 | 
			
		||||
	} else if (strstr(line, "rtpmap")) {
 | 
			
		||||
		if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) {
 | 
			
		||||
			/* The MGW may assign an own payload type in the
 | 
			
		||||
			 * response if the choosen codec falls into the IANA
 | 
			
		||||
			 * assigned dynamic payload type range (96-127).
 | 
			
		||||
			 * Normally the MGW should obey the 3gpp payload type
 | 
			
		||||
			 * assignments, which are fixed, so we likely wont see
 | 
			
		||||
			 * anything unexpected here. In order to be sure that
 | 
			
		||||
			 * we will now check the codec string and if the result
 | 
			
		||||
			 * does not match to what is IANA / 3gpp assigned, we
 | 
			
		||||
			 * will create an entry in the ptmap table so we can
 | 
			
		||||
			 * lookup later what has been assigned. */
 | 
			
		||||
			codec = map_str_to_codec(codec_resp);
 | 
			
		||||
			if (codec != pt) {
 | 
			
		||||
				if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) {
 | 
			
		||||
					r->ptmap[r->ptmap_len].pt = pt;
 | 
			
		||||
					r->ptmap[r->ptmap_len].codec = codec;
 | 
			
		||||
					r->ptmap_len++;
 | 
			
		||||
				} else
 | 
			
		||||
					goto response_parse_failure_rtpmap;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else
 | 
			
		||||
			goto response_parse_failure_rtpmap;
 | 
			
		||||
	if (osmo_str_startswith(line, A_PTIME)) {
 | 
			
		||||
		if (sscanf(line, A_PTIME "%u", &r->ptime) != 1) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Failed to parse SDP parameter, invalid ptime (%s)\n", line);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (osmo_str_startswith(line, A_RTPMAP)) {
 | 
			
		||||
		if (sscanf(line, A_RTPMAP "%d %63s", &pt, codec_resp) != 2) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Failed to parse SDP parameter, invalid rtpmap: %s\n", osmo_quote_str(line, -1));
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		if (r->ptmap_len >= ARRAY_SIZE(r->ptmap)) {
 | 
			
		||||
			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);
 | 
			
		||||
		r->ptmap[r->ptmap_len].pt = pt;
 | 
			
		||||
		r->ptmap[r->ptmap_len].codec = codec;
 | 
			
		||||
		r->ptmap_len++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
response_parse_failure_ptime:
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
	     "Failed to parse SDP parameter, invalid ptime (%s)\n", line);
 | 
			
		||||
	return -EINVAL;		
 | 
			
		||||
response_parse_failure_rtpmap:
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
	     "Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
 | 
			
		||||
	return -EINVAL;		
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Parse a line like "c=IN IP4 10.11.12.13" */
 | 
			
		||||
static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line)
 | 
			
		||||
{
 | 
			
		||||
	struct in_addr ip_test;
 | 
			
		||||
	struct in6_addr ip_test;
 | 
			
		||||
	bool is_ipv6;
 | 
			
		||||
 | 
			
		||||
	if (strlen(line) < 16)
 | 
			
		||||
	if (strncmp("c=IN IP", line, 7) != 0)
 | 
			
		||||
		goto response_parse_failure;
 | 
			
		||||
 | 
			
		||||
	/* The current implementation strictly supports IPV4 only ! */
 | 
			
		||||
	if (memcmp("c=IN IP4 ", line, 9) != 0)
 | 
			
		||||
	line += 7;
 | 
			
		||||
	if (*line == '6')
 | 
			
		||||
		is_ipv6 = true;
 | 
			
		||||
	else if (*line == '4')
 | 
			
		||||
		is_ipv6 = false;
 | 
			
		||||
	else
 | 
			
		||||
		goto response_parse_failure;
 | 
			
		||||
 | 
			
		||||
	/* Extract IP-Address */
 | 
			
		||||
	osmo_strlcpy(r->audio_ip, line + 9, sizeof(r->audio_ip));
 | 
			
		||||
 | 
			
		||||
	/* Check IP-Address */
 | 
			
		||||
	if (inet_aton(r->audio_ip, &ip_test) == 0)
 | 
			
		||||
	line++;
 | 
			
		||||
	if (*line != ' ')
 | 
			
		||||
		goto response_parse_failure;
 | 
			
		||||
	line++;
 | 
			
		||||
 | 
			
		||||
	/* Extract and check IP-Address */
 | 
			
		||||
	if (is_ipv6) {
 | 
			
		||||
		/* 45 = INET6_ADDRSTRLEN -1 */
 | 
			
		||||
		if (sscanf(line, "%45s", r->audio_ip) != 1)
 | 
			
		||||
			goto response_parse_failure;
 | 
			
		||||
		if (inet_pton(AF_INET6, r->audio_ip, &ip_test) != 1)
 | 
			
		||||
			goto response_parse_failure;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* 15 = INET_ADDRSTRLEN -1 */
 | 
			
		||||
		if (sscanf(line, "%15s", r->audio_ip) != 1)
 | 
			
		||||
			goto response_parse_failure;
 | 
			
		||||
		if (inet_pton(AF_INET, r->audio_ip, &ip_test) != 1)
 | 
			
		||||
			goto response_parse_failure;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
response_parse_failure:
 | 
			
		||||
@@ -393,6 +412,37 @@ response_parse_failure:
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Extract OSMUX CID from an MGCP parameter line (string).
 | 
			
		||||
 *  \param[in] line single parameter line from the MGCP message
 | 
			
		||||
 *  \returns OSMUX CID, -1 wildcard, -2 on error
 | 
			
		||||
 * FIXME: This is a copy of function in mgcp_msg.c. Have some common.c file between both libs?
 | 
			
		||||
 */
 | 
			
		||||
static int mgcp_parse_osmux_cid(const char *line)
 | 
			
		||||
{
 | 
			
		||||
	int osmux_cid;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (strcasecmp(line + 2, "Osmux: *") == 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
 | 
			
		||||
		     line);
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (osmux_cid > OSMUX_CID_MAX) { /* OSMUX_CID_MAX from libosmo-netif */
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
 | 
			
		||||
		     osmux_cid, OSMUX_CID_MAX);
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
 | 
			
		||||
 | 
			
		||||
	return osmux_cid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* A new section is marked by a double line break, check a few more
 | 
			
		||||
 * patterns as there may be variants */
 | 
			
		||||
static char *mgcp_find_section_end(char *string)
 | 
			
		||||
@@ -434,9 +484,8 @@ int mgcp_response_parse_params(struct mgcp_response *r)
 | 
			
		||||
	/* Find beginning of the parameter (SDP) section */
 | 
			
		||||
	data_ptr = mgcp_find_section_end(data);
 | 
			
		||||
	if (!data_ptr) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "MGCP response: cannot find start of SDP parameters\n");
 | 
			
		||||
		rc = -EINVAL;
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "MGCP response contains no SDP parameters\n");
 | 
			
		||||
		rc = 0;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -538,7 +587,7 @@ static int parse_head_params(struct mgcp_response *r)
 | 
			
		||||
		*data_end = '\0';
 | 
			
		||||
 | 
			
		||||
	for_each_non_empty_line(line, data_ptr) {
 | 
			
		||||
		switch (line[0]) {
 | 
			
		||||
		switch (toupper(line[0])) {
 | 
			
		||||
		case 'Z':
 | 
			
		||||
			rc = mgcp_parse_head_param(r->head.endpoint,
 | 
			
		||||
						   sizeof(r->head.endpoint),
 | 
			
		||||
@@ -567,6 +616,21 @@ static int parse_head_params(struct mgcp_response *r)
 | 
			
		||||
			if (rc)
 | 
			
		||||
				goto exit;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'X':
 | 
			
		||||
			if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
 | 
			
		||||
				rc = mgcp_parse_osmux_cid(line);
 | 
			
		||||
				if (rc < 0) {
 | 
			
		||||
					/* -1: we don't want wildcards in response. -2: error */
 | 
			
		||||
					rc = -EINVAL;
 | 
			
		||||
					goto exit;
 | 
			
		||||
				}
 | 
			
		||||
				r->head.x_osmo_osmux_use = true;
 | 
			
		||||
				r->head.x_osmo_osmux_cid = (uint8_t) rc;
 | 
			
		||||
				rc = 0;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			/* Ignore unknown X-headers */
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* skip unhandled parameters */
 | 
			
		||||
			break;
 | 
			
		||||
@@ -689,6 +753,8 @@ struct mgcp_client *mgcp_client_init(void *ctx,
 | 
			
		||||
	struct mgcp_client *mgcp;
 | 
			
		||||
 | 
			
		||||
	mgcp = talloc_zero(ctx, struct mgcp_client);
 | 
			
		||||
	if (!mgcp)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&mgcp->responses_pending);
 | 
			
		||||
	INIT_LLIST_HEAD(&mgcp->inuse_endpoints);
 | 
			
		||||
@@ -730,7 +796,7 @@ static int init_socket(struct mgcp_client *mgcp)
 | 
			
		||||
 | 
			
		||||
		/* Initalize socket with the currently configured port
 | 
			
		||||
		 * number */
 | 
			
		||||
		rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
 | 
			
		||||
		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)
 | 
			
		||||
@@ -759,7 +825,6 @@ static int init_socket(struct mgcp_client *mgcp)
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on error. */
 | 
			
		||||
int mgcp_client_connect(struct mgcp_client *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	struct osmo_wqueue *wq;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
@@ -769,6 +834,11 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
@@ -779,14 +849,6 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
 | 
			
		||||
		goto error_close_fd;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
 | 
			
		||||
	mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
 | 
			
		||||
 | 
			
		||||
	osmo_wqueue_init(wq, 10);
 | 
			
		||||
	wq->bfd.when = BSC_FD_READ;
 | 
			
		||||
	wq->bfd.data = mgcp;
 | 
			
		||||
	wq->read_cb = mgcp_do_read;
 | 
			
		||||
	wq->write_cb = mgcp_do_write;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s\n", osmo_sock_get_name2(wq->bfd.fd));
 | 
			
		||||
 | 
			
		||||
@@ -813,12 +875,13 @@ uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp)
 | 
			
		||||
	return mgcp->actual.remote_port;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Get the IP-Aaddress of the associated MGW as its numeric representation.
 | 
			
		||||
/*! Get the IP-Address of the associated MGW as its numeric representation.
 | 
			
		||||
 *  DEPRECATED, DON'T USE.
 | 
			
		||||
 *  \param[in] mgcp MGCP client descriptor.
 | 
			
		||||
 *  \returns IP-Address as 32 bit integer (network byte order) */
 | 
			
		||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	return mgcp->remote_addr;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* To compose endpoint names, usually for CRCX, use this as domain name.
 | 
			
		||||
@@ -828,16 +891,15 @@ const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp)
 | 
			
		||||
	return mgcp->actual.endpoint_domain_name[0] ? mgcp->actual.endpoint_domain_name : "mgw";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
 | 
			
		||||
static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, char *name)
 | 
			
		||||
{
 | 
			
		||||
	static char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
#define RTPBRIDGE_WILDCARD_FMT "rtpbridge/*@%s"
 | 
			
		||||
	rc = snprintf(endpoint, sizeof(endpoint), RTPBRIDGE_WILDCARD_FMT, mgcp_client_endpoint_domain(mgcp));
 | 
			
		||||
	rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp));
 | 
			
		||||
	if (rc > sizeof(endpoint) - 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '" RTPBRIDGE_WILDCARD_FMT "'\n",
 | 
			
		||||
		     sizeof(endpoint) - 1, mgcp_client_endpoint_domain(mgcp));
 | 
			
		||||
		LOGP(DLMGCP, 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) {
 | 
			
		||||
@@ -847,6 +909,66 @@ const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
 | 
			
		||||
	return endpoint;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Compose endpoint name for a wildcarded request to the virtual trunk
 | 
			
		||||
 *  \param[in] mgcp MGCP client descriptor.
 | 
			
		||||
 *  \returns string containing the endpoint name (e.g. rtpbridge\*@mgw) */
 | 
			
		||||
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	return _mgcp_client_name_append_domain(mgcp, "rtpbridge/*");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Compose endpoint name for an E1 endpoint.
 | 
			
		||||
 *  \param[in] ctx talloc context.
 | 
			
		||||
 *  \param[in] mgcp MGCP client descriptor.
 | 
			
		||||
 *  \param[in] trunk_id id number of the E1 trunk (1-64).
 | 
			
		||||
 *  \param[in] ts timeslot on the E1 trunk (1-31).
 | 
			
		||||
 *  \param[in] rate bitrate used on the E1 trunk (e.g 16 for 16kbit).
 | 
			
		||||
 *  \param[in] offset bit offset of the E1 subslot (e.g. 4 for the third 16k subslot).
 | 
			
		||||
 *  \returns string containing the endpoint name (e.g. ds/e1-1/s-1/su16-4). */
 | 
			
		||||
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
 | 
			
		||||
				  uint8_t rate, uint8_t offset)
 | 
			
		||||
{
 | 
			
		||||
	/* See also comment in libosmo-mgcp, mgcp_client.c, gen_e1_epname() */
 | 
			
		||||
	const uint8_t valid_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
 | 
			
		||||
	const uint8_t valid_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
 | 
			
		||||
 | 
			
		||||
	uint8_t i;
 | 
			
		||||
	bool rate_offs_valid = false;
 | 
			
		||||
	char *epname;
 | 
			
		||||
 | 
			
		||||
	epname =
 | 
			
		||||
	    talloc_asprintf(ctx, "ds/e1-%u/s-%u/su%u-%u@%s", trunk_id, ts, rate, offset,
 | 
			
		||||
			    mgcp_client_endpoint_domain(mgcp));
 | 
			
		||||
	if (!epname) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP e1-endpoint name!\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if the supplied rate/offset pair resembles a valid combination */
 | 
			
		||||
	for (i = 0; i < sizeof(valid_rates); i++) {
 | 
			
		||||
		if (valid_rates[i] == rate && valid_offsets[i] == offset)
 | 
			
		||||
			rate_offs_valid = true;
 | 
			
		||||
	}
 | 
			
		||||
	if (!rate_offs_valid) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Cannot compose MGCP e1-endpoint name (%s), rate(%u)/offset(%u) combination is invalid!\n", epname,
 | 
			
		||||
		     rate, offset);
 | 
			
		||||
		talloc_free(epname);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* An E1 line has a maximum of 32 timeslots, while the first (ts=0) is
 | 
			
		||||
	 * reserverd for framing and alignment, so we can not use it here. */
 | 
			
		||||
	if (ts == 0 || ts > NUM_E1_TS-1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Cannot compose MGCP e1-endpoint name (%s), E1-timeslot number (%u) is invalid!\n", epname, ts);
 | 
			
		||||
		talloc_free(epname);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return epname;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mgcp_response_pending * mgcp_client_pending_add(
 | 
			
		||||
					struct mgcp_client *mgcp,
 | 
			
		||||
					mgcp_trans_id_t trans_id,
 | 
			
		||||
@@ -856,6 +978,9 @@ struct mgcp_response_pending * mgcp_client_pending_add(
 | 
			
		||||
	struct mgcp_response_pending *pending;
 | 
			
		||||
 | 
			
		||||
	pending = talloc_zero(mgcp, struct mgcp_response_pending);
 | 
			
		||||
	if (!pending)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	pending->trans_id = trans_id;
 | 
			
		||||
	pending->response_cb = response_cb;
 | 
			
		||||
	pending->priv = priv;
 | 
			
		||||
@@ -875,7 +1000,7 @@ struct mgcp_response_pending * mgcp_client_pending_add(
 | 
			
		||||
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
 | 
			
		||||
		   mgcp_response_cb_t response_cb, void *priv)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_response_pending *pending;
 | 
			
		||||
	struct mgcp_response_pending *pending = NULL;
 | 
			
		||||
	mgcp_trans_id_t trans_id;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
@@ -887,7 +1012,14 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
 | 
			
		||||
	/* Do not allocate a dummy 'mgcp_response_pending' entry */
 | 
			
		||||
	if (response_cb != NULL) {
 | 
			
		||||
		pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
 | 
			
		||||
		if (!pending) {
 | 
			
		||||
			talloc_free(msg);
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (msgb_l2len(msg) > 4096) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
@@ -909,9 +1041,13 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
mgcp_tx_error:
 | 
			
		||||
	if (!pending)
 | 
			
		||||
		return rc;
 | 
			
		||||
	/* Dequeue pending response, it's going to be free()d */
 | 
			
		||||
	llist_del(&pending->entry);
 | 
			
		||||
	/* Pass NULL to response cb to indicate an error */
 | 
			
		||||
	mgcp_client_handle_response(mgcp, pending, NULL);
 | 
			
		||||
	return -1;
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Cancel a pending transaction.
 | 
			
		||||
@@ -978,7 +1114,7 @@ static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
 | 
			
		||||
		rc += msgb_printf(msg, " a:");
 | 
			
		||||
		for (i = 0; i < mgcp_msg->codecs_len; i++) {
 | 
			
		||||
			pt = mgcp_msg->codecs[i];
 | 
			
		||||
			codec = get_value_string_or_null(codec_table, pt);
 | 
			
		||||
			codec = get_value_string_or_null(osmo_mgcpc_codec_names, pt);
 | 
			
		||||
 | 
			
		||||
			/* Note: Use codec descriptors from enum mgcp_codecs
 | 
			
		||||
			 * in mgcp_client only! */
 | 
			
		||||
@@ -1000,7 +1136,8 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	char local_ip[INET_ADDRSTRLEN];
 | 
			
		||||
	char local_ip[INET6_ADDRSTRLEN];
 | 
			
		||||
	int local_ip_family, audio_ip_family;
 | 
			
		||||
	const char *codec;
 | 
			
		||||
	unsigned int pt;
 | 
			
		||||
 | 
			
		||||
@@ -1017,10 +1154,21 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
	local_ip_family = osmo_ip_str_type(local_ip);
 | 
			
		||||
	if (local_ip_family == AF_UNSPEC) {
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
	audio_ip_family = osmo_ip_str_type(mgcp_msg->audio_ip);
 | 
			
		||||
	if (audio_ip_family == AF_UNSPEC) {
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Add owner/creator (SDP) */
 | 
			
		||||
	rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
 | 
			
		||||
			  mgcp_msg->call_id, local_ip);
 | 
			
		||||
	rc += msgb_printf(msg, "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");
 | 
			
		||||
@@ -1038,7 +1186,9 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
	rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
 | 
			
		||||
	rc += msgb_printf(msg, "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");
 | 
			
		||||
@@ -1051,6 +1201,20 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
 | 
			
		||||
	}
 | 
			
		||||
	rc += msgb_printf(msg, "\r\n");
 | 
			
		||||
 | 
			
		||||
	/* Add optional codec parameters (fmtp) */
 | 
			
		||||
	if (mgcp_msg->param_present) {
 | 
			
		||||
		for (i = 0; i < mgcp_msg->codecs_len; i++) {
 | 
			
		||||
			/* The following is only applicable for AMR */
 | 
			
		||||
			if (mgcp_msg->codecs[i] != CODEC_AMR_8000_1 && mgcp_msg->codecs[i] != CODEC_AMRWB_16000_1)
 | 
			
		||||
				   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);
 | 
			
		||||
			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);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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]);
 | 
			
		||||
 | 
			
		||||
@@ -1058,7 +1222,7 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
 | 
			
		||||
		 * require to be explained further via rtpmap. All others
 | 
			
		||||
		 * are implcitly definedby the number in m=audio */
 | 
			
		||||
		if (pt >= 96 && pt <= 127) {
 | 
			
		||||
			codec = get_value_string_or_null(codec_table, mgcp_msg->codecs[i]);
 | 
			
		||||
			codec = get_value_string_or_null(osmo_mgcpc_codec_names, mgcp_msg->codecs[i]);
 | 
			
		||||
 | 
			
		||||
			/* Note: Use codec descriptors from enum mgcp_codecs
 | 
			
		||||
			 * in mgcp_client only! */
 | 
			
		||||
@@ -1086,6 +1250,7 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int rc_sdp;
 | 
			
		||||
	bool use_sdp = false;
 | 
			
		||||
	char buf[32];
 | 
			
		||||
 | 
			
		||||
	msg->l2h = msg->data;
 | 
			
		||||
	msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
 | 
			
		||||
@@ -1141,6 +1306,7 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
 | 
			
		||||
			     "Endpoint name (%s) lacks separator (@), can not generate MGCP message\n",
 | 
			
		||||
			     mgcp_msg->endpoint);
 | 
			
		||||
			msgb_free(msg);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rc += msgb_printf(msg, " %s", mgcp_msg->endpoint);
 | 
			
		||||
@@ -1189,6 +1355,22 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
 | 
			
		||||
		    msgb_printf(msg, 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) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Wrong Osmux CID %d, can not generate MGCP message\n",
 | 
			
		||||
			     mgcp_msg->x_osmo_osmux_cid);
 | 
			
		||||
			msgb_free(msg);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
		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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* Add session description protocol (SDP) */
 | 
			
		||||
	if (use_sdp
 | 
			
		||||
	    && (mgcp_msg->verb == MGCP_VERB_CRCX
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										990
									
								
								src/libosmo-mgcp-client/mgcp_client_endpoint_fsm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										990
									
								
								src/libosmo-mgcp-client/mgcp_client_endpoint_fsm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,990 @@
 | 
			
		||||
/* FSM to manage multiple connections of an MGW endpoint
 | 
			
		||||
 *
 | 
			
		||||
 * (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Hofmeyr <neels@hofmeyr.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/fsm.h>
 | 
			
		||||
#include <osmocom/core/byteswap.h>
 | 
			
		||||
#include <osmocom/core/tdef.h>
 | 
			
		||||
#include <osmocom/core/sockaddr_str.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
 | 
			
		||||
 | 
			
		||||
#define LOG_CI(ci, level, fmt, args...) do { \
 | 
			
		||||
	if (!ci || !ci->ep) \
 | 
			
		||||
		LOGP(DLGLOBAL, level, "(unknown MGW endpoint) " fmt, ## args); \
 | 
			
		||||
	else \
 | 
			
		||||
		LOG_MGCPC_EP(ci->ep, level, "CI[%d] %s%s%s: " fmt, \
 | 
			
		||||
			(int)(ci - ci->ep->ci), \
 | 
			
		||||
			ci->label ? : "-", \
 | 
			
		||||
			ci->mgcp_ci_str[0] ? " CI=" : "", \
 | 
			
		||||
			ci->mgcp_ci_str[0] ? ci->mgcp_ci_str : "", \
 | 
			
		||||
			## args); \
 | 
			
		||||
	} while(0)
 | 
			
		||||
 | 
			
		||||
#define LOG_CI_VERB(ci, level, fmt, args...) do { \
 | 
			
		||||
	if (ci->verb_info.addr[0]) \
 | 
			
		||||
		LOG_CI(ci, level, "%s %s:%u: " fmt, \
 | 
			
		||||
			osmo_mgcp_verb_name(ci->verb), ci->verb_info.addr, ci->verb_info.port, \
 | 
			
		||||
			## args); \
 | 
			
		||||
	else \
 | 
			
		||||
		LOG_CI(ci, level, "%s: " fmt, \
 | 
			
		||||
			osmo_mgcp_verb_name(ci->verb), \
 | 
			
		||||
			## args); \
 | 
			
		||||
	} while(0)
 | 
			
		||||
 | 
			
		||||
enum osmo_mgcpc_ep_fsm_state {
 | 
			
		||||
	OSMO_MGCPC_EP_ST_UNUSED = 0,
 | 
			
		||||
	OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE,
 | 
			
		||||
	OSMO_MGCPC_EP_ST_IN_USE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmo_mgcpc_ep_fsm_event {
 | 
			
		||||
	_OSMO_MGCPC_EP_EV_LAST = 0,
 | 
			
		||||
	/* and MGW response events are allocated dynamically */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define FIRST_CI_EVENT (_OSMO_MGCPC_EP_EV_LAST + (_OSMO_MGCPC_EP_EV_LAST & 1)) /* rounded up to even nr */
 | 
			
		||||
#define USABLE_CI ((32 - FIRST_CI_EVENT)/2)
 | 
			
		||||
#define EV_TO_CI_IDX(event) ((event - FIRST_CI_EVENT) / 2)
 | 
			
		||||
 | 
			
		||||
#define CI_EV_SUCCESS(ci) (FIRST_CI_EVENT + (((ci) - ci->ep->ci) * 2))
 | 
			
		||||
#define CI_EV_FAILURE(ci) (CI_EV_SUCCESS(ci) + 1)
 | 
			
		||||
 | 
			
		||||
static struct osmo_fsm osmo_mgcpc_ep_fsm;
 | 
			
		||||
 | 
			
		||||
struct fsm_notify {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	uint32_t success;
 | 
			
		||||
	uint32_t failure;
 | 
			
		||||
	void *data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! One connection on an endpoint, corresponding to a connection identifier (CI) as returned by the MGW.
 | 
			
		||||
 * An endpoint has a fixed number of slots of these, which may or may not be in use.
 | 
			
		||||
 */
 | 
			
		||||
struct osmo_mgcpc_ep_ci {
 | 
			
		||||
	struct osmo_mgcpc_ep *ep;
 | 
			
		||||
 | 
			
		||||
	bool occupied;
 | 
			
		||||
	char label[64];
 | 
			
		||||
	struct osmo_fsm_inst *mgcp_client_fi;
 | 
			
		||||
 | 
			
		||||
	bool pending;
 | 
			
		||||
	bool sent;
 | 
			
		||||
	enum mgcp_verb verb;
 | 
			
		||||
	struct mgcp_conn_peer verb_info;
 | 
			
		||||
	struct fsm_notify notify;
 | 
			
		||||
 | 
			
		||||
	bool got_port_info;
 | 
			
		||||
	struct mgcp_conn_peer rtp_info;
 | 
			
		||||
	char mgcp_ci_str[MGCP_CONN_ID_LENGTH];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! An MGW endpoint with N connections, like "rtpbridge/23@mgw". */
 | 
			
		||||
struct osmo_mgcpc_ep {
 | 
			
		||||
	/*! MGCP client connection to the MGW. */
 | 
			
		||||
	struct mgcp_client *mgcp_client;
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
 | 
			
		||||
	/*! Endpoint string; at first this might be a wildcard, and upon the first CRCX OK response, this will reflect
 | 
			
		||||
	 * the endpoint name returned by the MGW. */
 | 
			
		||||
	char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
 | 
			
		||||
	/*! Timeout definitions used for this endpoint, see osmo_mgcpc_ep_fsm_timeouts. */
 | 
			
		||||
	const struct osmo_tdef *T_defs;
 | 
			
		||||
 | 
			
		||||
	/*! True as soon as the first CRCX OK is received. The endpoint name may be determined by the first CRCX
 | 
			
		||||
	 * response, so to dispatch any other messages, the FSM instance *must* wait for the first CRCX OK to arrive
 | 
			
		||||
	 * first. Once the endpoint name is pinpointed, any amount of operations may be dispatched concurrently. */
 | 
			
		||||
	bool first_crcx_complete;
 | 
			
		||||
 | 
			
		||||
	/*! Endpoint connection slots. Note that each connection has its own set of FSM event numbers to signal success
 | 
			
		||||
	 * and failure, depending on its index within this array. See CI_EV_SUCCESS and CI_EV_FAILURE. */
 | 
			
		||||
	struct osmo_mgcpc_ep_ci ci[USABLE_CI];
 | 
			
		||||
 | 
			
		||||
	/*! Internal use: if a function keeps an fsm_notify for later dispatch while already clearing or re-using the
 | 
			
		||||
	 * ci[], the fsm_notify should be kept here to also get canceled by osmo_mgcpc_ep_cancel_notify(). */
 | 
			
		||||
	struct llist_head background_notify;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct value_string osmo_mgcp_verb_names[] = {
 | 
			
		||||
	{ MGCP_VERB_CRCX, "CRCX" },
 | 
			
		||||
	{ MGCP_VERB_MDCX, "MDCX" },
 | 
			
		||||
	{ MGCP_VERB_DLCX, "DLCX" },
 | 
			
		||||
	{ MGCP_VERB_AUEP, "AUEP" },
 | 
			
		||||
	{ MGCP_VERB_RSIP, "RSIP" },
 | 
			
		||||
	{}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void osmo_mgcpc_ep_count(struct osmo_mgcpc_ep *ep, int *occupied, int *pending_not_sent,
 | 
			
		||||
				int *waiting_for_response);
 | 
			
		||||
 | 
			
		||||
static struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_check_ci(struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	if (!ci)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	if (!ci->ep)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	if (ci < ci->ep->ci || ci >= &ci->ep->ci[USABLE_CI])
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return ci;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_for_event(struct osmo_mgcpc_ep *ep, uint32_t event)
 | 
			
		||||
{
 | 
			
		||||
	int idx;
 | 
			
		||||
	if (event < FIRST_CI_EVENT)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	idx = EV_TO_CI_IDX(event);
 | 
			
		||||
	if (idx >= sizeof(ep->ci))
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return osmo_mgcpc_ep_check_ci(&ep->ci[idx]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep)
 | 
			
		||||
{
 | 
			
		||||
	if (!ep)
 | 
			
		||||
		return "NULL";
 | 
			
		||||
	if (ep->endpoint[0])
 | 
			
		||||
		return ep->endpoint;
 | 
			
		||||
	return osmo_fsm_inst_name(ep->fi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *mgcp_conn_peer_name(const struct mgcp_conn_peer *info)
 | 
			
		||||
{
 | 
			
		||||
	/* I'd be fine with a smaller buffer and accept truncation, but gcc possibly refuses to build if
 | 
			
		||||
	 * this buffer is too small. */
 | 
			
		||||
	static char buf[1024];
 | 
			
		||||
 | 
			
		||||
	if (!info)
 | 
			
		||||
		return "NULL";
 | 
			
		||||
 | 
			
		||||
	if (info->endpoint[0]
 | 
			
		||||
	    && info->addr[0])
 | 
			
		||||
		snprintf(buf, sizeof(buf), "%s:%s:%u",
 | 
			
		||||
			 info->endpoint, info->addr, info->port);
 | 
			
		||||
	else if (info->endpoint[0])
 | 
			
		||||
		snprintf(buf, sizeof(buf), "%s", info->endpoint);
 | 
			
		||||
	else if (info->addr[0])
 | 
			
		||||
		snprintf(buf, sizeof(buf), "%s:%u", info->addr, info->port);
 | 
			
		||||
	else
 | 
			
		||||
		return "empty";
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	const struct mgcp_conn_peer *rtp_info;
 | 
			
		||||
 | 
			
		||||
	if (!ci)
 | 
			
		||||
		return "NULL";
 | 
			
		||||
 | 
			
		||||
	rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
 | 
			
		||||
 | 
			
		||||
	if (rtp_info)
 | 
			
		||||
		return mgcp_conn_peer_name(rtp_info);
 | 
			
		||||
	return osmo_mgcpc_ep_name(ci->ep);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	if (!ci || !ci->mgcp_ci_str[0])
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return ci->mgcp_ci_str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct value_string osmo_mgcpc_ep_fsm_event_names[33] = {};
 | 
			
		||||
 | 
			
		||||
static char osmo_mgcpc_ep_fsm_event_name_bufs[32][32] = {};
 | 
			
		||||
 | 
			
		||||
static void fill_event_names()
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) {
 | 
			
		||||
		if (i < _OSMO_MGCPC_EP_EV_LAST)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (i < FIRST_CI_EVENT || EV_TO_CI_IDX(i) > USABLE_CI) {
 | 
			
		||||
			osmo_mgcpc_ep_fsm_event_names[i] = (struct value_string){i, "Unused"};
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		snprintf(osmo_mgcpc_ep_fsm_event_name_bufs[i], sizeof(osmo_mgcpc_ep_fsm_event_name_bufs[i]),
 | 
			
		||||
			 "MGW Response for CI #%d", EV_TO_CI_IDX(i));
 | 
			
		||||
		osmo_mgcpc_ep_fsm_event_names[i] = (struct value_string){i, osmo_mgcpc_ep_fsm_event_name_bufs[i]};
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init()
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0);
 | 
			
		||||
	fill_event_names();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_fi_mgwep(struct osmo_fsm_inst *fi)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(fi);
 | 
			
		||||
	OSMO_ASSERT(fi->fsm == &osmo_mgcpc_ep_fsm);
 | 
			
		||||
	OSMO_ASSERT(fi->priv);
 | 
			
		||||
	return fi->priv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Allocate an osmo_mgcpc_ep FSM.
 | 
			
		||||
 * MGCP messages to set up the endpoint will be sent on the given mgcp_client, as soon as the first
 | 
			
		||||
 * osmo_mgcpc_ep_ci_request() is invoked.
 | 
			
		||||
 *
 | 
			
		||||
 * IMPORTANT: To avoid use-after-free problems, using this FSM requires use of deferred FSM deallocation using
 | 
			
		||||
 * osmo_fsm_set_dealloc_ctx(), e.g. using osmo_select_main_ctx(OTC_SELECT) with osmo_select_main_ctx() as main loop.
 | 
			
		||||
 *
 | 
			
		||||
 * A typical sequence of events would be:
 | 
			
		||||
 *
 | 
			
		||||
 *    ep = osmo_mgcpc_ep_alloc(..., mgcp_client_rtpbridge_wildcard(client));
 | 
			
		||||
 *    ci_to_ran = osmo_mgcpc_ep_ci_add(ep);
 | 
			
		||||
 *    osmo_mgcpc_ep_ci_request(ci_to_ran, MGCP_VERB_CRCX, verb_info,
 | 
			
		||||
 *                             my_call_fsm, MY_EVENT_MGCP_OK, MY_EVENT_MGCP_FAIL);
 | 
			
		||||
 *    ci_to_cn = osmo_mgcpc_ep_ci_add(ep);
 | 
			
		||||
 *    osmo_mgcpc_ep_ci_request(ci_to_cn, MGCP_VERB_CRCX, verb_info,
 | 
			
		||||
 *                             my_call_fsm, MY_EVENT_MGCP_OK, MY_EVENT_MGCP_FAIL);
 | 
			
		||||
 *    ...
 | 
			
		||||
 *    osmo_mgcpc_ep_ci_request(ci_to_ran, MGCP_VERB_MDCX, ...);
 | 
			
		||||
 *    ...
 | 
			
		||||
 *    osmo_mgcpc_ep_clear(ep);
 | 
			
		||||
 *    ep = NULL;
 | 
			
		||||
 *
 | 
			
		||||
 * \param parent  Parent FSM.
 | 
			
		||||
 * \param parent_term_event  Event to dispatch to the parent on termination of this FSM instance.
 | 
			
		||||
 * \param mgcp_client  Connection to the MGW.
 | 
			
		||||
 * \param T_defs  Timeout definitions to be used for FSM states, see osmo_mgcpc_ep_fsm_timeouts.
 | 
			
		||||
 * \param fsm_id  FSM instance ID.
 | 
			
		||||
 * \param endpoint_str_fmt  The endpoint string format to send to the MGW upon the first CRCX.
 | 
			
		||||
 *                          See mgcp_client_rtpbridge_wildcard() for "rtpbridge" endpoints.
 | 
			
		||||
 */
 | 
			
		||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_alloc(struct osmo_fsm_inst *parent, uint32_t parent_term_event,
 | 
			
		||||
					  struct mgcp_client *mgcp_client,
 | 
			
		||||
					  const struct osmo_tdef *T_defs,
 | 
			
		||||
					  const char *fsm_id,
 | 
			
		||||
					  const char *endpoint_str_fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (!mgcp_client)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	fi = osmo_fsm_inst_alloc_child(&osmo_mgcpc_ep_fsm, parent, parent_term_event);
 | 
			
		||||
	OSMO_ASSERT(fi);
 | 
			
		||||
 | 
			
		||||
	osmo_fsm_inst_update_id(fi, fsm_id);
 | 
			
		||||
 | 
			
		||||
	ep = talloc_zero(fi, struct osmo_mgcpc_ep);
 | 
			
		||||
	OSMO_ASSERT(ep);
 | 
			
		||||
 | 
			
		||||
	*ep = (struct osmo_mgcpc_ep){
 | 
			
		||||
		.mgcp_client = mgcp_client,
 | 
			
		||||
		.fi = fi,
 | 
			
		||||
		.T_defs = T_defs,
 | 
			
		||||
	};
 | 
			
		||||
	INIT_LLIST_HEAD(&ep->background_notify);
 | 
			
		||||
	fi->priv = ep;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, endpoint_str_fmt);
 | 
			
		||||
	rc = vsnprintf(ep->endpoint, sizeof(ep->endpoint), endpoint_str_fmt ? : "", ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
 | 
			
		||||
	if (rc <= 0 || rc >= sizeof(ep->endpoint)) {
 | 
			
		||||
		LOG_MGCPC_EP(ep, LOGL_ERROR, "Endpoint name too long or too short: %s\n",
 | 
			
		||||
			  ep->endpoint);
 | 
			
		||||
		osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_ERROR, 0);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ep;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Add a connection to an endpoint.
 | 
			
		||||
 * Allocate a connection identifier slot in the osmo_mgcpc_ep instance, do not yet dispatch a CRCX.
 | 
			
		||||
 * The CRCX is dispatched only upon the first osmo_mgcpc_ep_ci_request().
 | 
			
		||||
 * \param ep  Parent endpoint instance.
 | 
			
		||||
 * \param label_fmt  Label for logging.
 | 
			
		||||
 */
 | 
			
		||||
struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_add(struct osmo_mgcpc_ep *ep,
 | 
			
		||||
					      const char *label_fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	int i;
 | 
			
		||||
	struct osmo_mgcpc_ep_ci *ci;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < USABLE_CI; i++) {
 | 
			
		||||
		ci = &ep->ci[i];
 | 
			
		||||
 | 
			
		||||
		if (ci->occupied || ci->mgcp_client_fi)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		*ci = (struct osmo_mgcpc_ep_ci){
 | 
			
		||||
			.ep = ep,
 | 
			
		||||
			.occupied = true,
 | 
			
		||||
		};
 | 
			
		||||
		if (label_fmt) {
 | 
			
		||||
			va_start(ap, label_fmt);
 | 
			
		||||
			vsnprintf(ci->label, sizeof(ci->label), label_fmt, ap);
 | 
			
		||||
			va_end(ap);
 | 
			
		||||
		}
 | 
			
		||||
		return ci;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG_MGCPC_EP(ep, LOGL_ERROR,
 | 
			
		||||
		  "Cannot allocate another endpoint, all "
 | 
			
		||||
		  OSMO_STRINGIFY_VAL(USABLE_CI) " are in use\n");
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool osmo_mgcpc_ep_fsm_check_state_chg_after_response(struct osmo_fsm_inst *fi);
 | 
			
		||||
 | 
			
		||||
static void on_failure(struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = ci->ep;
 | 
			
		||||
	struct fsm_notify notify;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (!ci->occupied)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* When dispatching an event for this CI, the user may decide to trigger the next request for this conn right
 | 
			
		||||
	 * away. So we must be ready with a cleared *ci. Store the notify separately and clear before dispatching. */
 | 
			
		||||
	notify = ci->notify;
 | 
			
		||||
	/* Register the planned notification in ep->background_notify so we also catch any osmo_mgcpc_ep_cancel_notify()
 | 
			
		||||
	 * that might be triggered between clearing the ci and actually dispatching the event. */
 | 
			
		||||
	llist_add(¬ify.entry, &ep->background_notify);
 | 
			
		||||
 | 
			
		||||
	*ci = (struct osmo_mgcpc_ep_ci){
 | 
			
		||||
		.ep = ci->ep,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/* An MGCP failure typically means the endpoint becomes unusable, cancel all pending request (except DLCX).
 | 
			
		||||
	 * Particularly, if two CRCX were scheduled and the first fails, we must no longer dispatch the second CRCX. */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
 | 
			
		||||
		struct osmo_mgcpc_ep_ci *other_ci = &ep->ci[i];
 | 
			
		||||
		if (other_ci == ci)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!other_ci->occupied)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!other_ci->pending)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (other_ci->sent)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (other_ci->verb == MGCP_VERB_DLCX)
 | 
			
		||||
			continue;
 | 
			
		||||
		/* Just clear the pending request, don't fire more events than below. */
 | 
			
		||||
		other_ci->pending = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* If this check has terminated the FSM instance, don't fire any more events to prevent use-after-free problems.
 | 
			
		||||
	 * The endpoint FSM does dispatch a term event to its parent, and everything should be cleaned like that. */
 | 
			
		||||
	if (!osmo_mgcpc_ep_fsm_check_state_chg_after_response(ep->fi)) {
 | 
			
		||||
		/* The ep has deallocated, no need to llist_del(¬ify.entry) here. */
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (notify.fi)
 | 
			
		||||
		osmo_fsm_inst_dispatch(notify.fi, notify.failure, notify.data);
 | 
			
		||||
 | 
			
		||||
	llist_del(¬ify.entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int update_endpoint_name(struct osmo_mgcpc_ep_ci *ci, const char *new_endpoint_name)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = ci->ep;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (!strcmp(ep->endpoint, new_endpoint_name)) {
 | 
			
		||||
		/* Same endpoint name, nothing to do. */
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* The endpoint name should only change on the very first CRCX response. */
 | 
			
		||||
	if (ep->first_crcx_complete) {
 | 
			
		||||
		LOG_CI(ci, LOGL_ERROR, "Reponse returned mismatching endpoint name."
 | 
			
		||||
		       " This is endpoint %s, instead received %s\n",
 | 
			
		||||
		       ep->endpoint, new_endpoint_name);
 | 
			
		||||
		on_failure(ci);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* This is the first CRCX response, update endpoint name. */
 | 
			
		||||
	rc = OSMO_STRLCPY_ARRAY(ep->endpoint, new_endpoint_name);
 | 
			
		||||
	if (rc <= 0 || rc >= sizeof(ep->endpoint)) {
 | 
			
		||||
		LOG_CI(ci, LOGL_ERROR, "Unable to copy endpoint name %s\n", osmo_quote_str(new_endpoint_name, -1));
 | 
			
		||||
		osmo_mgcpc_ep_ci_dlcx(ci);
 | 
			
		||||
		on_failure(ci);
 | 
			
		||||
		return -ENOSPC;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Make sure already pending requests use this updated endpoint name. */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
 | 
			
		||||
		struct osmo_mgcpc_ep_ci *other_ci = &ep->ci[i];
 | 
			
		||||
		if (!other_ci->occupied)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!other_ci->pending)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (other_ci->sent)
 | 
			
		||||
			continue;
 | 
			
		||||
		OSMO_STRLCPY_ARRAY(other_ci->verb_info.endpoint, ep->endpoint);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn_peer *rtp_info;
 | 
			
		||||
 | 
			
		||||
	if (!ci->occupied)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	ci->pending = false;
 | 
			
		||||
 | 
			
		||||
	rtp_info = data;
 | 
			
		||||
 | 
			
		||||
	switch (ci->verb) {
 | 
			
		||||
	case MGCP_VERB_CRCX:
 | 
			
		||||
		/* If we sent a wildcarded endpoint name on CRCX, we need to store the resulting endpoint
 | 
			
		||||
		 * name here. Also, we receive the MGW's RTP port information. */
 | 
			
		||||
		osmo_strlcpy(ci->mgcp_ci_str, mgcp_conn_get_ci(ci->mgcp_client_fi),
 | 
			
		||||
			sizeof(ci->mgcp_ci_str));
 | 
			
		||||
		if (rtp_info->endpoint[0]) {
 | 
			
		||||
			/* On errors, this instance might already be deallocated. Make sure to not access anything after
 | 
			
		||||
			 * error. */
 | 
			
		||||
			if (update_endpoint_name(ci, rtp_info->endpoint))
 | 
			
		||||
				return;
 | 
			
		||||
		}
 | 
			
		||||
		ci->ep->first_crcx_complete = true;
 | 
			
		||||
		OSMO_ASSERT(rtp_info);
 | 
			
		||||
		/* fall through */
 | 
			
		||||
	case MGCP_VERB_MDCX:
 | 
			
		||||
		/* Always update the received RTP ip/port information, since MGW
 | 
			
		||||
		 * may provide new one after remote end params changed */
 | 
			
		||||
		if (rtp_info) {
 | 
			
		||||
			ci->got_port_info = true;
 | 
			
		||||
			ci->rtp_info = *rtp_info;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG_CI(ci, LOGL_DEBUG, "received successful response to %s: RTP=%s%s\n",
 | 
			
		||||
	       osmo_mgcp_verb_name(ci->verb),
 | 
			
		||||
	       mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL),
 | 
			
		||||
	       ci->notify.fi ? "" : " (not sending a notification)");
 | 
			
		||||
 | 
			
		||||
	if (ci->notify.fi)
 | 
			
		||||
		osmo_fsm_inst_dispatch(ci->notify.fi, ci->notify.success, ci->notify.data);
 | 
			
		||||
 | 
			
		||||
	osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
 | 
			
		||||
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	ci = osmo_mgcpc_ep_check_ci((struct osmo_mgcpc_ep_ci*)ci);
 | 
			
		||||
	if (!ci)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	if (!ci->got_port_info)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return &ci->rtp_info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
 | 
			
		||||
bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest)
 | 
			
		||||
{
 | 
			
		||||
	const struct mgcp_conn_peer *rtp_info;
 | 
			
		||||
	int family;
 | 
			
		||||
	struct sockaddr_in *sin;
 | 
			
		||||
	struct sockaddr_in6 *sin6;
 | 
			
		||||
 | 
			
		||||
	rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
 | 
			
		||||
	if (!rtp_info)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	family = osmo_ip_str_type(rtp_info->addr);
 | 
			
		||||
	switch (family) {
 | 
			
		||||
	case AF_INET:
 | 
			
		||||
		sin = (struct sockaddr_in *)dest;
 | 
			
		||||
		sin->sin_family = AF_INET;
 | 
			
		||||
		sin->sin_port = osmo_ntohs(rtp_info->port);
 | 
			
		||||
		if (inet_pton(AF_INET, rtp_info->addr, &sin->sin_addr) != 1)
 | 
			
		||||
			return false;
 | 
			
		||||
		break;
 | 
			
		||||
	case AF_INET6:
 | 
			
		||||
		sin6 = (struct sockaddr_in6 *)dest;
 | 
			
		||||
		sin6->sin6_family = AF_INET6;
 | 
			
		||||
		sin6->sin6_port = osmo_ntohs(rtp_info->port);
 | 
			
		||||
		if (inet_pton(AF_INET6, rtp_info->addr, &sin6->sin6_addr) != 1)
 | 
			
		||||
			return false;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(const struct osmo_mgcpc_ep_ci *ci, uint8_t* cid)
 | 
			
		||||
{
 | 
			
		||||
	const struct mgcp_conn_peer *rtp_info;
 | 
			
		||||
 | 
			
		||||
	rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
 | 
			
		||||
	if (!rtp_info)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (!rtp_info->x_osmo_osmux_use)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	*cid = rtp_info->x_osmo_osmux_cid;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct osmo_tdef_state_timeout osmo_mgcpc_ep_fsm_timeouts[32] = {
 | 
			
		||||
	[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = { .T=-2427 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Transition to a state, using the T timer defined in assignment_fsm_timeouts.
 | 
			
		||||
 * The actual timeout value is in turn obtained from osmo_mgcpc_ep.T_defs.
 | 
			
		||||
 * Assumes local variable fi exists. */
 | 
			
		||||
#define osmo_mgcpc_ep_fsm_state_chg(state) \
 | 
			
		||||
	osmo_tdef_fsm_inst_state_chg(fi, state, osmo_mgcpc_ep_fsm_timeouts, \
 | 
			
		||||
				     ((struct osmo_mgcpc_ep*)fi->priv)->T_defs, 5)
 | 
			
		||||
 | 
			
		||||
/*! Dispatch an actual CRCX/MDCX/DLCX message for this connection.
 | 
			
		||||
 *
 | 
			
		||||
 * If the 'notify' instance deallocates before it received a notification of event_success or event_failure,
 | 
			
		||||
 * osmo_mgcpc_ep_ci_cancel_notify() or osmo_mgcpc_ep_cancel_notify() must be called. It is not harmful to cancel
 | 
			
		||||
 * notification after an event has been received.
 | 
			
		||||
 *
 | 
			
		||||
 * \param ci  Connection identifier as obtained from osmo_mgcpc_ep_ci_add().
 | 
			
		||||
 * \param verb  MGCP operation to dispatch.
 | 
			
		||||
 * \param verb_info  Parameters for the MGCP operation.
 | 
			
		||||
 * \param notify  Peer FSM instance to notify of completed/failed operation.
 | 
			
		||||
 * \param event_success  Which event to dispatch to 'notify' upon OK response.
 | 
			
		||||
 * \param event_failure  Which event to dispatch to 'notify' upon failure response.
 | 
			
		||||
 * \param notify_data  Data pointer to pass to the event dispatch for both success and failure.
 | 
			
		||||
 */
 | 
			
		||||
void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
 | 
			
		||||
			      enum mgcp_verb verb, const struct mgcp_conn_peer *verb_info,
 | 
			
		||||
			      struct osmo_fsm_inst *notify,
 | 
			
		||||
			      uint32_t event_success, uint32_t event_failure,
 | 
			
		||||
			      void *notify_data)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_mgcpc_ep *ep;
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	struct osmo_mgcpc_ep_ci cleared_ci;
 | 
			
		||||
	ci = osmo_mgcpc_ep_check_ci(ci);
 | 
			
		||||
 | 
			
		||||
	if (!ci) {
 | 
			
		||||
		LOGP(DLGLOBAL, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n");
 | 
			
		||||
		goto dispatch_error;
 | 
			
		||||
	}
 | 
			
		||||
	if (!verb_info && verb != MGCP_VERB_DLCX) {
 | 
			
		||||
		LOG_CI(ci, LOGL_ERROR, "Invalid MGW endpoint request: missing verb details for %s\n",
 | 
			
		||||
		       osmo_mgcp_verb_name(verb));
 | 
			
		||||
		goto dispatch_error;
 | 
			
		||||
	}
 | 
			
		||||
	if ((verb < 0) || (verb > MGCP_VERB_RSIP)) {
 | 
			
		||||
		LOG_CI(ci, LOGL_ERROR, "Invalid MGW endpoint request: unknown verb: %s\n",
 | 
			
		||||
		       osmo_mgcp_verb_name(verb));
 | 
			
		||||
		goto dispatch_error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ep = ci->ep;
 | 
			
		||||
	fi = ep->fi;
 | 
			
		||||
 | 
			
		||||
	/* Clear volatile state by explicitly keeping those that should remain. Because we can't assign
 | 
			
		||||
	 * the char[] directly, dance through cleared_ci and copy back. */
 | 
			
		||||
	cleared_ci = (struct osmo_mgcpc_ep_ci){
 | 
			
		||||
		.ep = ep,
 | 
			
		||||
		.mgcp_client_fi = ci->mgcp_client_fi,
 | 
			
		||||
		.got_port_info = ci->got_port_info,
 | 
			
		||||
		.rtp_info = ci->rtp_info,
 | 
			
		||||
 | 
			
		||||
		.occupied = true,
 | 
			
		||||
		/* .pending = true follows below */
 | 
			
		||||
		.verb = verb,
 | 
			
		||||
		.notify = {
 | 
			
		||||
			.fi = notify,
 | 
			
		||||
			.success = event_success,
 | 
			
		||||
			.failure = event_failure,
 | 
			
		||||
			.data = notify_data,
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
	osmo_strlcpy(cleared_ci.label, ci->label, sizeof(cleared_ci.label));
 | 
			
		||||
	osmo_strlcpy(cleared_ci.mgcp_ci_str, ci->mgcp_ci_str, sizeof(cleared_ci.mgcp_ci_str));
 | 
			
		||||
	*ci = cleared_ci;
 | 
			
		||||
 | 
			
		||||
	LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
 | 
			
		||||
 | 
			
		||||
	if (verb_info)
 | 
			
		||||
		ci->verb_info = *verb_info;
 | 
			
		||||
 | 
			
		||||
	if (ep->endpoint[0]) {
 | 
			
		||||
		if (ci->verb_info.endpoint[0] && strcmp(ci->verb_info.endpoint, ep->endpoint))
 | 
			
		||||
			LOG_CI(ci, LOGL_ERROR,
 | 
			
		||||
			       "Warning: Requested %s on endpoint %s, but this CI is on endpoint %s."
 | 
			
		||||
			       " Using the proper endpoint instead.\n",
 | 
			
		||||
			       osmo_mgcp_verb_name(verb), ci->verb_info.endpoint, ep->endpoint);
 | 
			
		||||
		osmo_strlcpy(ci->verb_info.endpoint, ep->endpoint, sizeof(ci->verb_info.endpoint));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (ci->verb) {
 | 
			
		||||
	case MGCP_VERB_CRCX:
 | 
			
		||||
		if (ci->mgcp_client_fi) {
 | 
			
		||||
			LOG_CI(ci, LOGL_ERROR, "CRCX can be called only once per MGW endpoint CI\n");
 | 
			
		||||
			on_failure(ci);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MGCP_VERB_MDCX:
 | 
			
		||||
		if (!ci->mgcp_client_fi) {
 | 
			
		||||
			LOG_CI_VERB(ci, LOGL_ERROR, "The first verb on an unused MGW endpoint CI must be CRCX, not %s\n",
 | 
			
		||||
				    osmo_mgcp_verb_name(ci->verb));
 | 
			
		||||
			on_failure(ci);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MGCP_VERB_DLCX:
 | 
			
		||||
		if (!ci->mgcp_client_fi) {
 | 
			
		||||
			LOG_CI_VERB(ci, LOGL_DEBUG, "Ignoring DLCX on unused MGW endpoint CI\n");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		LOG_CI(ci, LOGL_ERROR, "This verb is not supported: %s\n", osmo_mgcp_verb_name(ci->verb));
 | 
			
		||||
		on_failure(ci);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ci->pending = true;
 | 
			
		||||
 | 
			
		||||
	LOG_CI_VERB(ci, LOGL_DEBUG, "Scheduling\n");
 | 
			
		||||
 | 
			
		||||
	if (ep->fi->state != OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE)
 | 
			
		||||
		osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
dispatch_error:
 | 
			
		||||
	if (notify)
 | 
			
		||||
		osmo_fsm_inst_dispatch(notify, event_failure, notify_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! No longer notify for any state changes for any conns of this endpoint.
 | 
			
		||||
 * Useful if the notify instance passed to osmo_mgcpc_ep_ci_request() is about to deallocate.
 | 
			
		||||
 * \param ep  The endpoint FSM instance.
 | 
			
		||||
 * \param notify  Which target to cancel notification for, if NULL cancel all notifications. */
 | 
			
		||||
void osmo_mgcpc_ep_cancel_notify(struct osmo_mgcpc_ep *ep, struct osmo_fsm_inst *notify)
 | 
			
		||||
{
 | 
			
		||||
	struct fsm_notify *n;
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
 | 
			
		||||
		struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
 | 
			
		||||
		if (!notify || ci->notify.fi == notify)
 | 
			
		||||
			ci->notify.fi = NULL;
 | 
			
		||||
	}
 | 
			
		||||
	llist_for_each_entry(n, &ep->background_notify, entry) {
 | 
			
		||||
		if (!notify || n->fi == notify)
 | 
			
		||||
			n->fi = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return the osmo_mgcpc_ep that this conn belongs to. */
 | 
			
		||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_ci_ep(struct osmo_mgcpc_ep_ci *conn)
 | 
			
		||||
{
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return conn->ep;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int send_verb(struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = ci->ep;
 | 
			
		||||
	struct fsm_notify notify;
 | 
			
		||||
 | 
			
		||||
	if (!ci->occupied || !ci->pending || ci->sent)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	switch (ci->verb) {
 | 
			
		||||
 | 
			
		||||
	case MGCP_VERB_CRCX:
 | 
			
		||||
		OSMO_ASSERT(!ci->mgcp_client_fi);
 | 
			
		||||
		LOG_CI_VERB(ci, LOGL_DEBUG, "Sending\n");
 | 
			
		||||
		ci->mgcp_client_fi = mgcp_conn_create(ep->mgcp_client, ep->fi,
 | 
			
		||||
						      CI_EV_FAILURE(ci), CI_EV_SUCCESS(ci),
 | 
			
		||||
						      &ci->verb_info);
 | 
			
		||||
		ci->sent = true;
 | 
			
		||||
		if (!ci->mgcp_client_fi){
 | 
			
		||||
			LOG_CI_VERB(ci, LOGL_ERROR, "Cannot send\n");
 | 
			
		||||
			on_failure(ci);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		osmo_fsm_inst_update_id(ci->mgcp_client_fi, ci->label);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MGCP_VERB_MDCX:
 | 
			
		||||
		OSMO_ASSERT(ci->mgcp_client_fi);
 | 
			
		||||
		LOG_CI_VERB(ci, LOGL_DEBUG, "Sending\n");
 | 
			
		||||
		rc = mgcp_conn_modify(ci->mgcp_client_fi, CI_EV_SUCCESS(ci), &ci->verb_info);
 | 
			
		||||
		ci->sent = true;
 | 
			
		||||
		if (rc) {
 | 
			
		||||
			LOG_CI_VERB(ci, LOGL_ERROR, "Cannot send (rc=%d %s)\n", rc, strerror(-rc));
 | 
			
		||||
			on_failure(ci);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MGCP_VERB_DLCX:
 | 
			
		||||
		LOG_CI(ci, LOGL_DEBUG, "Sending MGCP: %s %s\n",
 | 
			
		||||
		       osmo_mgcp_verb_name(ci->verb), ci->mgcp_ci_str);
 | 
			
		||||
		/* The way this is designed, we actually need to forget all about the ci right away. */
 | 
			
		||||
		mgcp_conn_delete(ci->mgcp_client_fi);
 | 
			
		||||
		notify = ci->notify;
 | 
			
		||||
		*ci = (struct osmo_mgcpc_ep_ci){
 | 
			
		||||
			.ep = ep,
 | 
			
		||||
		};
 | 
			
		||||
		/* When dispatching an event for this CI, the user may decide to trigger the next request for this conn
 | 
			
		||||
		 * right away. So we must be ready with a cleared *ci. */
 | 
			
		||||
		if (notify.fi)
 | 
			
		||||
			osmo_fsm_inst_dispatch(notify.fi, notify.success, notify.data);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! DLCX all connections, terminate the endpoint FSM and free. */
 | 
			
		||||
void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep)
 | 
			
		||||
{
 | 
			
		||||
	if (!ep)
 | 
			
		||||
		return;
 | 
			
		||||
	osmo_mgcpc_ep_cancel_notify(ep, NULL);
 | 
			
		||||
	osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_REGULAR, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void osmo_mgcpc_ep_count(struct osmo_mgcpc_ep *ep, int *occupied, int *pending_not_sent,
 | 
			
		||||
				int *waiting_for_response)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (occupied)
 | 
			
		||||
		*occupied = 0;
 | 
			
		||||
 | 
			
		||||
	if (pending_not_sent)
 | 
			
		||||
		*pending_not_sent = 0;
 | 
			
		||||
 | 
			
		||||
	if (waiting_for_response)
 | 
			
		||||
		*waiting_for_response = 0;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
 | 
			
		||||
		struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
 | 
			
		||||
		if (ci->occupied) {
 | 
			
		||||
			if (occupied)
 | 
			
		||||
				(*occupied)++;
 | 
			
		||||
		} else
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (ci->pending)
 | 
			
		||||
			LOG_CI_VERB(ci, LOGL_DEBUG, "%s\n",
 | 
			
		||||
				    ci->sent ? "waiting for response" : "waiting to be sent");
 | 
			
		||||
		else
 | 
			
		||||
			LOG_CI_VERB(ci, LOGL_DEBUG, "done (%s)\n", mgcp_conn_peer_name(osmo_mgcpc_ep_ci_get_rtp_info(ci)));
 | 
			
		||||
 | 
			
		||||
		if (ci->pending && ci->sent)
 | 
			
		||||
			if (waiting_for_response)
 | 
			
		||||
				(*waiting_for_response)++;
 | 
			
		||||
		if (ci->pending && !ci->sent)
 | 
			
		||||
			if (pending_not_sent)
 | 
			
		||||
				(*pending_not_sent)++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool osmo_mgcpc_ep_fsm_check_state_chg_after_response(struct osmo_fsm_inst *fi)
 | 
			
		||||
{
 | 
			
		||||
	int waiting_for_response;
 | 
			
		||||
	int occupied;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
 | 
			
		||||
 | 
			
		||||
	osmo_mgcpc_ep_count(ep, &occupied, NULL, &waiting_for_response);
 | 
			
		||||
	LOG_MGCPC_EP(ep, LOGL_DEBUG, "CI in use: %d, waiting for response: %d\n", occupied, waiting_for_response);
 | 
			
		||||
 | 
			
		||||
	if (!occupied)  {
 | 
			
		||||
		/* All CI have been released. The endpoint no longer exists. Notify the parent FSM, by
 | 
			
		||||
		 * terminating. */
 | 
			
		||||
		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, 0);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!waiting_for_response) {
 | 
			
		||||
		if (fi->state != OSMO_MGCPC_EP_ST_IN_USE)
 | 
			
		||||
			osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_IN_USE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void osmo_mgcpc_ep_fsm_wait_mgw_response_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	static int re_enter = 0;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int count = 0;
 | 
			
		||||
	int i;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
 | 
			
		||||
 | 
			
		||||
	re_enter++;
 | 
			
		||||
	OSMO_ASSERT(re_enter < 10);
 | 
			
		||||
 | 
			
		||||
	/* The first CRCX gives us the endpoint name in the CRCX response. So we must wait for the first CRCX endpoint
 | 
			
		||||
	 * response to come in before sending any other MGCP requests -- otherwise we might end up creating new
 | 
			
		||||
	 * endpoints instead of acting on the same. This FSM always sends out N requests and waits for all of them to
 | 
			
		||||
	 * complete before sending out new requests. Hence we're safe when the very first time at most one request is
 | 
			
		||||
	 * sent (which needs to be a CRCX). */
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
 | 
			
		||||
		struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
 | 
			
		||||
 | 
			
		||||
		/* Make sure that only CRCX get dispatched if no CRCX were sent yet. */
 | 
			
		||||
		if (!ep->first_crcx_complete) {
 | 
			
		||||
			if (ci->occupied && ci->verb != MGCP_VERB_CRCX)
 | 
			
		||||
				continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rc = send_verb(&ep->ci[i]);
 | 
			
		||||
		/* Need to be careful not to access the instance after failure. Event chains may already have
 | 
			
		||||
		 * deallocated this memory. */
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return;
 | 
			
		||||
		if (!rc)
 | 
			
		||||
			continue;
 | 
			
		||||
		count++;
 | 
			
		||||
		/* Make sure that we wait for the first CRCX response before dispatching more requests. */
 | 
			
		||||
		if (!ep->first_crcx_complete)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG_MGCPC_EP(ep, LOGL_DEBUG, "Sent messages: %d\n", count);
 | 
			
		||||
	if (ep->first_crcx_complete)
 | 
			
		||||
		osmo_mgcpc_ep_fsm_check_state_chg_after_response(fi);
 | 
			
		||||
	re_enter--;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void osmo_mgcpc_ep_fsm_handle_ci_events(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_mgcpc_ep_ci *ci;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
 | 
			
		||||
	ci = osmo_mgcpc_ep_ci_for_event(ep, event);
 | 
			
		||||
	if (ci) {
 | 
			
		||||
		if (event == CI_EV_SUCCESS(ci))
 | 
			
		||||
			on_success(ci, data);
 | 
			
		||||
		else
 | 
			
		||||
			on_failure(ci);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void osmo_mgcpc_ep_fsm_in_use_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	int pending_not_sent;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
 | 
			
		||||
 | 
			
		||||
	osmo_mgcpc_ep_count(ep, NULL, &pending_not_sent, NULL);
 | 
			
		||||
	if (pending_not_sent)
 | 
			
		||||
		osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define S(x)	(1 << (x))
 | 
			
		||||
 | 
			
		||||
static const struct osmo_fsm_state osmo_mgcpc_ep_fsm_states[] = {
 | 
			
		||||
	[OSMO_MGCPC_EP_ST_UNUSED] = {
 | 
			
		||||
		.name = "UNUSED",
 | 
			
		||||
		.in_event_mask = 0,
 | 
			
		||||
		.out_state_mask = 0
 | 
			
		||||
			| S(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE)
 | 
			
		||||
			,
 | 
			
		||||
	},
 | 
			
		||||
	[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = {
 | 
			
		||||
		.name = "WAIT_MGW_RESPONSE",
 | 
			
		||||
		.onenter = osmo_mgcpc_ep_fsm_wait_mgw_response_onenter,
 | 
			
		||||
		.action = osmo_mgcpc_ep_fsm_handle_ci_events,
 | 
			
		||||
		.in_event_mask = 0xffffffff,
 | 
			
		||||
		.out_state_mask = 0
 | 
			
		||||
			| S(OSMO_MGCPC_EP_ST_IN_USE)
 | 
			
		||||
			,
 | 
			
		||||
	},
 | 
			
		||||
	[OSMO_MGCPC_EP_ST_IN_USE] = {
 | 
			
		||||
		.name = "IN_USE",
 | 
			
		||||
		.onenter = osmo_mgcpc_ep_fsm_in_use_onenter,
 | 
			
		||||
		.action = osmo_mgcpc_ep_fsm_handle_ci_events,
 | 
			
		||||
		.in_event_mask = 0xffffffff, /* mgcp_client_fsm may send parent term anytime */
 | 
			
		||||
		.out_state_mask = 0
 | 
			
		||||
			| S(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE)
 | 
			
		||||
			,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
 | 
			
		||||
 | 
			
		||||
	switch (fi->T) {
 | 
			
		||||
	default:
 | 
			
		||||
		for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
 | 
			
		||||
			struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
 | 
			
		||||
			if (!ci->occupied)
 | 
			
		||||
				continue;
 | 
			
		||||
			if (!(ci->pending && ci->sent))
 | 
			
		||||
				continue;
 | 
			
		||||
			on_failure(ci);
 | 
			
		||||
		}
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct osmo_fsm osmo_mgcpc_ep_fsm = {
 | 
			
		||||
	.name = "mgw-endp",
 | 
			
		||||
	.states = osmo_mgcpc_ep_fsm_states,
 | 
			
		||||
	.num_states = ARRAY_SIZE(osmo_mgcpc_ep_fsm_states),
 | 
			
		||||
	.log_subsys = DLMGCP,
 | 
			
		||||
	.event_names = osmo_mgcpc_ep_fsm_event_names,
 | 
			
		||||
	.timer_cb = osmo_mgcpc_ep_fsm_timer_cb,
 | 
			
		||||
	/* The FSM termination will automatically trigger any mgcp_client_fsm instances to DLCX. */
 | 
			
		||||
};
 | 
			
		||||
@@ -25,6 +25,7 @@
 | 
			
		||||
#include <osmocom/core/byteswap.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/sockaddr_str.h>
 | 
			
		||||
 | 
			
		||||
/* Context information, this is attached to the priv pointer of the FSM and
 | 
			
		||||
 * is also handed back when dispatcheing events to the parent FSM. This is
 | 
			
		||||
@@ -114,16 +115,23 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
 | 
			
		||||
		.conn_mode = MGCP_CONN_RECV_ONLY,
 | 
			
		||||
		.ptime = info->ptime,
 | 
			
		||||
		.codecs_len = info->codecs_len,
 | 
			
		||||
		.ptmap_len = info->ptmap_len
 | 
			
		||||
		.ptmap_len = info->ptmap_len,
 | 
			
		||||
		.param_present = info->param_present
 | 
			
		||||
	};
 | 
			
		||||
	osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN);
 | 
			
		||||
	memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs));
 | 
			
		||||
	memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap));
 | 
			
		||||
	memcpy(&mgcp_msg->param, &info->param, sizeof(mgcp_msg->param));
 | 
			
		||||
 | 
			
		||||
	if (info->x_osmo_ign) {
 | 
			
		||||
		mgcp_msg->x_osmo_ign = info->x_osmo_ign;
 | 
			
		||||
		mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_IGN;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (info->x_osmo_osmux_use) {
 | 
			
		||||
		mgcp_msg->x_osmo_osmux_cid = info->x_osmo_osmux_cid;
 | 
			
		||||
		mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
 | 
			
		||||
@@ -134,6 +142,13 @@ static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
 | 
			
		||||
	mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer)
 | 
			
		||||
{
 | 
			
		||||
	enum mgcp_connection_mode conn_mode = peer->conn_mode;
 | 
			
		||||
	if (conn_mode != MGCP_CONN_NONE)
 | 
			
		||||
		mgcp_msg->conn_mode = conn_mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_msg mgcp_msg;
 | 
			
		||||
@@ -149,11 +164,20 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
		.audio_port = mgcp_ctx->conn_peer_local.port,
 | 
			
		||||
		.ptime = mgcp_ctx->conn_peer_local.ptime,
 | 
			
		||||
		.codecs_len = mgcp_ctx->conn_peer_local.codecs_len,
 | 
			
		||||
		.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len
 | 
			
		||||
		.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len,
 | 
			
		||||
		.param_present = mgcp_ctx->conn_peer_local.param_present
 | 
			
		||||
	};
 | 
			
		||||
	osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
 | 
			
		||||
	memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
 | 
			
		||||
	memcpy(mgcp_msg.ptmap, mgcp_ctx->conn_peer_local.ptmap, sizeof(mgcp_msg.ptmap));
 | 
			
		||||
	memcpy(&mgcp_msg.param, &mgcp_ctx->conn_peer_local.param, sizeof(mgcp_ctx->conn_peer_local.param));
 | 
			
		||||
 | 
			
		||||
	set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
 | 
			
		||||
 | 
			
		||||
	if (mgcp_ctx->conn_peer_local.x_osmo_osmux_use) {
 | 
			
		||||
		mgcp_msg.x_osmo_osmux_cid = mgcp_ctx->conn_peer_local.x_osmo_osmux_cid;
 | 
			
		||||
		mgcp_msg.presence |= MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Note: We take the endpoint and the call_id from the remote
 | 
			
		||||
	 * connection info, because we can be confident that the
 | 
			
		||||
@@ -199,6 +223,8 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
		make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local);
 | 
			
		||||
		if (mgcp_ctx->conn_peer_local.port)
 | 
			
		||||
			add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
 | 
			
		||||
		set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
 | 
			
		||||
 | 
			
		||||
		msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
 | 
			
		||||
		OSMO_ASSERT(msg);
 | 
			
		||||
 | 
			
		||||
@@ -255,6 +281,11 @@ static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
 | 
			
		||||
	if (r->head.x_osmo_osmux_use) {
 | 
			
		||||
		LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded using Osmux %u\n", r->head.x_osmo_osmux_cid);
 | 
			
		||||
		mgcp_ctx->conn_peer_remote.x_osmo_osmux_use = true;
 | 
			
		||||
		mgcp_ctx->conn_peer_remote.x_osmo_osmux_cid = r->head.x_osmo_osmux_cid;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr));
 | 
			
		||||
	mgcp_ctx->conn_peer_remote.port = r->audio_port;
 | 
			
		||||
@@ -374,6 +405,12 @@ static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv)
 | 
			
		||||
	}
 | 
			
		||||
	LOGPFSML(fi, LOGL_DEBUG, "MGW/MDCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
 | 
			
		||||
 | 
			
		||||
	if (r->head.x_osmo_osmux_use) {
 | 
			
		||||
		LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded using Osmux %u\n", r->head.x_osmo_osmux_cid);
 | 
			
		||||
		mgcp_ctx->conn_peer_remote.x_osmo_osmux_use = true;
 | 
			
		||||
		mgcp_ctx->conn_peer_remote.x_osmo_osmux_cid = r->head.x_osmo_osmux_cid;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr));
 | 
			
		||||
	mgcp_ctx->conn_peer_remote.port = r->audio_port;
 | 
			
		||||
 | 
			
		||||
@@ -486,11 +523,13 @@ static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
 | 
			
		||||
	 * connection. This is not the normal case. The user should always use
 | 
			
		||||
	 * mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
 | 
			
		||||
	if (strlen(mgcp_ctx->conn_id)) {
 | 
			
		||||
		LOGPFSML(fi, LOGL_ERROR,
 | 
			
		||||
			 "MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n");
 | 
			
		||||
		LOGPFSML(fi, LOGL_NOTICE,
 | 
			
		||||
			 "MGW/DLCX: FSM termination with connections still present, sending unconditional DLCX...\n");
 | 
			
		||||
		msg = make_dlcx_msg(mgcp_ctx);
 | 
			
		||||
		OSMO_ASSERT(msg);
 | 
			
		||||
		mgcp_client_tx(mgcp, msg, NULL, NULL);
 | 
			
		||||
		if (!msg)
 | 
			
		||||
			LOGPFSML(fi, LOGL_ERROR, "MGW/DLCX: Error composing DLCX message\n");
 | 
			
		||||
		else
 | 
			
		||||
			mgcp_client_tx(mgcp, msg, NULL, NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(mgcp_ctx);
 | 
			
		||||
@@ -568,24 +607,19 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
 | 
			
		||||
				       uint32_t parent_term_evt, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_ctx *mgcp_ctx;
 | 
			
		||||
	static bool fsm_registered = false;
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	struct in_addr ip_test;
 | 
			
		||||
	struct in6_addr ip_test;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(parent_fi);
 | 
			
		||||
	OSMO_ASSERT(mgcp);
 | 
			
		||||
	OSMO_ASSERT(conn_peer);
 | 
			
		||||
 | 
			
		||||
	/* Check if IP/Port information in conn info makes sense */
 | 
			
		||||
	if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
 | 
			
		||||
	if (conn_peer->port && inet_pton(osmo_ip_str_type(conn_peer->addr),
 | 
			
		||||
					 conn_peer->addr, &ip_test) != 1)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Register the fsm description (if not already done) */
 | 
			
		||||
	if (fsm_registered == false) {
 | 
			
		||||
		osmo_fsm_register(&fsm_mgcp_client);
 | 
			
		||||
		fsm_registered = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Allocate and configure a new fsm instance */
 | 
			
		||||
	fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt);
 | 
			
		||||
	OSMO_ASSERT(fi);
 | 
			
		||||
@@ -613,7 +647,7 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(fi);
 | 
			
		||||
	struct mgcp_ctx *mgcp_ctx = fi->priv;
 | 
			
		||||
	struct in_addr ip_test;
 | 
			
		||||
	struct in6_addr ip_test;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(mgcp_ctx);
 | 
			
		||||
	OSMO_ASSERT(conn_peer);
 | 
			
		||||
@@ -637,8 +671,8 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
 | 
			
		||||
		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	if (inet_aton(conn_peer->addr, &ip_test) == 0) {
 | 
			
		||||
		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n");
 | 
			
		||||
	if (inet_pton(osmo_ip_str_type(conn_peer->addr), conn_peer->addr, &ip_test) != 1) {
 | 
			
		||||
		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address %s\n", conn_peer->addr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -670,6 +704,9 @@ void mgcp_conn_delete(struct osmo_fsm_inst *fi)
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(mgcp_ctx);
 | 
			
		||||
 | 
			
		||||
	if (fi->proc.terminating)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Unlink FSM from parent */
 | 
			
		||||
	osmo_fsm_inst_unlink_parent(fi, NULL);
 | 
			
		||||
 | 
			
		||||
@@ -685,3 +722,30 @@ void mgcp_conn_delete(struct osmo_fsm_inst *fi)
 | 
			
		||||
	}
 | 
			
		||||
	osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info)
 | 
			
		||||
{
 | 
			
		||||
	/* I'd be fine with a smaller buffer and accept truncation, but gcc possibly refuses to build if
 | 
			
		||||
	 * this buffer is too small. */
 | 
			
		||||
	static char buf[1024];
 | 
			
		||||
 | 
			
		||||
	if (!info)
 | 
			
		||||
		return "NULL";
 | 
			
		||||
 | 
			
		||||
	if (info->endpoint[0]
 | 
			
		||||
	    && info->addr[0])
 | 
			
		||||
		snprintf(buf, sizeof(buf), "%s:%s:%u",
 | 
			
		||||
			 info->endpoint, info->addr, info->port);
 | 
			
		||||
	else if (info->endpoint[0])
 | 
			
		||||
		snprintf(buf, sizeof(buf), "%s", info->endpoint);
 | 
			
		||||
	else if (info->addr[0])
 | 
			
		||||
		snprintf(buf, sizeof(buf), "%s:%u", info->addr, info->port);
 | 
			
		||||
	else
 | 
			
		||||
		return "empty";
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init()
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,9 +36,10 @@ void *global_mgcp_client_ctx = NULL;
 | 
			
		||||
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
 | 
			
		||||
      "mgw local-ip A.B.C.D",
 | 
			
		||||
      "mgw local-ip " VTY_IPV46_CMD,
 | 
			
		||||
      MGW_STR "local bind to connect to MGW from\n"
 | 
			
		||||
      "local bind IP address\n")
 | 
			
		||||
      "local bind IPv4 address\n"
 | 
			
		||||
      "local bind IPv6 address\n")
 | 
			
		||||
{
 | 
			
		||||
	if (!global_mgcp_client_conf)
 | 
			
		||||
		return CMD_ERR_NOTHING_TODO;
 | 
			
		||||
@@ -69,9 +70,10 @@ ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
 | 
			
		||||
		 "local bind port\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
 | 
			
		||||
      "mgw remote-ip A.B.C.D",
 | 
			
		||||
      "mgw remote-ip " VTY_IPV46_CMD,
 | 
			
		||||
      MGW_STR "remote IP address to reach the MGW at\n"
 | 
			
		||||
      "remote IP address\n")
 | 
			
		||||
      "remote IPv4 address\n"
 | 
			
		||||
      "remote IPv6 address\n")
 | 
			
		||||
{
 | 
			
		||||
	if (!global_mgcp_client_conf)
 | 
			
		||||
		return CMD_ERR_NOTHING_TODO;
 | 
			
		||||
@@ -188,21 +190,21 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
 | 
			
		||||
	global_mgcp_client_ctx = talloc_ctx;
 | 
			
		||||
	global_mgcp_client_conf = conf;
 | 
			
		||||
 | 
			
		||||
	install_element(node, &cfg_mgw_local_ip_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_local_port_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_remote_ip_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_remote_port_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_endpoint_range_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_endpoint_domain_name_cmd);
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	/* deprecated 'mgcpgw' commands */
 | 
			
		||||
	install_element(node, &cfg_mgcpgw_local_ip_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgcpgw_local_port_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgcpgw_remote_ip_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgcpgw_remote_port_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgcpgw_endpoint_range_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgcpgw_local_ip_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgcpgw_local_port_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgcpgw_remote_ip_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgcpgw_remote_port_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgcpgw_endpoint_range_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
 | 
			
		||||
 | 
			
		||||
	osmo_fsm_vty_add_cmds();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,8 @@ AM_CFLAGS = \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOABIS_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOTRAU_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
@@ -18,6 +20,8 @@ AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(LIBOSMOTRAU_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
@@ -40,4 +44,8 @@ libosmo_mgcp_a_SOURCES = \
 | 
			
		||||
	mgcp_conn.c \
 | 
			
		||||
	mgcp_stat.c \
 | 
			
		||||
	mgcp_endp.c \
 | 
			
		||||
	mgcp_trunk.c \
 | 
			
		||||
	mgcp_ctrl.c \
 | 
			
		||||
	mgcp_ratectr.c \
 | 
			
		||||
	mgcp_e1.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -17,8 +17,13 @@
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
/* Helper function to dump codec information of a specified codec to a printable
 | 
			
		||||
@@ -56,8 +61,8 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
 | 
			
		||||
	endp = conn->conn->endp;
 | 
			
		||||
 | 
			
		||||
	if (rtp->codecs_assigned == 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x conn:%s no codecs available\n", ENDPOINT_NUMBER(endp),
 | 
			
		||||
		     mgcp_conn_dump(conn->conn));
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR, "conn:%s no codecs available\n",
 | 
			
		||||
			 mgcp_conn_dump(conn->conn));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -65,8 +70,8 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
 | 
			
		||||
	for (i = 0; i < rtp->codecs_assigned; i++) {
 | 
			
		||||
		codec = &rtp->codecs[i];
 | 
			
		||||
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x conn:%s codecs[%u]:%s", ENDPOINT_NUMBER(endp),
 | 
			
		||||
		     mgcp_conn_dump(conn->conn), i, dump_codec(codec));
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s codecs[%u]:%s",
 | 
			
		||||
			 mgcp_conn_dump(conn->conn), i, dump_codec(codec));
 | 
			
		||||
 | 
			
		||||
		if (codec == rtp->codec)
 | 
			
		||||
			LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
 | 
			
		||||
@@ -76,36 +81,61 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initalize or reset codec information with default data. */
 | 
			
		||||
void codec_init(struct mgcp_rtp_codec *codec)
 | 
			
		||||
static void codec_init(struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	*codec = (struct mgcp_rtp_codec){
 | 
			
		||||
		.payload_type = -1,
 | 
			
		||||
		.frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM,
 | 
			
		||||
		.frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN,
 | 
			
		||||
		.rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE,
 | 
			
		||||
		.channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS,
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void codec_free(struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	if (codec->subtype_name)
 | 
			
		||||
		talloc_free(codec->subtype_name);
 | 
			
		||||
	if (codec->audio_name)
 | 
			
		||||
		talloc_free(codec->audio_name);
 | 
			
		||||
	memset(codec, 0, sizeof(*codec));
 | 
			
		||||
	codec->payload_type = -1;
 | 
			
		||||
	codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
 | 
			
		||||
	codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
 | 
			
		||||
	codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
 | 
			
		||||
	codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
 | 
			
		||||
	*codec = (struct mgcp_rtp_codec){};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Initalize or reset codec information with default data.
 | 
			
		||||
 *  \param[out] conn related rtp-connection. */
 | 
			
		||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	memset(conn->end.codecs, 0, sizeof(conn->end.codecs));
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; i < conn->end.codecs_assigned; i++)
 | 
			
		||||
		codec_free(&conn->end.codecs[i]);
 | 
			
		||||
	conn->end.codecs_assigned = 0;
 | 
			
		||||
	conn->end.codec = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information */
 | 
			
		||||
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
 | 
			
		||||
		     int payload_type, const char *audio_name, unsigned int pt_offset)
 | 
			
		||||
/*! Add codec configuration depending on payload type and/or codec name. This
 | 
			
		||||
 *  function uses the input parameters to extrapolate the full codec information.
 | 
			
		||||
 *  \param[out] codec configuration (caller provided memory).
 | 
			
		||||
 *  \param[out] conn related rtp-connection.
 | 
			
		||||
 *  \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
 | 
			
		||||
 *  \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
 | 
			
		||||
 *  \param[in] param optional codec parameters (set to NULL when unused).
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on failure. */
 | 
			
		||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
 | 
			
		||||
{
 | 
			
		||||
	int rate;
 | 
			
		||||
	int channels;
 | 
			
		||||
	char audio_codec[64];
 | 
			
		||||
	struct mgcp_rtp_codec *codec;
 | 
			
		||||
	unsigned int pt_offset = conn->end.codecs_assigned;
 | 
			
		||||
	void *ctx = conn->conn;
 | 
			
		||||
 | 
			
		||||
	/* The amount of codecs we can store is limited, make sure we do not
 | 
			
		||||
	 * overrun this limit. */
 | 
			
		||||
	if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* First unused entry */
 | 
			
		||||
	codec = &conn->end.codecs[conn->end.codecs_assigned];
 | 
			
		||||
 | 
			
		||||
	/* Initalize the codec struct with some default data to begin with */
 | 
			
		||||
	codec_init(codec);
 | 
			
		||||
@@ -113,12 +143,13 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
 | 
			
		||||
	if (payload_type != PTYPE_UNDEFINED) {
 | 
			
		||||
		/* Make sure we do not get any reserved or undefined type numbers */
 | 
			
		||||
		/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
 | 
			
		||||
		if (payload_type == 1 || payload_type == 2 || payload_type == 19)
 | 
			
		||||
			goto error;
 | 
			
		||||
		if (payload_type >= 72 && payload_type <= 76)
 | 
			
		||||
			goto error;
 | 
			
		||||
		if (payload_type >= 127)
 | 
			
		||||
		if ((payload_type == 1 || payload_type == 2 || payload_type == 19)
 | 
			
		||||
		    || (payload_type >= 72 && payload_type <= 76)
 | 
			
		||||
		    || (payload_type >= 127)) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR, "Cannot add codec, payload type number %d is reserved\n",
 | 
			
		||||
			     payload_type);
 | 
			
		||||
			goto error;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		codec->payload_type = payload_type;
 | 
			
		||||
	}
 | 
			
		||||
@@ -144,6 +175,8 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
 | 
			
		||||
			/* The given payload type is not known to us, or it
 | 
			
		||||
			 * it is a dynamic payload type for which we do not
 | 
			
		||||
			 * know the audio name. We must give up here */
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR, "No audio codec name given, and payload type %d unknown\n",
 | 
			
		||||
			     payload_type);
 | 
			
		||||
			goto error;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -151,16 +184,23 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
 | 
			
		||||
	/* Now we extract the codec subtype name, rate and channels. The latter
 | 
			
		||||
	 * two are optional. If they are not present we use the safe defaults
 | 
			
		||||
	 * above. */
 | 
			
		||||
	if (strlen(audio_name) > sizeof(audio_codec))
 | 
			
		||||
	if (strlen(audio_name) >= sizeof(audio_codec)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(audio_name, -1));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
	channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
 | 
			
		||||
	rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
 | 
			
		||||
	if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1)
 | 
			
		||||
	if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(audio_name, -1));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Note: We only accept configurations with one audio channel! */
 | 
			
		||||
	if (channels != 1)
 | 
			
		||||
	if (channels != 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot handle audio codec with more than one channel: %s\n",
 | 
			
		||||
		     osmo_quote_str(audio_name, -1));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	codec->rate = rate;
 | 
			
		||||
	codec->channels = channels;
 | 
			
		||||
@@ -178,6 +218,7 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
 | 
			
		||||
 | 
			
		||||
	/* Derive the payload type if it is unknown */
 | 
			
		||||
	if (codec->payload_type == PTYPE_UNDEFINED) {
 | 
			
		||||
		/* TODO: This is semi dead code, see OS#4150 */
 | 
			
		||||
 | 
			
		||||
		/* For the known codecs from the static range we restore
 | 
			
		||||
		 * the IANA or 3GPP assigned payload type number */
 | 
			
		||||
@@ -213,80 +254,46 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
 | 
			
		||||
		 * 110 onwards 3gpp defines prefered codec types, which are
 | 
			
		||||
		 * also fixed, see above)  */
 | 
			
		||||
		if (codec->payload_type < 0) {
 | 
			
		||||
			/* FIXME: pt_offset is completely unrelated and useless here, any of those numbers may already
 | 
			
		||||
			 * have been added to the codecs. Instead, there should be an iterator checking for an actually
 | 
			
		||||
			 * unused dynamic payload type number. */
 | 
			
		||||
			codec->payload_type = 96 + pt_offset;
 | 
			
		||||
			if (codec->payload_type > 109)
 | 
			
		||||
			if (codec->payload_type > 109) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR, "Ran out of payload type numbers to assign dynamically\n");
 | 
			
		||||
				goto error;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Copy over optional codec parameters */
 | 
			
		||||
	if (param) {
 | 
			
		||||
		codec->param = *param;
 | 
			
		||||
		codec->param_present = true;
 | 
			
		||||
	} else
 | 
			
		||||
		codec->param_present = false;
 | 
			
		||||
 | 
			
		||||
	conn->end.codecs_assigned++;
 | 
			
		||||
	return 0;
 | 
			
		||||
error:
 | 
			
		||||
	/* Make sure we leave a clean codec entry on error. */
 | 
			
		||||
	codec_init(codec);
 | 
			
		||||
	memset(codec, 0, sizeof(*codec));
 | 
			
		||||
	codec_free(codec);
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Add codec configuration depending on payload type and/or codec name. This
 | 
			
		||||
 *  function uses the input parameters to extrapolate the full codec information.
 | 
			
		||||
 *  \param[out] codec configuration (caller provided memory).
 | 
			
		||||
 *  \param[out] conn related rtp-connection.
 | 
			
		||||
 *  \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
 | 
			
		||||
 *  \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on failure. */
 | 
			
		||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* The amount of codecs we can store is limited, make sure we do not
 | 
			
		||||
	 * overrun this limit. */
 | 
			
		||||
	if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
 | 
			
		||||
		       conn->end.codecs_assigned);
 | 
			
		||||
	if (rc != 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	conn->end.codecs_assigned++;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the given codec is applicable on the specified endpoint
 | 
			
		||||
 * Helper function for mgcp_codec_decide() */
 | 
			
		||||
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	char codec_name[64];
 | 
			
		||||
 | 
			
		||||
	/* A codec name must be set, if not, this might mean that the codec
 | 
			
		||||
	 * (payload type) that was assigned is unknown to us so we must stop
 | 
			
		||||
	 * here. */
 | 
			
		||||
	if (!codec->subtype_name)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* We now extract the codec_name (letters before the /, e.g. "GSM"
 | 
			
		||||
	 * from the audio name that is stored in the trunk configuration.
 | 
			
		||||
	 * We do not compare to the full audio_name because we expect that
 | 
			
		||||
	 * "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
 | 
			
		||||
	 * audio name of the codec is set to "GSM" */
 | 
			
		||||
	if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
 | 
			
		||||
		return false;
 | 
			
		||||
	/* FIXME: implement meaningful checks to make sure that the given codec
 | 
			
		||||
	 * is compatible with the given endpoint */
 | 
			
		||||
 | 
			
		||||
	/* Finally we check if the subtype_name we have generated from the
 | 
			
		||||
	 * audio_name in the trunc struct patches the codec_name of the
 | 
			
		||||
	 * given codec */
 | 
			
		||||
	if (strcasecmp(codec_name, codec->subtype_name) == 0)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: It is questinable that the method to pick a compatible
 | 
			
		||||
	 * codec can work properly. Since this useses tcfg->audio_name, as
 | 
			
		||||
	 * a reference, which is set to "AMR/8000" permanently.
 | 
			
		||||
	 * tcfg->audio_name must be updated by the first connection that
 | 
			
		||||
	 * has been made on an endpoint, so that the second connection
 | 
			
		||||
	 * can make a meaningful decision here */
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Decide for one suitable codec
 | 
			
		||||
@@ -313,7 +320,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
 | 
			
		||||
	for (i = 0; i < rtp->codecs_assigned; i++) {
 | 
			
		||||
		/* When no transcoding is available, avoid codecs that would
 | 
			
		||||
		 * require transcoding. */
 | 
			
		||||
		if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
 | 
			
		||||
		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;
 | 
			
		||||
@@ -342,9 +349,28 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode.
 | 
			
		||||
 * See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
 | 
			
		||||
 * Media Type Registration":
 | 
			
		||||
 *
 | 
			
		||||
 *    octet-align: Permissible values are 0 and 1.  If 1, octet-aligned
 | 
			
		||||
 *                 operation SHALL be used.  If 0 or if not present,
 | 
			
		||||
 *                 bandwidth-efficient operation is employed.
 | 
			
		||||
 *
 | 
			
		||||
 * https://tools.ietf.org/html/rfc4867
 | 
			
		||||
 */
 | 
			
		||||
static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	if (!codec->param_present)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (!codec->param.amr_octet_aligned_present)
 | 
			
		||||
		return false;
 | 
			
		||||
	return codec->param.amr_octet_aligned;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Compare two codecs, all parameters must match up, except for the payload type
 | 
			
		||||
 * number. */
 | 
			
		||||
static bool codecs_cmp(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
 | 
			
		||||
static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
 | 
			
		||||
{
 | 
			
		||||
	if (codec_a->rate != codec_b->rate)
 | 
			
		||||
		return false;
 | 
			
		||||
@@ -354,10 +380,12 @@ static bool codecs_cmp(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *co
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->frame_duration_den != codec_b->frame_duration_den)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (strcmp(codec_a->audio_name, codec_b->audio_name))
 | 
			
		||||
		return false;
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
 | 
			
		||||
		return false;
 | 
			
		||||
	if (!strcmp(codec_a->subtype_name, "AMR")) {
 | 
			
		||||
		if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
@@ -398,7 +426,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
 | 
			
		||||
	codecs_assigned = rtp_dst->codecs_assigned;
 | 
			
		||||
	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
 | 
			
		||||
	for (i = 0; i < codecs_assigned; i++) {
 | 
			
		||||
		if (codecs_cmp(codec_src, &rtp_dst->codecs[i])) {
 | 
			
		||||
		if (codecs_same(codec_src, &rtp_dst->codecs[i])) {
 | 
			
		||||
			codec_dst = &rtp_dst->codecs[i];
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
@@ -408,3 +436,28 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
 | 
			
		||||
 | 
			
		||||
	return codec_dst->payload_type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find the payload type number configured for a specific codec by SDP.
 | 
			
		||||
 * For example, IuUP gets assigned a payload type number, and the endpoint needs to translate that to the number
 | 
			
		||||
 * assigned to "AMR" on the other conn (by a=rtpmap:N).
 | 
			
		||||
 * \param conn  The side of an endpoint to get the payload type number for (to translate the payload type number to).
 | 
			
		||||
 * \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.
 | 
			
		||||
 */
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; i < conn->end.codecs_assigned; i++) {
 | 
			
		||||
		if (!strcmp(conn->end.codecs[i].subtype_name, subtype_name)) {
 | 
			
		||||
			if (match_nr) {
 | 
			
		||||
				match_nr--;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			return &conn->end.codecs[i];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,13 +22,16 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_network.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_sdp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
 | 
			
		||||
@@ -74,14 +77,13 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x, unable to generate a unique connectionIdentifier\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp));
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_ERROR, "unable to generate a unique connectionIdentifier\n");
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize rtp connection struct with default values */
 | 
			
		||||
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
 | 
			
		||||
static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_end *end = &conn_rtp->end;
 | 
			
		||||
	/* FIXME: Each new rate counter group requires an unique index. At the
 | 
			
		||||
@@ -90,7 +92,8 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn
 | 
			
		||||
	static unsigned int rate_ctr_index = 0;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->type = MGCP_RTP_DEFAULT;
 | 
			
		||||
	conn_rtp->osmux.allocated_cid = -1;
 | 
			
		||||
	conn_rtp->osmux.cid_allocated = false;
 | 
			
		||||
	conn_rtp->osmux.cid = 0;
 | 
			
		||||
 | 
			
		||||
	/* backpointer to the generic part of the connection */
 | 
			
		||||
	conn->u.rtp.conn = conn;
 | 
			
		||||
@@ -108,21 +111,44 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn
 | 
			
		||||
	end->maximum_packet_time = -1;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
 | 
			
		||||
	if (!conn_rtp->rate_ctr_group)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR];
 | 
			
		||||
	conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR];
 | 
			
		||||
	rate_ctr_index++;
 | 
			
		||||
 | 
			
		||||
	/* Make sure codec table is reset */
 | 
			
		||||
	mgcp_codec_reset_all(conn_rtp);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Cleanup rtp connection struct */
 | 
			
		||||
static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
{
 | 
			
		||||
	osmux_disable_conn(conn_rtp);
 | 
			
		||||
	osmux_release_cid(conn_rtp);
 | 
			
		||||
	if (mgcp_conn_rtp_is_osmux(conn_rtp))
 | 
			
		||||
		conn_osmux_disable(conn_rtp);
 | 
			
		||||
	mgcp_free_rtp_port(&conn_rtp->end);
 | 
			
		||||
	rate_ctr_group_free(conn_rtp->rate_ctr_group);
 | 
			
		||||
	mgcp_codec_reset_all(conn_rtp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_conn_watchdog_cb(void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn = data;
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_ERROR, "connection timed out!\n");
 | 
			
		||||
	mgcp_conn_free(conn->endp, conn->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	int timeout = conn->endp->cfg->conn_timeout;
 | 
			
		||||
	if (!timeout)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "watchdog kicked\n");
 | 
			
		||||
	osmo_timer_schedule(&conn->watchdog, timeout, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! allocate a new connection list entry.
 | 
			
		||||
@@ -158,7 +184,10 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		mgcp_rtp_conn_init(&conn->u.rtp, conn);
 | 
			
		||||
		if (mgcp_rtp_conn_init(&conn->u.rtp, conn) < 0) {
 | 
			
		||||
			talloc_free(conn);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		/* NOTE: This should never be called with an
 | 
			
		||||
@@ -167,7 +196,10 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_add(&conn->entry, &endp->conns);
 | 
			
		||||
	/* Initialize watchdog */
 | 
			
		||||
	osmo_timer_setup(&conn->watchdog, mgcp_conn_watchdog_cb, conn);
 | 
			
		||||
	mgcp_conn_watchdog_kick(conn);
 | 
			
		||||
	mgcp_endp_add_conn(endp, conn);
 | 
			
		||||
 | 
			
		||||
	return conn;
 | 
			
		||||
}
 | 
			
		||||
@@ -224,12 +256,10 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
aggregate_rtp_conn_stats(struct mgcp_trunk_config *trunk, struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
{
 | 
			
		||||
	struct rate_ctr_group *all_stats = trunk->all_rtp_conn_stats;
 | 
			
		||||
	struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
 | 
			
		||||
	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (all_stats == NULL || conn_stats == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
@@ -239,8 +269,11 @@ aggregate_rtp_conn_stats(struct mgcp_trunk_config *trunk, struct mgcp_conn_rtp *
 | 
			
		||||
	 * All other counters in both counter groups correspond to each other. */
 | 
			
		||||
	OSMO_ASSERT(conn_stats->desc->num_ctr + 1 == all_stats->desc->num_ctr);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < conn_stats->desc->num_ctr; i++)
 | 
			
		||||
		rate_ctr_add(&all_stats->ctr[i], conn_stats->ctr[i].current);
 | 
			
		||||
	/* all other counters are [now] updated in real-time */
 | 
			
		||||
	rate_ctr_add(&all_stats->ctr[IN_STREAM_ERR_TSTMP_CTR],
 | 
			
		||||
		     conn_stats->ctr[IN_STREAM_ERR_TSTMP_CTR].current);
 | 
			
		||||
	rate_ctr_add(&all_stats->ctr[OUT_STREAM_ERR_TSTMP_CTR],
 | 
			
		||||
		     conn_stats->ctr[OUT_STREAM_ERR_TSTMP_CTR].current);
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(&all_stats->ctr[RTP_NUM_CONNECTIONS]);
 | 
			
		||||
}
 | 
			
		||||
@@ -256,15 +289,9 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Run endpoint cleanup action. By this we inform the endpoint about
 | 
			
		||||
	 * the removal of the connection and allow it to clean up its inner
 | 
			
		||||
	 * state accordingly */
 | 
			
		||||
	if (endp->type->cleanup_cb)
 | 
			
		||||
		endp->type->cleanup_cb(endp, conn);
 | 
			
		||||
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		aggregate_rtp_conn_stats(endp->tcfg, &conn->u.rtp);
 | 
			
		||||
		aggregate_rtp_conn_stats(endp, &conn->u.rtp);
 | 
			
		||||
		mgcp_rtp_conn_cleanup(&conn->u.rtp);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
@@ -274,7 +301,9 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_del(&conn->entry);
 | 
			
		||||
	osmo_timer_del(&conn->watchdog);
 | 
			
		||||
	mgcp_endp_remove_conn(endp, conn);
 | 
			
		||||
	/* WARN: endp may have be freed after call to mgcp_endp_remove_conn */
 | 
			
		||||
	talloc_free(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -315,6 +344,7 @@ void mgcp_conn_free_all(struct mgcp_endpoint *endp)
 | 
			
		||||
char *mgcp_conn_dump(struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	static char str[sizeof(conn->name)+sizeof(conn->id)+256];
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		snprintf(str, sizeof(str), "(null connection)");
 | 
			
		||||
@@ -328,7 +358,7 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
 | 
			
		||||
			 "rtp:%u rtcp:%u)",
 | 
			
		||||
			 conn->name,
 | 
			
		||||
			 conn->id,
 | 
			
		||||
			 inet_ntoa(conn->u.rtp.end.addr),
 | 
			
		||||
			 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));
 | 
			
		||||
		break;
 | 
			
		||||
@@ -364,3 +394,13 @@ struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! get oldest connection in the list.
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	if (llist_empty(&endp->conns))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								src/libosmo-mgcp/mgcp_ctrl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/libosmo-mgcp/mgcp_ctrl.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										685
									
								
								src/libosmo-mgcp/mgcp_e1.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										685
									
								
								src/libosmo-mgcp/mgcp_e1.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,685 @@
 | 
			
		||||
/* E1 traffic handling */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Philipp Maier
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/abis/e1_input.h>
 | 
			
		||||
#include <osmocom/abis/abis.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/trau/trau_sync.h>
 | 
			
		||||
#include <osmocom/trau/trau_frame.h>
 | 
			
		||||
#include <osmocom/trau/trau_rtp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/netif/rtp.h>
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_e1.h>
 | 
			
		||||
#include <osmocom/codec/codec.h>
 | 
			
		||||
 | 
			
		||||
#define DEBUG_BITS_MAX 80
 | 
			
		||||
#define DEBUG_BYTES_MAX 40
 | 
			
		||||
#define DEBUG_E1_TS 0
 | 
			
		||||
#define E1_TS_BYTES 160
 | 
			
		||||
#define E1_TRAU_BITS 320
 | 
			
		||||
#define E1_TRAU_BITS_MSGB 2048
 | 
			
		||||
 | 
			
		||||
static struct mgcp_config *cfg;
 | 
			
		||||
 | 
			
		||||
static const struct e1inp_line_ops dummy_e1_line_ops = {
 | 
			
		||||
	.sign_link_up = NULL,
 | 
			
		||||
	.sign_link_down = NULL,
 | 
			
		||||
	.sign_link = NULL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* EFR idle frame */
 | 
			
		||||
static const ubit_t idle_tf_efr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 1, 1, 0, 1, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 1, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				      1, 0, 0, 0, 0, 0, 1, 0,
 | 
			
		||||
				      1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* FR idle frame */
 | 
			
		||||
static const ubit_t idle_tf_fr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 1, 1, 1, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 1, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				     1, 0, 0, 0, 0, 0, 1, 0,
 | 
			
		||||
				     1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Idle speech frame, see also GSM 08.60, chapter 3.4 */
 | 
			
		||||
static const ubit_t idle_tf_spch[] = { 0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				       0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
				       1, 0, 1, 1, 1, 0, 0, 0,
 | 
			
		||||
				       0, 0, 0, 0, 1, 0, 0, 0,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 0,
 | 
			
		||||
				       1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* If the RTP transmission has dropouts for some reason the I.460 TX-Queue may
 | 
			
		||||
 * run empty. In order to make sure that the TRAU frame transmission continues
 | 
			
		||||
 * we generate idle TRAU frames here. */
 | 
			
		||||
static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp = user_data;
 | 
			
		||||
	struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
 | 
			
		||||
	struct msgb *msg = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
 | 
			
		||||
	uint8_t *ptr;
 | 
			
		||||
	const uint8_t *ptr_ft;
 | 
			
		||||
	enum osmo_trau_frame_type ft;
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_MUX_EMPTY_CTR]);
 | 
			
		||||
 | 
			
		||||
	/* Choose an appropiate idle frame type */
 | 
			
		||||
	ft = endp->e1.trau_rtp_st->type;
 | 
			
		||||
	switch (ft) {
 | 
			
		||||
	case OSMO_TRAU16_FT_FR:
 | 
			
		||||
		ptr_ft = idle_tf_fr;
 | 
			
		||||
		break;
 | 
			
		||||
	case OSMO_TRAU16_FT_EFR:
 | 
			
		||||
		ptr_ft = idle_tf_efr;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		/* FIXME: What about 8k subslots and AMR frames? */
 | 
			
		||||
		ptr_ft = idle_tf_spch;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Put the replacement into a message buffer and enqueue it into the
 | 
			
		||||
	 * I.460 multiplexer */
 | 
			
		||||
	ptr = msgb_put(msg, E1_TRAU_BITS);
 | 
			
		||||
	memcpy(ptr, ptr_ft, E1_TRAU_BITS);
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-IDLE-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg),
 | 
			
		||||
		 osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
 | 
			
		||||
	osmo_i460_mux_enqueue(endp->e1.schan, msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* called by I.460 de-multeiplexer; 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)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp = user_data;
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u bits from subslot: %s...\n", num_bits,
 | 
			
		||||
		 osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(endp->e1.trau_sync_fi);
 | 
			
		||||
	osmo_trau_sync_rx_ubits(endp->e1.trau_sync_fi, bits, num_bits);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* called for each synchronized TRAU frame received; decode frame + convert to RTP
 | 
			
		||||
 * (the resulting frame will be prepended with an all-zero (12-byte) rtp header) */
 | 
			
		||||
static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = msgb_alloc(RTP_BUF_SIZE, "RTP-rx-from-E1");
 | 
			
		||||
	unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
 | 
			
		||||
	struct mgcp_endpoint *endp = user_data;
 | 
			
		||||
	struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
 | 
			
		||||
	struct mgcp_conn *conn_dst;
 | 
			
		||||
	struct osmo_trau_frame fr;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (!bits || num_bits == 0)
 | 
			
		||||
		goto skip;
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u TRAU frame bits from E1 subslot: %s...\n",
 | 
			
		||||
		 num_bits, osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
 | 
			
		||||
 | 
			
		||||
	/* Decode TRAU frame */
 | 
			
		||||
	switch (endp->e1.scd.rate) {
 | 
			
		||||
	case OSMO_I460_RATE_8k:
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 8k trau frame...\n");
 | 
			
		||||
		rc = osmo_trau_frame_decode_8k(&fr, bits, OSMO_TRAU_DIR_UL);
 | 
			
		||||
		break;
 | 
			
		||||
	case OSMO_I460_RATE_16k:
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 16k trau frame...\n");
 | 
			
		||||
		rc = osmo_trau_frame_decode_16k(&fr, bits, OSMO_TRAU_DIR_UL);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		/* TRAU frames only exist in 8K or 16K subslots. */
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	if (rc != 0) {
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to decode trau frame\n");
 | 
			
		||||
		goto skip;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if the payload type is supported and what the expected lenth
 | 
			
		||||
	 * of the RTP payload will be. */
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoded trau frame type: %s\n",
 | 
			
		||||
		 osmo_trau_frame_type_name(fr.type));
 | 
			
		||||
 | 
			
		||||
	/* Convert decoded trau frame to RTP frame */
 | 
			
		||||
	struct osmo_trau2rtp_state t2rs = {
 | 
			
		||||
		.type = fr.type,
 | 
			
		||||
	};
 | 
			
		||||
	rc = osmo_trau2rtp(msgb_data(msg) + rtp_hdr_len, msg->data_len - rtp_hdr_len, &fr, &t2rs);
 | 
			
		||||
	if (rc <= 0) {
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to convert trau frame to RTP audio\n");
 | 
			
		||||
		goto skip;
 | 
			
		||||
	}
 | 
			
		||||
	msgb_put(msg, rtp_hdr_len + rc);
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: encoded %u bytes of RTP audio: %s\n", rc,
 | 
			
		||||
		 osmo_hexdump(msgb_data(msg) + rtp_hdr_len, msgb_length(msg) - rtp_hdr_len));
 | 
			
		||||
 | 
			
		||||
	/* Forward RTP data to IP */
 | 
			
		||||
	conn_dst = llist_first_entry(&endp->conns, struct mgcp_conn, entry);
 | 
			
		||||
	if (!conn_dst) {
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_DEBUG,
 | 
			
		||||
			 "E1-I.460-RX: unable to forward RTP audio data from E1: no connection to forward an incoming RTP packet to\n");
 | 
			
		||||
		goto skip;
 | 
			
		||||
	}
 | 
			
		||||
	OSMO_ASSERT(conn_dst->type == MGCP_CONN_TYPE_RTP);
 | 
			
		||||
 | 
			
		||||
	mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
 | 
			
		||||
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return;
 | 
			
		||||
skip:
 | 
			
		||||
	rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_RX_FAIL_CTR]);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Function to handle outgoing E1 traffic */
 | 
			
		||||
static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = msgb_alloc(E1_TS_BYTES, "E1-TX-timeslot-bytes");
 | 
			
		||||
	uint8_t *ptr;
 | 
			
		||||
 | 
			
		||||
	/* Get E1 frame from I.460 multiplexer */
 | 
			
		||||
	ptr = msgb_put(msg, E1_TS_BYTES);
 | 
			
		||||
	osmo_i460_mux_out(&trunk->e1.i460_ts[ts->num - 1], ptr, E1_TS_BYTES);
 | 
			
		||||
 | 
			
		||||
#if DEBUG_E1_TS == 1
 | 
			
		||||
	LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-TX: (ts:%u) sending %u bytes: %s...\n", ts->num, msgb_length(msg),
 | 
			
		||||
		  osmo_hexdump_nospc(msgb_data(msg),
 | 
			
		||||
				     msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
 | 
			
		||||
#endif
 | 
			
		||||
	/* Hand data over to the E1 stack */
 | 
			
		||||
	msgb_enqueue(&ts->raw.tx_queue, msg);
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Callback function to handle incoming E1 traffic */
 | 
			
		||||
static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk *trunk;
 | 
			
		||||
 | 
			
		||||
	/* Find associated trunk */
 | 
			
		||||
	trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
 | 
			
		||||
	if (!trunk) {
 | 
			
		||||
		LOGP(DE1, LOGL_DEBUG, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if the incoming data looks sane */
 | 
			
		||||
	if (msgb_length(msg) != E1_TS_BYTES) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_NOTICE,
 | 
			
		||||
			  "E1-RX: (ts:%u) expected length is %u, actual length is %u!\n", ts->num, E1_TS_BYTES,
 | 
			
		||||
			  msgb_length(msg));
 | 
			
		||||
	}
 | 
			
		||||
#if DEBUG_E1_TS == 1
 | 
			
		||||
	LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-RX: (ts:%u) receiving %u bytes: %s...\n", ts->num,
 | 
			
		||||
		  msgb_length(msg), osmo_hexdump_nospc(msgb_data(msg),
 | 
			
		||||
						       msgb_length(msg) >
 | 
			
		||||
						       DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Hand data over to the I.460 demultiplexer. */
 | 
			
		||||
	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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int e1_init(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. */
 | 
			
		||||
 | 
			
		||||
	struct e1inp_line *e1_line;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	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_DEBUG, "E1 timeslot %u already set up, skipping...\n", ts_nr);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Get E1 line */
 | 
			
		||||
	e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
 | 
			
		||||
	if (!e1_line) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "no such E1 line %u - check VTY config!\n",
 | 
			
		||||
			  trunk->e1.vty_line_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	e1inp_line_bind_ops(e1_line, &dummy_e1_line_ops);
 | 
			
		||||
 | 
			
		||||
	/* Configure E1 timeslot */
 | 
			
		||||
	rc = e1inp_ts_config_raw(&e1_line->ts[ts_nr - 1], e1_line, e1_recv_cb);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to put E1 timeslot %u in raw mode.\n", ts_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	rc = e1inp_line_update(e1_line);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 timeslot %u.\n", ts_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u set up successfully.\n", ts_nr);
 | 
			
		||||
	trunk->e1.ts_in_use[ts_nr - 1] = true;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Determine a suitable TRAU frame type for a given codec */
 | 
			
		||||
static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
 | 
			
		||||
							uint8_t amr_ft, struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "GSM") == 0)
 | 
			
		||||
		return OSMO_TRAU16_FT_FR;
 | 
			
		||||
	else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
 | 
			
		||||
		return OSMO_TRAU16_FT_EFR;
 | 
			
		||||
	else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
 | 
			
		||||
		return OSMO_TRAU16_FT_HR;
 | 
			
		||||
	else if (strcmp(sdp_subtype_name, "AMR") == 0) {
 | 
			
		||||
		if (i460_rate == OSMO_I460_RATE_8k) {
 | 
			
		||||
			switch (amr_ft) {
 | 
			
		||||
			case AMR_4_75:
 | 
			
		||||
			case AMR_5_15:
 | 
			
		||||
			case AMR_5_90:
 | 
			
		||||
				return OSMO_TRAU8_AMR_LOW;
 | 
			
		||||
			case AMR_6_70:
 | 
			
		||||
				return OSMO_TRAU8_AMR_6k7;
 | 
			
		||||
			case AMR_7_40:
 | 
			
		||||
				return OSMO_TRAU8_AMR_7k4;
 | 
			
		||||
			default:
 | 
			
		||||
				LOGPENDP(endp, DE1, LOGL_ERROR,
 | 
			
		||||
					 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
 | 
			
		||||
				return OSMO_TRAU_FT_NONE;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return OSMO_TRAU16_FT_AMR;
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
 | 
			
		||||
			 sdp_subtype_name);
 | 
			
		||||
		return OSMO_TRAU_FT_NONE;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Determine a suitable TRAU frame type for a given codec */
 | 
			
		||||
static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
 | 
			
		||||
							  uint8_t amr_ft, struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "GSM") == 0)
 | 
			
		||||
		return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
	else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
 | 
			
		||||
		return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
	else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
 | 
			
		||||
		return OSMO_TRAU_SYNCP_8_HR;
 | 
			
		||||
	else if (strcmp(sdp_subtype_name, "AMR") == 0) {
 | 
			
		||||
		if (i460_rate == OSMO_I460_RATE_8k) {
 | 
			
		||||
			switch (amr_ft) {
 | 
			
		||||
			case AMR_4_75:
 | 
			
		||||
			case AMR_5_15:
 | 
			
		||||
			case AMR_5_90:
 | 
			
		||||
				return OSMO_TRAU_SYNCP_8_AMR_LOW;
 | 
			
		||||
			case AMR_6_70:
 | 
			
		||||
				return OSMO_TRAU_SYNCP_8_AMR_6K7;
 | 
			
		||||
			case AMR_7_40:
 | 
			
		||||
				return OSMO_TRAU_SYNCP_8_AMR_7K4;
 | 
			
		||||
			default:
 | 
			
		||||
				LOGPENDP(endp, DE1, LOGL_ERROR,
 | 
			
		||||
					 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
 | 
			
		||||
				return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
 | 
			
		||||
			 sdp_subtype_name);
 | 
			
		||||
		return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find out if a given TRAU frame type is AMR */
 | 
			
		||||
static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	switch (ft) {
 | 
			
		||||
	case OSMO_TRAU16_FT_AMR:
 | 
			
		||||
	case OSMO_TRAU8_AMR_LOW:
 | 
			
		||||
	case OSMO_TRAU8_AMR_6k7:
 | 
			
		||||
	case OSMO_TRAU8_AMR_7k4:
 | 
			
		||||
		return true;
 | 
			
		||||
	default:
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* !Equip E1 endpoint with I.460 mux resources.
 | 
			
		||||
 *  \param[in] endp endpoint to equip
 | 
			
		||||
 *  \param[in] ts E1 timeslot number.
 | 
			
		||||
 *  \param[in] ss E1 subslot number.
 | 
			
		||||
 *  \param[in] offset E1 bit offset.
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on error. */
 | 
			
		||||
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	enum osmo_tray_sync_pat_id sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ts != 0);
 | 
			
		||||
	OSMO_ASSERT(ts != 0xFF);
 | 
			
		||||
	OSMO_ASSERT(ss != 0xFF);
 | 
			
		||||
	OSMO_ASSERT(offs != 0xFF);
 | 
			
		||||
 | 
			
		||||
	memset(&endp->e1, 0, sizeof(endp->e1));
 | 
			
		||||
 | 
			
		||||
	endp->e1.last_amr_ft = AMR_4_75;
 | 
			
		||||
 | 
			
		||||
	/* Set up E1 line / timeslot */
 | 
			
		||||
	rc = e1_init(endp->trunk, ts);
 | 
			
		||||
	if (rc != 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Set up I.460 mux */
 | 
			
		||||
	switch (e1_rates[ss]) {
 | 
			
		||||
	case 64:
 | 
			
		||||
		endp->e1.scd.rate = OSMO_I460_RATE_64k;
 | 
			
		||||
		endp->e1.scd.demux.num_bits = 160 * 8;
 | 
			
		||||
		break;
 | 
			
		||||
	case 32:
 | 
			
		||||
		endp->e1.scd.rate = OSMO_I460_RATE_32k;
 | 
			
		||||
		endp->e1.scd.demux.num_bits = 80 * 8;
 | 
			
		||||
		break;
 | 
			
		||||
	case 16:
 | 
			
		||||
		endp->e1.scd.rate = OSMO_I460_RATE_16k;
 | 
			
		||||
		endp->e1.scd.demux.num_bits = 40 * 8;
 | 
			
		||||
		sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
		break;
 | 
			
		||||
	case 8:
 | 
			
		||||
		endp->e1.scd.rate = OSMO_I460_RATE_8k;
 | 
			
		||||
		endp->e1.scd.demux.num_bits = 20 * 8;
 | 
			
		||||
		sync_pat_id = OSMO_TRAU_SYNCP_8_HR;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	endp->e1.scd.bit_offset = offs;
 | 
			
		||||
	endp->e1.scd.demux.out_cb_bits = e1_i460_demux_bits_cb;
 | 
			
		||||
	endp->e1.scd.demux.out_cb_bytes = NULL;
 | 
			
		||||
	endp->e1.scd.demux.user_data = endp;
 | 
			
		||||
	endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
 | 
			
		||||
	endp->e1.scd.mux.user_data = endp;
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts,
 | 
			
		||||
		 offs, e1_rates[ss], endp->e1.scd.demux.num_bits);
 | 
			
		||||
	endp->e1.schan = osmo_i460_subchan_add(endp, &endp->trunk->e1.i460_ts[ts - 1], &endp->e1.scd);
 | 
			
		||||
	if (!endp->e1.schan) {
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 subchannel: failed!\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (endp->e1.scd.rate == OSMO_I460_RATE_16k || endp->e1.scd.rate == OSMO_I460_RATE_8k) {
 | 
			
		||||
		/* TRAU frames are only specified for 16k and 8k subslots. For all other subslot
 | 
			
		||||
		 * types the concept of TRAU frames does not apply. However, at the moment this
 | 
			
		||||
		 * is the only format we currently support in osmo-mgw */
 | 
			
		||||
		endp->e1.trau_sync_fi = osmo_trau_sync_alloc(endp, "trau-sync", sync_frame_out_cb, sync_pat_id, endp);
 | 
			
		||||
		if (!endp->e1.trau_sync_fi) {
 | 
			
		||||
			LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 TRAU frame sync: failed!\n");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		endp->e1.trau_rtp_st = talloc_zero(endp->e1.trau_sync_fi, struct osmo_trau2rtp_state);
 | 
			
		||||
		endp->e1.trau_rtp_st->type = OSMO_TRAU_FT_NONE;
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_ERROR,
 | 
			
		||||
			 "osmo-mgw currently only supports 16K and 8K subslots (TRAU frames)!\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Update E1 related parameters (codec and sync pattern).
 | 
			
		||||
 *  \param[in] endp endpoint to update. */
 | 
			
		||||
void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	struct mgcp_rtp_codec *codec;
 | 
			
		||||
	enum osmo_tray_sync_pat_id sync_pat_id;
 | 
			
		||||
 | 
			
		||||
	/* In order to determine the codec, find the oldest connection on
 | 
			
		||||
	 * the endpoint and use its codec information. Normally on an E1
 | 
			
		||||
	 * endpoint no more than one connection should exist. */
 | 
			
		||||
	conn = mgcp_conn_get_oldest(endp);
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
	codec = conn->u.rtp.end.codec;
 | 
			
		||||
	OSMO_ASSERT(codec);
 | 
			
		||||
 | 
			
		||||
	/* Update codec information */
 | 
			
		||||
	endp->e1.trau_rtp_st->type =
 | 
			
		||||
	    determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
 | 
			
		||||
	endp->e1.last_codec = codec;
 | 
			
		||||
 | 
			
		||||
	/* Update sync pattern */
 | 
			
		||||
	sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
 | 
			
		||||
	osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Remove E1 resources from endpoint
 | 
			
		||||
 *  \param[in] endp endpoint to release. */
 | 
			
		||||
void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
 | 
			
		||||
 | 
			
		||||
	if (endp->e1.schan)
 | 
			
		||||
		osmo_i460_subchan_del(endp->e1.schan);
 | 
			
		||||
	if (endp->e1.trau_rtp_st)
 | 
			
		||||
		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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.
 | 
			
		||||
 *  \param[in] endp related endpoint (does not take ownership).
 | 
			
		||||
 *  \param[in] codec configuration.
 | 
			
		||||
 *  \param[in] msg RTP message buffer (including RTP header).
 | 
			
		||||
 *  \returns 0 on success, -1 on ERROR. */
 | 
			
		||||
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg_tf = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame");
 | 
			
		||||
	struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
 | 
			
		||||
	unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
 | 
			
		||||
	struct osmo_trau_frame tf;
 | 
			
		||||
	uint8_t amr_ft;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* Extract AMR frame type from AMR head (if AMR is used) */
 | 
			
		||||
	if (tf_type_is_amr(endp->e1.trau_rtp_st->type))
 | 
			
		||||
		amr_ft = (msgb_data(msg)[rtp_hdr_len + 1] >> 3) & 0xf;
 | 
			
		||||
	else
 | 
			
		||||
		amr_ft = 0xff;
 | 
			
		||||
 | 
			
		||||
	/* Adapt TRAU frame type on codec changes */
 | 
			
		||||
	OSMO_ASSERT(endp->e1.last_codec);
 | 
			
		||||
	if (codec != endp->e1.last_codec || (amr_ft != 0xff && amr_ft != endp->e1.last_amr_ft)) {
 | 
			
		||||
		endp->e1.trau_rtp_st->type =
 | 
			
		||||
		    determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, amr_ft, endp);
 | 
			
		||||
		endp->e1.last_codec = codec;
 | 
			
		||||
		endp->e1.last_amr_ft = amr_ft;
 | 
			
		||||
	}
 | 
			
		||||
	if (endp->e1.trau_rtp_st->type == OSMO_TRAU_FT_NONE)
 | 
			
		||||
		goto skip;
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: using trau frame type for encoding: %s\n",
 | 
			
		||||
		 osmo_trau_frame_type_name(endp->e1.trau_rtp_st->type));
 | 
			
		||||
 | 
			
		||||
	/* Convert from RTP to TRAU format */
 | 
			
		||||
	msg->l2h = msgb_data(msg) + rtp_hdr_len;
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: decoding %u bytes of RTP audio to TRAU format: %s\n",
 | 
			
		||||
		 msgb_length(msg), osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
 | 
			
		||||
	memset(&tf, 0, sizeof(tf));
 | 
			
		||||
	tf.dir = OSMO_TRAU_DIR_DL;
 | 
			
		||||
	rc = osmo_rtp2trau(&tf, msgb_l2(msg), msgb_l2len(msg), endp->e1.trau_rtp_st);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_DEBUG,
 | 
			
		||||
			 "E1-I.460-TX: failed to decode from RTP payload format to TRAU format\n");
 | 
			
		||||
		goto skip;
 | 
			
		||||
	}
 | 
			
		||||
	rc = osmo_trau_frame_encode(msgb_data(msg_tf), msg_tf->data_len, &tf);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: failed to encode TRAU frame\n");
 | 
			
		||||
		goto skip;
 | 
			
		||||
	}
 | 
			
		||||
	msgb_put(msg_tf, rc);
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg_tf),
 | 
			
		||||
		 osmo_ubit_dump(msgb_data(msg_tf),
 | 
			
		||||
				msgb_length(msg_tf) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg_tf)));
 | 
			
		||||
 | 
			
		||||
	/* Enqueue data to I.460 multiplexer */
 | 
			
		||||
	OSMO_ASSERT(endp->e1.schan);
 | 
			
		||||
	OSMO_ASSERT(endp->e1.trau_sync_fi);
 | 
			
		||||
 | 
			
		||||
	osmo_i460_mux_enqueue(endp->e1.schan, msg_tf);
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: %u bits of audio enqued for E1 tx\n", msgb_length(msg_tf));
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
skip:
 | 
			
		||||
	rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_TX_FAIL_CTR]);
 | 
			
		||||
	msgb_free(msg_tf);
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/* Endpoint types */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Philipp Maier
 | 
			
		||||
@@ -21,23 +21,100 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/abis/e1_input.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_e1.h>
 | 
			
		||||
 | 
			
		||||
#define E1_RATE_MAX 64
 | 
			
		||||
#define E1_OFFS_MAX 8
 | 
			
		||||
 | 
			
		||||
/* Endpoint typeset definition */
 | 
			
		||||
const struct mgcp_endpoint_typeset ep_typeset = {
 | 
			
		||||
	/* Specify endpoint properties for RTP endpoint */
 | 
			
		||||
	.rtp.max_conns = 2,
 | 
			
		||||
	.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
 | 
			
		||||
	.rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
 | 
			
		||||
	.rtp = {
 | 
			
		||||
		.max_conns = 2,
 | 
			
		||||
		.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
 | 
			
		||||
		.cleanup_cb = mgcp_cleanup_rtp_bridge_cb,
 | 
			
		||||
	},
 | 
			
		||||
	/* Specify endpoint properties for E1 endpoint */
 | 
			
		||||
	.e1 = {
 | 
			
		||||
		.max_conns = 1,
 | 
			
		||||
		.dispatch_rtp_cb = mgcp_dispatch_e1_bridge_cb,
 | 
			
		||||
		.cleanup_cb = mgcp_cleanup_e1_bridge_cb,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Generate virtual endpoint name from given parameters */
 | 
			
		||||
static char *gen_virtual_epname(void *ctx, const char *domain,
 | 
			
		||||
			       unsigned int index)
 | 
			
		||||
{
 | 
			
		||||
	return talloc_asprintf(ctx, "%s%x@%s",
 | 
			
		||||
		 MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, index, domain);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Generate E1 endpoint name from given numeric parameters */
 | 
			
		||||
static char *gen_e1_epname(void *ctx, const char *domain, uint8_t trunk_nr,
 | 
			
		||||
			   uint8_t ts_nr, uint8_t ss_nr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int rate;
 | 
			
		||||
	unsigned int offset;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ss_nr < sizeof(e1_rates));
 | 
			
		||||
 | 
			
		||||
	rate = e1_rates[ss_nr];
 | 
			
		||||
	offset = e1_offsets[ss_nr];
 | 
			
		||||
 | 
			
		||||
	return talloc_asprintf(ctx, "%s%u/s-%u/su%u-%u@%s",
 | 
			
		||||
			       MGCP_ENDPOINT_PREFIX_E1_TRUNK, trunk_nr, ts_nr,
 | 
			
		||||
			       rate, offset, domain);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! allocate an endpoint and set default values.
 | 
			
		||||
 *  \param[in] trunk configuration.
 | 
			
		||||
 *  \param[in] name endpoint index.
 | 
			
		||||
 *  \returns endpoint on success, NULL on failure. */
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
 | 
			
		||||
				      unsigned int index)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
 | 
			
		||||
	endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint);
 | 
			
		||||
	if (!endp)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&endp->conns);
 | 
			
		||||
	endp->cfg = trunk->cfg;
 | 
			
		||||
	endp->trunk = trunk;
 | 
			
		||||
 | 
			
		||||
	switch (trunk->trunk_type) {
 | 
			
		||||
	case MGCP_TRUNK_VIRTUAL:
 | 
			
		||||
		endp->type = &ep_typeset.rtp;
 | 
			
		||||
		endp->name = gen_virtual_epname(endp, trunk->cfg->domain, index);
 | 
			
		||||
		break;
 | 
			
		||||
	case MGCP_TRUNK_E1:
 | 
			
		||||
		endp->type = &ep_typeset.e1;
 | 
			
		||||
		endp->name = gen_e1_epname(endp, trunk->cfg->domain,
 | 
			
		||||
					   trunk->trunk_nr,
 | 
			
		||||
					   index / MGCP_ENDP_E1_SUBSLOTS, index % MGCP_ENDP_E1_SUBSLOTS);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
 | 
			
		||||
			   trunk->trunk_type, __FILE__, __LINE__);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return endp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! release endpoint, all open connections are closed.
 | 
			
		||||
 *  \param[in] endp endpoint to release */
 | 
			
		||||
void mgcp_endp_release(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:0x%x\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp));
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
 | 
			
		||||
 | 
			
		||||
	/* Normally this function should only be called when
 | 
			
		||||
	 * all connections have been removed already. In case
 | 
			
		||||
@@ -53,4 +130,555 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
 | 
			
		||||
	talloc_free(endp->local_options.codec);
 | 
			
		||||
	endp->local_options.codec = NULL;
 | 
			
		||||
	endp->wildcarded_req = false;
 | 
			
		||||
 | 
			
		||||
	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,
 | 
			
		||||
 * so no ambiguity is introduced) */
 | 
			
		||||
static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	size_t prefix_len;
 | 
			
		||||
	switch (trunk->trunk_type) {
 | 
			
		||||
	case MGCP_TRUNK_VIRTUAL:
 | 
			
		||||
		prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
 | 
			
		||||
		if (strncmp
 | 
			
		||||
		    (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
 | 
			
		||||
		     prefix_len) == 0)
 | 
			
		||||
			memmove(epname, epname + prefix_len,
 | 
			
		||||
				strlen(epname) - prefix_len + 1);
 | 
			
		||||
		return;
 | 
			
		||||
	case MGCP_TRUNK_E1:
 | 
			
		||||
		prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
 | 
			
		||||
		if (strncmp
 | 
			
		||||
		    (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
 | 
			
		||||
		     prefix_len) == 0)
 | 
			
		||||
			memmove(epname, epname + prefix_len,
 | 
			
		||||
				strlen(epname) - prefix_len + 1);
 | 
			
		||||
		return;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
 | 
			
		||||
 * epname by writing a '\0' char where the suffix starts. */
 | 
			
		||||
static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	char *suffix_begin;
 | 
			
		||||
 | 
			
		||||
	/* Endpoints on the virtual trunk may have a domain name that is
 | 
			
		||||
	 * followed after an @ character, this can be chopped off. All
 | 
			
		||||
	 * other supported trunk types do not have any suffixes that may
 | 
			
		||||
	 * be chopped off */
 | 
			
		||||
	if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
 | 
			
		||||
		suffix_begin = strchr(epname, '@');
 | 
			
		||||
		if (!suffix_begin)
 | 
			
		||||
			return;
 | 
			
		||||
		*suffix_begin = '\0';
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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
 | 
			
		||||
 * result is either equal or lower then the length of the input string
 | 
			
		||||
 * (epname) */
 | 
			
		||||
static void strip_epname(char *epname_stripped, const char *epname,
 | 
			
		||||
			 const struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
 | 
			
		||||
	chop_epname_prefix(epname_stripped, trunk);
 | 
			
		||||
	chop_epname_suffix(epname_stripped, trunk);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Go through the trunk and find a random free (no active calls) endpoint,
 | 
			
		||||
 * this function is called when a wildcarded request is carried out, which
 | 
			
		||||
 * means that it is up to the MGW to choose a random free endpoint. */
 | 
			
		||||
static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < trunk->number_endpoints; i++) {
 | 
			
		||||
		endp = trunk->endpoints[i];
 | 
			
		||||
		/* A free endpoint must not serve a call already and it must
 | 
			
		||||
		 * be available. */
 | 
			
		||||
		if (endp->callid == NULL && mgcp_endp_avail(endp))
 | 
			
		||||
			return endp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find an endpoint specified by its name. If the endpoint can not be found,
 | 
			
		||||
 * return NULL */
 | 
			
		||||
static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
 | 
			
		||||
						    const struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	char epname_stripped[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	char epname_stripped_endp[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	/* Strip irrelevant information from the endpoint name */
 | 
			
		||||
	strip_epname(epname_stripped, epname, trunk);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < trunk->number_endpoints; i++) {
 | 
			
		||||
		endp = trunk->endpoints[i];
 | 
			
		||||
		strip_epname(epname_stripped_endp, endp->name, trunk);
 | 
			
		||||
		if (strcmp(epname_stripped_endp, epname_stripped) == 0)
 | 
			
		||||
			return endp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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.
 | 
			
		||||
 *  \param[in] trunk where the endpoint is located.
 | 
			
		||||
 *  \returns endpoint or NULL if endpoint was not found. */
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
 | 
			
		||||
					      const struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
 | 
			
		||||
	if (cause)
 | 
			
		||||
		*cause = 0;
 | 
			
		||||
 | 
			
		||||
	/* At the moment we only support a primitive ('*'-only) method of
 | 
			
		||||
	 * wildcarded endpoint searches that picks the next free endpoint on
 | 
			
		||||
	 * a trunk. */
 | 
			
		||||
	if (strstr(epname, "*")) {
 | 
			
		||||
		endp = find_free_endpoint(trunk);
 | 
			
		||||
		if (endp) {
 | 
			
		||||
			LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
 | 
			
		||||
				 "(trunk:%d) found free endpoint: %s\n",
 | 
			
		||||
				 trunk->trunk_nr, endp->name);
 | 
			
		||||
			endp->wildcarded_req = true;
 | 
			
		||||
			return endp;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "(trunk:%d) Not able to find a free endpoint\n",
 | 
			
		||||
		     trunk->trunk_nr);
 | 
			
		||||
		if (cause)
 | 
			
		||||
			*cause = -403;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Find an endpoint by its name (if wildcarded request is not
 | 
			
		||||
	 * applicable) */
 | 
			
		||||
	endp = find_specific_endpoint(epname, trunk);
 | 
			
		||||
	if (endp) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
 | 
			
		||||
			 "(trunk:%d) found endpoint: %s\n",
 | 
			
		||||
			 trunk->trunk_nr, endp->name);
 | 
			
		||||
		endp->wildcarded_req = false;
 | 
			
		||||
		return endp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
	     "(trunk:%d) Not able to find specified endpoint: %s\n",
 | 
			
		||||
	     trunk->trunk_nr, epname);
 | 
			
		||||
	if (cause)
 | 
			
		||||
		*cause = -500;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the domain name, which is supplied with the endpoint name
 | 
			
		||||
 * matches the configuration. */
 | 
			
		||||
static int check_domain_name(const char *epname, struct mgcp_config *cfg)
 | 
			
		||||
{
 | 
			
		||||
	char *domain_to_check;
 | 
			
		||||
 | 
			
		||||
	domain_to_check = strstr(epname, "@");
 | 
			
		||||
	if (!domain_to_check) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
 | 
			
		||||
		     epname, cfg->domain);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Accept any domain if configured as "*" */
 | 
			
		||||
	if (!strcmp(cfg->domain, "*"))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(domain_to_check+1, cfg->domain) != 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
 | 
			
		||||
		     epname, cfg->domain);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Find an endpoint by its name, search at all trunks.
 | 
			
		||||
 *  \param[out] cause, pointer to store cause code, can be NULL.
 | 
			
		||||
 *  \param[in] epname, must contain trunk prefix.
 | 
			
		||||
 *  \param[in] cfg, mgcp configuration (trunks).
 | 
			
		||||
 *  \returns endpoint or NULL if endpoint was not found. */
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
 | 
			
		||||
					struct mgcp_config *cfg)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk *trunk;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	char epname_lc[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
 | 
			
		||||
	osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
 | 
			
		||||
	epname = epname_lc;
 | 
			
		||||
 | 
			
		||||
	if (cause)
 | 
			
		||||
		*cause = -500;
 | 
			
		||||
 | 
			
		||||
	/* Identify the trunk where the endpoint is located */
 | 
			
		||||
	trunk = mgcp_trunk_by_name(cfg, epname);
 | 
			
		||||
	if (!trunk)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* All endpoint names require a domain as suffix */
 | 
			
		||||
	if (check_domain_name(epname, cfg))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Identify the endpoint on the trunk */
 | 
			
		||||
        endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
 | 
			
		||||
	if (!endp) {
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cause)
 | 
			
		||||
		*cause = 0;
 | 
			
		||||
	return endp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get the E1 timeslot number from a given E1 endpoint name
 | 
			
		||||
 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
 | 
			
		||||
static uint8_t e1_ts_nr_from_epname(const char *epname)
 | 
			
		||||
{
 | 
			
		||||
	char buf[MGCP_ENDPOINT_MAXLEN + 1];
 | 
			
		||||
	char *save_ptr = NULL;
 | 
			
		||||
	char *buf_ptr = buf;
 | 
			
		||||
	char *token;
 | 
			
		||||
	unsigned long int res = 0;
 | 
			
		||||
 | 
			
		||||
	strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		token = strtok_r(buf_ptr, "/", &save_ptr);
 | 
			
		||||
		buf_ptr = NULL;
 | 
			
		||||
		if (!token)
 | 
			
		||||
			break;
 | 
			
		||||
		if (strncmp(token, "s-", 2) == 0) {
 | 
			
		||||
			errno = 0;
 | 
			
		||||
			res = strtoul(token + 2, NULL, 10);
 | 
			
		||||
			if (errno == ERANGE || res > NUM_E1_TS)
 | 
			
		||||
				return 0xff;
 | 
			
		||||
			return (uint8_t) res;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0xff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get the E1 timeslot number from a given E1 endpoint name
 | 
			
		||||
 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
 | 
			
		||||
static uint8_t e1_rate_from_epname(const char *epname)
 | 
			
		||||
{
 | 
			
		||||
	char buf[MGCP_ENDPOINT_MAXLEN + 1];
 | 
			
		||||
	char *save_ptr = NULL;
 | 
			
		||||
	char *buf_ptr = buf;
 | 
			
		||||
	char *token;
 | 
			
		||||
	unsigned long int res = 0;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		token = strtok_r(buf_ptr, "/", &save_ptr);
 | 
			
		||||
		buf_ptr = NULL;
 | 
			
		||||
		if (!token)
 | 
			
		||||
			break;
 | 
			
		||||
		if (strncmp(token, "su", 2) == 0) {
 | 
			
		||||
			errno = 0;
 | 
			
		||||
			res = strtoul(token + 2, NULL, 10);
 | 
			
		||||
			if (errno == ERANGE || res > E1_RATE_MAX)
 | 
			
		||||
				return 0xff;
 | 
			
		||||
			/* Make sure the rate is a valid rate */
 | 
			
		||||
			for (i = 0; i < sizeof(e1_rates); i++) {
 | 
			
		||||
				if (res == e1_rates[i])
 | 
			
		||||
					return (uint8_t) res;
 | 
			
		||||
			}
 | 
			
		||||
			return 0xff;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0xff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get the E1 bitstream offset from a given E1 endpoint name
 | 
			
		||||
 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
 | 
			
		||||
static uint8_t e1_offs_from_epname(const char *epname)
 | 
			
		||||
{
 | 
			
		||||
	char buf[MGCP_ENDPOINT_MAXLEN + 1];
 | 
			
		||||
	char *save_ptr = NULL;
 | 
			
		||||
	char *buf_ptr = buf;
 | 
			
		||||
	char *token;
 | 
			
		||||
	unsigned long int res = 0;
 | 
			
		||||
 | 
			
		||||
	strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		token = strtok_r(buf_ptr, "/", &save_ptr);
 | 
			
		||||
		buf_ptr = NULL;
 | 
			
		||||
		if (!token)
 | 
			
		||||
			break;
 | 
			
		||||
		if (strncmp(token, "su", 2) == 0) {
 | 
			
		||||
			token = strstr(token, "-");
 | 
			
		||||
			if (!token)
 | 
			
		||||
				return 0xff;
 | 
			
		||||
			token += 1;
 | 
			
		||||
			errno = 0;
 | 
			
		||||
			res = strtoul(token, NULL, 10);
 | 
			
		||||
			if (errno == ERANGE || res > E1_OFFS_MAX)
 | 
			
		||||
				return 0xff;
 | 
			
		||||
			return (uint8_t) res;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0xff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get the E1 subslot number (internal) from a given E1 endpoint name
 | 
			
		||||
 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
 | 
			
		||||
static uint8_t e1_ss_nr_from_epname(const char *epname)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t rate;
 | 
			
		||||
	uint8_t offs;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	rate = e1_rate_from_epname(epname);
 | 
			
		||||
	offs = e1_offs_from_epname(epname);
 | 
			
		||||
 | 
			
		||||
	osmo_static_assert(sizeof(e1_rates) == sizeof(e1_offsets), e1_rates_e1_offsets_size);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < sizeof(e1_rates); i++) {
 | 
			
		||||
		if ((e1_rates[i] == rate) && (e1_offsets[i] == offs))
 | 
			
		||||
			return i;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0xff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the selected E1 endpoint is avalable, which means that none of
 | 
			
		||||
 * the overlapping endpoints are currently serving a call. (if the system
 | 
			
		||||
 * is properly configured such a situation should never ocurr!) */
 | 
			
		||||
static bool endp_avail_e1(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	/* The following map shows the overlapping of the subslots and their
 | 
			
		||||
	 * respective rates. The numbers on the right running from top to bottom
 | 
			
		||||
	 * are the bit offsets in the whole 64k timeslot. The numbers inside the
 | 
			
		||||
	 * boxes symbolize the internal subslot number (array index) and the
 | 
			
		||||
	 * rate in the form: i:r where i is the subslot number and r the
 | 
			
		||||
	 * respective rate.
 | 
			
		||||
	 *
 | 
			
		||||
	 * +--------+--------+--------+--------+ 0
 | 
			
		||||
	 * |        |        |        |  7:8k  |
 | 
			
		||||
	 * |        |        + 3:16k  +--------+ 1
 | 
			
		||||
	 * |        |        |        |  8:8k  |
 | 
			
		||||
	 * |        | 1:32k  +--------+--------+ 2
 | 
			
		||||
	 * |        |        |        |  9:8k  |
 | 
			
		||||
	 * |        |        + 4:16k  +--------+ 3
 | 
			
		||||
	 * |        |        |        | 10:8k  |
 | 
			
		||||
	 * | 0:64k  +--------+--------+--------+ 4
 | 
			
		||||
	 * |        |        |        | 11:8k  |
 | 
			
		||||
	 * |        |        + 5:16k  +--------+ 5
 | 
			
		||||
	 * |        |        |        | 12:8k  |
 | 
			
		||||
	 * |        | 2:32k  +--------+--------+ 6
 | 
			
		||||
	 * |        |        |        | 13:8k  |
 | 
			
		||||
	 * |        |        + 6:16k  +--------+ 7
 | 
			
		||||
	 * |        |        |        | 14:8k  |
 | 
			
		||||
	 * +--------+--------+--------+--------+ 8
 | 
			
		||||
	 *
 | 
			
		||||
	 * The following array contains tables with the subslot numbers that must be
 | 
			
		||||
	 * unused for each subslot. During this test we do not have to check the
 | 
			
		||||
	 * endpoint we need to verify, only the overlaps need to be checked. This is
 | 
			
		||||
	 * also the reason why the related subslot number is missing from each each
 | 
			
		||||
	 * line. */
 | 
			
		||||
	const int8_t interlock_tab[MGCP_ENDP_E1_SUBSLOTS][15] = {
 | 
			
		||||
		{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1 },
 | 
			
		||||
		{ 0, 3, 4, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 5, 6, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 1, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 2, 11, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 2, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
 | 
			
		||||
		{ 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } };
 | 
			
		||||
 | 
			
		||||
	const int8_t *interlock;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	uint8_t ts_nr = 0;
 | 
			
		||||
	uint8_t ss_nr = 0;
 | 
			
		||||
	char *epname_check;
 | 
			
		||||
	struct mgcp_endpoint *endp_check;
 | 
			
		||||
	bool available = true;
 | 
			
		||||
 | 
			
		||||
	/* This function must only be used with E1 type endpoints! */
 | 
			
		||||
	OSMO_ASSERT(endp->trunk->trunk_type == MGCP_TRUNK_E1);
 | 
			
		||||
 | 
			
		||||
	ts_nr = e1_ts_nr_from_epname(endp->name);
 | 
			
		||||
	ss_nr = e1_ss_nr_from_epname(endp->name);
 | 
			
		||||
	if (ts_nr == 0xff || ss_nr == 0xff) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "cannot check endpoint availability, endpoint name not parseable!\n");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	interlock = interlock_tab[ss_nr];
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < sizeof(interlock_tab[0]); i++) {
 | 
			
		||||
		/* Detect row end */
 | 
			
		||||
		if (interlock[i] == -1)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		/* Pick overlapping endpoint to check */
 | 
			
		||||
		epname_check = gen_e1_epname(endp, endp->trunk->cfg->domain,
 | 
			
		||||
					     endp->trunk->trunk_nr, ts_nr,
 | 
			
		||||
					     interlock[i]);
 | 
			
		||||
		endp_check = find_specific_endpoint(epname_check, endp->trunk);
 | 
			
		||||
		if (!endp_check) {
 | 
			
		||||
			LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
				 "cannot check endpoint availability, overlapping endpoint:%s not found!\n",
 | 
			
		||||
				 epname_check);
 | 
			
		||||
			talloc_free(epname_check);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		talloc_free(epname_check);
 | 
			
		||||
 | 
			
		||||
		/* Check if overlapping endpoint currently serves another call
 | 
			
		||||
		 * (This is an exceptional situation, that should not occur
 | 
			
		||||
		 * in a properly configured environment!) */
 | 
			
		||||
		if (endp_check->callid) {
 | 
			
		||||
			LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
				 "endpoint unavailable - overlapping endpoint:%s already serves a call!\n",
 | 
			
		||||
				 endp_check->name);
 | 
			
		||||
			available = false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return available;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! check if an endpoint is available for any kind of operation.
 | 
			
		||||
 *  \param[in] endp endpoint to check.
 | 
			
		||||
 *  \returns true if endpoint is avalable, false it is blocked for any reason. */
 | 
			
		||||
bool mgcp_endp_avail(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	switch (endp->trunk->trunk_type) {
 | 
			
		||||
	case MGCP_TRUNK_VIRTUAL:
 | 
			
		||||
		/* There are no obstacles that may render a virtual trunk
 | 
			
		||||
		 * endpoint unusable, so virtual trunk endpoints are always
 | 
			
		||||
		 * available */
 | 
			
		||||
		return true;
 | 
			
		||||
	case MGCP_TRUNK_E1:
 | 
			
		||||
		return endp_avail_e1(endp);
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! claim endpoint, sets callid and activates endpoint, should be called at the
 | 
			
		||||
 *  beginning of the CRCX procedure when it is clear that a new call should be
 | 
			
		||||
 *  created.
 | 
			
		||||
 *  \param[in] endp endpoint to claim.
 | 
			
		||||
 *  \param[in] callid that is assingned to this endpoint. */
 | 
			
		||||
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	uint8_t ts;
 | 
			
		||||
	uint8_t ss;
 | 
			
		||||
	uint8_t offs;
 | 
			
		||||
 | 
			
		||||
	/* TODO: Make this function more intelligent, it should run the
 | 
			
		||||
	 * call id checks we currently have in protocol.c directly here. */
 | 
			
		||||
 | 
			
		||||
	/* Set the callid, creation of another connection will only be possible
 | 
			
		||||
	 * when the callid matches up. (Connections are distinguished by their
 | 
			
		||||
	 * connection ids) */
 | 
			
		||||
	endp->callid = talloc_strdup(endp, callid);
 | 
			
		||||
	OSMO_ASSERT(endp->callid);
 | 
			
		||||
 | 
			
		||||
	/* Allocate resources */
 | 
			
		||||
	switch (endp->trunk->trunk_type) {
 | 
			
		||||
	case MGCP_TRUNK_VIRTUAL:
 | 
			
		||||
		/* No additional initaliziation required here, virtual
 | 
			
		||||
		 * endpoints will open/close network sockets themselves
 | 
			
		||||
		 * on demand. */
 | 
			
		||||
		break;
 | 
			
		||||
	case MGCP_TRUNK_E1:
 | 
			
		||||
		ts = e1_ts_nr_from_epname(endp->name);
 | 
			
		||||
		ss = e1_ss_nr_from_epname(endp->name);
 | 
			
		||||
		offs = e1_offs_from_epname(endp->name);
 | 
			
		||||
		OSMO_ASSERT(ts != 0xFF);
 | 
			
		||||
		OSMO_ASSERT(ts != 0);
 | 
			
		||||
		OSMO_ASSERT(ss != 0xFF);
 | 
			
		||||
		OSMO_ASSERT(offs != 0xFF);
 | 
			
		||||
		rc = mgcp_e1_endp_equip(endp, ts, ss, offs);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Make sure the endpoint is released when claiming the endpoint fails. */
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		mgcp_endp_release(endp);
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! update endpoint, updates internal endpoint specific data, should be
 | 
			
		||||
 *  after when MDCX or CRCX has been executed successuflly.
 | 
			
		||||
 *  \param[in] endp endpoint to update. */
 | 
			
		||||
void mgcp_endp_update(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	/* Allocate resources */
 | 
			
		||||
	switch (endp->trunk->trunk_type) {
 | 
			
		||||
	case MGCP_TRUNK_VIRTUAL:
 | 
			
		||||
		/* No updating initaliziation required for virtual endpoints. */
 | 
			
		||||
		break;
 | 
			
		||||
	case MGCP_TRUNK_E1:
 | 
			
		||||
		mgcp_e1_endp_update(endp);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	llist_add(&conn->entry, &endp->conns);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	/* Run endpoint cleanup action. By this we inform the endpoint about
 | 
			
		||||
	 * the removal of the connection and allow it to clean up its inner
 | 
			
		||||
	 * state accordingly */
 | 
			
		||||
	if (endp->type->cleanup_cb)
 | 
			
		||||
		endp->type->cleanup_cb(endp, conn);
 | 
			
		||||
	llist_del(&conn->entry);
 | 
			
		||||
	if (llist_empty(&endp->conns))
 | 
			
		||||
		mgcp_endp_release(endp);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,9 @@
 | 
			
		||||
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_msg.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
@@ -82,9 +84,8 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	if (!mode) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x missing connection mode\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "missing connection mode\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if (!conn)
 | 
			
		||||
@@ -92,39 +93,35 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
 | 
			
		||||
	if (!endp)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(mode, "recvonly") == 0)
 | 
			
		||||
	if (strcasecmp(mode, "recvonly") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_RECV_ONLY;
 | 
			
		||||
	else if (strcmp(mode, "sendrecv") == 0)
 | 
			
		||||
	else if (strcasecmp(mode, "sendrecv") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_RECV_SEND;
 | 
			
		||||
	else if (strcmp(mode, "sendonly") == 0)
 | 
			
		||||
	else if (strcasecmp(mode, "sendonly") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_SEND_ONLY;
 | 
			
		||||
	else if (strcmp(mode, "loopback") == 0)
 | 
			
		||||
	else if (strcasecmp(mode, "loopback") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_LOOPBACK;
 | 
			
		||||
	else {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x unknown connection mode: '%s'\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), mode);
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "unknown connection mode: '%s'\n", mode);
 | 
			
		||||
		ret = -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Special handling für RTP connections */
 | 
			
		||||
	/* Special handling for RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		conn->u.rtp.end.output_enabled =
 | 
			
		||||
		    conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG,
 | 
			
		||||
	     "endpoint:0x%x conn:%s\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn));
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG,
 | 
			
		||||
	     "endpoint:0x%x connection mode '%s' %d\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), mode, conn->mode);
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
 | 
			
		||||
		 mode, conn->mode);
 | 
			
		||||
 | 
			
		||||
	/* Special handling für RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x output_enabled %d\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), conn->u.rtp.end.output_enabled);
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n",
 | 
			
		||||
			 conn->u.rtp.end.output_enabled);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* The VTY might change the connection mode at any time, so we have
 | 
			
		||||
@@ -134,167 +131,6 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* We have a null terminated string with the endpoint name here. We only
 | 
			
		||||
 * support two kinds. Simple ones as seen on the BSC level and the ones
 | 
			
		||||
 * seen on the trunk side. (helper function for find_endpoint()) */
 | 
			
		||||
static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
 | 
			
		||||
					      const char *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	char *rest = NULL;
 | 
			
		||||
	struct mgcp_trunk_config *tcfg;
 | 
			
		||||
	int trunk, endp;
 | 
			
		||||
	struct mgcp_endpoint *endp_ptr;
 | 
			
		||||
 | 
			
		||||
	trunk = strtoul(mgcp + 6, &rest, 10);
 | 
			
		||||
	if (rest == NULL || rest[0] != '/' || trunk < 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endp = strtoul(rest + 1, &rest, 10);
 | 
			
		||||
	if (rest == NULL || rest[0] != '@') {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* signalling is on timeslot 1 */
 | 
			
		||||
	if (endp == 1)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	tcfg = mgcp_trunk_num(cfg, trunk);
 | 
			
		||||
	if (!tcfg) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
 | 
			
		||||
		     trunk);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!tcfg->endpoints) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Endpoints of trunk %d not allocated.\n", trunk);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (endp < 1 || endp >= tcfg->number_endpoints) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
 | 
			
		||||
		     mgcp);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endp_ptr = &tcfg->endpoints[endp];
 | 
			
		||||
	endp_ptr->wildcarded_req = false;
 | 
			
		||||
	return endp_ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find an endpoint that is not in use. Do this by going through the endpoint
 | 
			
		||||
 * array, check the callid. A callid nullpointer indicates that the endpoint
 | 
			
		||||
 * is free */
 | 
			
		||||
static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
 | 
			
		||||
						unsigned int number_endpoints)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < number_endpoints; i++) {
 | 
			
		||||
		if (endpoints[i].callid == NULL) {
 | 
			
		||||
			endp = &endpoints[i];
 | 
			
		||||
			LOGP(DLMGCP, LOGL_DEBUG,
 | 
			
		||||
			     "endpoint:0x%x found free endpoint\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp));
 | 
			
		||||
			endp->wildcarded_req = true;
 | 
			
		||||
			return endp;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n");
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the domain name, which is supplied with the endpoint name
 | 
			
		||||
 * matches the configuration. */
 | 
			
		||||
static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	char *domain_to_check;
 | 
			
		||||
 | 
			
		||||
	domain_to_check = strstr(mgcp, "@");
 | 
			
		||||
	if (!domain_to_check)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Accept any domain if configured as "*" */
 | 
			
		||||
	if (!strcmp(cfg->domain, "*"))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(domain_to_check+1, cfg->domain) != 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Search the endpoint pool for the endpoint that had been selected via the
 | 
			
		||||
 * MGCP message (helper function for mgcp_analyze_header()) */
 | 
			
		||||
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
 | 
			
		||||
					   const char *mgcp,
 | 
			
		||||
					   int *cause)
 | 
			
		||||
{
 | 
			
		||||
	char *endptr = NULL;
 | 
			
		||||
	unsigned int gw = INT_MAX;
 | 
			
		||||
	const char *endpoint_number_str;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
 | 
			
		||||
	*cause = 0;
 | 
			
		||||
 | 
			
		||||
	/* Check if the domainname in the request is correct */
 | 
			
		||||
	if (check_domain_name(cfg, mgcp)) {
 | 
			
		||||
		*cause = -500;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if the E1 trunk is requested */
 | 
			
		||||
	if (strncmp(mgcp, "ds/e1", 5) == 0) {
 | 
			
		||||
		endp = find_e1_endpoint(cfg, mgcp);
 | 
			
		||||
		if (!endp)
 | 
			
		||||
			*cause = -500;
 | 
			
		||||
		return endp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if the virtual trunk is addressed (new, correct way with prefix) */
 | 
			
		||||
	if (strncmp
 | 
			
		||||
	    (mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
 | 
			
		||||
	     strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) {
 | 
			
		||||
		endpoint_number_str =
 | 
			
		||||
		    mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
 | 
			
		||||
		if (endpoint_number_str[0] == '*') {
 | 
			
		||||
			endp = find_free_endpoint(cfg->trunk.endpoints,
 | 
			
		||||
						  cfg->trunk.number_endpoints);
 | 
			
		||||
			if (!endp)
 | 
			
		||||
				*cause = -403;
 | 
			
		||||
			return endp;
 | 
			
		||||
		}
 | 
			
		||||
		gw = strtoul(endpoint_number_str, &endptr, 16);
 | 
			
		||||
		if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
 | 
			
		||||
			endp = &cfg->trunk.endpoints[gw];
 | 
			
		||||
			endp->wildcarded_req = false;
 | 
			
		||||
			return endp;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Deprecated method without prefix */
 | 
			
		||||
	LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
	     "Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n",
 | 
			
		||||
	     MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp);
 | 
			
		||||
	gw = strtoul(mgcp, &endptr, 16);
 | 
			
		||||
	if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
 | 
			
		||||
		endp = &cfg->trunk.endpoints[gw];
 | 
			
		||||
		endp->wildcarded_req = false;
 | 
			
		||||
		return endp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
 | 
			
		||||
	*cause = -500;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Analyze and parse the the hader of an MGCP messeage string.
 | 
			
		||||
 *  \param[out] pdata caller provided memory to store the parsing results
 | 
			
		||||
 *  \param[in] data mgcp message string
 | 
			
		||||
@@ -322,7 +158,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
 | 
			
		||||
			pdata->trans = elem;
 | 
			
		||||
			break;
 | 
			
		||||
		case 1:
 | 
			
		||||
			pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
 | 
			
		||||
			pdata->endp = mgcp_endp_by_name(&cause, elem, pdata->cfg);
 | 
			
		||||
			if (!pdata->endp) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
				     "Unable to find Endpoint `%s'\n", elem);
 | 
			
		||||
@@ -331,7 +167,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case 2:
 | 
			
		||||
			if (strcmp("MGCP", elem)) {
 | 
			
		||||
			if (strcasecmp("MGCP", elem)) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
				     "MGCP header parsing error\n");
 | 
			
		||||
				return -510;
 | 
			
		||||
@@ -360,18 +196,27 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
 | 
			
		||||
 | 
			
		||||
/*! Extract OSMUX CID from an MGCP parameter line (string).
 | 
			
		||||
 *  \param[in] line single parameter line from the MGCP message
 | 
			
		||||
 *  \returns OSMUX CID, -1 on error */
 | 
			
		||||
 *  \returns OSMUX CID, -1 wildcard, -2 on error */
 | 
			
		||||
int mgcp_parse_osmux_cid(const char *line)
 | 
			
		||||
{
 | 
			
		||||
	int osmux_cid;
 | 
			
		||||
 | 
			
		||||
	if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1)
 | 
			
		||||
 | 
			
		||||
	if (strcasecmp(line + 2, "Osmux: *") == 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
 | 
			
		||||
		     line);
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (osmux_cid > OSMUX_CID_MAX) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
 | 
			
		||||
		     osmux_cid, OSMUX_CID_MAX);
 | 
			
		||||
		return -1;
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
 | 
			
		||||
 | 
			
		||||
@@ -387,8 +232,8 @@ int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
 | 
			
		||||
	const size_t line_len = strlen(line);
 | 
			
		||||
	if (line[0] != '\0' && line_len < 2) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Wrong MGCP option format: '%s' on 0x%x\n",
 | 
			
		||||
		     line, ENDPOINT_NUMBER(endp));
 | 
			
		||||
		     "Wrong MGCP option format: '%s' on %s\n",
 | 
			
		||||
		     line, endp->name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -400,7 +245,7 @@ int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
 | 
			
		||||
/*! Check if the specified callid seems plausible.
 | 
			
		||||
  * \param[in] endp pointer to endpoint
 | 
			
		||||
  * \param{in] callid to verify
 | 
			
		||||
  * \returns 1 when callid seems plausible, 0 on error */
 | 
			
		||||
  * \returns 0 when callid seems plausible, -1 on error */
 | 
			
		||||
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
 | 
			
		||||
{
 | 
			
		||||
	/*! This function compares the supplied callid with the called that is
 | 
			
		||||
@@ -419,9 +264,9 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(endp->callid, callid) != 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x CallIDs mismatch: '%s' != '%s'\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), endp->callid, callid);
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "CallIDs mismatch: '%s' != '%s'\n",
 | 
			
		||||
			 endp->callid, callid);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -440,25 +285,23 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
 | 
			
		||||
 | 
			
		||||
	/* Check for null identifiers */
 | 
			
		||||
	if (!conn_id) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x invalid ConnectionIdentifier (missing)\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "invalid ConnectionIdentifier (missing)\n");
 | 
			
		||||
		return 510;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check for empty connection identifiers */
 | 
			
		||||
	if (strlen(conn_id) == 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x invalid ConnectionIdentifier (empty)\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "invalid ConnectionIdentifier (empty)\n");
 | 
			
		||||
		return 510;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check for over long connection identifiers */
 | 
			
		||||
	if (strlen(conn_id) > (MGCP_CONN_ID_MAXLEN-1)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x invalid ConnectionIdentifier (too long: %zu > %d) 0x%s\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), strlen(conn_id), MGCP_CONN_ID_MAXLEN-1, conn_id);
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			"invalid ConnectionIdentifier (too long: %zu > %d) 0x%s\n",
 | 
			
		||||
			 strlen(conn_id), MGCP_CONN_ID_MAXLEN-1, conn_id);
 | 
			
		||||
		return 510;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -466,9 +309,8 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
 | 
			
		||||
	if (mgcp_conn_get(endp, conn_id))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
	     "endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), conn_id);
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
	     "no connection found under ConnectionIdentifier 0x%s\n", conn_id);
 | 
			
		||||
 | 
			
		||||
	/* When the conn_id was not found, return error code 515 "The transaction refers to an incorrect
 | 
			
		||||
	 * connection-id (may have been already deleted)." */
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -15,16 +15,19 @@
 | 
			
		||||
#include <inttypes.h> /* for PRIu64 */
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/netif/osmux.h>
 | 
			
		||||
#include <osmocom/netif/rtp.h>
 | 
			
		||||
#include <osmocom/netif/amr.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
 | 
			
		||||
static struct osmo_fd osmux_fd;
 | 
			
		||||
 | 
			
		||||
@@ -32,9 +35,10 @@ static LLIST_HEAD(osmux_handle_list);
 | 
			
		||||
 | 
			
		||||
struct osmux_handle {
 | 
			
		||||
	struct llist_head head;
 | 
			
		||||
	struct mgcp_conn_rtp *conn;
 | 
			
		||||
	struct osmux_in_handle *in;
 | 
			
		||||
	struct in_addr rem_addr;
 | 
			
		||||
	int rem_port;
 | 
			
		||||
	int rem_port; /* network byte order */
 | 
			
		||||
	int refcnt;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -44,14 +48,17 @@ static void *osmux;
 | 
			
		||||
static void osmux_deliver_cb(struct msgb *batch_msg, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct osmux_handle *handle = data;
 | 
			
		||||
	struct sockaddr_in out = {
 | 
			
		||||
		.sin_family = AF_INET,
 | 
			
		||||
		.sin_port = handle->rem_port,
 | 
			
		||||
	};
 | 
			
		||||
	struct mgcp_conn_rtp *conn = handle->conn;
 | 
			
		||||
 | 
			
		||||
	memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr));
 | 
			
		||||
	sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0,
 | 
			
		||||
		(struct sockaddr *)&out, sizeof(out));
 | 
			
		||||
	if (conn->end.output_enabled) {
 | 
			
		||||
		struct sockaddr_in out = {
 | 
			
		||||
			.sin_family = AF_INET,
 | 
			
		||||
			.sin_port = handle->rem_port,
 | 
			
		||||
		};
 | 
			
		||||
		memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr));
 | 
			
		||||
		sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0,
 | 
			
		||||
			(struct sockaddr *)&out, sizeof(out));
 | 
			
		||||
	}
 | 
			
		||||
	msgb_free(batch_msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -106,13 +113,15 @@ static void osmux_handle_put(struct osmux_in_handle *in)
 | 
			
		||||
 | 
			
		||||
/* Allocate free OSMUX handle */
 | 
			
		||||
static struct osmux_handle *
 | 
			
		||||
osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
 | 
			
		||||
osmux_handle_alloc(struct mgcp_conn_rtp *conn, struct in_addr *addr, int rem_port)
 | 
			
		||||
{
 | 
			
		||||
	struct osmux_handle *h;
 | 
			
		||||
	struct mgcp_config *cfg = conn->conn->endp->cfg;
 | 
			
		||||
 | 
			
		||||
	h = talloc_zero(osmux, struct osmux_handle);
 | 
			
		||||
	if (!h)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	h->conn = conn;
 | 
			
		||||
	h->rem_addr = *addr;
 | 
			
		||||
	h->rem_port = rem_port;
 | 
			
		||||
	h->refcnt++;
 | 
			
		||||
@@ -145,15 +154,20 @@ osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
 | 
			
		||||
/* Lookup existing handle for a specified address, if the handle can not be
 | 
			
		||||
 * found, the function will automatically allocate one */
 | 
			
		||||
static struct osmux_in_handle *
 | 
			
		||||
osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
 | 
			
		||||
osmux_handle_lookup(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *addr, int rem_port)
 | 
			
		||||
{
 | 
			
		||||
	struct osmux_handle *h;
 | 
			
		||||
 | 
			
		||||
	h = osmux_handle_find_get(addr, rem_port);
 | 
			
		||||
	if (addr->u.sa.sa_family != AF_INET) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "IPv6 not supported in osmux yet!\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	h = osmux_handle_find_get(&addr->u.sin.sin_addr, rem_port);
 | 
			
		||||
	if (h != NULL)
 | 
			
		||||
		return h->in;
 | 
			
		||||
 | 
			
		||||
	h = osmux_handle_alloc(cfg, addr, rem_port);
 | 
			
		||||
	h = osmux_handle_alloc(conn, &addr->u.sin.sin_addr, rem_port);
 | 
			
		||||
	if (h == NULL)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
@@ -177,6 +191,12 @@ int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn)
 | 
			
		||||
	memcpy(msg->data, buf, buf_len);
 | 
			
		||||
	msgb_put(msg, buf_len);
 | 
			
		||||
 | 
			
		||||
	if (conn->osmux.state != OSMUX_STATE_ENABLED) {
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_INFO, "forwarding RTP to Osmux conn not yet enabled, dropping (cid=%d)\n",
 | 
			
		||||
		conn->osmux.cid);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.cid)) > 0) {
 | 
			
		||||
		/* batch full, build and deliver it */
 | 
			
		||||
		osmux_xfrm_input_deliver(conn->osmux.in);
 | 
			
		||||
@@ -185,111 +205,57 @@ int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Lookup the endpoint that corresponds to the specified address (port) */
 | 
			
		||||
static struct mgcp_endpoint *
 | 
			
		||||
endpoint_lookup(struct mgcp_config *cfg, int cid,
 | 
			
		||||
		struct in_addr *from_addr, int type)
 | 
			
		||||
static struct mgcp_conn_rtp*
 | 
			
		||||
osmux_conn_lookup(struct mgcp_config *cfg, uint8_t cid,
 | 
			
		||||
		struct in_addr *from_addr)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp = NULL;
 | 
			
		||||
	struct mgcp_trunk *trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	struct mgcp_conn *conn = NULL;
 | 
			
		||||
	struct mgcp_conn_rtp * conn_rtp;
 | 
			
		||||
	int i;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_net = NULL;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_bts = NULL;
 | 
			
		||||
 | 
			
		||||
	for (i=0; i<cfg->trunk.number_endpoints; i++) {
 | 
			
		||||
		struct in_addr *this;
 | 
			
		||||
	for (i = 0; i < trunk->number_endpoints; i++) {
 | 
			
		||||
 | 
			
		||||
		endp = &cfg->trunk.endpoints[i];
 | 
			
		||||
		endp = trunk->endpoints[i];
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
		if (!tmp->allocated)
 | 
			
		||||
			continue;
 | 
			
		||||
#endif
 | 
			
		||||
		llist_for_each_entry(conn, &endp->conns, entry) {
 | 
			
		||||
			if (conn->type != MGCP_CONN_TYPE_RTP)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
		switch(type) {
 | 
			
		||||
		case MGCP_DEST_NET:
 | 
			
		||||
			/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
			conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
 | 
			
		||||
			if (conn_net)
 | 
			
		||||
				this = &conn_net->end.addr;
 | 
			
		||||
			else
 | 
			
		||||
				this = NULL;
 | 
			
		||||
			break;
 | 
			
		||||
		case MGCP_DEST_BTS:
 | 
			
		||||
			/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
			conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
 | 
			
		||||
			if (conn_bts)
 | 
			
		||||
				this = &conn_bts->end.addr;
 | 
			
		||||
			else
 | 
			
		||||
				this = NULL;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* Should not ever happen */
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
 | 
			
		||||
			return NULL;
 | 
			
		||||
			conn_rtp = &conn->u.rtp;
 | 
			
		||||
			if (!mgcp_conn_rtp_is_osmux(conn_rtp))
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (conn_rtp->osmux.cid == cid)
 | 
			
		||||
				return conn_rtp;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
		conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
 | 
			
		||||
		if (conn_net && this && conn_net->osmux.cid == cid
 | 
			
		||||
		    && this->s_addr == from_addr->s_addr)
 | 
			
		||||
			return endp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid);
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Cannot find osmux conn with cid=%d\n", cid);
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void scheduled_tx_net_cb(struct msgb *msg, void *data)
 | 
			
		||||
/* FIXME: this is declared and used in mgcp_network.c, but documentation of mgcp_dispatch_rtp_bridge_cb() states another enum is to be used */
 | 
			
		||||
enum {
 | 
			
		||||
	MGCP_PROTO_RTP,
 | 
			
		||||
	MGCP_PROTO_RTCP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp = data;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_net = NULL;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_bts = NULL;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
	conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
 | 
			
		||||
	conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
 | 
			
		||||
	if (!conn_bts || !conn_net)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	struct sockaddr_in addr = {
 | 
			
		||||
		.sin_addr = conn_net->end.addr,
 | 
			
		||||
		.sin_port = conn_net->end.rtp_port,
 | 
			
		||||
	struct mgcp_conn_rtp *conn = data;
 | 
			
		||||
	struct mgcp_endpoint *endp = conn->conn->endp;
 | 
			
		||||
	struct osmo_sockaddr addr = { /* FIXME: do we know the source address?? */ };
 | 
			
		||||
	struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
 | 
			
		||||
	*mc = (struct osmo_rtp_msg_ctx){
 | 
			
		||||
		.proto = MGCP_PROTO_RTP,
 | 
			
		||||
		.conn_src = conn,
 | 
			
		||||
		.from_addr = &addr,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(&conn_bts->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
 | 
			
		||||
	rate_ctr_add(&conn_bts->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
 | 
			
		||||
 | 
			
		||||
	/* Send RTP data to NET */
 | 
			
		||||
	/* FIXME: Get rid of conn_bts and conn_net! */
 | 
			
		||||
	mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
 | 
			
		||||
		  conn_bts, conn_net);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp = data;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_net = NULL;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_bts = NULL;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
	conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
 | 
			
		||||
	conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
 | 
			
		||||
	if (!conn_bts || !conn_net)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	struct sockaddr_in addr = {
 | 
			
		||||
		.sin_addr = conn_bts->end.addr,
 | 
			
		||||
		.sin_port = conn_bts->end.rtp_port,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(&conn_net->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
 | 
			
		||||
	rate_ctr_add(&conn_net->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
 | 
			
		||||
 | 
			
		||||
	/* Send RTP data to BTS */
 | 
			
		||||
	/* FIXME: Get rid of conn_bts and conn_net! */
 | 
			
		||||
	mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
 | 
			
		||||
		  conn_net, conn_bts);
 | 
			
		||||
	endp->type->dispatch_rtp_cb(msg);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -320,26 +286,31 @@ static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
 | 
			
		||||
static int endp_osmux_state_check(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
 | 
			
		||||
				  bool sending)
 | 
			
		||||
{
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
 | 
			
		||||
	switch(conn->osmux.state) {
 | 
			
		||||
	case OSMUX_STATE_ACTIVATING:
 | 
			
		||||
	if (osmux_enable_conn(endp, conn, &conn->end.addr, htons(endp->cfg->osmux_port)) < 0) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
				"Could not enable osmux for conn:%s\n",
 | 
			
		||||
				mgcp_conn_dump(conn->conn));
 | 
			
		||||
	if (osmux_enable_conn(endp, conn, &conn->end.addr, conn->end.rtp_port) < 0) {
 | 
			
		||||
			LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
				 "Could not enable osmux for conn on %s: %s\n",
 | 
			
		||||
				 sending ? "sent" : "received",
 | 
			
		||||
				 mgcp_conn_dump(conn->conn));
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		    "Osmux CID %u for %s:%u is now enabled\n",
 | 
			
		||||
		    conn->osmux.cid, inet_ntoa(conn->end.addr),
 | 
			
		||||
		    endp->cfg->osmux_port);
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "Osmux %s CID %u towards %s:%u is now enabled\n",
 | 
			
		||||
			 sending ? "sent" : "received",
 | 
			
		||||
			 conn->osmux.cid,
 | 
			
		||||
			 osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf),
 | 
			
		||||
			 ntohs(conn->end.rtp_port));
 | 
			
		||||
		return 0;
 | 
			
		||||
	case OSMUX_STATE_ENABLED:
 | 
			
		||||
		return 0;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Osmux %s in conn %s without full negotiation, state %d\n",
 | 
			
		||||
		     sending ? "sent" : "received",
 | 
			
		||||
		     mgcp_conn_dump(conn->conn), conn->osmux.state);
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "Osmux %s in conn %s without full negotiation, state %d\n",
 | 
			
		||||
			 sending ? "sent" : "received",
 | 
			
		||||
			 mgcp_conn_dump(conn->conn), conn->osmux.state);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -347,9 +318,9 @@ static int endp_osmux_state_check(struct mgcp_endpoint *endp, struct mgcp_conn_r
 | 
			
		||||
static int osmux_legacy_dummy_parse_cid(struct sockaddr_in *addr, struct msgb *msg,
 | 
			
		||||
					uint8_t *osmux_cid)
 | 
			
		||||
{
 | 
			
		||||
	if (msg->len < 1 + sizeof(osmux_cid)) {
 | 
			
		||||
	if (msg->len < 1 + sizeof(*osmux_cid)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Discarding truncated Osmux dummy load\n");
 | 
			
		||||
		     "Discarding truncated Osmux dummy load: %s\n", osmo_hexdump(msg->data, msg->len));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -358,16 +329,40 @@ static int osmux_legacy_dummy_parse_cid(struct sockaddr_in *addr, struct msgb *m
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This is called from the bsc-nat */
 | 
			
		||||
static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
 | 
			
		||||
			      struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t osmux_cid;
 | 
			
		||||
	struct mgcp_conn_rtp *conn;
 | 
			
		||||
 | 
			
		||||
	if (osmux_legacy_dummy_parse_cid(addr, msg, &osmux_cid) < 0)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	conn = osmux_conn_lookup(cfg, osmux_cid, &addr->sin_addr);
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Cannot find conn for Osmux CID %d\n", osmux_cid);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endp_osmux_state_check(conn->conn->endp, conn, false);
 | 
			
		||||
	/* Only needed to punch hole in firewall, it can be dropped */
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define osmux_chunk_length(msg, rem) (rem - msg->len);
 | 
			
		||||
 | 
			
		||||
int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	struct osmux_hdr *osmuxh;
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	struct mgcp_config *cfg = ofd->data;
 | 
			
		||||
	uint32_t rem;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_bts = NULL;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_src;
 | 
			
		||||
 | 
			
		||||
	msg = osmux_recv(ofd, &addr);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
@@ -381,115 +376,34 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
 | 
			
		||||
	/* not any further processing dummy messages */
 | 
			
		||||
	if (msg->data[0] == MGCP_DUMMY_LOAD)
 | 
			
		||||
		goto out;
 | 
			
		||||
		return osmux_handle_dummy(cfg, &addr, msg);
 | 
			
		||||
 | 
			
		||||
	rem = msg->len;
 | 
			
		||||
	while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
 | 
			
		||||
		struct mgcp_endpoint *endp;
 | 
			
		||||
 | 
			
		||||
		/* Yes, we use MGCP_DEST_NET to locate the origin */
 | 
			
		||||
		endp = endpoint_lookup(cfg, osmuxh->circuit_id,
 | 
			
		||||
				       &addr.sin_addr, MGCP_DEST_NET);
 | 
			
		||||
 | 
			
		||||
		/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
		conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
 | 
			
		||||
		if (!conn_bts)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!endp) {
 | 
			
		||||
		conn_src = osmux_conn_lookup(cfg, osmuxh->circuit_id,
 | 
			
		||||
					     &addr.sin_addr);
 | 
			
		||||
		if (!conn_src) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Cannot find an endpoint for circuit_id=%d\n",
 | 
			
		||||
			     "Cannot find a src conn for circuit_id=%d\n",
 | 
			
		||||
			     osmuxh->circuit_id);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
		if (endp_osmux_state_check(endp, conn_bts, false) == 0) {
 | 
			
		||||
			conn_bts->osmux.stats.octets += osmux_chunk_length(msg, rem);
 | 
			
		||||
			conn_bts->osmux.stats.chunks++;
 | 
			
		||||
			osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh);
 | 
			
		||||
		}
 | 
			
		||||
		rem = msg->len;
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This is called from the bsc-nat */
 | 
			
		||||
static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
 | 
			
		||||
			      struct msgb *msg, int endp_type)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	uint8_t osmux_cid;
 | 
			
		||||
	struct mgcp_conn_rtp *conn = NULL;
 | 
			
		||||
		mgcp_conn_watchdog_kick(conn_src->conn);
 | 
			
		||||
 | 
			
		||||
	if (osmux_legacy_dummy_parse_cid(addr, msg, &osmux_cid) < 0)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, endp_type);
 | 
			
		||||
	if (!endp) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Cannot find endpoint for Osmux CID %d\n", osmux_cid);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
	conn = mgcp_conn_get_rtp(endp, endp_type == MGCP_DEST_BTS ? CONN_ID_NET : CONN_ID_BTS);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	endp_osmux_state_check(endp, conn, false);
 | 
			
		||||
	/* Only needed to punch hole in firewall, it can be dropped */
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	struct osmux_hdr *osmuxh;
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	struct mgcp_config *cfg = ofd->data;
 | 
			
		||||
	uint32_t rem;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_net = NULL;
 | 
			
		||||
 | 
			
		||||
	msg = osmux_recv(ofd, &addr);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (!cfg->osmux) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "bsc wants to use Osmux but bsc-nat did not request it\n");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* not any further processing dummy messages */
 | 
			
		||||
	if (msg->data[0] == MGCP_DUMMY_LOAD)
 | 
			
		||||
		return osmux_handle_dummy(cfg, &addr, msg, MGCP_DEST_BTS);
 | 
			
		||||
 | 
			
		||||
	rem = msg->len;
 | 
			
		||||
	while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
 | 
			
		||||
		struct mgcp_endpoint *endp;
 | 
			
		||||
 | 
			
		||||
		/* Yes, we use MGCP_DEST_BTS to locate the origin */
 | 
			
		||||
		endp = endpoint_lookup(cfg, osmuxh->circuit_id,
 | 
			
		||||
				       &addr.sin_addr, MGCP_DEST_BTS);
 | 
			
		||||
 | 
			
		||||
		/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
		conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
 | 
			
		||||
		if (!conn_net)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!endp) {
 | 
			
		||||
		/*conn_dst = mgcp_find_dst_conn(conn_src->conn);
 | 
			
		||||
		if (!conn_dst) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Cannot find an endpoint for circuit_id=%d\n",
 | 
			
		||||
			     "Cannot find a dst conn for circuit_id=%d\n",
 | 
			
		||||
			     osmuxh->circuit_id);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
		if (endp_osmux_state_check(endp, conn_net, false) == 0) {
 | 
			
		||||
			conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
 | 
			
		||||
			conn_net->osmux.stats.chunks++;
 | 
			
		||||
			osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh);
 | 
			
		||||
		}*/
 | 
			
		||||
 | 
			
		||||
		if (endp_osmux_state_check(conn_src->conn->endp, conn_src, false) == 0) {
 | 
			
		||||
			conn_src->osmux.stats.octets += osmux_chunk_length(msg, rem);
 | 
			
		||||
			conn_src->osmux.stats.chunks++;
 | 
			
		||||
			osmux_xfrm_output_sched(&conn_src->osmux.out, osmuxh);
 | 
			
		||||
		}
 | 
			
		||||
		rem = msg->len;
 | 
			
		||||
	}
 | 
			
		||||
@@ -502,34 +416,27 @@ int osmux_init(int role, struct mgcp_config *cfg)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	switch(role) {
 | 
			
		||||
	case OSMUX_ROLE_BSC:
 | 
			
		||||
		osmux_fd.cb = osmux_read_from_bsc_nat_cb;
 | 
			
		||||
		break;
 | 
			
		||||
	case OSMUX_ROLE_BSC_NAT:
 | 
			
		||||
		osmux_fd.cb = osmux_read_from_bsc_cb;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "wrong role for OSMUX\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	osmux_fd.data = cfg;
 | 
			
		||||
	osmo_fd_setup(&osmux_fd, -1, OSMO_FD_READ, osmux_read_fd_cb, cfg, 0);
 | 
			
		||||
 | 
			
		||||
	ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port);
 | 
			
		||||
	if (ret < 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n");
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket to %s:%u\n",
 | 
			
		||||
		     cfg->osmux_addr, cfg->osmux_port);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
	mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
 | 
			
		||||
	osmux_fd.when |= BSC_FD_READ;
 | 
			
		||||
 | 
			
		||||
	ret = osmo_fd_register(&osmux_fd);
 | 
			
		||||
	if (ret < 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket\n");
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket %s\n",
 | 
			
		||||
		     osmo_sock_get_name2(osmux_fd.fd));
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
	cfg->osmux_init = 1;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_INFO, "OSMUX socket listening on %s\n",
 | 
			
		||||
		 osmo_sock_get_name2(osmux_fd.fd));
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -537,10 +444,10 @@ int osmux_init(int role, struct mgcp_config *cfg)
 | 
			
		||||
 *  \param[in] endp mgcp endpoint (configuration)
 | 
			
		||||
 *  \param[in] conn connection to disable
 | 
			
		||||
 *  \param[in] addr IP address of remote OSMUX endpoint
 | 
			
		||||
 *  \param[in] port portnumber of the remote OSMUX endpoint
 | 
			
		||||
 *  \param[in] port portnumber of the remote OSMUX endpoint (in network byte order)
 | 
			
		||||
 *  \returns 0 on success, -1 on ERROR */
 | 
			
		||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
 | 
			
		||||
		      struct in_addr *addr, uint16_t port)
 | 
			
		||||
		      struct osmo_sockaddr *addr, uint16_t port)
 | 
			
		||||
{
 | 
			
		||||
	/*! If osmux is enabled, initialize the output handler. This handler is
 | 
			
		||||
	 *  used to reconstruct the RTP flow from osmux. The RTP SSRC is
 | 
			
		||||
@@ -551,44 +458,49 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
 | 
			
		||||
	 *  overlapping RTP SSRC traveling to the BTSes behind the BSC,
 | 
			
		||||
	 *  similarly, for flows traveling to the MSC.
 | 
			
		||||
	 */
 | 
			
		||||
	struct in6_addr addr_unset = {};
 | 
			
		||||
	static const uint32_t rtp_ssrc_winlen = UINT32_MAX / (OSMUX_CID_MAX + 1);
 | 
			
		||||
	uint16_t osmux_dummy = endp->cfg->osmux_dummy;
 | 
			
		||||
 | 
			
		||||
	/* Check if osmux is enabled for the specified connection */
 | 
			
		||||
	if (conn->osmux.state != OSMUX_STATE_ACTIVATING) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "conn:%s didn't negotiate Osmux, state %d\n",
 | 
			
		||||
		     mgcp_conn_dump(conn->conn), conn->osmux.state);
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "conn:%s didn't negotiate Osmux, state %d\n",
 | 
			
		||||
			 mgcp_conn_dump(conn->conn), conn->osmux.state);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
 | 
			
		||||
	if (!conn->osmux.in) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle for conn:%s\n",
 | 
			
		||||
		     mgcp_conn_dump(conn->conn));
 | 
			
		||||
	/* Wait until we have the connection information from MDCX */
 | 
			
		||||
	if (memcmp(&conn->end.addr, &addr_unset,
 | 
			
		||||
		   conn->end.addr.u.sa.sa_family == AF_INET6 ?
 | 
			
		||||
			sizeof(struct in6_addr) :
 | 
			
		||||
			sizeof(struct in_addr)) == 0) {
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
 | 
			
		||||
			"Osmux remote address/port still unknown\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if (!osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u for conn:%s\n",
 | 
			
		||||
 | 
			
		||||
	conn->osmux.in = osmux_handle_lookup(conn, addr, port);
 | 
			
		||||
	if (!conn->osmux.in) {
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			"Cannot allocate input osmux handle for conn:%s\n",
 | 
			
		||||
			mgcp_conn_dump(conn->conn));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if (osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy) < 0) {
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			"Cannot open osmux circuit %u for conn:%s\n",
 | 
			
		||||
		     conn->osmux.cid, mgcp_conn_dump(conn->conn));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osmux_xfrm_output_init(&conn->osmux.out,
 | 
			
		||||
	osmux_xfrm_output_init2(&conn->osmux.out,
 | 
			
		||||
			       (conn->osmux.cid * rtp_ssrc_winlen) +
 | 
			
		||||
			       (random() % rtp_ssrc_winlen));
 | 
			
		||||
			        (random() % rtp_ssrc_winlen),
 | 
			
		||||
			       conn->end.codec->payload_type);
 | 
			
		||||
 | 
			
		||||
	switch (endp->cfg->role) {
 | 
			
		||||
		case MGCP_BSC_NAT:
 | 
			
		||||
			conn->type = MGCP_OSMUX_BSC_NAT;
 | 
			
		||||
			osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
 | 
			
		||||
							scheduled_tx_net_cb, endp);
 | 
			
		||||
			break;
 | 
			
		||||
		case MGCP_BSC:
 | 
			
		||||
			conn->type = MGCP_OSMUX_BSC;
 | 
			
		||||
			osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
 | 
			
		||||
							scheduled_tx_bts_cb, endp);
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
 | 
			
		||||
				    scheduled_from_osmux_tx_rtp_cb, conn);
 | 
			
		||||
 | 
			
		||||
	conn->osmux.state = OSMUX_STATE_ENABLED;
 | 
			
		||||
 | 
			
		||||
@@ -597,48 +509,65 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
 | 
			
		||||
 | 
			
		||||
/*! disable OSXMUX circuit for a specified connection.
 | 
			
		||||
 *  \param[in] conn connection to disable */
 | 
			
		||||
void osmux_disable_conn(struct mgcp_conn_rtp *conn)
 | 
			
		||||
void conn_osmux_disable(struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (conn->osmux.state != OSMUX_STATE_ENABLED)
 | 
			
		||||
		return;
 | 
			
		||||
	OSMO_ASSERT(conn->osmux.state != OSMUX_STATE_DISABLED);
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
 | 
			
		||||
	     conn->conn->id, conn->osmux.cid);
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
 | 
			
		||||
		"Releasing connection using Osmux CID %u\n", conn->osmux.cid);
 | 
			
		||||
 | 
			
		||||
	/* We are closing, we don't need pending RTP packets to be transmitted */
 | 
			
		||||
	osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
 | 
			
		||||
	osmux_xfrm_output_flush(&conn->osmux.out);
 | 
			
		||||
	if (conn->osmux.state == OSMUX_STATE_ENABLED) {
 | 
			
		||||
		/* We are closing, we don't need pending RTP packets to be transmitted */
 | 
			
		||||
		osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
 | 
			
		||||
		osmux_xfrm_output_flush(&conn->osmux.out);
 | 
			
		||||
 | 
			
		||||
	osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
 | 
			
		||||
	conn->osmux.state = OSMUX_STATE_DISABLED;
 | 
			
		||||
	conn->osmux.cid = -1;
 | 
			
		||||
	osmux_handle_put(conn->osmux.in);
 | 
			
		||||
		osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
 | 
			
		||||
		conn->osmux.state = OSMUX_STATE_DISABLED;
 | 
			
		||||
		conn_osmux_release_cid(conn);
 | 
			
		||||
		osmux_handle_put(conn->osmux.in);
 | 
			
		||||
	}
 | 
			
		||||
	conn_osmux_release_cid(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! relase OSXMUX cid, that had been allocated to this connection.
 | 
			
		||||
 *  \param[in] conn connection with OSMUX cid to release */
 | 
			
		||||
void osmux_release_cid(struct mgcp_conn_rtp *conn)
 | 
			
		||||
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (conn->osmux.state != OSMUX_STATE_ENABLED)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (conn->osmux.allocated_cid >= 0)
 | 
			
		||||
		osmux_put_cid(conn->osmux.allocated_cid);
 | 
			
		||||
	conn->osmux.allocated_cid = -1;
 | 
			
		||||
	if (conn->osmux.cid_allocated)
 | 
			
		||||
		osmux_cid_pool_put(conn->osmux.cid);
 | 
			
		||||
	conn->osmux.cid = 0;
 | 
			
		||||
	conn->osmux.cid_allocated = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! allocate OSXMUX cid to connection.
 | 
			
		||||
 *  \param[in] conn connection for which we allocate the OSMUX cid*/
 | 
			
		||||
void osmux_allocate_cid(struct mgcp_conn_rtp *conn)
 | 
			
		||||
 *  \param[in] conn connection for which we allocate the OSMUX cid
 | 
			
		||||
 * \param[in] osmux_cid OSMUX cid to allocate. -1 Means take next available one.
 | 
			
		||||
 * \returns Allocated OSMUX cid, -1 on error (no free  cids avail, or selected one is already taken).
 | 
			
		||||
 */
 | 
			
		||||
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid)
 | 
			
		||||
{
 | 
			
		||||
	osmux_release_cid(conn);
 | 
			
		||||
	conn->osmux.allocated_cid = osmux_get_cid();
 | 
			
		||||
	if (osmux_cid != -1 && osmux_cid_pool_allocated((uint8_t) osmux_cid)) {
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
 | 
			
		||||
			 "Osmux CID %d already allocated!\n",
 | 
			
		||||
			 osmux_cid);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (osmux_cid == -1) {
 | 
			
		||||
		osmux_cid = osmux_cid_pool_get_next();
 | 
			
		||||
		if (osmux_cid == -1) {
 | 
			
		||||
			LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
 | 
			
		||||
				 "no available Osmux CID to allocate!\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	} else
 | 
			
		||||
		osmux_cid_pool_get(osmux_cid);
 | 
			
		||||
 | 
			
		||||
	conn->osmux.cid = (uint8_t) osmux_cid;
 | 
			
		||||
	conn->osmux.cid_allocated = true;
 | 
			
		||||
	conn->type = MGCP_OSMUX_BSC;
 | 
			
		||||
	return osmux_cid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! send RTP dummy packet to OSMUX connection port.
 | 
			
		||||
@@ -647,7 +576,9 @@ void osmux_allocate_cid(struct mgcp_conn_rtp *conn)
 | 
			
		||||
 *  \returns bytes sent, -1 on error */
 | 
			
		||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	char buf[1 + sizeof(uint8_t)];
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
	struct osmux_hdr *osmuxh;
 | 
			
		||||
	int buf_len;
 | 
			
		||||
	struct in_addr addr_unset = {};
 | 
			
		||||
 | 
			
		||||
	/*! The dummy packet will not be sent via the actual OSMUX connection,
 | 
			
		||||
@@ -659,9 +590,6 @@ int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
 | 
			
		||||
	 *  endpoint may have already punched the hole in the firewall. This
 | 
			
		||||
	 *  approach is simple though. */
 | 
			
		||||
 | 
			
		||||
	buf[0] = MGCP_DUMMY_LOAD;
 | 
			
		||||
	memcpy(&buf[1], &conn->osmux.cid, sizeof(conn->osmux.cid));
 | 
			
		||||
 | 
			
		||||
	/* Wait until we have the connection information from MDCX */
 | 
			
		||||
	if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
@@ -669,12 +597,20 @@ int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
 | 
			
		||||
	if (endp_osmux_state_check(endp, conn, true) < 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG,
 | 
			
		||||
	     "sending OSMUX dummy load to %s CID %u\n",
 | 
			
		||||
	     inet_ntoa(conn->end.addr), conn->osmux.cid);
 | 
			
		||||
	buf_len = sizeof(struct osmux_hdr) + osmo_amr_bytes(AMR_FT_0);
 | 
			
		||||
	osmuxh = (struct osmux_hdr *) alloca(buf_len);
 | 
			
		||||
	memset(osmuxh, 0, buf_len);
 | 
			
		||||
	osmuxh->ft = OSMUX_FT_DUMMY;
 | 
			
		||||
	osmuxh->amr_ft = AMR_FT_0;
 | 
			
		||||
	osmuxh->circuit_id = conn->osmux.cid;
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
 | 
			
		||||
		 "sending OSMUX dummy load to %s:%u CID %u\n",
 | 
			
		||||
		 osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf),
 | 
			
		||||
		 ntohs(conn->end.rtp_port), conn->osmux.cid);
 | 
			
		||||
 | 
			
		||||
	return mgcp_udp_send(osmux_fd.fd, &conn->end.addr,
 | 
			
		||||
			     htons(endp->cfg->osmux_port), buf, sizeof(buf));
 | 
			
		||||
			     conn->end.rtp_port, (char*)osmuxh, buf_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* bsc-nat allocates/releases the Osmux circuit ID. +7 to round up to 8 bit boundary. */
 | 
			
		||||
@@ -682,7 +618,7 @@ static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1 + 7) / 8];
 | 
			
		||||
 | 
			
		||||
/*! count the number of taken OSMUX cids.
 | 
			
		||||
 *  \returns number of OSMUX cids in use */
 | 
			
		||||
int osmux_used_cid(void)
 | 
			
		||||
int osmux_cid_pool_count_used(void)
 | 
			
		||||
{
 | 
			
		||||
	int i, j, used = 0;
 | 
			
		||||
 | 
			
		||||
@@ -698,7 +634,7 @@ int osmux_used_cid(void)
 | 
			
		||||
 | 
			
		||||
/*! take a free OSMUX cid.
 | 
			
		||||
 *  \returns OSMUX cid */
 | 
			
		||||
int osmux_get_cid(void)
 | 
			
		||||
int osmux_cid_pool_get_next(void)
 | 
			
		||||
{
 | 
			
		||||
	int i, j;
 | 
			
		||||
 | 
			
		||||
@@ -718,10 +654,24 @@ int osmux_get_cid(void)
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! take a specific OSMUX cid.
 | 
			
		||||
 *  \param[in] osmux_cid OSMUX cid */
 | 
			
		||||
void osmux_cid_pool_get(uint8_t osmux_cid)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "Allocating Osmux CID %u from pool\n", osmux_cid);
 | 
			
		||||
	osmux_cid_bitmap[osmux_cid / 8] |= (1 << (osmux_cid % 8));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! put back a no longer used OSMUX cid.
 | 
			
		||||
 *  \param[in] osmux_cid OSMUX cid */
 | 
			
		||||
void osmux_put_cid(uint8_t osmux_cid)
 | 
			
		||||
void osmux_cid_pool_put(uint8_t osmux_cid)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid);
 | 
			
		||||
	osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! check if OSMUX cid is already taken */
 | 
			
		||||
bool osmux_cid_pool_allocated(uint8_t osmux_cid)
 | 
			
		||||
{
 | 
			
		||||
	return !!(osmux_cid_bitmap[osmux_cid / 8] & (1 << (osmux_cid % 8)));
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										229
									
								
								src/libosmo-mgcp/mgcp_ratectr.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								src/libosmo-mgcp/mgcp_ratectr.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,229 @@
 | 
			
		||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
			
		||||
/* rate-counter implementation */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2012 by On-Waves
 | 
			
		||||
 * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <osmocom/core/stats.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_ratectr.h>
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
 | 
			
		||||
	/* rx_msgs = rx_msgs_retransmitted + rx_msgs_handled + rx_msgs_unhandled + err_rx_msg_parse + err_rx_no_endpoint */
 | 
			
		||||
	[MGCP_GENERAL_RX_MSGS_TOTAL] = { "mgcp:rx_msgs", "total number of MGCP messages received." },
 | 
			
		||||
	[MGCP_GENERAL_RX_MSGS_RETRANSMITTED] = { "mgcp:rx_msgs_retransmitted", "number of received retransmissions." },
 | 
			
		||||
	[MGCP_GENERAL_RX_MSGS_HANDLED] = { "mgcp:rx_msgs_handled", "number of handled MGCP messages." },
 | 
			
		||||
	[MGCP_GENERAL_RX_MSGS_UNHANDLED] = { "mgcp:rx_msgs_unhandled", "number of unhandled MGCP messages." },
 | 
			
		||||
	[MGCP_GENERAL_RX_FAIL_MSG_PARSE] = { "mgcp:err_rx_msg_parse", "error parsing MGCP message." },
 | 
			
		||||
	[MGCP_GENERAL_RX_FAIL_NO_ENDPOINT] =
 | 
			
		||||
	    { "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 = {
 | 
			
		||||
	.group_name_prefix = "mgcp",
 | 
			
		||||
	.group_description = "mgcp general statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(mgcp_general_ctr_desc),
 | 
			
		||||
	.ctr_desc = mgcp_general_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
 | 
			
		||||
	[MGCP_CRCX_SUCCESS] = { "crcx:success", "CRCX command processed successfully." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_BAD_ACTION] = { "crcx:bad_action", "bad action in CRCX command." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in CRCX command." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_MISSING_CALLID] = { "crcx:missing_callid", "missing CallId in CRCX command." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_INVALID_MODE] = { "crcx:invalid_mode", "invalid connection mode in CRCX command." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = { "crcx:limit_exceeded", "limit of concurrent connections was reached." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_UNKNOWN_CALLID] = { "crcx:unkown_callid", "unknown CallId in CRCX command." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_ALLOC_CONN] = { "crcx:alloc_conn_fail", "connection allocation failure." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] =
 | 
			
		||||
	    { "crcx:no_remote_conn_desc", "no opposite end specified for connection." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_START_RTP] = { "crcx:start_rtp_failure", "failure to start RTP processing." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = { "crcx:conn_rejected", "connection rejected by policy." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_NO_OSMUX] = { "crcx:no_osmux", "no osmux offered by peer." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = { "crcx:conn_opt", "connection options invalid." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = { "crcx:codec_nego", "codec negotiation failure." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_BIND_PORT] = { "crcx:bind_port", "port bind failure." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_AVAIL] = { "crcx:unavailable", "endpoint unavailable." },
 | 
			
		||||
	[MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "crcx",
 | 
			
		||||
	.group_description = "crxc statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc),
 | 
			
		||||
	.ctr_desc = mgcp_crcx_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
 | 
			
		||||
	[MGCP_MDCX_SUCCESS] = { "mdcx:success", "MDCX command processed successfully." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_WILDCARD] = { "mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_NO_CONN] = { "mdcx:no_conn", "endpoint specified in MDCX command has no active connections." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_INVALID_CALLID] = { "mdcx:callid", "invalid CallId specified in MDCX command." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_INVALID_CONNID] = { "mdcx:connid", "invalid connection ID specified in MDCX command." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in MDCX command." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_NO_CONNID] = { "mdcx:no_connid", "no connection ID specified in MDCX command." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_CONN_NOT_FOUND] =
 | 
			
		||||
	    { "mdcx:conn_not_found", "connection specified in MDCX command does not exist." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_INVALID_MODE] = { "mdcx:invalid_mode", "invalid connection mode in MDCX command." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = { "mdcx:conn_opt", "connection options invalid." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] =
 | 
			
		||||
	    { "mdcx:no_remote_conn_desc", "no opposite end specified for connection." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_START_RTP] = { "mdcx:start_rtp_failure", "failure to start RTP processing." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = { "mdcx:conn_rejected", "connection rejected by policy." },
 | 
			
		||||
	[MGCP_MDCX_DEFERRED_BY_POLICY] = { "mdcx:conn_deferred", "connection deferred by policy." },
 | 
			
		||||
	[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "mdcx",
 | 
			
		||||
	.group_description = "mdcx statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc),
 | 
			
		||||
	.ctr_desc = mgcp_mdcx_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
 | 
			
		||||
	[MGCP_DLCX_SUCCESS] = { "dlcx:success", "DLCX command processed successfully." },
 | 
			
		||||
	[MGCP_DLCX_FAIL_WILDCARD] = { "dlcx:wildcard", "wildcard names in DLCX commands are unsupported." },
 | 
			
		||||
	[MGCP_DLCX_FAIL_NO_CONN] = { "dlcx:no_conn", "endpoint specified in DLCX command has no active connections." },
 | 
			
		||||
	[MGCP_DLCX_FAIL_INVALID_CALLID] =
 | 
			
		||||
	    { "dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ." },
 | 
			
		||||
	[MGCP_DLCX_FAIL_INVALID_CONNID] =
 | 
			
		||||
	    { "dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint." },
 | 
			
		||||
	[MGCP_DLCX_FAIL_UNHANDLED_PARAM] = { "dlcx:unhandled_param", "unhandled parameter in DLCX command." },
 | 
			
		||||
	[MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = { "dlcx:rejected", "connection deletion rejected by policy." },
 | 
			
		||||
	[MGCP_DLCX_DEFERRED_BY_POLICY] = { "dlcx:deferred", "connection deletion deferred by policy." },
 | 
			
		||||
	[MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "dlcx",
 | 
			
		||||
	.group_description = "dlcx statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(mgcp_dlcx_ctr_desc),
 | 
			
		||||
	.ctr_desc = mgcp_dlcx_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_desc e1_rate_ctr_desc[] = {
 | 
			
		||||
	[E1_I460_TRAU_RX_FAIL_CTR] = {"e1:rx_fail", "Inbound I.460 TRAU failures."},
 | 
			
		||||
	[E1_I460_TRAU_TX_FAIL_CTR] = {"e1:tx_fail", "Outbound I.460 TRAU failures."},
 | 
			
		||||
	[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 = {
 | 
			
		||||
	.group_name_prefix = "e1",
 | 
			
		||||
	.group_description = "e1 statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(e1_rate_ctr_desc),
 | 
			
		||||
	.ctr_desc = e1_rate_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static 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,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc),
 | 
			
		||||
	.ctr_desc = all_rtp_conn_rate_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
 | 
			
		||||
{
 | 
			
		||||
	rate_ctr_group_free(rate_ctr_group);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! allocate global rate counters into a given rate counter struct
 | 
			
		||||
 *  (called once at startup)
 | 
			
		||||
 *  \param[in] ctx talloc context.
 | 
			
		||||
 *  \param[out] ratectr struct that holds the counters
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on failure */
 | 
			
		||||
int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr)
 | 
			
		||||
{
 | 
			
		||||
	/* FIXME: Each new rate counter group requires a unique index. At the
 | 
			
		||||
	 * moment we generate an index using a counter, but perhaps there is
 | 
			
		||||
	 * a better way of assigning indices? */
 | 
			
		||||
	static unsigned int general_rate_ctr_index = 0;
 | 
			
		||||
 | 
			
		||||
	if (ratectr->mgcp_general_ctr_group == NULL) {
 | 
			
		||||
		ratectr->mgcp_general_ctr_group =
 | 
			
		||||
		    rate_ctr_group_alloc(ctx, &mgcp_general_ctr_group_desc, general_rate_ctr_index);
 | 
			
		||||
		if (!ratectr->mgcp_general_ctr_group)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		talloc_set_destructor(ratectr->mgcp_general_ctr_group, free_rate_counter_group);
 | 
			
		||||
		general_rate_ctr_index++;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! allocate trunk specific rate counters into a given rate counter struct
 | 
			
		||||
 *  (called once on trunk initialization)
 | 
			
		||||
 *  \param[in] ctx talloc context.
 | 
			
		||||
 *  \param[out] ratectr struct that holds the counters
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on failure */
 | 
			
		||||
int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr)
 | 
			
		||||
{
 | 
			
		||||
	/* FIXME: see comment in mgcp_ratectr_global_alloc()  */
 | 
			
		||||
	static unsigned int crcx_rate_ctr_index = 0;
 | 
			
		||||
	static unsigned int mdcx_rate_ctr_index = 0;
 | 
			
		||||
	static unsigned int dlcx_rate_ctr_index = 0;
 | 
			
		||||
	static unsigned int all_rtp_conn_rate_ctr_index = 0;
 | 
			
		||||
 | 
			
		||||
	if (ratectr->mgcp_crcx_ctr_group == NULL) {
 | 
			
		||||
		ratectr->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index);
 | 
			
		||||
		if (!ratectr->mgcp_crcx_ctr_group)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		talloc_set_destructor(ratectr->mgcp_crcx_ctr_group, free_rate_counter_group);
 | 
			
		||||
		crcx_rate_ctr_index++;
 | 
			
		||||
	}
 | 
			
		||||
	if (ratectr->mgcp_mdcx_ctr_group == NULL) {
 | 
			
		||||
		ratectr->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index);
 | 
			
		||||
		if (!ratectr->mgcp_mdcx_ctr_group)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		talloc_set_destructor(ratectr->mgcp_mdcx_ctr_group, free_rate_counter_group);
 | 
			
		||||
		mdcx_rate_ctr_index++;
 | 
			
		||||
	}
 | 
			
		||||
	if (ratectr->mgcp_dlcx_ctr_group == NULL) {
 | 
			
		||||
		ratectr->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index);
 | 
			
		||||
		if (!ratectr->mgcp_dlcx_ctr_group)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		talloc_set_destructor(ratectr->mgcp_dlcx_ctr_group, free_rate_counter_group);
 | 
			
		||||
		dlcx_rate_ctr_index++;
 | 
			
		||||
	}
 | 
			
		||||
	if (ratectr->all_rtp_conn_stats == NULL) {
 | 
			
		||||
		ratectr->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc,
 | 
			
		||||
								 all_rtp_conn_rate_ctr_index);
 | 
			
		||||
		if (!ratectr->all_rtp_conn_stats)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		talloc_set_destructor(ratectr->all_rtp_conn_stats, free_rate_counter_group);
 | 
			
		||||
		all_rtp_conn_rate_ctr_index++;
 | 
			
		||||
	}
 | 
			
		||||
	if (ratectr->e1_stats == NULL) {
 | 
			
		||||
		ratectr->e1_stats = rate_ctr_group_alloc(ctx, &e1_rate_ctr_group_desc, mdcx_rate_ctr_index);
 | 
			
		||||
		if (!ratectr->e1_stats)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		talloc_set_destructor(ratectr->e1_stats, free_rate_counter_group);
 | 
			
		||||
		mdcx_rate_ctr_index++;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -21,17 +21,27 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/sockaddr_str.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_msg.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_sdp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
 | 
			
		||||
/* A struct to store intermediate parsing results. The function
 | 
			
		||||
 * mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
 | 
			
		||||
 * codec information. */
 | 
			
		||||
/* Two structs to store intermediate parsing results. The function
 | 
			
		||||
 * mgcp_parse_sdp_data() is using the following two structs as temporary
 | 
			
		||||
 * storage for parsing the SDP codec information. */
 | 
			
		||||
struct sdp_rtp_map {
 | 
			
		||||
	/* the type */
 | 
			
		||||
	int payload_type;
 | 
			
		||||
@@ -43,6 +53,11 @@ struct sdp_rtp_map {
 | 
			
		||||
	int rate;
 | 
			
		||||
	int channels;
 | 
			
		||||
};
 | 
			
		||||
struct sdp_fmtp_param {
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Helper function to extrapolate missing codec parameters in a codec mao from
 | 
			
		||||
 * an already filled in payload_type, called from: mgcp_parse_sdp_data() */
 | 
			
		||||
@@ -123,7 +138,8 @@ static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
 | 
			
		||||
	char *str;
 | 
			
		||||
	char *str_ptr;
 | 
			
		||||
	char *pt_str;
 | 
			
		||||
	unsigned int pt;
 | 
			
		||||
	char *pt_end;
 | 
			
		||||
	unsigned long int pt;
 | 
			
		||||
	unsigned int count = 0;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
@@ -148,7 +164,14 @@ static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
 | 
			
		||||
		if (!pt_str)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		pt = atoi(pt_str);
 | 
			
		||||
		errno = 0;
 | 
			
		||||
		pt = strtoul(pt_str, &pt_end, 0);
 | 
			
		||||
		if ((errno == ERANGE && pt == ULONG_MAX) || (errno && !pt) ||
 | 
			
		||||
		    pt_str == pt_end)
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		if (pt >> 7) /* PT is 7 bit field, higher values not allowed */
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		/* Do not allow duplicate payload types */
 | 
			
		||||
		for (i = 0; i < count; i++)
 | 
			
		||||
@@ -167,6 +190,128 @@ error:
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Extract fmtp parameters from SDP, called from: mgcp_parse_sdp_data() */
 | 
			
		||||
static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp)
 | 
			
		||||
{
 | 
			
		||||
	char *str;
 | 
			
		||||
	char *str_ptr;
 | 
			
		||||
	char *param_str;
 | 
			
		||||
	unsigned int pt;
 | 
			
		||||
	unsigned int count = 0;
 | 
			
		||||
	char delimiter;
 | 
			
		||||
	unsigned int amr_octet_aligned;
 | 
			
		||||
 | 
			
		||||
	memset(fmtp_param, 0, sizeof(*fmtp_param));
 | 
			
		||||
 | 
			
		||||
	str = talloc_zero_size(ctx, strlen(sdp) + 1);
 | 
			
		||||
	str_ptr = str;
 | 
			
		||||
	strcpy(str_ptr, sdp);
 | 
			
		||||
 | 
			
		||||
	/* Check if the input string begins with an fmtp token */
 | 
			
		||||
	str_ptr = strstr(str_ptr, "fmtp:");
 | 
			
		||||
	if (!str_ptr)
 | 
			
		||||
		goto exit;
 | 
			
		||||
	str_ptr += 5;
 | 
			
		||||
 | 
			
		||||
	/* Extract payload type */
 | 
			
		||||
	if (sscanf(str_ptr, "%u ", &pt) != 1)
 | 
			
		||||
		goto error;
 | 
			
		||||
	fmtp_param->payload_type = pt;
 | 
			
		||||
 | 
			
		||||
	/* Advance pointer to the beginning of the parameter section and
 | 
			
		||||
	 * tokenize string */
 | 
			
		||||
	str_ptr = strstr(str_ptr, " ");
 | 
			
		||||
	if (!str_ptr)
 | 
			
		||||
		goto error;
 | 
			
		||||
	str_ptr++;
 | 
			
		||||
 | 
			
		||||
	param_str = strtok(str_ptr, " ");
 | 
			
		||||
	if (!param_str)
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		/* Make sure that we don't get trapped in an endless loop */
 | 
			
		||||
		if (count > 256)
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		/* Chop off delimiters ';' at the end */
 | 
			
		||||
		delimiter = str_ptr[strlen(str_ptr) - 1];
 | 
			
		||||
		if (delimiter == ';' || delimiter == ',')
 | 
			
		||||
			str_ptr[strlen(str_ptr) - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
		/* AMR octet aligned parameter */
 | 
			
		||||
		if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
 | 
			
		||||
			fmtp_param->param.amr_octet_aligned_present = true;
 | 
			
		||||
			fmtp_param->param.amr_octet_aligned = false;
 | 
			
		||||
			if (amr_octet_aligned == 1)
 | 
			
		||||
				fmtp_param->param.amr_octet_aligned = true;
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		param_str = strtok(NULL, " ");
 | 
			
		||||
		if (!param_str)
 | 
			
		||||
			break;
 | 
			
		||||
		count++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
	talloc_free(str);
 | 
			
		||||
	return 0;
 | 
			
		||||
error:
 | 
			
		||||
	talloc_free(str);
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int audio_ip_from_sdp(struct osmo_sockaddr *dst_addr, char *sdp)
 | 
			
		||||
{
 | 
			
		||||
	bool is_ipv6;
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
	if (strncmp("c=IN IP", sdp, 7) != 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
	sdp += 7;
 | 
			
		||||
	if (*sdp == '6')
 | 
			
		||||
	       is_ipv6 = true;
 | 
			
		||||
	else if (*sdp == '4')
 | 
			
		||||
	       is_ipv6 = false;
 | 
			
		||||
	else
 | 
			
		||||
	       return -1;
 | 
			
		||||
	sdp++;
 | 
			
		||||
	if (*sdp != ' ')
 | 
			
		||||
		return -1;
 | 
			
		||||
	sdp++;
 | 
			
		||||
	if (is_ipv6) {
 | 
			
		||||
		/* 45 = INET6_ADDRSTRLEN -1 */
 | 
			
		||||
		if (sscanf(sdp, "%45s", ipbuf) != 1)
 | 
			
		||||
			return -1;
 | 
			
		||||
		if (inet_pton(AF_INET6, ipbuf, &dst_addr->u.sin6.sin6_addr) != 1)
 | 
			
		||||
			return -1;
 | 
			
		||||
		dst_addr->u.sa.sa_family = AF_INET6;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* 15 = INET_ADDRSTRLEN -1 */
 | 
			
		||||
		if (sscanf(sdp, "%15s", ipbuf) != 1)
 | 
			
		||||
			return -1;
 | 
			
		||||
		if (inet_pton(AF_INET, ipbuf, &dst_addr->u.sin.sin_addr) != 1)
 | 
			
		||||
			return -1;
 | 
			
		||||
		dst_addr->u.sa.sa_family = AF_INET;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Pick optional fmtp parameters by payload type, if there are no fmtp
 | 
			
		||||
 * parameters, a nullpointer is returned */
 | 
			
		||||
static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < fmtp_params_len; i++) {
 | 
			
		||||
		if (fmtp_params[i].payload_type == pt)
 | 
			
		||||
			return &fmtp_params[i].param;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Analyze SDP input string.
 | 
			
		||||
 *  \param[in] endp trunk endpoint.
 | 
			
		||||
 *  \param[out] conn associated rtp connection.
 | 
			
		||||
@@ -180,6 +325,10 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
{
 | 
			
		||||
	struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int codecs_used = 0;
 | 
			
		||||
	struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int fmtp_used = 0;
 | 
			
		||||
	struct mgcp_codec_param *codec_param;
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
	char *line;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	void *tmp_ctx = talloc_new(NULL);
 | 
			
		||||
@@ -189,7 +338,6 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
	int ptime, ptime2 = 0;
 | 
			
		||||
	char audio_name[64];
 | 
			
		||||
	int port, rc;
 | 
			
		||||
	char ipv4[16];
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
@@ -207,20 +355,31 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
			/* skip these SDP attributes */
 | 
			
		||||
			break;
 | 
			
		||||
		case 'a':
 | 
			
		||||
			if (sscanf(line, "a=rtpmap:%d %63s",
 | 
			
		||||
				   &payload, audio_name) == 2) {
 | 
			
		||||
				codecs_update(tmp_ctx, codecs,
 | 
			
		||||
					      codecs_used, payload, audio_name);
 | 
			
		||||
			} else
 | 
			
		||||
			    if (sscanf
 | 
			
		||||
				(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
 | 
			
		||||
			if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
 | 
			
		||||
				codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
 | 
			
		||||
				if (ptime2 > 0 && ptime2 != ptime)
 | 
			
		||||
					rtp->packet_duration_ms = 0;
 | 
			
		||||
				else
 | 
			
		||||
					rtp->packet_duration_ms = ptime;
 | 
			
		||||
			} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
 | 
			
		||||
				rtp->maximum_packet_time = ptime2;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
 | 
			
		||||
				rtp->maximum_packet_time = ptime2;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (strncmp("a=fmtp:", line, 6) == 0) {
 | 
			
		||||
				rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line);
 | 
			
		||||
				if (rc >= 0)
 | 
			
		||||
					fmtp_used++;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
		case 'm':
 | 
			
		||||
			rc = sscanf(line, "m=audio %d RTP/AVP", &port);
 | 
			
		||||
@@ -235,17 +394,16 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
				codecs_used = rc;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'c':
 | 
			
		||||
 | 
			
		||||
			if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
 | 
			
		||||
				inet_aton(ipv4, &rtp->addr);
 | 
			
		||||
			}
 | 
			
		||||
			if (audio_ip_from_sdp(&rtp->addr, line) < 0)
 | 
			
		||||
				return -1;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			if (p->endp)
 | 
			
		||||
				/* TODO: Check spec: We used the bare endpoint number before,
 | 
			
		||||
				 * now we use the endpoint name as a whole? Is this allowed? */
 | 
			
		||||
				LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
				     "Unhandled SDP option: '%c'/%d on 0x%x\n",
 | 
			
		||||
				     line[0], line[0],
 | 
			
		||||
				     ENDPOINT_NUMBER(p->endp));
 | 
			
		||||
				     "Unhandled SDP option: '%c'/%d on %s\n",
 | 
			
		||||
				     line[0], line[0], endp->name);
 | 
			
		||||
			else
 | 
			
		||||
				LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
				     "Unhandled SDP option: '%c'/%d\n",
 | 
			
		||||
@@ -262,16 +420,17 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
	/* Store parsed codec information */
 | 
			
		||||
	for (i = 0; i < codecs_used; i++) {
 | 
			
		||||
		rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
 | 
			
		||||
		codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
 | 
			
		||||
		rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
 | 
			
		||||
			LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(tmp_ctx);
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
	     "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
 | 
			
		||||
	     ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
 | 
			
		||||
	     ntohs(rtp->rtp_port), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
 | 
			
		||||
	     rtp->packet_duration_ms);
 | 
			
		||||
	if (codecs_used == 0)
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_NOTICE, "none");
 | 
			
		||||
@@ -304,15 +463,12 @@ static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add audio string to sdp payload */
 | 
			
		||||
/* Add audio strings to sdp payload */
 | 
			
		||||
static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	if (payload_types_len < 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
@@ -330,6 +486,66 @@ static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add fmtp strings to sdp payload */
 | 
			
		||||
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
 | 
			
		||||
		    const char *fmtp_extra)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int fmtp_extra_pt = -1;
 | 
			
		||||
	char *fmtp_extra_pars = "";
 | 
			
		||||
 | 
			
		||||
	/* When no fmtp parameters ara available but an fmtp extra string
 | 
			
		||||
	 * is configured, just add the fmtp extra string */
 | 
			
		||||
	if (fmtp_params_len == 0 && fmtp_extra) {
 | 
			
		||||
		return msgb_printf(sdp, "%s\r\n", fmtp_extra);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* When there is fmtp extra configured we dissect it in order to drop
 | 
			
		||||
	 * in the configured extra parameters at the right place when
 | 
			
		||||
	 * generating the fmtp strings. */
 | 
			
		||||
	if (fmtp_extra) {
 | 
			
		||||
		if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
 | 
			
		||||
			fmtp_extra_pt = -1;
 | 
			
		||||
 | 
			
		||||
		fmtp_extra_pars = strstr(fmtp_extra, " ");
 | 
			
		||||
 | 
			
		||||
		if (!fmtp_extra_pars)
 | 
			
		||||
			fmtp_extra_pars = "";
 | 
			
		||||
		else
 | 
			
		||||
			fmtp_extra_pars++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < fmtp_params_len; i++) {
 | 
			
		||||
		rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		/* Add amr octet align parameter */
 | 
			
		||||
		if (fmtp_params[i].param.amr_octet_aligned_present) {
 | 
			
		||||
			if (fmtp_params[i].param.amr_octet_aligned)
 | 
			
		||||
				rc = msgb_printf(sdp, " octet-align=1");
 | 
			
		||||
			else
 | 
			
		||||
				rc = msgb_printf(sdp, " octet-align=0");
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Append extra parameters from fmtp extra */
 | 
			
		||||
		if (fmtp_params[i].payload_type == fmtp_extra_pt) {
 | 
			
		||||
			rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rc = msgb_printf(sdp, "\r\n");
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Generate SDP response string.
 | 
			
		||||
 *  \param[in] endp trunk endpoint.
 | 
			
		||||
 *  \param[in] conn associated rtp connection.
 | 
			
		||||
@@ -340,11 +556,17 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
			    const struct mgcp_conn_rtp *conn, struct msgb *sdp,
 | 
			
		||||
			    const char *addr)
 | 
			
		||||
{
 | 
			
		||||
	const struct mgcp_rtp_codec *codec;
 | 
			
		||||
	const char *fmtp_extra;
 | 
			
		||||
	const char *audio_name;
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	struct sdp_fmtp_param fmtp_param;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int payload_types[1];
 | 
			
		||||
	int local_port;
 | 
			
		||||
	struct sdp_fmtp_param fmtp_params[1];
 | 
			
		||||
        unsigned int fmtp_params_len = 0;
 | 
			
		||||
	bool addr_is_v6;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
@@ -353,16 +575,22 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
	/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
 | 
			
		||||
	endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
 | 
			
		||||
					      &payload_type, &audio_name,
 | 
			
		||||
					      &fmtp_extra,
 | 
			
		||||
					      &codec, &fmtp_extra,
 | 
			
		||||
					      (struct mgcp_conn_rtp *)conn);
 | 
			
		||||
 | 
			
		||||
	audio_name = codec->audio_name;
 | 
			
		||||
	payload_type = codec->payload_type;
 | 
			
		||||
 | 
			
		||||
	addr_is_v6 = osmo_ip_str_type(addr) == AF_INET6;
 | 
			
		||||
 | 
			
		||||
	rc = msgb_printf(sdp,
 | 
			
		||||
			 "v=0\r\n"
 | 
			
		||||
			 "o=- %s 23 IN IP4 %s\r\n"
 | 
			
		||||
			 "o=- %s 23 IN IP%c %s\r\n"
 | 
			
		||||
			 "s=-\r\n"
 | 
			
		||||
			 "c=IN IP4 %s\r\n"
 | 
			
		||||
			 "t=0 0\r\n", conn->conn->id, addr, addr);
 | 
			
		||||
			 "c=IN IP%c %s\r\n"
 | 
			
		||||
			 "t=0 0\r\n", conn->conn->id,
 | 
			
		||||
			 addr_is_v6 ? '6' : '4', addr,
 | 
			
		||||
			 addr_is_v6 ? '6' : '4', addr);
 | 
			
		||||
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		goto buffer_too_small;
 | 
			
		||||
@@ -370,24 +598,31 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
	if (payload_type >= 0) {
 | 
			
		||||
 | 
			
		||||
		payload_types[0] = payload_type;
 | 
			
		||||
		rc = add_audio(sdp, payload_types, 1, conn->end.local_port);
 | 
			
		||||
		if (mgcp_conn_rtp_is_osmux(conn))
 | 
			
		||||
			local_port = endp->cfg->osmux_port;
 | 
			
		||||
		else
 | 
			
		||||
			local_port = conn->end.local_port;
 | 
			
		||||
		rc = add_audio(sdp, payload_types, 1, local_port);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto buffer_too_small;
 | 
			
		||||
 | 
			
		||||
		if (endp->tcfg->audio_send_name) {
 | 
			
		||||
		if (endp->trunk->audio_send_name) {
 | 
			
		||||
			rc = add_rtpmap(sdp, payload_type, audio_name);
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				goto buffer_too_small;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (fmtp_extra) {
 | 
			
		||||
			rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
 | 
			
		||||
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				goto buffer_too_small;
 | 
			
		||||
		if (codec->param_present) {
 | 
			
		||||
			fmtp_param.payload_type = payload_type;
 | 
			
		||||
			fmtp_param.param = codec->param;
 | 
			
		||||
			fmtp_params[0] = fmtp_param;
 | 
			
		||||
			fmtp_params_len = 1;
 | 
			
		||||
		}
 | 
			
		||||
		rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto buffer_too_small;
 | 
			
		||||
	}
 | 
			
		||||
	if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
 | 
			
		||||
	if (conn->end.packet_duration_ms > 0 && endp->trunk->audio_send_ptime) {
 | 
			
		||||
		rc = msgb_printf(sdp, "a=ptime:%u\r\n",
 | 
			
		||||
				 conn->end.packet_duration_ms);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
@@ -397,6 +632,6 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
buffer_too_small:
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,10 +22,12 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_stat.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_stat.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
 | 
			
		||||
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
 | 
			
		||||
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										255
									
								
								src/libosmo-mgcp/mgcp_trunk.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								src/libosmo-mgcp/mgcp_trunk.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,255 @@
 | 
			
		||||
/* Trunk handling */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2012 by On-Waves
 | 
			
		||||
 * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_e1.h>
 | 
			
		||||
#include <osmocom/abis/e1_input.h>
 | 
			
		||||
 | 
			
		||||
/*! allocate trunk and add it (if required) to the trunk list.
 | 
			
		||||
 *  (called once at startup by VTY).
 | 
			
		||||
 *  \param[in] cfg mgcp configuration.
 | 
			
		||||
 *  \param[in] ttype trunk type.
 | 
			
		||||
 *  \param[in] nr trunk number.
 | 
			
		||||
 *  \returns pointer to allocated trunk, NULL on failure. */
 | 
			
		||||
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk *trunk;
 | 
			
		||||
 | 
			
		||||
	trunk = talloc_zero(cfg, struct mgcp_trunk);
 | 
			
		||||
	if (!trunk) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	trunk->cfg = cfg;
 | 
			
		||||
	trunk->trunk_type = ttype;
 | 
			
		||||
	trunk->trunk_nr = nr;
 | 
			
		||||
 | 
			
		||||
	trunk->audio_send_ptime = 1;
 | 
			
		||||
	trunk->audio_send_name = 1;
 | 
			
		||||
	trunk->v.vty_number_endpoints = 512;
 | 
			
		||||
	trunk->omit_rtcp = 0;
 | 
			
		||||
 | 
			
		||||
	mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
 | 
			
		||||
 | 
			
		||||
	llist_add_tail(&trunk->entry, &cfg->trunks);
 | 
			
		||||
 | 
			
		||||
        mgcp_ratectr_trunk_alloc(cfg, &trunk->ratectr);
 | 
			
		||||
 | 
			
		||||
	return trunk;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! allocate endpoints and set default values
 | 
			
		||||
 *  (called once at startup by VTY).
 | 
			
		||||
 *  \param[in] trunk trunk configuration.
 | 
			
		||||
 *  \returns 0 on success, -1 on failure. */
 | 
			
		||||
int mgcp_trunk_alloc_endpts(struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	unsigned int number_endpoints;
 | 
			
		||||
	unsigned int first_endpoint_nr;
 | 
			
		||||
 | 
			
		||||
	/* This function is called once on startup by the VTY to allocate the
 | 
			
		||||
	 * endpoints. The number of endpoints must not change througout the
 | 
			
		||||
	 * runtime of the MGW */
 | 
			
		||||
	OSMO_ASSERT(trunk->number_endpoints == 0);
 | 
			
		||||
	OSMO_ASSERT(trunk->endpoints == NULL);
 | 
			
		||||
 | 
			
		||||
	switch (trunk->trunk_type) {
 | 
			
		||||
	case MGCP_TRUNK_VIRTUAL:
 | 
			
		||||
		/* Due to historical reasons the endpoints on the virtual
 | 
			
		||||
		 * trunk start counting at 1. */
 | 
			
		||||
		first_endpoint_nr = 1;
 | 
			
		||||
		number_endpoints = trunk->v.vty_number_endpoints;
 | 
			
		||||
		break;
 | 
			
		||||
	case MGCP_TRUNK_E1:
 | 
			
		||||
		/* The first timeslot on an E1 line is reserved for framing
 | 
			
		||||
		 * and alignment and can not be used for audio transport */
 | 
			
		||||
	        first_endpoint_nr = 1 * MGCP_ENDP_E1_SUBSLOTS;
 | 
			
		||||
		number_endpoints = (NUM_E1_TS-1) * MGCP_ENDP_E1_SUBSLOTS;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Make sure the amount of requested endpoints does not execeed
 | 
			
		||||
	 * sane limits. The VTY already limits the possible amount,
 | 
			
		||||
	 * however miss-initialization of the struct or memory corruption
 | 
			
		||||
	 * could still lead to an excessive allocation of endpoints, so
 | 
			
		||||
	 * better stop early if that is the case. */
 | 
			
		||||
	OSMO_ASSERT(number_endpoints < 65534);
 | 
			
		||||
 | 
			
		||||
	/* allocate pointer array for the endpoints */
 | 
			
		||||
	trunk->endpoints = talloc_zero_array(trunk->cfg, struct mgcp_endpoint*,
 | 
			
		||||
					     number_endpoints);
 | 
			
		||||
	if (!trunk->endpoints)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	/* create endpoints */
 | 
			
		||||
	for (i = 0; i < number_endpoints; i++) {
 | 
			
		||||
		endp = mgcp_endp_alloc(trunk, i + first_endpoint_nr);
 | 
			
		||||
		if (!endp) {
 | 
			
		||||
			talloc_free(trunk->endpoints);
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		trunk->endpoints[i] = endp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* make the endpoints we just created available to the MGW code */
 | 
			
		||||
	trunk->number_endpoints = number_endpoints;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Equip trunk with endpoints and resources
 | 
			
		||||
 *  (called once at startup by VTY).
 | 
			
		||||
 *  \param[in] trunk trunk configuration.
 | 
			
		||||
 *  \returns 0 on success, -1 on failure. */
 | 
			
		||||
int mgcp_trunk_equip(struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	/* Allocate endpoints */
 | 
			
		||||
	if(mgcp_trunk_alloc_endpts(trunk) != 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	/* Allocate resources */
 | 
			
		||||
	switch (trunk->trunk_type) {
 | 
			
		||||
	case MGCP_TRUNK_VIRTUAL:
 | 
			
		||||
		/* No additional initaliziation required here, virtual
 | 
			
		||||
		 * endpoints will open/close network sockets themselves
 | 
			
		||||
		 * on demand. */
 | 
			
		||||
		break;
 | 
			
		||||
	case MGCP_TRUNK_E1:
 | 
			
		||||
		/* The TS initalization happens once on startup for all
 | 
			
		||||
		 * timeslots. This only affects the i460 multiplexer. Until
 | 
			
		||||
		 * now no E1 resources are claimed yet. This happens on demand
 | 
			
		||||
		 * when the related endpoint is actually used */
 | 
			
		||||
		memset(trunk->e1.i460_ts, 0, sizeof(trunk->e1.i460_ts));
 | 
			
		||||
		for (i = 0; i < (NUM_E1_TS-1); i++)
 | 
			
		||||
			osmo_i460_ts_init(&trunk->e1.i460_ts[i]);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! get trunk configuration by trunk number (index).
 | 
			
		||||
 *  \param[in] cfg mgcp configuration.
 | 
			
		||||
 *  \param[in] ttype trunk type.
 | 
			
		||||
 *  \param[in] nr trunk number.
 | 
			
		||||
 *  \returns pointer to trunk configuration, NULL on error. */
 | 
			
		||||
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk *trunk;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(trunk, &cfg->trunks, entry) {
 | 
			
		||||
		if (trunk->trunk_nr == nr && trunk->trunk_type == ttype)
 | 
			
		||||
			return trunk;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Made public for unit-testing, do not use from outside this file */
 | 
			
		||||
int e1_trunk_nr_from_epname(const char *epname)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long int trunk_nr;
 | 
			
		||||
	size_t prefix_len;
 | 
			
		||||
	char *str_trunk_nr_end;
 | 
			
		||||
 | 
			
		||||
	prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
 | 
			
		||||
	if (strncmp(epname, MGCP_ENDPOINT_PREFIX_E1_TRUNK, prefix_len) != 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	errno = 0;
 | 
			
		||||
	trunk_nr = strtoul(epname + prefix_len, &str_trunk_nr_end, 10);
 | 
			
		||||
	if (errno == ERANGE || trunk_nr > 64
 | 
			
		||||
	    || epname + prefix_len == str_trunk_nr_end
 | 
			
		||||
	    || str_trunk_nr_end[0] != '/')
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	else
 | 
			
		||||
		return trunk_nr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Find a trunk by the trunk prefix in the endpoint name.
 | 
			
		||||
 *  \param[in] epname endpoint name with trunk prefix to look up.
 | 
			
		||||
 *  \param[in] cfg that contains the trunks where the endpoint is located.
 | 
			
		||||
 *  \returns trunk or NULL if trunk was not found. */
 | 
			
		||||
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname)
 | 
			
		||||
{
 | 
			
		||||
	size_t prefix_len;
 | 
			
		||||
	char epname_lc[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	int trunk_nr;
 | 
			
		||||
 | 
			
		||||
	osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
 | 
			
		||||
	epname = epname_lc;
 | 
			
		||||
 | 
			
		||||
	prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
 | 
			
		||||
	if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, prefix_len) == 0) {
 | 
			
		||||
		return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	trunk_nr = e1_trunk_nr_from_epname(epname);
 | 
			
		||||
	if (trunk_nr >= 0)
 | 
			
		||||
		return mgcp_trunk_by_num(cfg, MGCP_TRUNK_E1, trunk_nr);
 | 
			
		||||
 | 
			
		||||
	/* Earlier versions of osmo-mgw were accepting endpoint names
 | 
			
		||||
	 * without trunk prefix. This is normally not allowed, each MGCP
 | 
			
		||||
	 * request should supply an endpoint name with trunk prefix.
 | 
			
		||||
	 * However in order to stay compatible with old versions of
 | 
			
		||||
	 * osmo-bsc and osmo-msc we still accept endpoint names without
 | 
			
		||||
	 * trunk prefix and just assume that the virtual trunk should
 | 
			
		||||
	 * be selected. There is even a TTCN3 test for this, see also:
 | 
			
		||||
	 * MGCP_Test.TC_crcx_noprefix */
 | 
			
		||||
	if ((epname[0] >= '0' && epname[0] <= '9') || (epname[0] >= 'a' && epname[0] <= 'f')) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "missing trunk prefix in endpoint name \"%s\", assuming trunk \"%s\"!\n", epname,
 | 
			
		||||
		     MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
 | 
			
		||||
		return  mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "unable to find trunk for endpoint name \"%s\"!\n", epname);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Find a trunk (E1) by its associated E1 line number.
 | 
			
		||||
 *  \param[in] num e1 line number.
 | 
			
		||||
 *  \returns trunk or NULL if trunk was not found. */
 | 
			
		||||
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num)
 | 
			
		||||
{
 | 
			
		||||
	/*! When used on trunks other than E1, the result will always be NULL. */
 | 
			
		||||
	struct mgcp_trunk *trunk;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(trunk, &cfg->trunks, entry) {
 | 
			
		||||
		if (trunk->trunk_type == MGCP_TRUNK_E1 && trunk->e1.vty_line_nr == num)
 | 
			
		||||
			return trunk;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -9,7 +9,10 @@ AM_CFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOCTRL_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOABIS_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOTRAU_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
@@ -26,5 +29,8 @@ osmo_mgw_LDADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOCTRL_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(LIBOSMOTRAU_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -30,14 +30,18 @@
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/abis/e1_input.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/vty.h>
 | 
			
		||||
#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>
 | 
			
		||||
@@ -48,22 +52,32 @@
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/ctrl/control_vty.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/telnet_interface.h>
 | 
			
		||||
#include <osmocom/vty/logging.h>
 | 
			
		||||
#include <osmocom/vty/ports.h>
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/stats.h>
 | 
			
		||||
#include <osmocom/vty/misc.h>
 | 
			
		||||
#include <osmocom/vty/cpu_sched_vty.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/abis/abis.h>
 | 
			
		||||
 | 
			
		||||
#include "../../bscconfig.h"
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
 | 
			
		||||
/* can be changed once libosmocore 1.4.0 is released */
 | 
			
		||||
#ifndef OSMO_CTRL_PORT_MGW
 | 
			
		||||
#define OSMO_CTRL_PORT_MGW 4267
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* FIXME: Make use of the rtp proxy code */
 | 
			
		||||
 | 
			
		||||
static struct mgcp_config *cfg;
 | 
			
		||||
static struct mgcp_trunk_config *reset_trunk;
 | 
			
		||||
static struct mgcp_trunk *reset_trunk;
 | 
			
		||||
static int reset_endpoints = 0;
 | 
			
		||||
static int daemonize = 0;
 | 
			
		||||
 | 
			
		||||
@@ -79,28 +93,60 @@ const char *osmomgw_copyright =
 | 
			
		||||
static char *config_file = "osmo-mgw.cfg";
 | 
			
		||||
 | 
			
		||||
/* used by msgb and mgcp */
 | 
			
		||||
void *tall_bsc_ctx = NULL;
 | 
			
		||||
void *tall_mgw_ctx = NULL;
 | 
			
		||||
 | 
			
		||||
static void print_help()
 | 
			
		||||
{
 | 
			
		||||
	printf("Some useful help...\n");
 | 
			
		||||
	printf("Some useful options:\n");
 | 
			
		||||
	printf(" -h --help is printing this text.\n");
 | 
			
		||||
	printf(" -c --config-file filename The config file to use.\n");
 | 
			
		||||
	printf(" -s --disable-color\n");
 | 
			
		||||
	printf(" -D --daemonize Fork the process into a background daemon\n");
 | 
			
		||||
	printf(" -V --version Print the version number\n");
 | 
			
		||||
 | 
			
		||||
	printf("\nVTY reference generation:\n");
 | 
			
		||||
	printf("    --vty-ref-mode MODE		VTY reference generation mode (e.g. 'expert').\n");
 | 
			
		||||
	printf("    --vty-ref-xml		Generate the VTY reference XML output and exit.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_long_options(const char *prog_name, const int long_option)
 | 
			
		||||
{
 | 
			
		||||
	static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
 | 
			
		||||
 | 
			
		||||
	switch (long_option) {
 | 
			
		||||
	case 1:
 | 
			
		||||
		vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
 | 
			
		||||
		if (vty_ref_mode < 0) {
 | 
			
		||||
			fprintf(stderr, "%s: Unknown VTY reference generation "
 | 
			
		||||
				"mode '%s'\n", prog_name, optarg);
 | 
			
		||||
			exit(2);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case 2:
 | 
			
		||||
		fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
 | 
			
		||||
			get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
 | 
			
		||||
			get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
 | 
			
		||||
		vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	default:
 | 
			
		||||
		fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_options(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	while (1) {
 | 
			
		||||
		int option_index = 0, c;
 | 
			
		||||
		static int long_option = 0;
 | 
			
		||||
		static struct option long_options[] = {
 | 
			
		||||
			{"help", 0, 0, 'h'},
 | 
			
		||||
			{"config-file", 1, 0, 'c'},
 | 
			
		||||
			{"daemonize", 0, 0, 'D'},
 | 
			
		||||
			{"version", 0, 0, 'V'},
 | 
			
		||||
			{"disable-color", 0, 0, 's'},
 | 
			
		||||
			{"vty-ref-mode", 1, &long_option, 1},
 | 
			
		||||
			{"vty-ref-xml", 0, &long_option, 2},
 | 
			
		||||
			{0, 0, 0, 0},
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
@@ -114,8 +160,11 @@ static void handle_options(int argc, char **argv)
 | 
			
		||||
			print_help();
 | 
			
		||||
			exit(0);
 | 
			
		||||
			break;
 | 
			
		||||
		case 0:
 | 
			
		||||
			handle_long_options(argv[0], long_option);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'c':
 | 
			
		||||
			config_file = talloc_strdup(tall_bsc_ctx, optarg);
 | 
			
		||||
			config_file = talloc_strdup(tall_mgw_ctx, optarg);
 | 
			
		||||
			break;
 | 
			
		||||
		case 's':
 | 
			
		||||
			log_set_use_color(osmo_stderr_target, 0);
 | 
			
		||||
@@ -132,24 +181,28 @@ static void handle_options(int argc, char **argv)
 | 
			
		||||
			break;
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
	if (argc > optind) {
 | 
			
		||||
		fprintf(stderr, "Unsupported positional arguments on command line\n");
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Callback function to be called when the RSIP ("Reset in Progress") mgcp
 | 
			
		||||
 * command is received */
 | 
			
		||||
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
 | 
			
		||||
static int mgcp_rsip_cb(struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	/* Set flag so that, when read_call_agent() is called next time
 | 
			
		||||
	 * the reset can progress */
 | 
			
		||||
	reset_endpoints = 1;
 | 
			
		||||
 | 
			
		||||
	reset_trunk = tcfg;
 | 
			
		||||
	reset_trunk = trunk;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int read_call_agent(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	struct osmo_sockaddr addr;
 | 
			
		||||
	socklen_t slen = sizeof(addr);
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	struct msgb *resp;
 | 
			
		||||
@@ -175,7 +228,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
	msgb_reset(msg);
 | 
			
		||||
 | 
			
		||||
	if (resp) {
 | 
			
		||||
		sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
 | 
			
		||||
		sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, &addr.u.sa, sizeof(addr));
 | 
			
		||||
		msgb_free(resp);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -191,7 +244,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
		/* Walk over all endpoints and trigger a release, this will release all
 | 
			
		||||
		 * endpoints, possible open connections are forcefully dropped */
 | 
			
		||||
		for (i = 1; i < reset_trunk->number_endpoints; ++i)
 | 
			
		||||
			mgcp_endp_release(&reset_trunk->endpoints[i]);
 | 
			
		||||
			mgcp_endp_release(reset_trunk->endpoints[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -243,7 +296,13 @@ static const struct log_info_cat log_categories[] = {
 | 
			
		||||
		  .description = "RTP stream handling",
 | 
			
		||||
		  .color = "\033[1;30m",
 | 
			
		||||
		  .enabled = 1,.loglevel = LOGL_NOTICE,
 | 
			
		||||
		  },
 | 
			
		||||
	},
 | 
			
		||||
	[DE1] = {
 | 
			
		||||
		  .name = "DE1",
 | 
			
		||||
		  .description = "E1 line handling",
 | 
			
		||||
		  .color = "\033[1;31m",
 | 
			
		||||
		  .enabled = 1,.loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct log_info log_info = {
 | 
			
		||||
@@ -256,40 +315,54 @@ int main(int argc, char **argv)
 | 
			
		||||
	unsigned int flags;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
 | 
			
		||||
	vty_info.tall_ctx = tall_bsc_ctx;
 | 
			
		||||
	tall_mgw_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
 | 
			
		||||
	vty_info.tall_ctx = tall_mgw_ctx;
 | 
			
		||||
 | 
			
		||||
	msgb_talloc_ctx_init(tall_bsc_ctx, 0);
 | 
			
		||||
	msgb_talloc_ctx_init(tall_mgw_ctx, 0);
 | 
			
		||||
 | 
			
		||||
	osmo_init_ignore_signals();
 | 
			
		||||
	osmo_init_logging2(tall_bsc_ctx, &log_info);
 | 
			
		||||
	osmo_init_logging2(tall_mgw_ctx, &log_info);
 | 
			
		||||
	libosmo_abis_init(tall_mgw_ctx);
 | 
			
		||||
 | 
			
		||||
	cfg = mgcp_config_alloc();
 | 
			
		||||
	if (!cfg)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	vty_info.copyright = osmomgw_copyright;
 | 
			
		||||
	vty_info.usr_attr_desc[MGW_CMD_ATTR_NEWCONN] = \
 | 
			
		||||
		"This command applies when a new connection is created";
 | 
			
		||||
	vty_info.usr_attr_letters[MGW_CMD_ATTR_NEWCONN] = 'n';
 | 
			
		||||
 | 
			
		||||
	vty_init(&vty_info);
 | 
			
		||||
	logging_vty_add_cmds(NULL);
 | 
			
		||||
	logging_vty_add_cmds();
 | 
			
		||||
	osmo_talloc_vty_add_cmds();
 | 
			
		||||
	osmo_stats_vty_add_cmds(&log_info);
 | 
			
		||||
	osmo_stats_vty_add_cmds();
 | 
			
		||||
	mgcp_vty_init();
 | 
			
		||||
	ctrl_vty_init(cfg);
 | 
			
		||||
	e1inp_vty_init();
 | 
			
		||||
	osmo_cpu_sched_vty_init(tall_mgw_ctx);
 | 
			
		||||
 | 
			
		||||
	handle_options(argc, argv);
 | 
			
		||||
 | 
			
		||||
	rate_ctr_init(tall_bsc_ctx);
 | 
			
		||||
	osmo_stats_init(tall_bsc_ctx);
 | 
			
		||||
	rate_ctr_init(tall_mgw_ctx);
 | 
			
		||||
	osmo_stats_init(tall_mgw_ctx);
 | 
			
		||||
 | 
			
		||||
	rc = mgcp_parse_config(config_file, cfg, MGCP_BSC);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	/* start telnet after reading config for vty_get_bind_addr() */
 | 
			
		||||
	rc = telnet_init_dynif(tall_bsc_ctx, NULL,
 | 
			
		||||
	rc = telnet_init_dynif(tall_mgw_ctx, NULL,
 | 
			
		||||
			       vty_get_bind_addr(), 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);
 | 
			
		||||
	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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Set the reset callback function. This functions is called when the
 | 
			
		||||
	 * mgcp-command "RSIP" (Reset in Progress) is received */
 | 
			
		||||
	cfg->reset_cb = mgcp_rsip_cb;
 | 
			
		||||
@@ -299,7 +372,7 @@ int main(int argc, char **argv)
 | 
			
		||||
	if (cfg->call_agent_addr)
 | 
			
		||||
		flags |= OSMO_SOCK_F_CONNECT;
 | 
			
		||||
 | 
			
		||||
	rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
 | 
			
		||||
	rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
 | 
			
		||||
				cfg->source_addr, cfg->source_port,
 | 
			
		||||
				cfg->call_agent_addr, cfg->call_agent_addr ? 2727 : 0, flags);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,8 @@ AM_CFLAGS = \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOABIS_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOTRAU_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
@@ -35,7 +37,10 @@ mgcp_test_LDADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(LIBOSMOTRAU_LIBS) \
 | 
			
		||||
	$(LIBRARY_DL) \
 | 
			
		||||
	$(LIBRARY_DLSYM) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	-lm  \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -65,7 +65,7 @@ Testing CRCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
m: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
@@ -125,7 +125,7 @@ Testing MDCX4_PT1
 | 
			
		||||
Testing MDCX4_PT1
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983217 1@mgw MGCP 1.0
 | 
			
		||||
MDCX 18983217 1@mgw MGCP 1.0
 | 
			
		||||
M: SENDRECV
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
@@ -193,10 +193,34 @@ Response matches our expectations.
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_PT4
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983220 1@mgw MGCP 1.0
 | 
			
		||||
m: sendrecv
 | 
			
		||||
c: 2
 | 
			
		||||
i: %s
 | 
			
		||||
l: A:amr, NT:IN
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- %s 23 IN IP4 0.0.0.0
 | 
			
		||||
c=IN IP4 0.0.0.0
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_SO
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
@@ -219,7 +243,7 @@ Response matches our expectations.
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_RO
 | 
			
		||||
@@ -359,7 +383,7 @@ Testing CRCX
 | 
			
		||||
DLCX 7 1@mgw MGCP 1.0
 | 
			
		||||
I: %s
 | 
			
		||||
C: 2
 | 
			
		||||
---------8<---------
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
@@ -446,7 +470,7 @@ Dummy packets: 2
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
---------8<---------
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
@@ -459,9 +483,33 @@ Response matches our expectations.
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983223 1@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
I: 123456789012345678901234567890123
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 7@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
X
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 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)
 | 
			
		||||
@@ -478,7 +526,7 @@ Re-transmitting CRCX
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
@@ -590,7 +638,7 @@ Testing packet loss calculation.
 | 
			
		||||
Re-transmitting DLCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
I: %s
 | 
			
		||||
DLCX 7 1@mgw MGCP 1.0
 | 
			
		||||
I: %s
 | 
			
		||||
C: 2
 | 
			
		||||
 | 
			
		||||
@@ -1141,7 +1189,7 @@ Testing no rtpmap name
 | 
			
		||||
a=rtpmap:4 G723/8000
 | 
			
		||||
a=rtpmap:96 iLBC/8000
 | 
			
		||||
a=fmtp:96 mode=20
 | 
			
		||||
a=fmtp:97 mode=30
 | 
			
		||||
a=rtpmap:97 iLBC/8000
 | 
			
		||||
a=fmtp:97 mode=30
 | 
			
		||||
a=rtpmap:101 telephone-event/8000
 | 
			
		||||
a=fmtp:101 0-15
 | 
			
		||||
@@ -1170,6 +1218,148 @@ p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
Testing get_lco_identifier()
 | 
			
		||||
p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
 | 
			
		||||
'XXXX,p:10,a:PCMU' -> 'p:10,a:PCMU'
 | 
			
		||||
'10,a:PCMU' -> 'a:PCMU'
 | 
			
		||||
'10, a:PCMU' -> 'a:PCMU'
 | 
			
		||||
'10,a: PCMU' -> 'a: PCMU'
 | 
			
		||||
'10 ,a: PCMU' -> 'a: PCMU'
 | 
			
		||||
', 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
 | 
			
		||||
    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
 | 
			
		||||
    2:  97 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
 | 
			
		||||
 - 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_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
 | 
			
		||||
    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
 | 
			
		||||
    2:  96 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
 | 
			
		||||
 - 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_pt_translate(conn0, conn1, 123) -> -22
 | 
			
		||||
#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
 | 
			
		||||
    2:  96 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 96) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 96
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 97) -> 96
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
			
		||||
#3: conn0 has no codecs
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    (none)
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0:  96 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2:  97 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
			
		||||
#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_pt_translate(conn0, conn1, 112) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
			
		||||
#5: test AMR with differing octet-aligned settings
 | 
			
		||||
 - 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_pt_translate(conn0, conn1, 111) -> 121
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 121) -> 111
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
 | 
			
		||||
#6: test AMR with missing octet-aligned settings (defaults to 0)
 | 
			
		||||
 - 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=unset  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
 | 
			
		||||
#7: test AMR with NULL param (defaults to 0)
 | 
			
		||||
 - 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  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
 | 
			
		||||
#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
 | 
			
		||||
    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
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
 | 
			
		||||
 - 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_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
 | 
			
		||||
    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
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
 | 
			
		||||
 - 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_pt_translate(conn0, conn1, 123) -> -22
 | 
			
		||||
#10: 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
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,7 @@ mgcp_client_test_LDADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBRARY_DL) \
 | 
			
		||||
	$(LIBRARY_DLSYM) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -157,6 +157,7 @@ void test_mgcp_msg(void)
 | 
			
		||||
		.ptmap[0].pt = 96,
 | 
			
		||||
		.ptmap_len = 1,
 | 
			
		||||
		.x_osmo_ign = MGCP_X_OSMO_IGN_CALLID,
 | 
			
		||||
		.x_osmo_osmux_cid = -1, /* wildcard */
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (mgcp)
 | 
			
		||||
@@ -254,6 +255,38 @@ void test_mgcp_msg(void)
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	printf("%s\n", (char *)msg->data);
 | 
			
		||||
 | 
			
		||||
	printf("Generate X-Osmo-Osmux message:\n");
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	mgcp_msg.verb = MGCP_VERB_CRCX;
 | 
			
		||||
	mgcp_msg.presence =
 | 
			
		||||
	    (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
 | 
			
		||||
	     MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE
 | 
			
		||||
	     | MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID);
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	printf("%s\n", (char *)msg->data);
 | 
			
		||||
 | 
			
		||||
	printf("Generate X-Osmo-Osmux message (fixed CID 2):\n");
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	mgcp_msg.verb = MGCP_VERB_CRCX;
 | 
			
		||||
	mgcp_msg.x_osmo_osmux_cid = 2;
 | 
			
		||||
	mgcp_msg.presence =
 | 
			
		||||
	    (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
 | 
			
		||||
	     MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE
 | 
			
		||||
	     | MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID);
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	printf("%s\n", (char *)msg->data);
 | 
			
		||||
 | 
			
		||||
	printf("Generate X-Osmo-Osmux message (MDCX):\n");
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	mgcp_msg.verb = MGCP_VERB_MDCX;
 | 
			
		||||
	mgcp_msg.x_osmo_osmux_cid = 2;
 | 
			
		||||
	mgcp_msg.presence =
 | 
			
		||||
	    (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
 | 
			
		||||
	     MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE
 | 
			
		||||
	     | MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID);
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	printf("%s\n", (char *)msg->data);
 | 
			
		||||
 | 
			
		||||
	printf("Overfolow test:\n");
 | 
			
		||||
	mgcp_msg.verb = MGCP_VERB_MDCX;
 | 
			
		||||
	mgcp_msg.presence =
 | 
			
		||||
@@ -261,11 +294,23 @@ void test_mgcp_msg(void)
 | 
			
		||||
	     MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
 | 
			
		||||
	     MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
 | 
			
		||||
	memset(audio_ip_overflow, 'X', sizeof(audio_ip_overflow));
 | 
			
		||||
	audio_ip_overflow[1] = '.';
 | 
			
		||||
	audio_ip_overflow[sizeof(audio_ip_overflow) - 1] = '\0';
 | 
			
		||||
	mgcp_msg.audio_ip = audio_ip_overflow;
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	OSMO_ASSERT(msg == NULL);
 | 
			
		||||
 | 
			
		||||
	printf("IPv6 test:\n");
 | 
			
		||||
	mgcp_msg.verb = MGCP_VERB_MDCX;
 | 
			
		||||
	mgcp_msg.presence =
 | 
			
		||||
	    (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
 | 
			
		||||
	     MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
 | 
			
		||||
	     MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
 | 
			
		||||
	mgcp_msg.audio_ip = "2001:db8:1::ab9:c0a8:102";
 | 
			
		||||
	mgcp->actual.remote_addr = "::1";
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	printf("%s\n", (char *)msg->data);
 | 
			
		||||
 | 
			
		||||
	printf("\n");
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
}
 | 
			
		||||
@@ -327,7 +372,7 @@ struct sdp_section_start_test {
 | 
			
		||||
static struct sdp_section_start_test sdp_section_start_tests[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.body = "",
 | 
			
		||||
		.expect_rc = -EINVAL,
 | 
			
		||||
		.expect_rc = 0,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "\n\n",
 | 
			
		||||
@@ -366,19 +411,79 @@ static struct sdp_section_start_test sdp_section_start_tests[] = {
 | 
			
		||||
		.body = "some mgcp header data\r\nand header params"
 | 
			
		||||
			"\n\r\n"
 | 
			
		||||
			"m=audio 23\r\n",
 | 
			
		||||
		.expect_rc = -EINVAL,
 | 
			
		||||
		.expect_rc = 0,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "some mgcp header data\r\nand header params"
 | 
			
		||||
			"\r\n\r"
 | 
			
		||||
			"m=audio 23\r\n",
 | 
			
		||||
		.expect_rc = -EINVAL,
 | 
			
		||||
		.expect_rc = 0,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "some mgcp header data\r\nand header params"
 | 
			
		||||
			"\n\r\r"
 | 
			
		||||
			"m=audio 23\r\n",
 | 
			
		||||
		.expect_rc = -EINVAL,
 | 
			
		||||
		.expect_rc = 0,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "some mgcp header data\r\nand header params"
 | 
			
		||||
			"\r\n\r\n"
 | 
			
		||||
			"c=IN IP4 1.2.3.4\r\n",
 | 
			
		||||
		.expect_params = {
 | 
			
		||||
			.audio_ip = "1.2.3.4",
 | 
			
		||||
		},
 | 
			
		||||
		.expect_rc = 0,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "some mgcp header data\r\nand header params"
 | 
			
		||||
			"\r\n\r\n"
 | 
			
		||||
			"c=IN IP6 2001:db8:1::ab9:c0a8:102\r\n",
 | 
			
		||||
		.expect_params = {
 | 
			
		||||
			.audio_ip = "2001:db8:1::ab9:c0a8:102",
 | 
			
		||||
		},
 | 
			
		||||
		.expect_rc = 0,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "some mgcp header data\r\nand header params"
 | 
			
		||||
			"\r\n\r\n"
 | 
			
		||||
			"c=IN IP6 1.2.3.4\r\n",
 | 
			
		||||
		.expect_rc = -22,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "some mgcp header data\r\nand header params"
 | 
			
		||||
			"\r\n\r\n"
 | 
			
		||||
			"c=IN IP4 ::1\r\n",
 | 
			
		||||
		.expect_rc = -22,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "some mgcp header data\r\nand header params"
 | 
			
		||||
			"\r\n\r\n"
 | 
			
		||||
			"c=IN IP4 notanip\r\n",
 | 
			
		||||
		.expect_rc = -22,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "some mgcp header data\r\nand header params"
 | 
			
		||||
			"\r\n\r\n"
 | 
			
		||||
			"c=IN IP4 1.2.3.4.5.6\r\n",
 | 
			
		||||
		.expect_rc = -22,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "some mgcp header data\r\nand header params"
 | 
			
		||||
			"\r\n\r\n"
 | 
			
		||||
			"c=IN IP4 1.2 .3\r\n",
 | 
			
		||||
		.expect_rc = -22,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "some mgcp header data\r\nand header params"
 | 
			
		||||
			"\r\n\r\n"
 | 
			
		||||
			"c=IN IP4 1.2 .3\r\n",
 | 
			
		||||
		.expect_rc = -22,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "some mgcp header data\r\nand header params"
 | 
			
		||||
			"\r\n\r\n"
 | 
			
		||||
			"c=IN IP4 \r\n",
 | 
			
		||||
		.expect_rc = -22,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -410,7 +515,12 @@ void test_sdp_section_start()
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fprintf(stderr, "got audio_port=%u\n", t->expect_params.audio_port);
 | 
			
		||||
		fprintf(stderr, "got audio_ip=\"%s\"\n", r->audio_ip);
 | 
			
		||||
		if (strcmp(r->audio_ip, t->expect_params.audio_ip)) {
 | 
			
		||||
			fprintf(stderr, "FAIL: Expected audio_ip=\"%s\"\n", t->expect_params.audio_ip);
 | 
			
		||||
			failures++;
 | 
			
		||||
		}
 | 
			
		||||
		fprintf(stderr, "got audio_port=%u\n", r->audio_port);
 | 
			
		||||
		if (r->audio_port != t->expect_params.audio_port) {
 | 
			
		||||
			fprintf(stderr, "FAIL: Expected audio_port=%u\n", t->expect_params.audio_port);
 | 
			
		||||
			failures++;
 | 
			
		||||
@@ -514,6 +624,57 @@ static void test_map_codec_to_pt_and_map_pt_to_codec(void)
 | 
			
		||||
	printf("\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_mgcp_client_e1_epname(void)
 | 
			
		||||
{
 | 
			
		||||
	char *epname;
 | 
			
		||||
 | 
			
		||||
	if (mgcp)
 | 
			
		||||
		talloc_free(mgcp);
 | 
			
		||||
	mgcp = mgcp_client_init(ctx, &conf);
 | 
			
		||||
 | 
			
		||||
	/* Valid endpoint names */
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 1, 15, 64, 0);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 2, 14, 32, 0);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 3, 13, 32, 4);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 4, 12, 16, 0);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 5, 11, 16, 2);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 6, 10, 16, 4);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 7, 9, 16, 6);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 8, 8, 8, 0);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 9, 7, 8, 1);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 10, 6, 8, 2);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 11, 5, 8, 3);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 12, 4, 8, 4);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 13, 3, 8, 5);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 14, 2, 8, 6);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 1, 8, 7);
 | 
			
		||||
	printf("%s\n", epname);
 | 
			
		||||
 | 
			
		||||
	/* A few invalid enpoint names */
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 1, 128, 0);
 | 
			
		||||
	OSMO_ASSERT(epname == NULL);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 1, 8, 16);
 | 
			
		||||
	OSMO_ASSERT(epname == NULL);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 0, 8, 2);
 | 
			
		||||
	OSMO_ASSERT(epname == NULL);
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 64, 8, 2);
 | 
			
		||||
	OSMO_ASSERT(epname == NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct log_info_cat log_categories[] = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -528,9 +689,10 @@ int main(int argc, char **argv)
 | 
			
		||||
	ctx = talloc_named_const(NULL, 1, "mgcp_client_test");
 | 
			
		||||
	msgb_talloc_ctx_init(ctx, 0);
 | 
			
		||||
	osmo_init_logging2(ctx, &log_info);
 | 
			
		||||
	log_set_print_filename(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
 | 
			
		||||
	log_set_print_timestamp(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_use_color(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category_hex(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category(osmo_stderr_target, 1);
 | 
			
		||||
 | 
			
		||||
	log_set_category_filter(osmo_stderr_target, DLMGCP, 1, LOGL_DEBUG);
 | 
			
		||||
@@ -542,6 +704,7 @@ int main(int argc, char **argv)
 | 
			
		||||
	test_sdp_section_start();
 | 
			
		||||
	test_map_codec_to_pt_and_map_pt_to_codec();
 | 
			
		||||
	test_map_pt_to_codec();
 | 
			
		||||
	test_mgcp_client_e1_epname();
 | 
			
		||||
 | 
			
		||||
	printf("Done\n");
 | 
			
		||||
	fprintf(stderr, "Done\n");
 | 
			
		||||
 
 | 
			
		||||
@@ -17,55 +17,121 @@ test_mgcp_client_cancel() done
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [0]:
 | 
			
		||||
body: ""
 | 
			
		||||
DLMGCP MGCP response: cannot find start of SDP parameters
 | 
			
		||||
got rc=-22
 | 
			
		||||
DLMGCP MGCP response contains no SDP parameters
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip=""
 | 
			
		||||
got audio_port=0
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [1]:
 | 
			
		||||
body: "\n\n"
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip=""
 | 
			
		||||
got audio_port=0
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [2]:
 | 
			
		||||
body: "\r\n\r\n"
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip=""
 | 
			
		||||
got audio_port=0
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [3]:
 | 
			
		||||
body: "\n\r\n\r"
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip=""
 | 
			
		||||
got audio_port=0
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [4]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\n\nm=audio 23\r\n"
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip=""
 | 
			
		||||
got audio_port=23
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [5]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\r\n\r\nm=audio 23\r\n"
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip=""
 | 
			
		||||
got audio_port=23
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [6]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\n\r\n\rm=audio 23\r\n"
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip=""
 | 
			
		||||
got audio_port=23
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [7]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\n\r\nm=audio 23\r\n"
 | 
			
		||||
DLMGCP MGCP response: cannot find start of SDP parameters
 | 
			
		||||
got rc=-22
 | 
			
		||||
DLMGCP MGCP response contains no SDP parameters
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip=""
 | 
			
		||||
got audio_port=0
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [8]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\r\n\rm=audio 23\r\n"
 | 
			
		||||
DLMGCP MGCP response: cannot find start of SDP parameters
 | 
			
		||||
got rc=-22
 | 
			
		||||
DLMGCP MGCP response contains no SDP parameters
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip=""
 | 
			
		||||
got audio_port=0
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [9]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n"
 | 
			
		||||
DLMGCP MGCP response: cannot find start of SDP parameters
 | 
			
		||||
DLMGCP MGCP response contains no SDP parameters
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip=""
 | 
			
		||||
got audio_port=0
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [10]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 1.2.3.4\r\n"
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip="1.2.3.4"
 | 
			
		||||
got audio_port=0
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [11]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP6 2001:db8:1::ab9:c0a8:102\r\n"
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip="2001:db8:1::ab9:c0a8:102"
 | 
			
		||||
got audio_port=0
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [12]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP6 1.2.3.4\r\n"
 | 
			
		||||
DLMGCP Failed to parse MGCP response header (audio ip)
 | 
			
		||||
got rc=-22
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [13]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 ::1\r\n"
 | 
			
		||||
DLMGCP Failed to parse MGCP response header (audio ip)
 | 
			
		||||
got rc=-22
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [14]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 notanip\r\n"
 | 
			
		||||
DLMGCP Failed to parse MGCP response header (audio ip)
 | 
			
		||||
got rc=-22
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [15]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 1.2.3.4.5.6\r\n"
 | 
			
		||||
DLMGCP Failed to parse MGCP response header (audio ip)
 | 
			
		||||
got rc=-22
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [16]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 1.2 .3\r\n"
 | 
			
		||||
DLMGCP Failed to parse MGCP response header (audio ip)
 | 
			
		||||
got rc=-22
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [17]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 1.2 .3\r\n"
 | 
			
		||||
DLMGCP Failed to parse MGCP response header (audio ip)
 | 
			
		||||
got rc=-22
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [18]:
 | 
			
		||||
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 \r\n"
 | 
			
		||||
DLMGCP Failed to parse MGCP response header (audio ip)
 | 
			
		||||
got rc=-22
 | 
			
		||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
 | 
			
		||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
 | 
			
		||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
 | 
			
		||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
 | 
			
		||||
DLMGCP MGCP client: using endpoint domain '@mgw'
 | 
			
		||||
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw), rate(128)/offset(0) combination is invalid!
 | 
			
		||||
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
 | 
			
		||||
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid!
 | 
			
		||||
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid!
 | 
			
		||||
Done
 | 
			
		||||
 
 | 
			
		||||
@@ -84,7 +84,45 @@ L: p:20, a:GSM, nt:IN
 | 
			
		||||
M: sendrecv
 | 
			
		||||
X-Osmo-IGN: C
 | 
			
		||||
 | 
			
		||||
Generate X-Osmo-Osmux message:
 | 
			
		||||
CRCX 13 23@mgw MGCP 1.0
 | 
			
		||||
C: 2f
 | 
			
		||||
I: 11
 | 
			
		||||
L: p:20, a:GSM, nt:IN
 | 
			
		||||
M: sendrecv
 | 
			
		||||
X-Osmux: *
 | 
			
		||||
 | 
			
		||||
Generate X-Osmo-Osmux message (fixed CID 2):
 | 
			
		||||
CRCX 15 23@mgw MGCP 1.0
 | 
			
		||||
C: 2f
 | 
			
		||||
I: 11
 | 
			
		||||
L: p:20, a:GSM, nt:IN
 | 
			
		||||
M: sendrecv
 | 
			
		||||
X-Osmux: 2
 | 
			
		||||
 | 
			
		||||
Generate X-Osmo-Osmux message (MDCX):
 | 
			
		||||
MDCX 17 23@mgw MGCP 1.0
 | 
			
		||||
C: 2f
 | 
			
		||||
I: 11
 | 
			
		||||
L: p:20, a:GSM, nt:IN
 | 
			
		||||
M: sendrecv
 | 
			
		||||
X-Osmux: 2
 | 
			
		||||
 | 
			
		||||
Overfolow test:
 | 
			
		||||
IPv6 test:
 | 
			
		||||
MDCX 19 23@mgw MGCP 1.0
 | 
			
		||||
C: 2f
 | 
			
		||||
I: 11
 | 
			
		||||
M: sendrecv
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- 2f 23 IN IP6 ::1
 | 
			
		||||
s=-
 | 
			
		||||
c=IN IP6 2001:db8:1::ab9:c0a8:102
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 1234 RTP/AVP 3
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
test_mgcp_client_cancel():
 | 
			
		||||
@@ -125,6 +163,24 @@ test_sdp_section_start() test [7]:
 | 
			
		||||
test_sdp_section_start() test [8]:
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [9]:
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [10]:
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [11]:
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [12]:
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [13]:
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [14]:
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [15]:
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [16]:
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [17]:
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [18]:
 | 
			
		||||
 110 => 96
 | 
			
		||||
 111 => 97
 | 
			
		||||
 112 => 98
 | 
			
		||||
@@ -154,4 +210,19 @@ test_sdp_section_start() test [9]:
 | 
			
		||||
 2 <= 2
 | 
			
		||||
 100 <= 100
 | 
			
		||||
 | 
			
		||||
ds/e1-1/s-15/su64-0@mgw
 | 
			
		||||
ds/e1-2/s-14/su32-0@mgw
 | 
			
		||||
ds/e1-3/s-13/su32-4@mgw
 | 
			
		||||
ds/e1-4/s-12/su16-0@mgw
 | 
			
		||||
ds/e1-5/s-11/su16-2@mgw
 | 
			
		||||
ds/e1-6/s-10/su16-4@mgw
 | 
			
		||||
ds/e1-7/s-9/su16-6@mgw
 | 
			
		||||
ds/e1-8/s-8/su8-0@mgw
 | 
			
		||||
ds/e1-9/s-7/su8-1@mgw
 | 
			
		||||
ds/e1-10/s-6/su8-2@mgw
 | 
			
		||||
ds/e1-11/s-5/su8-3@mgw
 | 
			
		||||
ds/e1-12/s-4/su8-4@mgw
 | 
			
		||||
ds/e1-13/s-3/su8-5@mgw
 | 
			
		||||
ds/e1-14/s-2/su8-6@mgw
 | 
			
		||||
ds/e1-15/s-1/su8-7@mgw
 | 
			
		||||
Done
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user