mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-10-31 20:13:58 +00:00 
			
		
		
		
	Compare commits
	
		
			252 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 069dd16b67 | ||
|  | e144275757 | ||
|  | e0058b7207 | ||
|  | bb3ccdea1a | ||
|  | 2799ff9bb9 | ||
|  | fed5feafd3 | ||
|  | 1de5ed6f97 | ||
|  | 80751d850b | ||
|  | 2c40164ff0 | ||
|  | e308202285 | ||
|  | b4a067c9fb | ||
|  | 8029b12146 | ||
|  | ebb05c1f90 | ||
|  | bc3f3b40fe | ||
|  | fae09f4562 | ||
|  | 7c0fe31697 | ||
|  | 98aef217c6 | ||
|  | e03e34f8bb | ||
|  | ee6958c9a8 | ||
|  | fbcf4a6f6c | ||
|  | 58dc88bc31 | ||
|  | aac84edc9d | ||
|  | 172f5acfce | ||
|  | fdbefde869 | ||
|  | 25ecc91c3b | ||
|  | 995529548b | ||
|  | 0c04500ac5 | ||
|  | a0b69f1896 | ||
|  | fbf78d13f1 | ||
|  | df9192efee | ||
|  | 70a658a2ba | ||
|  | 2764bdb1aa | ||
|  | 55fdfc223e | ||
|  | 8f33303660 | ||
|  | a94c56e4c6 | ||
|  | 2ebcf5c34a | ||
|  | f936e10f07 | ||
|  | e303fa9ff3 | ||
|  | 958f5e74cc | ||
|  | 1e8d5fa44b | ||
|  | 374ad788d8 | ||
|  | da07b90f0f | ||
|  | fa495d669f | ||
|  | 439c86fc58 | ||
|  | 74e83d3d82 | ||
|  | d55be05af1 | ||
|  | 8f91a7f450 | ||
|  | da3a5759e1 | ||
|  | 29e671f257 | ||
|  | d6a7e17911 | ||
|  | 3f4a4cb49c | ||
|  | 3d2b76fd95 | ||
|  | 276a414aa3 | ||
|  | c534ad17dc | ||
|  | 3f2c15f275 | ||
|  | 38533ba9b3 | ||
|  | 39889e4389 | ||
|  | c824fe4eb0 | ||
|  | 124a3e0b34 | ||
|  | 96c6e06681 | ||
|  | ce18705875 | ||
|  | 41ab87f67c | ||
|  | a8739fc7be | ||
|  | 21dfeff8aa | ||
|  | f486e741a4 | ||
|  | 268568593e | ||
|  | 41d59205c0 | ||
|  | 036612b035 | ||
|  | 6bad138c96 | ||
|  | 8dc3597085 | ||
|  | d70eef6421 | ||
|  | 33d97f721d | ||
|  | d57310731f | ||
|  | a8c684b51f | ||
|  | 03fcc91aad | ||
|  | 9de30e7cc4 | ||
|  | d64c041cdb | ||
|  | d02716d6c2 | ||
|  | a065e632c0 | ||
|  | bd060c3c99 | ||
|  | 97cae477fb | ||
|  | 02c880a7dd | ||
|  | ba09687768 | ||
|  | a4b677c45a | ||
|  | 6b7afe8f57 | ||
|  | 4131a65c4d | ||
|  | 6a5e5ac2d4 | ||
|  | b066bd0c86 | ||
|  | d6769ea207 | ||
|  | ca280a1a84 | ||
|  | a24dcc61d7 | ||
|  | 62d9df684f | ||
|  | 8358c4ba07 | ||
|  | 4c77e9b748 | ||
|  | 33347a4f5d | ||
|  | 7d86d4c523 | ||
|  | eb984bd630 | ||
|  | b3d14eb552 | ||
|  | 907744e2fc | ||
|  | 776846a0b9 | ||
|  | 59e7cf4437 | ||
|  | b2064423fe | ||
|  | 97a9312be8 | ||
|  | 427cede4ba | ||
|  | 7371663aeb | ||
|  | fe53edd776 | ||
|  | 4f6a7ad5f8 | ||
|  | 55a9229922 | ||
|  | 7802ccc8d6 | ||
|  | 3ff5151661 | ||
|  | 5936a9c23e | ||
|  | 9ffaba7c1b | ||
|  | 535ede2598 | ||
|  | 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 | 
							
								
								
									
										118
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| # SPDX-License-Identifier: GPL-2.0 | ||||
| # | ||||
| # clang-format configuration file. Intended for clang-format >= 4. | ||||
| # | ||||
| # For more information, see: | ||||
| # | ||||
| #   Documentation/process/clang-format.rst | ||||
| #   https://clang.llvm.org/docs/ClangFormat.html | ||||
| #   https://clang.llvm.org/docs/ClangFormatStyleOptions.html | ||||
| # | ||||
| --- | ||||
| AccessModifierOffset: -4 | ||||
| AlignAfterOpenBracket: Align | ||||
| AlignConsecutiveAssignments: false | ||||
| AlignConsecutiveDeclarations: false | ||||
| #AlignEscapedNewlines: Left # Unknown to clang-format-4.0 | ||||
| AlignOperands: true | ||||
| AlignTrailingComments: false | ||||
| AllowAllParametersOfDeclarationOnNextLine: false | ||||
| AllowShortBlocksOnASingleLine: false | ||||
| AllowShortCaseLabelsOnASingleLine: false | ||||
| AllowShortFunctionsOnASingleLine: None | ||||
| AllowShortIfStatementsOnASingleLine: false | ||||
| AllowShortLoopsOnASingleLine: false | ||||
| AlwaysBreakAfterDefinitionReturnType: None | ||||
| AlwaysBreakAfterReturnType: None | ||||
| AlwaysBreakBeforeMultilineStrings: false | ||||
| AlwaysBreakTemplateDeclarations: false | ||||
| BinPackArguments: true | ||||
| BinPackParameters: true | ||||
| BraceWrapping: | ||||
|   AfterClass: false | ||||
|   AfterControlStatement: false | ||||
|   AfterEnum: false | ||||
|   AfterFunction: true | ||||
|   AfterNamespace: true | ||||
|   AfterObjCDeclaration: false | ||||
|   AfterStruct: false | ||||
|   AfterUnion: false | ||||
|   #AfterExternBlock: false # Unknown to clang-format-5.0 | ||||
|   BeforeCatch: false | ||||
|   BeforeElse: false | ||||
|   IndentBraces: false | ||||
|   #SplitEmptyFunction: true # Unknown to clang-format-4.0 | ||||
|   #SplitEmptyRecord: true # Unknown to clang-format-4.0 | ||||
|   #SplitEmptyNamespace: true # Unknown to clang-format-4.0 | ||||
| BreakBeforeBinaryOperators: None | ||||
| BreakBeforeBraces: Custom | ||||
| #BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 | ||||
| BreakBeforeTernaryOperators: false | ||||
| BreakConstructorInitializersBeforeComma: false | ||||
| #BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 | ||||
| BreakAfterJavaFieldAnnotations: false | ||||
| BreakStringLiterals: false | ||||
| ColumnLimit: 120 | ||||
| CommentPragmas: '^ IWYU pragma:' | ||||
| #CompactNamespaces: false # Unknown to clang-format-4.0 | ||||
| ConstructorInitializerAllOnOneLineOrOnePerLine: false | ||||
| ConstructorInitializerIndentWidth: 8 | ||||
| ContinuationIndentWidth: 8 | ||||
| Cpp11BracedListStyle: false | ||||
| DerivePointerAlignment: false | ||||
| DisableFormat: false | ||||
| ExperimentalAutoDetectBinPacking: false | ||||
| #FixNamespaceComments: false # Unknown to clang-format-4.0 | ||||
|  | ||||
| #IncludeBlocks: Preserve # Unknown to clang-format-5.0 | ||||
| IncludeCategories: | ||||
|   - Regex: '.*' | ||||
|     Priority: 1 | ||||
| IncludeIsMainRegex: '(Test)?$' | ||||
| IndentCaseLabels: false | ||||
| #IndentPPDirectives: None # Unknown to clang-format-5.0 | ||||
| IndentWidth: 8 | ||||
| IndentWrappedFunctionNames: false | ||||
| JavaScriptQuotes: Leave | ||||
| JavaScriptWrapImports: true | ||||
| KeepEmptyLinesAtTheStartOfBlocks: false | ||||
| MacroBlockBegin: '' | ||||
| MacroBlockEnd: '' | ||||
| MaxEmptyLinesToKeep: 1 | ||||
| NamespaceIndentation: None | ||||
| #ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 | ||||
| ObjCBlockIndentWidth: 8 | ||||
| ObjCSpaceAfterProperty: true | ||||
| ObjCSpaceBeforeProtocolList: true | ||||
|  | ||||
| # Taken from git's rules | ||||
| #PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 | ||||
| PenaltyBreakBeforeFirstCallParameter: 30 | ||||
| PenaltyBreakComment: 10 | ||||
| PenaltyBreakFirstLessLess: 0 | ||||
| PenaltyBreakString: 10 | ||||
| PenaltyExcessCharacter: 100 | ||||
| PenaltyReturnTypeOnItsOwnLine: 60 | ||||
|  | ||||
| PointerAlignment: Right | ||||
| ReflowComments: false | ||||
| SortIncludes: false | ||||
| #SortUsingDeclarations: false # Unknown to clang-format-4.0 | ||||
| SpaceAfterCStyleCast: false | ||||
| SpaceAfterTemplateKeyword: true | ||||
| SpaceBeforeAssignmentOperators: true | ||||
| #SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 | ||||
| #SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 | ||||
| SpaceBeforeParens: ControlStatements | ||||
| #SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 | ||||
| SpaceInEmptyParentheses: false | ||||
| SpacesBeforeTrailingComments: 1 | ||||
| SpacesInAngles: false | ||||
| SpacesInContainerLiterals: false | ||||
| SpacesInCStyleCastParentheses: false | ||||
| SpacesInParentheses: false | ||||
| SpacesInSquareBrackets: false | ||||
| Standard: Cpp03 | ||||
| TabWidth: 8 | ||||
| UseTab: Always | ||||
| ... | ||||
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -16,6 +16,7 @@ src/osmo-mgw/osmo-mgw | ||||
| *.gcda | ||||
| *.gcno | ||||
| *.pc | ||||
| *~ | ||||
|  | ||||
| #configure | ||||
| aclocal.m4 | ||||
| @@ -61,3 +62,10 @@ doc/manuals/generated/ | ||||
| doc/manuals/osmomsc-usermanual.xml | ||||
| doc/manuals/common | ||||
| doc/manuals/build | ||||
|  | ||||
| contrib/osmo-mgw.spec | ||||
|  | ||||
| #vs code | ||||
| .cache | ||||
| .vscode | ||||
|  | ||||
|   | ||||
							
								
								
									
										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) | ||||
|   | ||||
							
								
								
									
										39
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								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 | ||||
| @@ -29,20 +36,24 @@ if test "x$PKG_CONFIG_INSTALLED" = "xno"; then | ||||
| fi | ||||
| PKG_PROG_PKG_CONFIG([0.20]) | ||||
|  | ||||
| dnl check for AX_CHECK_COMPILE_FLAG | ||||
| m4_ifdef([AX_CHECK_COMPILE_FLAG], [], [ | ||||
| 	AC_MSG_ERROR([Please install autoconf-archive; re-run 'autoreconf -fi' for it to take effect.]) | ||||
| 	]) | ||||
|  | ||||
| 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.1.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.1.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.1.0) | ||||
| PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.6.0) | ||||
|  | ||||
| PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0) | ||||
| PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.2.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.2.0) | ||||
|  | ||||
| CFLAGS="$CFLAGS -pthread" | ||||
| LDFLAGS="$LDFLAGS -pthread" | ||||
|  | ||||
| AC_ARG_ENABLE(sanitize, | ||||
| 	[AS_HELP_STRING( | ||||
| @@ -74,15 +85,6 @@ then | ||||
| 	CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" | ||||
| fi | ||||
|  | ||||
| dnl Checks for typedefs, structures and compiler characteristics | ||||
|  | ||||
| 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([-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"]) | ||||
|  | ||||
| # Coverage build taken from WebKit's configure.in | ||||
| AC_MSG_CHECKING([whether to enable code coverage support]) | ||||
| AC_ARG_ENABLE(coverage, | ||||
| @@ -200,4 +202,5 @@ AC_OUTPUT( | ||||
|     doc/manuals/Makefile | ||||
|     contrib/Makefile | ||||
|     contrib/systemd/Makefile | ||||
|     contrib/osmo-mgw.spec | ||||
|     Makefile) | ||||
|   | ||||
| @@ -15,10 +15,6 @@ | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along | ||||
|  * with this program; if not, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  */ | ||||
| """ | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
| @@ -50,18 +49,18 @@ set -x | ||||
|  | ||||
| cd "$base" | ||||
| autoreconf --install --force | ||||
| ./configure --enable-vty-tests --enable-external-tests --enable-werror $CONFIG | ||||
| ./configure --enable-sanitize --enable-vty-tests --enable-external-tests --enable-werror $CONFIG | ||||
| $MAKE $PARALLEL_MAKE | ||||
| LD_LIBRARY_PATH="$inst/lib" $MAKE check \ | ||||
|   || cat-testlogs.sh | ||||
| LD_LIBRARY_PATH="$inst/lib" \ | ||||
|   DISTCHECK_CONFIGURE_FLAGS="--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 maintainer-clean | ||||
| $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.6.0 | ||||
| BuildRequires:  pkgconfig(libosmoctrl) >= 1.6.0 | ||||
| BuildRequires:  pkgconfig(libosmogsm) >= 1.6.0 | ||||
| BuildRequires:  pkgconfig(libosmovty) >= 1.6.0 | ||||
| BuildRequires:  pkgconfig(libosmocoding) >= 1.6.0 | ||||
| BuildRequires:  pkgconfig(libosmoabis) >= 1.2.0 | ||||
| BuildRequires:  pkgconfig(libosmotrau) >= 1.2.0 | ||||
| %{?systemd_requires} | ||||
|  | ||||
| %description | ||||
| OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks. | ||||
|  | ||||
| %package -n libosmo-mgcp-client9 | ||||
| Summary:        Osmocom's Media Gateway Control Protocol client library | ||||
| Group:          System/Libraries | ||||
|  | ||||
| %description -n libosmo-mgcp-client9 | ||||
| Osmocom's Media Gateway Control Protocol client library. | ||||
|  | ||||
| %package -n libosmo-mgcp-client-devel | ||||
| Summary:        Development files for Osmocom's Media Gateway Control Protocol client library | ||||
| Group:          Development/Libraries/C and C++ | ||||
| Requires:       libosmo-mgcp-client9 = %{version} | ||||
|  | ||||
| %description -n libosmo-mgcp-client-devel | ||||
| Osmocom's Media Gateway Control Protocol client librarary. | ||||
|  | ||||
| This subpackage contains libraries and header files for developing | ||||
| applications that want to make use of libosmo-mgcp-client. | ||||
|  | ||||
| %package -n libosmo-mgcp-devel | ||||
| Summary:        Development files for Osmocom's Media Gateway server library | ||||
| Group:          Development/Libraries/C and C++ | ||||
|  | ||||
| %description -n libosmo-mgcp-devel | ||||
| Osmocom's Media Gateway Control Protocol server library. | ||||
|  | ||||
| This subpackage contains libraries and header files for developing | ||||
| applications that want to make use of libosmo-mgcp. | ||||
|  | ||||
| %prep | ||||
| %setup -q | ||||
|  | ||||
| %build | ||||
| echo "%{version}" >.tarball-version | ||||
| autoreconf -fi | ||||
| %configure \ | ||||
|   --disable-static \ | ||||
|   --docdir=%{_docdir}/%{name} \ | ||||
|   --with-systemdsystemunitdir=%{_unitdir} | ||||
|  | ||||
| make %{?_smp_mflags} | ||||
|  | ||||
| %install | ||||
| %make_install | ||||
| find %{buildroot} -type f -name "*.la" -delete -print | ||||
|  | ||||
| %check | ||||
| make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +) | ||||
|  | ||||
| %post   -n libosmo-mgcp-client9 -p /sbin/ldconfig | ||||
| %postun -n libosmo-mgcp-client9 -p /sbin/ldconfig | ||||
|  | ||||
| %if 0%{?suse_version} | ||||
| %preun | ||||
| %service_del_preun osmo-mgw.service | ||||
|  | ||||
| %postun | ||||
| %service_del_postun osmo-mgw.service | ||||
|  | ||||
| %pre | ||||
| %service_add_pre osmo-mgw.service | ||||
|  | ||||
| %post | ||||
| %service_add_post osmo-mgw.service | ||||
| %endif | ||||
|  | ||||
| %files | ||||
| %license COPYING | ||||
| %doc AUTHORS README | ||||
| %dir %{_docdir}/%{name}/examples | ||||
| %dir %{_docdir}/%{name}/examples/osmo-mgw | ||||
| %{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw.cfg | ||||
| %{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw-abis_e1.cfg | ||||
| %{_bindir}/osmo-mgw | ||||
| %{_unitdir}/osmo-mgw.service | ||||
| %dir %{_sysconfdir}/osmocom | ||||
| %config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg | ||||
|  | ||||
| %files -n libosmo-mgcp-client9 | ||||
| %{_libdir}/libosmo-mgcp-client.so.9* | ||||
|  | ||||
| %files -n libosmo-mgcp-client-devel | ||||
| %{_libdir}/libosmo-mgcp-client.so | ||||
| %{_libdir}/pkgconfig/libosmo-mgcp-client.pc | ||||
| %dir %{_includedir}/osmocom | ||||
| %dir %{_includedir}/osmocom/mgcp_client | ||||
| %{_includedir}/osmocom/mgcp_client/*.h | ||||
|  | ||||
| %files -n libosmo-mgcp-devel | ||||
| %dir %{_includedir}/osmocom | ||||
| %dir %{_includedir}/osmocom/mgcp | ||||
| %{_includedir}/osmocom/mgcp/*.h | ||||
|  | ||||
| %changelog | ||||
							
								
								
									
										280
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										280
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,283 @@ | ||||
| osmo-mgw (1.9.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * TOS bits != DSCP | ||||
|   * manual: don't define fig-bsc twice | ||||
|   * switch from osmo_sock_set_dscp() to OSMO_SOCK_F_DSCP() | ||||
|   * mgw: Add support for setting socket priority from VTY | ||||
|   * manual: Include QoS chapter and add osmo-mgw specific example | ||||
|  | ||||
|   [ Keith ] | ||||
|   * Add vty command 'show mgcp active' | ||||
|   * Log some useful messages at ERROR/INFO instead of DEBUG | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * send DLCX only once | ||||
|   * tweak termination DLCX log msg | ||||
|   * add osmo_mgcpc_ep_ci_get_remote_rtp_info() | ||||
|   * check_rtp_destin(): clarify log msg | ||||
|   * mgcp_client_fsm delete: set mgcp_client as ctx, not NULL | ||||
|   * mgcp_client_endpoint_fsm: on term, still let conns wait for DLCX OK | ||||
|   * mgcp_client_fsm: add missing log_subsys | ||||
|   * mgcp_client: add logging on received MGCP messages | ||||
|  | ||||
|   [ Philipp Maier ] | ||||
|   * mgcp_network: fix implicit address loopback | ||||
|   * mgcp_common, mgcp_udp_send: make parameter buf const | ||||
|   * mgcp_network: refactor MGCP_DUMMY_LOAD | ||||
|   * mgcp_client: drop nunnecessary else statement | ||||
|   * mgcp_client: fix error handling in mgcp message generation | ||||
|   * mgcp_protocol: fix loop that sends dummy RTP packets | ||||
|   * mgw_main: fix loop that resets all endpoints | ||||
|   * mgcp_trunk: drop ws line | ||||
|   * mgcp_ratectr: drop ws line | ||||
|   * mgcp_ratectr: fix sourcecode formatting | ||||
|   * mgcp_trunk: add value string for trunk type. | ||||
|   * mgcp_ratectr: refactor rate counter and set group name | ||||
|   * mgcp_protocol: forward declare mgcp_endpoint | ||||
|   * mgcp_endp: make wildcarded detection separate | ||||
|   * mgcp_protocol: refactor request handler array | ||||
|   * mgcp_trunk: use unsigned int instead of int as trunk_nr | ||||
|   * mgcp_protocol: refactor MGCP request handling | ||||
|   * mgcp_ratectr: add stats items to monitor trunk usage | ||||
|   * mgcp_msg: add trunk parameter to mgcp_check_param for logging | ||||
|   * mgcp_protocol: refactor function create_response_with_sdp | ||||
|   * mgcp_protocol: add support for wildcarded DLCX | ||||
|   * remove struct member wildcarded_req from struct mgcp_endpoint | ||||
|   * mgcp_lient: remove unsubstantial FIXME note | ||||
|   * mgcp_protocol: assert endp when it becomes mandatory | ||||
|   * mgcp_trunk: check MGW domain name earlier | ||||
|   * mgcp_ratectr: add stats items to monitor trunk usage | ||||
|   * mgcp_client: fix typo Initalize -> Initialize | ||||
|   * mgcp_protocol: get rid of policy_cb and change_cb | ||||
|   * mgcp_ratectr: do not set talloc destructor on library allocated item | ||||
|   * mgcp_client: allow to reset endpoints on startup | ||||
|   * mgcp_client_vty: remove unnecessary checks | ||||
|   * mgcp_client: do not print (null) when address is ANY | ||||
|   * mgcp_client: refactor function init_socket | ||||
|   * libosmo-mgcp-client: extend the mgcp_client for MGW pooling | ||||
|   * mgcp_client_vty: add missing docstrings | ||||
|   * mgcp_client_vty: add OSMO_ASSERT on pool parameter | ||||
|   * mgcp_client_vty: fix docstrings for mgw-pool | ||||
|   * mgcp_client_vty: cosmetic: doc string should terminated with \n | ||||
|   * mgcp_client_vty: fixing docstring | ||||
|   * mgcp_ratectr: remove unusued rate counters | ||||
|   * mgcp_client: fix typo in doxygen comment | ||||
|   * mgcp_client: add MGW name as logging context | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * Use new stat item/ctr getter APIs | ||||
|   * constify arg in addr_is_any() | ||||
|   * Define patch_ssrc as bool type | ||||
|   * constify some function arg pointers | ||||
|   * mgcp_network.c: Reorder some functions in file | ||||
|   * mgcp_send_dummy: Check RTP destination is available before attempt tx | ||||
|   * mgw: rx CRCX: Avoid sending dummy rtp if remote address not provided | ||||
|   * Fail rx MDCX sendrecv with invalid remote addr | ||||
|   * Take into account Marker bit when patching RTP stream | ||||
|   * Use DLMGCP instead of DLGLOBAL in log lines | ||||
|   * Fix attribute parsing on gcc 11.1.0 | ||||
|  | ||||
|   [ neels ] | ||||
|   * Revert "mgcp_ratectr: add stats items to monitor trunk usage" | ||||
|  | ||||
|   [ Daniel Willmann ] | ||||
|   * contrib/jenkins: Use ASAN for osmo-mgw | ||||
|  | ||||
|   [ Oliver Smith ] | ||||
|   * mgcp_client_vty: add missing NO_STR | ||||
|   * Revert "Turn some compiler warnings into errors" | ||||
|  | ||||
|   [ Eric ] | ||||
|   * add vscode stuff to gitignore | ||||
|   * configure.ac: fix maybe-uninitialized for clang | ||||
|   * fix missing includes and forward declarations | ||||
|   * rename do_retransmission | ||||
|   * mgcp_sdp: fix potential leak | ||||
|   * stats: make sanitizers happy | ||||
|   * libosmo-mgcp: cleanup audio codex alloc | ||||
|   * libosmo-mgcp: atomic rate counter group indexes | ||||
|   * libosmo-mgcp: do not use the default msgb talloc context | ||||
|   * globally lock the portrange when trying to grab a port to prep for multithreading | ||||
|   * embed strings into structs | ||||
|   * adjust talloc context | ||||
|   * endp: do not cache cfg pointer | ||||
|   * add modified .clang-format | ||||
|   * rename strip_epname and find_specific_endpoint and make them available | ||||
|   * fix mgcp_conn_free_all | ||||
|   * fix up some docstrings that lost their dot | ||||
|  | ||||
|   [ Vadim Yanitskiy ] | ||||
|   * libosmo-mgcp: use OSMO_STRLCPY_ARRAY in mgcp_codec_add() | ||||
|  | ||||
|  -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 16 Nov 2021 16:59:21 +0100 | ||||
|  | ||||
| osmo-mgw (1.8.1) unstable; urgency=medium | ||||
|  | ||||
|   * attempt to fix RPM spec file after recent soversion bump | ||||
|  | ||||
|  -- Harald Welte <laforge@osmocom.org>  Wed, 24 Feb 2021 10:56:17 +0100 | ||||
|  | ||||
| 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 ] | ||||
|   | ||||
							
								
								
									
										13
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -1,14 +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, | ||||
|                osmo-gsm-manuals-dev | ||||
|                libosmocore-dev (>= 1.6.0), | ||||
|                libosmo-netif-dev (>= 1.1.0), | ||||
|                libosmo-abis-dev (>= 1.2.0), | ||||
|                osmo-gsm-manuals-dev (>= 1.2.0) | ||||
| Standards-Version: 3.9.8 | ||||
| Vcs-Git: git://git.osmocom.org/osmo-mgw.git | ||||
| Vcs-Browser: https://git.osmocom.org/osmo-mgw/ | ||||
| @@ -20,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-client6 | ||||
| Package: libosmo-mgcp-client9 | ||||
| Section: libs | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| @@ -32,7 +33,7 @@ Package: libosmo-mgcp-client-dev | ||||
| Section: libdevel | ||||
| Architecture: any | ||||
| Multi-Arch: same | ||||
| Depends: libosmo-mgcp-client6 (= ${binary:Version}), ${misc:Depends} | ||||
| Depends: libosmo-mgcp-client9 (= ${binary:Version}), ${misc:Depends} | ||||
| Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities | ||||
|  | ||||
| Package: osmo-mgw-doc | ||||
|   | ||||
							
								
								
									
										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 | ||||
|   | ||||
							
								
								
									
										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-dscp 46 | ||||
|   bind port 2427 | ||||
|   sdp audio payload number 98 | ||||
|   sdp audio payload name GSM | ||||
|   number endpoints 512 | ||||
|   loop 0 | ||||
|   force-realloc 1 | ||||
|   rtcp-omit | ||||
|   rtp-patch ssrc | ||||
|   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 | ||||
|   rtp ip-dscp 46 | ||||
|   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 | ||||
|   | ||||
| @@ -13,6 +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,77 @@ 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. | ||||
|  | ||||
| NOTE: The endpoint name that is used on MGCP level will include the trunk number, | ||||
| not the E1 line number. For simplicity (and compatibility with OsmoBSC) it is | ||||
| recommended to use equal numbers for trunk and E1 line. However, if required any | ||||
| E1 line can be mapped flexible on any trunk as long as the mapping is bijective. | ||||
|  | ||||
| .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-dscp 46 | ||||
|  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. | ||||
|   | ||||
							
								
								
									
										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) | ||||
| @@ -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-e1]] | ||||
| .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 | ||||
|  | ||||
| @@ -92,8 +107,8 @@ We are planning to add endpoint types for: | ||||
|  | ||||
| You can find the OsmoMGW issue tracker and wiki online at | ||||
|  | ||||
| - https://osmocom.org/projects/osmomgw | ||||
| - https://osmocom.org/projects/osmomgw/wiki | ||||
| - https://osmocom.org/projects/osmo-mgw | ||||
| - https://osmocom.org/projects/osmo-mgw/wiki | ||||
|  | ||||
| RFC 3435 for MGCP is located at | ||||
|  | ||||
|   | ||||
							
								
								
									
										42
									
								
								doc/manuals/chapters/qos-example.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								doc/manuals/chapters/qos-example.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| ==== Full example of QoS for osmo-mgw | ||||
|  | ||||
| In the below example we will show the full set of configuration required | ||||
| for both DSCP and PCP differentiation of RTP traffic by osmo-mgw. | ||||
|  | ||||
| What we want to achieve in this example is the following configuration: | ||||
|  | ||||
| .DSCP and PCP assignments for osmo-mgw Abis downlink traffic in this example | ||||
| [options="header",width="30%",cols="2,1,1"] | ||||
| |=== | ||||
| |Traffic      |DSCP|PCP | ||||
| |RTP    |  46|  6 | ||||
| |=== | ||||
|  | ||||
| . configure the osmo-mgw program to set the DSCP value | ||||
| . configure an egrees QoS map to map from priority to PCP | ||||
|  | ||||
| .Example Step 1: add related VTY configuration to `osmo-mgw.cfg` | ||||
| ---- | ||||
| ... | ||||
| mgcp | ||||
|  rtp ip-dscp 46 | ||||
|  rtp socket-priority 6 | ||||
| ... | ||||
| ---- | ||||
|  | ||||
| .Example Step 2: egress QoS map to map from socket priority to PCP values | ||||
| ---- | ||||
| $ sudo ip link set dev eth0.9<1> type vlan egress-qos-map 0:0 5:5 6:6 7:7 <2> | ||||
| ---- | ||||
| <1> make sure to specify your specific VLAN interface name here instead of `eth0.9`. | ||||
| <2> create a egress QoS map that maps the priority value 1:1 to the PCP. We also include the | ||||
|     mapping 5:5 and 7:7 from the osmo-bsc example (see <<userman-osmobsc>>) here. | ||||
|  | ||||
| NOTE:: The settings of the `ip` command are volatile and only active until | ||||
| the next reboot (or the network device or VLAN is removed).  Please refer to | ||||
| the documentation of your specific Linux distribution in order to find out how | ||||
| to make such settings persistent by means of an `ifup` hook whenever the interface | ||||
| comes up.  For CentOS/RHEL 8 this can e.g. be achieved by means of an `/sbin/ifup-local | ||||
| script` (when using `network-scripts` and not NetworkManager).  For Debian or Ubuntu, | ||||
| this typically involves adding `up` lines to `/etc/network/interfaces` or a `/etc/network/if-up.d` | ||||
| script. | ||||
| @@ -18,12 +18,18 @@ 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::./common/chapters/qos-dscp-pcp.adoc[] | ||||
|  | ||||
| //include::{srcdir}/chapters/counters.adoc[] | ||||
|  | ||||
| include::./common/chapters/vty_cpu_sched.adoc[] | ||||
|  | ||||
| include::./common/chapters/port_numbers.adoc[] | ||||
|  | ||||
| include::./common/chapters/bibliography.adoc[] | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -6,9 +6,9 @@ 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_client_pool.h \ | ||||
| 	osmocom/mgcp/mgcp.h \ | ||||
| 	osmocom/mgcp/mgcp_common.h \ | ||||
| 	osmocom/mgcp/mgcp_internal.h \ | ||||
| 	osmocom/mgcp/osmux.h \ | ||||
| 	$(NULL) | ||||
|  | ||||
|   | ||||
| @@ -6,5 +6,12 @@ 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 \ | ||||
| 	mgcp_iuup.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> | ||||
| @@ -33,6 +34,9 @@ | ||||
| #include <sys/types.h> | ||||
| #include <sys/socket.h> | ||||
| #include <netinet/in.h> | ||||
| #include <pthread.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 +46,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 +63,7 @@ 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_reset)(struct mgcp_trunk *cfg); | ||||
| typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone); | ||||
|  | ||||
| /** | ||||
| @@ -92,8 +93,10 @@ typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp, | ||||
|  * This holds information on how to allocate ports | ||||
|  */ | ||||
| struct mgcp_port_range { | ||||
| 	pthread_mutex_t lock; | ||||
| 	/* addr or NULL to fall-back to default */ | ||||
| 	char *bind_addr; | ||||
| 	char bind_addr_v4[INET6_ADDRSTRLEN]; | ||||
| 	char bind_addr_v6[INET6_ADDRSTRLEN]; | ||||
|  | ||||
| 	/* dynamically allocated */ | ||||
| 	int range_start; | ||||
| @@ -119,104 +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; | ||||
| 	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; | ||||
| 	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, | ||||
| @@ -224,9 +129,9 @@ enum mgcp_role { | ||||
|  | ||||
| struct mgcp_config { | ||||
| 	int source_port; | ||||
| 	char *local_ip; | ||||
| 	char *source_addr; | ||||
| 	char *call_agent_addr; | ||||
| 	char local_ip[INET6_ADDRSTRLEN]; | ||||
| 	char source_addr[INET6_ADDRSTRLEN]; | ||||
| 	char call_agent_addr[INET6_ADDRSTRLEN]; | ||||
|  | ||||
| 	/* RTP processing */ | ||||
| 	mgcp_processing rtp_processing_cb; | ||||
| @@ -238,20 +143,15 @@ struct mgcp_config { | ||||
|  | ||||
| 	struct mgcp_port_range net_ports; | ||||
| 	int endp_dscp; | ||||
| 	int endp_priority; | ||||
|  | ||||
| 	int force_ptime; | ||||
|  | ||||
| 	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; | ||||
| @@ -259,7 +159,7 @@ struct mgcp_config { | ||||
| 	/* osmux translator: 0 means disabled, 1 means enabled */ | ||||
| 	int osmux; | ||||
| 	/* addr to bind the server to */ | ||||
| 	char *osmux_addr; | ||||
| 	char osmux_addr[INET6_ADDRSTRLEN]; | ||||
| 	/* The BSC-NAT may ask for enabling osmux on demand. This tells us if | ||||
| 	 * the osmux socket is already initialized. | ||||
| 	 */ | ||||
| @@ -279,6 +179,13 @@ struct mgcp_config { | ||||
|  | ||||
| 	/* 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 */ | ||||
| @@ -286,8 +193,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 | ||||
| @@ -295,9 +201,10 @@ void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval); | ||||
| struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg); | ||||
|  | ||||
|  | ||||
| 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_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp, | ||||
| 		     uint8_t prio); | ||||
| int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, const char *buf, int len); | ||||
|   | ||||
| @@ -1,7 +1,20 @@ | ||||
| #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, 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); | ||||
| bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec); | ||||
|   | ||||
| @@ -100,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,124 @@ | ||||
|  | ||||
| #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 <osmocom/gsm/iuup.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, | ||||
| 	MGCP_RTP_IUUP, | ||||
| }; | ||||
|  | ||||
| /*! 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 { | ||||
| 		struct osmo_iuup_instance *iui; | ||||
| 		bool active_init; /* true: Send IuUP Init */ | ||||
| 		int rfci_idx_no_data; /* Index for RFCI NO_DATA (-1 if not available) */ | ||||
| 		bool configured; | ||||
| 		struct osmo_iuup_rnl_prim *init_ind; | ||||
| 	} iuup; | ||||
|  | ||||
| 	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 +181,17 @@ 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; | ||||
| } | ||||
|  | ||||
| /* Was conn configured to handle Osmux? */ | ||||
| static inline bool mgcp_conn_rtp_is_iuup(const struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	return conn->type == MGCP_RTP_IUUP; | ||||
| } | ||||
|  | ||||
| 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 +202,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,36 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| struct sockaddr_in; | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/gsm/i460_mux.h> | ||||
| #include <osmocom/mgcp/mgcp_protocol.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 +76,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 +85,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; | ||||
|  | ||||
| @@ -72,11 +97,8 @@ struct mgcp_endpoint { | ||||
| 	/*! List of struct mgcp_conn, of the connections active on this endpoint */ | ||||
| 	struct llist_head conns; | ||||
|  | ||||
| 	/*! Backpointer to the MGW configuration */ | ||||
| 	struct mgcp_config *cfg; | ||||
|  | ||||
| 	/*! Backpointer to the Trunk specific configuration */ | ||||
| 	struct mgcp_trunk_config *tcfg; | ||||
| 	/*! Backpointer to the trunk this endpoint belongs to */ | ||||
| 	struct mgcp_trunk *trunk; | ||||
|  | ||||
| 	/*! Endpoint properties (see above) */ | ||||
| 	const struct mgcp_endpoint_type *type; | ||||
| @@ -87,15 +109,34 @@ struct mgcp_endpoint { | ||||
| 	/*! Last MGCP response (in case re-transmission is required) */ | ||||
| 	char *last_response; | ||||
|  | ||||
| 	/*! Memorize if this endpoint was choosen by the MGW (wildcarded, true) | ||||
| 	 *   or if the user has choosen the particular endpoint explicitly. */ | ||||
| 	bool wildcarded_req; | ||||
|  | ||||
| 	/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */ | ||||
| 	uint32_t x_osmo_ign; | ||||
|  | ||||
| 	/* 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); | ||||
| bool mgcp_endp_is_wildcarded(const char *epname); | ||||
| struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname, | ||||
| 					      const struct mgcp_trunk *trunk); | ||||
| struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname, | ||||
| 					struct mgcp_config *cfg); | ||||
| bool mgcp_endp_avail(struct mgcp_endpoint *endp); | ||||
| void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn); | ||||
| void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn); | ||||
| void mgcp_endp_strip_name(char *epname_stripped, const char *epname, | ||||
| 			 const struct mgcp_trunk *trunk); | ||||
| struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname, | ||||
| 			const struct mgcp_trunk *trunk); | ||||
|   | ||||
| @@ -1,352 +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; | ||||
|  | ||||
| 	bool param_present; | ||||
| 	struct mgcp_codec_param param; | ||||
| }; | ||||
|  | ||||
| /* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */ | ||||
| struct mgcp_rtp_end { | ||||
| 	/* local IP address of the RTP socket */ | ||||
| 	struct in_addr addr; | ||||
|  | ||||
| 	/* in network byte order */ | ||||
| 	int rtp_port, rtcp_port; | ||||
|  | ||||
| 	/* currently selected audio codec */ | ||||
| 	struct mgcp_rtp_codec *codec; | ||||
|  | ||||
| 	/* array with assigned audio codecs to choose from (SDP) */ | ||||
| 	struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS]; | ||||
|  | ||||
| 	/* number of assigned audio codecs (SDP) */ | ||||
| 	unsigned int codecs_assigned; | ||||
|  | ||||
| 	/* per endpoint data */ | ||||
| 	int  frames_per_packet; | ||||
| 	uint32_t packet_duration_ms; | ||||
| 	int maximum_packet_time; /* -1: not set */ | ||||
| 	char *fmtp_extra; | ||||
| 	/* are we transmitting packets (1) or dropping (0) outbound packets */ | ||||
| 	int output_enabled; | ||||
| 	/* FIXME: This parameter can be set + printed, but is nowhere used! */ | ||||
| 	int force_output_ptime; | ||||
|  | ||||
| 	/* RTP patching */ | ||||
| 	int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ | ||||
| 	/* should we perform align_rtp_timestamp_offset() (1) or not (0) */ | ||||
| 	int force_aligned_timing; | ||||
| 	bool rfc5993_hr_convert; | ||||
|  | ||||
| 	/* Each end has a separate socket for RTP and RTCP */ | ||||
| 	struct osmo_fd rtp; | ||||
| 	struct osmo_fd rtcp; | ||||
|  | ||||
| 	/* local UDP port number of the RTP socket; RTCP is +1 */ | ||||
| 	int local_port; | ||||
| }; | ||||
|  | ||||
| struct mgcp_rtp_tap { | ||||
| 	/* is this tap active (1) or not (0) */ | ||||
| 	int enabled; | ||||
| 	/* IP/port to which we're forwarding the tapped data */ | ||||
| 	struct sockaddr_in forward; | ||||
| }; | ||||
|  | ||||
| struct mgcp_lco { | ||||
| 	char *string; | ||||
| 	char *codec; | ||||
| 	int pkt_period_min; /* time in ms */ | ||||
| 	int pkt_period_max; /* time in ms */ | ||||
| }; | ||||
|  | ||||
| /* Specific rtp connection type (see struct mgcp_conn_rtp) */ | ||||
| enum mgcp_conn_rtp_type { | ||||
| 	MGCP_RTP_DEFAULT	= 0, | ||||
| 	MGCP_OSMUX_BSC, | ||||
| 	MGCP_OSMUX_BSC_NAT, | ||||
| }; | ||||
|  | ||||
| #include <osmocom/mgcp/osmux.h> | ||||
| struct mgcp_conn; | ||||
|  | ||||
| /* MGCP connection (RTP) */ | ||||
| struct mgcp_conn_rtp { | ||||
|  | ||||
| 	/* Backpointer to conn struct */ | ||||
| 	struct mgcp_conn *conn; | ||||
|  | ||||
| 	/* Specific connection type */ | ||||
| 	enum mgcp_conn_rtp_type type; | ||||
|  | ||||
| 	/* Port status */ | ||||
| 	struct mgcp_rtp_end end; | ||||
|  | ||||
| 	/* Sequence bits */ | ||||
| 	struct mgcp_rtp_state state; | ||||
|  | ||||
| 	/* taps for the rtp connection; one per direction */ | ||||
| 	struct mgcp_rtp_tap tap_in; | ||||
| 	struct mgcp_rtp_tap tap_out; | ||||
|  | ||||
| 	/* Osmux states (optional) */ | ||||
| 	struct { | ||||
| 		/* Osmux state: disabled, activating, active */ | ||||
| 		enum osmux_state state; | ||||
| 		/* Is cid holding valid data? is it allocated from pool? */ | ||||
| 		bool cid_allocated; | ||||
| 		/* Allocated Osmux circuit ID for this conn */ | ||||
| 		uint8_t cid; | ||||
| 		/* handle to batch messages */ | ||||
| 		struct osmux_in_handle *in; | ||||
| 		/* handle to unbatch messages */ | ||||
| 		struct osmux_out_handle out; | ||||
| 		/* statistics */ | ||||
| 		struct { | ||||
| 			uint32_t chunks; | ||||
| 			uint32_t octets; | ||||
| 		} stats; | ||||
| 	} osmux; | ||||
|  | ||||
| 	struct rate_ctr_group *rate_ctr_group; | ||||
| }; | ||||
|  | ||||
| /*! Connection type, specifies which member of the union "u" in mgcp_conn | ||||
|  *  contains a useful connection description (currently only RTP) */ | ||||
| enum mgcp_conn_type { | ||||
| 	MGCP_CONN_TYPE_RTP, | ||||
| }; | ||||
|  | ||||
| /*! MGCP connection (untyped) */ | ||||
| struct mgcp_conn { | ||||
| 	/*! list head */ | ||||
| 	struct llist_head entry; | ||||
|  | ||||
| 	/*! Backpointer to the endpoint where the conn belongs to */ | ||||
| 	struct mgcp_endpoint *endp; | ||||
|  | ||||
| 	/*! type of the connection (union) */ | ||||
| 	enum mgcp_conn_type type; | ||||
|  | ||||
| 	/*! mode of the connection */ | ||||
| 	enum mgcp_connection_mode mode; | ||||
|  | ||||
| 	/*! copy of the mode to restore the original setting (VTY) */ | ||||
| 	enum mgcp_connection_mode mode_orig; | ||||
|  | ||||
| 	/*! connection id to identify the connection */ | ||||
| 	char id[MGCP_CONN_ID_MAXLEN]; | ||||
|  | ||||
| 	/*! human readable name (vty, logging) */ | ||||
| 	char name[256]; | ||||
|  | ||||
| 	/*! activity tracker (for cleaning up inactive connections) */ | ||||
| 	struct osmo_timer_list watchdog; | ||||
|  | ||||
| 	/*! union with connection description */ | ||||
| 	union { | ||||
| 		struct mgcp_conn_rtp rtp; | ||||
| 	} u; | ||||
|  | ||||
| 	/*! pointer to optional private data */ | ||||
| 	void *priv; | ||||
| }; | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
|  | ||||
| struct mgcp_endpoint_type; | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Internal structure while parsing a request | ||||
|  */ | ||||
| struct mgcp_parse_data { | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	char *trans; | ||||
| 	char *save; | ||||
| }; | ||||
|  | ||||
| int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, | ||||
| 	      char *buf, int rc, struct mgcp_conn_rtp *conn_src, | ||||
| 	      struct mgcp_conn_rtp *conn_dst); | ||||
| int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn); | ||||
| int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf, | ||||
| 				unsigned int buf_size, struct mgcp_conn *conn); | ||||
| void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn); | ||||
| int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port, | ||||
| 			   struct mgcp_conn_rtp *conn); | ||||
| void mgcp_free_rtp_port(struct mgcp_rtp_end *end); | ||||
|  | ||||
| /* For transcoding we need to manage an in and an output that are connected */ | ||||
| static inline int endp_back_channel(int endpoint) | ||||
| { | ||||
| 	return endpoint + 60; | ||||
| } | ||||
|  | ||||
| struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index); | ||||
| struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index); | ||||
|  | ||||
| char *get_lco_identifier(const char *options); | ||||
| int check_local_cx_options(void *ctx, const char *options); | ||||
| void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, | ||||
| 			 struct mgcp_rtp_end *rtp); | ||||
| uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp, | ||||
| 				  struct mgcp_rtp_end *rtp); | ||||
|  | ||||
| /* payload processing default functions */ | ||||
| int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, | ||||
| 				char *data, int *len, int buf_size); | ||||
|  | ||||
| int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, | ||||
| 				      struct mgcp_conn_rtp *conn_dst, | ||||
| 				      struct mgcp_conn_rtp *conn_src); | ||||
|  | ||||
| void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, | ||||
| 					  const struct mgcp_rtp_codec **codec, | ||||
| 					  const char **fmtp_extra, | ||||
| 					  struct mgcp_conn_rtp *conn); | ||||
|  | ||||
| /* internal RTP Annex A counting */ | ||||
| void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, | ||||
| 			const uint16_t seq, const int32_t transit, | ||||
| 			const uint32_t ssrc); | ||||
|  | ||||
| int mgcp_set_ip_tos(int fd, int tos); | ||||
|  | ||||
| /* Was conn configured to handle Osmux? */ | ||||
| static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) { | ||||
| 	return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT; | ||||
| } | ||||
|  | ||||
| enum { | ||||
| 	MGCP_DEST_NET = 0, | ||||
| 	MGCP_DEST_BTS, | ||||
| }; | ||||
|  | ||||
|  | ||||
| #define MGCP_DUMMY_LOAD 0x23 | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * SDP related information | ||||
|  */ | ||||
| /* Assume audio frame length of 20ms */ | ||||
| #define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20 | ||||
| #define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000 | ||||
| #define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20 | ||||
| #define DEFAULT_RTP_AUDIO_DEFAULT_RATE  8000 | ||||
| #define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1 | ||||
|  | ||||
| #define PTYPE_UNDEFINED (-1) | ||||
|  | ||||
| void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn); | ||||
| void mgcp_conn_watchdog_kick(struct mgcp_conn *conn); | ||||
|  | ||||
| #define LOGPENDP(endp, cat, level, fmt, args...) \ | ||||
| LOGP(cat, level, "endpoint:0x%x " fmt, \ | ||||
|      endp ? ENDPOINT_NUMBER(endp) : -1, \ | ||||
|      ## args) | ||||
|  | ||||
| #define LOGPCONN(conn, cat, level, fmt, args...) \ | ||||
| LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \ | ||||
|          (conn)->id, \ | ||||
|          ## args) | ||||
							
								
								
									
										33
									
								
								include/osmocom/mgcp/mgcp_iuup.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								include/osmocom/mgcp/mgcp_iuup.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| /* IuUP connection functionalitites */ | ||||
|  | ||||
| /* | ||||
|  * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Pau Espin Pedrol | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/core/msgb.h> | ||||
|  | ||||
| struct mgcp_conn_rtp; | ||||
|  | ||||
| int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp); | ||||
| void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp); | ||||
| int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg); | ||||
| int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg); | ||||
| int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp); | ||||
| @@ -25,10 +25,12 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
|  | ||||
| struct mgcp_conn; | ||||
| struct mgcp_parse_data; | ||||
| struct mgcp_endpoint; | ||||
| struct mgcp_trunk; | ||||
|  | ||||
| void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble); | ||||
|  | ||||
| @@ -39,7 +41,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data); | ||||
|  | ||||
| int mgcp_parse_osmux_cid(const char *line); | ||||
|  | ||||
| int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line); | ||||
| bool mgcp_check_param(const struct mgcp_endpoint *endp, struct mgcp_trunk *trunk, const char *line); | ||||
|  | ||||
| int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid); | ||||
|  | ||||
|   | ||||
							
								
								
									
										182
									
								
								include/osmocom/mgcp/mgcp_network.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								include/osmocom/mgcp/mgcp_network.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,182 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <inttypes.h> | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #include <osmocom/core/socket.h> | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp.h> | ||||
|  | ||||
| /* The following constant defines an RTP dummy payload that is used for | ||||
|  * "UDP Hole Punching" (NAT) */ | ||||
| static const char rtp_dummy_payload[] = { 0x23 }; | ||||
|  | ||||
| /* Check if the data in a given message buffer matches the rtp dummy payload | ||||
|  * defined above */ | ||||
| #define mgcp_is_rtp_dummy_payload(msg) \ | ||||
| 	(msgb_length(msg) == sizeof(rtp_dummy_payload) && \ | ||||
| 	memcmp(msgb_data(msg), rtp_dummy_payload, sizeof(rtp_dummy_payload)) == 0) | ||||
|  | ||||
| #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? */ | ||||
| 		bool 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; | ||||
| }; | ||||
|  | ||||
| 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[64]; | ||||
| 	char subtype_name[64]; | ||||
|  | ||||
| 	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 (true) or dropping (false) outbound packets */ | ||||
| 	bool 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]; | ||||
| }; | ||||
|  | ||||
| bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end); | ||||
|  | ||||
| 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(const struct mgcp_endpoint *endp, | ||||
| 			  struct mgcp_rtp_state *state, | ||||
| 			  struct mgcp_rtp_end *rtp_end, | ||||
| 			  struct osmo_sockaddr *addr, struct msgb *msg); | ||||
| void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn); | ||||
|  | ||||
| /* 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(const struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, | ||||
| 			const uint16_t seq, const int32_t transit, | ||||
| 			const uint32_t ssrc, const bool marker_bit); | ||||
|  | ||||
| void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, | ||||
| 				 int id, int inc); | ||||
| void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg); | ||||
| uint32_t mgcp_get_current_ts(unsigned codec_rate); | ||||
							
								
								
									
										30
									
								
								include/osmocom/mgcp/mgcp_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								include/osmocom/mgcp/mgcp_protocol.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| #pragma once | ||||
|  | ||||
| /* Internal structure while parsing a request */ | ||||
| struct mgcp_parse_data { | ||||
| 	struct mgcp_config *cfg; | ||||
| 	char *epname; | ||||
| 	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 *mgcp_debug_get_last_endpoint_name(void); | ||||
|  | ||||
| char *get_lco_identifier(const char *options); | ||||
| int check_local_cx_options(void *ctx, const char *options); | ||||
|  | ||||
| struct mgcp_rtp_end; | ||||
| struct mgcp_endpoint; | ||||
| void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, | ||||
| 			 struct mgcp_rtp_end *rtp); | ||||
|  | ||||
| uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp, | ||||
| 				  const struct mgcp_rtp_end *rtp); | ||||
							
								
								
									
										107
									
								
								include/osmocom/mgcp/mgcp_ratectr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								include/osmocom/mgcp/mgcp_ratectr.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| #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_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_AVAIL, | ||||
| }; | ||||
|  | ||||
| /* Trunk-global MCGP DLCX related rate counters */ | ||||
| enum { | ||||
| 	MGCP_DLCX_SUCCESS, | ||||
| 	MGCP_DLCX_FAIL_NO_CONN, | ||||
| 	MGCP_DLCX_FAIL_INVALID_CALLID, | ||||
| 	MGCP_DLCX_FAIL_INVALID_CONNID, | ||||
| 	MGCP_DLCX_FAIL_UNHANDLED_PARAM, | ||||
| 	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; | ||||
| }; | ||||
|  | ||||
| struct mgcp_config; | ||||
| struct mgcp_trunk; | ||||
|  | ||||
| int mgcp_ratectr_global_alloc(struct mgcp_config *cfg); | ||||
| void mgcp_ratectr_global_free(struct mgcp_config *cfg); | ||||
| int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk); | ||||
| void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk); | ||||
|  | ||||
| /* Trunk-global common stat items */ | ||||
| enum { | ||||
| 	TRUNK_STAT_ENDPOINTS_TOTAL, | ||||
| 	TRUNK_STAT_ENDPOINTS_USED, | ||||
| }; | ||||
|  | ||||
| struct mgcp_stat_trunk { | ||||
| 	/* Stat item group which contains general status values of the trunk. */ | ||||
| 	struct osmo_stat_item_group *common; | ||||
| }; | ||||
|  | ||||
| int mgcp_stat_trunk_alloc(struct mgcp_trunk *trunk); | ||||
| void mgcp_stat_trunk_free(struct mgcp_trunk *trunk); | ||||
| @@ -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); | ||||
|   | ||||
							
								
								
									
										52
									
								
								include/osmocom/mgcp/mgcp_threads_queue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								include/osmocom/mgcp/mgcp_threads_queue.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| /* | ||||
|  * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Eric Wild | ||||
|  * | ||||
|  * 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 <stdatomic.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| struct spsc { | ||||
| 	atomic_uint readptr; | ||||
| 	atomic_uint writeptr; | ||||
|  | ||||
| 	int efd_r, efd_w; /* eventfds used to block/notify readers/writers */ | ||||
|  | ||||
| 	int count; | ||||
| 	int size_per_buf; | ||||
|  | ||||
| 	void *buf; /* buffer size count*size_per_buf */ | ||||
| 	uintptr_t data[0]; /* count sized array of pointers to size_per_buf chunks in buf array*/ | ||||
| }; | ||||
|  | ||||
| struct qchan { | ||||
| 	struct spsc *a; | ||||
| 	struct spsc *b; | ||||
| }; | ||||
|  | ||||
| bool spsc_push(struct spsc *q, void *elem); | ||||
| bool spsc_pop(struct spsc *q, void *elem); | ||||
| ssize_t spsc_prep_pop(struct spsc *q); | ||||
| int spsc_get_a_rdfd(struct qchan *q); | ||||
|  | ||||
| struct qchan spsc_chan_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf); | ||||
| struct qchan spsc_chan_init_ex(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr_a, | ||||
| 			       bool blockw_a, bool blockr_b, bool blockw_b); | ||||
| void spsc_chan_close(struct qchan *q); | ||||
							
								
								
									
										88
									
								
								include/osmocom/mgcp/mgcp_trunk.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								include/osmocom/mgcp/mgcp_trunk.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/gsm/i460_mux.h> | ||||
| #include <osmocom/abis/e1_input.h> | ||||
| #include <osmocom/mgcp/mgcp_ratectr.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, | ||||
| }; | ||||
|  | ||||
| extern const struct value_string mgcp_trunk_type_strs[]; | ||||
| static inline const char *mgcp_trunk_type_strs_str(enum mgcp_trunk_type val) | ||||
| { return get_value_string(mgcp_trunk_type_strs, val); } | ||||
|  | ||||
| struct mgcp_trunk { | ||||
| 	struct llist_head entry; | ||||
|  | ||||
| 	struct mgcp_config *cfg; | ||||
|  | ||||
| 	unsigned int trunk_nr; | ||||
| 	enum mgcp_trunk_type trunk_type; | ||||
|  | ||||
| 	char *audio_fmtp_extra; | ||||
| 	int audio_send_ptime; | ||||
| 	int audio_send_name; | ||||
|  | ||||
| 	int no_audio_transcoding; | ||||
|  | ||||
| 	int omit_rtcp; | ||||
| 	int keepalive_interval; | ||||
|  | ||||
| 	/* 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; | ||||
|  | ||||
| 	/* rate counters and stat items to measure the trunks overall performance and health */ | ||||
| 	struct mgcp_ratectr_trunk ratectr; | ||||
| 	struct mgcp_stat_trunk stats; | ||||
|  | ||||
| 	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, unsigned 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, unsigned int nr); | ||||
| struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname); | ||||
| int e1_trunk_nr_from_epname(unsigned int *trunk_nr, 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,7 +14,7 @@ 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); | ||||
| 		      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); | ||||
|   | ||||
| @@ -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, | ||||
| }; | ||||
|   | ||||
| @@ -4,6 +4,7 @@ BUILT_SOURCES = \ | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	mgcp_client_internal.h \ | ||||
| 	mgcp_client_pool_internal.h \ | ||||
| 	$(NULL) | ||||
|  | ||||
| mgcp_common.h: $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h | ||||
|   | ||||
| @@ -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 | ||||
| @@ -26,6 +26,15 @@ struct mgcp_client_conf { | ||||
| 	/* By default, we are always addressing the MGW with e.g. 'rtpbridge/123@mgw'. | ||||
| 	 * If this is nonempty, the contained name will be used instead of 'mgw'. */ | ||||
| 	char endpoint_domain_name[MGCP_ENDPOINT_MAXLEN]; | ||||
|  | ||||
| 	/* The user may configure certain endpoint names that are reset via DLCX | ||||
| 	 * on startup. Usually this will be one wildcarded endpoint e.g. | ||||
| 	 * 'rtpbridge/(wildcard)' or a number of specific E1 like e.g. | ||||
| 	 * 'ds/e1-0/s-3/su16-4' */ | ||||
| 	struct llist_head reset_epnames; | ||||
|  | ||||
| 	/* human readable name / description */ | ||||
| 	char *description; | ||||
| }; | ||||
|  | ||||
| typedef unsigned int mgcp_trans_id_t; | ||||
| @@ -40,6 +49,7 @@ enum mgcp_codecs { | ||||
| 	CODEC_GSMHR_8000_1 = 111, | ||||
| 	CODEC_AMR_8000_1 = 112, | ||||
| 	CODEC_AMRWB_16000_1 = 113, | ||||
| 	CODEC_IUFP = 96, | ||||
| }; | ||||
| /* Note: when new codec types are added, the corresponding value strings | ||||
|  * in mgcp_client.c (codec_table) must be updated as well. Enumerations | ||||
| @@ -75,7 +85,7 @@ 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; | ||||
| @@ -130,13 +140,17 @@ struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp); | ||||
| struct mgcp_client *mgcp_client_init(void *ctx, | ||||
| 				     struct mgcp_client_conf *conf); | ||||
| int mgcp_client_connect(struct mgcp_client *mgcp); | ||||
| int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports); | ||||
| void mgcp_client_disconnect(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. */ | ||||
| @@ -163,3 +177,5 @@ 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); | ||||
|  | ||||
| const char *mgcp_client_name(const struct mgcp_client *mgcp); | ||||
|   | ||||
| @@ -23,6 +23,8 @@ const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgc | ||||
| 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); | ||||
|  | ||||
| const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_remote_rtp_info(const struct osmo_mgcpc_ep_ci *ci); | ||||
|  | ||||
| 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, | ||||
| @@ -45,6 +47,7 @@ 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); | ||||
| struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep); | ||||
|  | ||||
| extern const struct value_string osmo_mgcp_verb_names[]; | ||||
| static inline const char *osmo_mgcp_verb_name(enum mgcp_verb 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; | ||||
| @@ -69,5 +69,6 @@ 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); | ||||
| struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi); | ||||
|  | ||||
| const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info); | ||||
|   | ||||
| @@ -4,13 +4,19 @@ | ||||
|  | ||||
| #define MSGB_CB_MGCP_TRANS_ID 0 | ||||
|  | ||||
| /* Struct that holds one endpoint name */ | ||||
| struct reset_ep { | ||||
| 	struct llist_head list; | ||||
| 	char name[MGCP_ENDPOINT_MAXLEN]; | ||||
| }; | ||||
|  | ||||
| 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; | ||||
| 	struct llist_head inuse_endpoints; | ||||
| 	struct mgcp_client_pool *pool; | ||||
| }; | ||||
|  | ||||
| struct mgcp_inuse_endpoint { | ||||
|   | ||||
							
								
								
									
										11
									
								
								include/osmocom/mgcp_client/mgcp_client_pool.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								include/osmocom/mgcp_client/mgcp_client_pool.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #pragma once | ||||
|  | ||||
| struct mgcp_client; | ||||
| struct mgcp_client_pool; | ||||
|  | ||||
| struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx); | ||||
| void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool); | ||||
| unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool); | ||||
| void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client); | ||||
| struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool); | ||||
| void mgcp_client_pool_put(struct mgcp_client *mgcp_client); | ||||
							
								
								
									
										45
									
								
								include/osmocom/mgcp_client/mgcp_client_pool_internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								include/osmocom/mgcp_client/mgcp_client_pool_internal.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| #pragma once | ||||
|  | ||||
| /* Struct to handle a member of a pool of MGWs. */ | ||||
| struct mgcp_client_pool_member { | ||||
| 	struct llist_head list; | ||||
|  | ||||
| 	/* Reference number assinged by VTY. This number is used to manage the pool from the VTY and to identify it in | ||||
| 	 * the log. */ | ||||
| 	unsigned int nr; | ||||
|  | ||||
| 	/* MGCP client configuration, this is not the running configuration, when mgcp_client_init() is executed, a | ||||
| 	 * copy of this config is created. */ | ||||
| 	struct mgcp_client_conf conf; | ||||
|  | ||||
| 	/* MGCP client descriptor, will be automatically allocated when mgcp_client_pool_connect() is called. (the MGCP | ||||
| 	 * client is connected when this pointer is populated) */ | ||||
| 	struct mgcp_client *client; | ||||
|  | ||||
| 	/* A pool member may be set as 'blocked' from the VTY, this means that the pool member may still work and serve | ||||
| 	 * ongoing calls, but it won't be picked from the pool anymore. */ | ||||
| 	bool blocked; | ||||
|  | ||||
| 	/* Reference counter to count how often this pool member is currently picked. */ | ||||
| 	unsigned int refcount; | ||||
| }; | ||||
|  | ||||
| /* Struct to handle a pool of MGWs. (Use _pool functions) */ | ||||
| struct mgcp_client_pool { | ||||
|  | ||||
| 	/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using | ||||
| 	 * mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */ | ||||
| 	struct mgcp_client *mgcp_client_single; | ||||
|  | ||||
| 	/* A list that manages the pool members (see above) */ | ||||
| 	struct llist_head pool; | ||||
|  | ||||
| 	/* String to use for indentation when writing the configuration file to the VTY. This field is populated by | ||||
| 	 * mgcp_client_pool_vty_init() */ | ||||
| 	char *vty_indent; | ||||
|  | ||||
| 	/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */ | ||||
| 	struct cmd_node *vty_node; | ||||
| }; | ||||
|  | ||||
| const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member); | ||||
| @@ -1,74 +0,0 @@ | ||||
| # =========================================================================== | ||||
| #   http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html | ||||
| # =========================================================================== | ||||
| # | ||||
| # SYNOPSIS | ||||
| # | ||||
| #   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) | ||||
| # | ||||
| # DESCRIPTION | ||||
| # | ||||
| #   Check whether the given FLAG works with the current language's compiler | ||||
| #   or gives an error.  (Warnings, however, are ignored) | ||||
| # | ||||
| #   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on | ||||
| #   success/failure. | ||||
| # | ||||
| #   If EXTRA-FLAGS is defined, it is added to the current language's default | ||||
| #   flags (e.g. CFLAGS) when the check is done.  The check is thus made with | ||||
| #   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to | ||||
| #   force the compiler to issue an error when a bad flag is given. | ||||
| # | ||||
| #   INPUT gives an alternative input source to AC_COMPILE_IFELSE. | ||||
| # | ||||
| #   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this | ||||
| #   macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. | ||||
| # | ||||
| # LICENSE | ||||
| # | ||||
| #   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> | ||||
| #   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> | ||||
| # | ||||
| #   This program is free software: you can redistribute it and/or modify it | ||||
| #   under the terms of the GNU General Public License as published by the | ||||
| #   Free Software Foundation, either version 3 of the License, or (at your | ||||
| #   option) any later version. | ||||
| # | ||||
| #   This program is distributed in the hope that it will be useful, but | ||||
| #   WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General | ||||
| #   Public License for more details. | ||||
| # | ||||
| #   You should have received a copy of the GNU General Public License along | ||||
| #   with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| #   As a special exception, the respective Autoconf Macro's copyright owner | ||||
| #   gives unlimited permission to copy, distribute and modify the configure | ||||
| #   scripts that are the output of Autoconf when processing the Macro. You | ||||
| #   need not follow the terms of the GNU General Public License when using | ||||
| #   or distributing such scripts, even though portions of the text of the | ||||
| #   Macro appear in them. The GNU General Public License (GPL) does govern | ||||
| #   all other use of the material that constitutes the Autoconf Macro. | ||||
| # | ||||
| #   This special exception to the GPL applies to versions of the Autoconf | ||||
| #   Macro released by the Autoconf Archive. When you make and distribute a | ||||
| #   modified version of the Autoconf Macro, you may extend this special | ||||
| #   exception to the GPL to apply to your modified version as well. | ||||
|  | ||||
| #serial 4 | ||||
|  | ||||
| AC_DEFUN([AX_CHECK_COMPILE_FLAG], | ||||
| [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF | ||||
| AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl | ||||
| AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ | ||||
|   ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS | ||||
|   _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" | ||||
|   AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], | ||||
|     [AS_VAR_SET(CACHEVAR,[yes])], | ||||
|     [AS_VAR_SET(CACHEVAR,[no])]) | ||||
|   _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) | ||||
| AS_VAR_IF(CACHEVAR,yes, | ||||
|   [m4_default([$2], :)], | ||||
|   [m4_default([$3], :)]) | ||||
| AS_VAR_POPDEF([CACHEVAR])dnl | ||||
| ])dnl AX_CHECK_COMPILE_FLAGS | ||||
| @@ -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=7:0:1 | ||||
| MGCP_CLIENT_LIBVERSION=9:0:0 | ||||
|  | ||||
| lib_LTLIBRARIES = \ | ||||
| 	libosmo-mgcp-client.la \ | ||||
| @@ -31,6 +32,7 @@ libosmo_mgcp_client_la_SOURCES = \ | ||||
| 	mgcp_client_vty.c \ | ||||
| 	mgcp_client_fsm.c \ | ||||
| 	mgcp_client_endpoint_fsm.c \ | ||||
| 	mgcp_client_pool.c \ | ||||
| 	$(NULL) | ||||
|  | ||||
| libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION) | ||||
|   | ||||
| @@ -25,10 +25,13 @@ | ||||
| #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> | ||||
|  | ||||
| @@ -43,6 +46,9 @@ | ||||
| #define OSMUX_CID_MAX 255 /* FIXME: use OSMUX_CID_MAX from libosmo-netif? */ | ||||
| #endif | ||||
|  | ||||
| #define LOGPMGW(mgcp, level, fmt, args...) \ | ||||
| LOGP(DLMGCP, level, "MGW(%s) " fmt, mgcp_client_name(mgcp), ## args) | ||||
|  | ||||
| /* Codec descripton for dynamic payload types (SDP) */ | ||||
| const struct value_string osmo_mgcpc_codec_names[] = { | ||||
| 	{ CODEC_PCMU_8000_1, "PCMU/8000/1" }, | ||||
| @@ -53,6 +59,7 @@ const struct value_string osmo_mgcpc_codec_names[] = { | ||||
| 	{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" }, | ||||
| 	{ CODEC_AMR_8000_1, "AMR/8000/1" }, | ||||
| 	{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" }, | ||||
| 	{ CODEC_IUFP, "VND.3GPP.IUFP/16000" }, | ||||
| 	{ 0, NULL }, | ||||
| }; | ||||
|  | ||||
| @@ -66,7 +73,6 @@ static char *extract_codec_name(const char *str) | ||||
| 	if (!str) | ||||
| 		return NULL; | ||||
|  | ||||
| 	/* FIXME osmo_strlcpy */ | ||||
| 	osmo_strlcpy(buf, str, sizeof(buf)); | ||||
|  | ||||
| 	for (i = 0; i < strlen(buf); i++) { | ||||
| @@ -187,7 +193,7 @@ enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len, | ||||
| 	return pt; | ||||
| } | ||||
|  | ||||
| /*! Initalize MGCP client configuration struct with default values. | ||||
| /*! Initialize MGCP client configuration struct with default values. | ||||
|  *  \param[out] conf Client configuration.*/ | ||||
| void mgcp_client_conf_init(struct mgcp_client_conf *conf) | ||||
| { | ||||
| @@ -198,6 +204,8 @@ void mgcp_client_conf_init(struct mgcp_client_conf *conf) | ||||
| 		.remote_addr = NULL, | ||||
| 		.remote_port = -1, | ||||
| 	}; | ||||
|  | ||||
| 	INIT_LLIST_HEAD(&conf->reset_epnames); | ||||
| } | ||||
|  | ||||
| static void mgcp_client_handle_response(struct mgcp_client *mgcp, | ||||
| @@ -205,14 +213,13 @@ static void mgcp_client_handle_response(struct mgcp_client *mgcp, | ||||
| 					struct mgcp_response *response) | ||||
| { | ||||
| 	if (!pending) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Cannot handle NULL response\n"); | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Cannot handle NULL response\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 	if (pending->response_cb) | ||||
| 		pending->response_cb(response, pending->priv); | ||||
| 	else | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "MGCP response ignored (NULL cb)\n"); | ||||
| 		LOGPMGW(mgcp, LOGL_DEBUG, "MGCP response ignored (NULL cb)\n"); | ||||
| 	talloc_free(pending); | ||||
| } | ||||
|  | ||||
| @@ -370,22 +377,37 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li | ||||
| /* 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: | ||||
| @@ -654,22 +676,24 @@ int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg) | ||||
|  | ||||
| 	rc = mgcp_response_parse_head(r, msg); | ||||
| 	if (rc) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head)\n"); | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Cannot parse MGCP response (head)\n"); | ||||
| 		rc = 1; | ||||
| 		goto error; | ||||
| 	} | ||||
|  | ||||
| 	LOGPMGW(mgcp, LOGL_DEBUG, "MGCP client: Rx %d %u %s\n", | ||||
| 	     r->head.response_code, r->head.trans_id, r->head.comment); | ||||
|  | ||||
| 	rc = parse_head_params(r); | ||||
| 	if (rc) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n"); | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n"); | ||||
| 		rc = 1; | ||||
| 		goto error; | ||||
| 	} | ||||
|  | ||||
| 	pending = mgcp_client_response_pending_get(mgcp, r->head.trans_id); | ||||
| 	if (!pending) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Cannot find matching MGCP transaction for trans_id %d\n", | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Cannot find matching MGCP transaction for trans_id %d\n", | ||||
| 			r->head.trans_id); | ||||
| 		rc = -ENOENT; | ||||
| 		goto error; | ||||
| @@ -691,19 +715,19 @@ static int mgcp_do_read(struct osmo_fd *fd) | ||||
|  | ||||
| 	msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw"); | ||||
| 	if (!msg) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n"); | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Failed to allocate MGCP message.\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	ret = read(fd->fd, msg->data, 4096 - 128); | ||||
| 	if (ret <= 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %s: %d='%s'\n", osmo_sock_get_name2(fd->fd), | ||||
| 		     errno, strerror(errno)); | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Failed to read: %s: %d='%s'\n", | ||||
| 		     osmo_sock_get_name2(fd->fd), errno, strerror(errno)); | ||||
|  | ||||
| 		msgb_free(msg); | ||||
| 		return -1; | ||||
| 	} else if (ret > 4096 - 128) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret); | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret); | ||||
| 		msgb_free(msg); | ||||
| 		return -1; | ||||
| 	} | ||||
| @@ -717,13 +741,15 @@ static int mgcp_do_read(struct osmo_fd *fd) | ||||
| static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct mgcp_client *mgcp = fd->data; | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n", | ||||
| 	     osmo_sock_get_name2(fd->fd), msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len))); | ||||
| 	LOGPMGW(mgcp, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n", | ||||
| 		osmo_sock_get_name2(fd->fd), msg->len, | ||||
| 		osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len))); | ||||
|  | ||||
| 	ret = write(fd->fd, msg->data, msg->len); | ||||
| 	if (ret != msg->len) | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n", | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n", | ||||
| 			osmo_sock_get_name2(fd->fd), errno, strerror(errno), | ||||
| 			msg->len, osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len))); | ||||
| 	return ret; | ||||
| @@ -733,8 +759,12 @@ struct mgcp_client *mgcp_client_init(void *ctx, | ||||
| 				     struct mgcp_client_conf *conf) | ||||
| { | ||||
| 	struct mgcp_client *mgcp; | ||||
| 	struct reset_ep *reset_ep; | ||||
| 	struct reset_ep *actual_reset_ep; | ||||
|  | ||||
| 	mgcp = talloc_zero(ctx, struct mgcp_client); | ||||
| 	if (!mgcp) | ||||
| 		return NULL; | ||||
|  | ||||
| 	INIT_LLIST_HEAD(&mgcp->responses_pending); | ||||
| 	INIT_LLIST_HEAD(&mgcp->inuse_endpoints); | ||||
| @@ -754,88 +784,144 @@ struct mgcp_client *mgcp_client_init(void *ctx, | ||||
| 	if (osmo_strlcpy(mgcp->actual.endpoint_domain_name, conf->endpoint_domain_name, | ||||
| 			 sizeof(mgcp->actual.endpoint_domain_name)) | ||||
| 	    >= sizeof(mgcp->actual.endpoint_domain_name)) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "MGCP client: endpoint domain name is too long, max length is %zu: '%s'\n", | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "MGCP client: endpoint domain name is too long, max length is %zu: '%s'\n", | ||||
| 			sizeof(mgcp->actual.endpoint_domain_name) - 1, conf->endpoint_domain_name); | ||||
| 		talloc_free(mgcp); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	LOGP(DLMGCP, LOGL_NOTICE, "MGCP client: using endpoint domain '@%s'\n", mgcp_client_endpoint_domain(mgcp)); | ||||
| 	LOGPMGW(mgcp, LOGL_NOTICE, "MGCP client: using endpoint domain '@%s'\n", mgcp_client_endpoint_domain(mgcp)); | ||||
|  | ||||
| 	INIT_LLIST_HEAD(&mgcp->actual.reset_epnames); | ||||
| 	llist_for_each_entry(reset_ep, &conf->reset_epnames, list) { | ||||
| 		actual_reset_ep = talloc_memdup(mgcp, reset_ep, sizeof(*reset_ep)); | ||||
| 		llist_add_tail(&actual_reset_ep->list, &mgcp->actual.reset_epnames); | ||||
| 	} | ||||
|  | ||||
| 	if (conf->description) | ||||
| 		mgcp->actual.description = talloc_strdup(mgcp, conf->description); | ||||
|  | ||||
| 	return mgcp; | ||||
| } | ||||
|  | ||||
| static int init_socket(struct mgcp_client *mgcp) | ||||
| static int init_socket(struct mgcp_client *mgcp, unsigned int retry_n_ports) | ||||
| { | ||||
| 	int rc; | ||||
| 	struct osmo_wqueue *wq; | ||||
| 	int i; | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	wq = &mgcp->wq; | ||||
|  | ||||
| 	for (i = 0; i < 100; i++) { | ||||
| 	for (i = 0; i < retry_n_ports + 1; i++) { | ||||
|  | ||||
| 		/* 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, | ||||
| 		/* Initialize socket with the currently configured port number */ | ||||
| 		rc = osmo_sock_init2_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr, | ||||
| 					 mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port, | ||||
| 					 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT); | ||||
| 		if (rc > 0) | ||||
| 			return rc; | ||||
|  | ||||
| 		/* If there is a different port than the default port | ||||
| 		 * configured then we assume that the user has choosen | ||||
| 		 * that port conciously and we will not try to resolve | ||||
| 		 * this by silently choosing a different port. */ | ||||
| 		/* If there is a different port than the default port configured then we assume that the user has | ||||
| 		 * chosen that port conciously and we will not try to resolve this by silently choosing a different | ||||
| 		 * port. */ | ||||
| 		if (mgcp->actual.local_port != MGCP_CLIENT_LOCAL_PORT_DEFAULT && i == 0) | ||||
| 			return -EINVAL; | ||||
|  | ||||
| 		if (i == retry_n_ports) { | ||||
| 			/* Last try failed */ | ||||
| 			LOGPMGW(mgcp, LOGL_NOTICE, "Failed to bind to %s:%d -- check configuration!\n", | ||||
| 			     mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port); | ||||
| 			if (retry_n_ports == 0) | ||||
| 				return -EINVAL; | ||||
| 		} else { | ||||
| 			/* Choose a new port number to try next */ | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 		     "MGCPGW failed to bind to %s:%u, retrying with port %u\n", | ||||
| 		     mgcp->actual.local_addr, mgcp->actual.local_port, mgcp->actual.local_port + 1); | ||||
| 			LOGPMGW(mgcp, LOGL_NOTICE, | ||||
| 			     "Failed to bind to %s:%d, retrying with port %d -- check configuration!\n", | ||||
| 			     mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port, | ||||
| 			     mgcp->actual.local_port + 1); | ||||
| 			mgcp->actual.local_port++; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_FATAL, "MGCPGW failed to find a port to bind on %i times.\n", i); | ||||
| 	LOGPMGW(mgcp, LOGL_FATAL, "Failed to find a port to bind on %u times -- check configuration!\n", i); | ||||
| 	return -EINVAL; | ||||
| } | ||||
|  | ||||
| /*! Initalize client connection (opens socket only, no request is sent yet) | ||||
|  *  \param[in,out] mgcp MGCP client descriptor. | ||||
|  *  \returns 0 on success, -EINVAL on error. */ | ||||
| int mgcp_client_connect(struct mgcp_client *mgcp) | ||||
| /* Safely ignore the MGCP response to the DLCX sent via _mgcp_client_send_dlcx() */ | ||||
| static void _ignore_mgcp_response(struct mgcp_response *response, void *priv) { } | ||||
|  | ||||
| /* Format DLCX message (fire and forget) and send it off to the MGW */ | ||||
| static void _mgcp_client_send_dlcx(struct mgcp_client *mgcp, const char *epname) | ||||
| { | ||||
| 	struct sockaddr_in addr; | ||||
| 	struct osmo_wqueue *wq; | ||||
| 	struct msgb *msgb_dlcx; | ||||
| 	struct mgcp_msg mgcp_msg_dlcx = { | ||||
| 		.verb = MGCP_VERB_DLCX, | ||||
| 		.presence = MGCP_MSG_PRESENCE_ENDPOINT, | ||||
| 	}; | ||||
| 	osmo_strlcpy(mgcp_msg_dlcx.endpoint, epname, sizeof(mgcp_msg_dlcx.endpoint)); | ||||
| 	msgb_dlcx = mgcp_msg_gen(mgcp, &mgcp_msg_dlcx); | ||||
| 	mgcp_client_tx(mgcp, msgb_dlcx, &_ignore_mgcp_response, NULL); | ||||
| } | ||||
|  | ||||
| static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, const char *name) | ||||
| { | ||||
| 	static char endpoint[MGCP_ENDPOINT_MAXLEN]; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp)); | ||||
| 	if (rc > sizeof(endpoint) - 1) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '%s@%s'\n", | ||||
| 			sizeof(endpoint) - 1, name, mgcp_client_endpoint_domain(mgcp)); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (rc < 1) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Cannot compose MGCP endpoint name\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	return endpoint; | ||||
| } | ||||
|  | ||||
| /*! Initialize client connection (opens socket) | ||||
|  *  \param[in,out] mgcp MGCP client descriptor. | ||||
|  *  \param[in] retry_n_ports number of consecutive local ports that should be used to retry on failure. | ||||
|  *  \returns 0 on success, -EINVAL on error. */ | ||||
| int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports) | ||||
| { | ||||
| 	struct osmo_wqueue *wq; | ||||
| 	int rc; | ||||
| 	struct reset_ep *reset_ep; | ||||
| 	const char *epname; | ||||
|  | ||||
| 	if (!mgcp) { | ||||
| 		LOGP(DLMGCP, LOGL_FATAL, "MGCPGW client not initialized properly\n"); | ||||
| 		LOGPMGW(mgcp, LOGL_FATAL, "Client not initialized properly\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	wq = &mgcp->wq; | ||||
|  | ||||
| 	rc = init_socket(mgcp); | ||||
| 	if (rc < 0) { | ||||
| 		LOGP(DLMGCP, LOGL_FATAL, | ||||
| 		     "Failed to initialize socket %s:%u -> %s:%u for MGCP GW: %s\n", | ||||
| 		     mgcp->actual.local_addr, mgcp->actual.local_port, | ||||
| 		     mgcp->actual.remote_addr, mgcp->actual.remote_port, strerror(errno)); | ||||
| 		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; | ||||
| 	osmo_wqueue_init(wq, 1024); | ||||
| 	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)); | ||||
| 	osmo_fd_setup(&wq->bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, mgcp, 0); | ||||
|  | ||||
| 	rc = init_socket(mgcp, retry_n_ports); | ||||
| 	if (rc < 0) { | ||||
| 		LOGPMGW(mgcp, LOGL_FATAL, | ||||
| 		     "Failed to initialize socket %s:%u -> %s:%u for MGW: %s\n", | ||||
| 		     mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port, | ||||
| 		     mgcp->actual.remote_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.remote_port, | ||||
| 		     strerror(errno)); | ||||
| 		goto error_close_fd; | ||||
| 	} | ||||
|  | ||||
| 	LOGPMGW(mgcp, LOGL_INFO, "MGW connection: %s\n", osmo_sock_get_name2(wq->bfd.fd)); | ||||
|  | ||||
| 	/* If configured, send a DLCX message to the endpoints that are configured to | ||||
| 	 * be reset on startup. Usually this is a wildcarded endpoint. */ | ||||
| 	llist_for_each_entry(reset_ep, &mgcp->actual.reset_epnames, list) { | ||||
| 		epname = _mgcp_client_name_append_domain(mgcp, reset_ep->name); | ||||
| 		LOGPMGW(mgcp, LOGL_INFO, "Sending DLCX to: %s\n", epname); | ||||
| 		_mgcp_client_send_dlcx(mgcp, epname); | ||||
| 	} | ||||
| 	return 0; | ||||
| error_close_fd: | ||||
| 	close(wq->bfd.fd); | ||||
| @@ -843,6 +929,35 @@ error_close_fd: | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /*! Initialize client connection (opens socket) | ||||
|  *  \param[in,out] mgcp MGCP client descriptor. | ||||
|  *  \returns 0 on success, -EINVAL on error. */ | ||||
| int mgcp_client_connect(struct mgcp_client *mgcp) | ||||
| { | ||||
| 	return mgcp_client_connect2(mgcp, 99); | ||||
| } | ||||
|  | ||||
| /*! Terminate client connection | ||||
|  *  \param[in,out] mgcp MGCP client descriptor. | ||||
|  *  \returns 0 on success, -EINVAL on error. */ | ||||
| void mgcp_client_disconnect(struct mgcp_client *mgcp) | ||||
| { | ||||
| 	struct osmo_wqueue *wq; | ||||
|  | ||||
| 	if (!mgcp) { | ||||
| 		LOGP(DLMGCP, LOGL_FATAL, "MGCP client not initialized properly\n"); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	wq = &mgcp->wq; | ||||
| 	osmo_wqueue_clear(wq); | ||||
| 	LOGPMGW(mgcp, LOGL_INFO, "MGCP association: %s -- closed!\n", osmo_sock_get_name2(wq->bfd.fd)); | ||||
| 	close(wq->bfd.fd); | ||||
| 	wq->bfd.fd = -1; | ||||
| 	if (osmo_fd_is_registered(&wq->bfd)) | ||||
| 		osmo_fd_unregister(&wq->bfd); | ||||
| } | ||||
|  | ||||
| /*! Get the IP-Aaddress of the associated MGW as string. | ||||
|  *  \param[in] mgcp MGCP client descriptor. | ||||
|  *  \returns a pointer to the address string. */ | ||||
| @@ -859,12 +974,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. | ||||
| @@ -874,29 +990,67 @@ const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp) | ||||
| 	return mgcp->actual.endpoint_domain_name[0] ? mgcp->actual.endpoint_domain_name : "mgw"; | ||||
| } | ||||
|  | ||||
| static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, char *name) | ||||
| { | ||||
| 	static char endpoint[MGCP_ENDPOINT_MAXLEN]; | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp)); | ||||
| 	if (rc > sizeof(endpoint) - 1) { | ||||
| 		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) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP endpoint name\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	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) { | ||||
| 		LOGPMGW(mgcp, 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) { | ||||
| 		LOGPMGW(mgcp, 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) { | ||||
| 		LOGPMGW(mgcp, 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, | ||||
| @@ -906,6 +1060,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; | ||||
| @@ -914,7 +1071,7 @@ struct mgcp_response_pending * mgcp_client_pending_add( | ||||
| 	return pending; | ||||
| } | ||||
|  | ||||
| /* Send the MGCP message in msg to the MGCP GW and handle a response with | ||||
| /* Send the MGCP message in msg to the MGW and handle a response with | ||||
|  * response_cb. NOTE: the response_cb still needs to call | ||||
|  * mgcp_response_parse_params(response) to get the parsed parameters -- to | ||||
|  * potentially save some CPU cycles, only the head line has been parsed when | ||||
| @@ -925,22 +1082,29 @@ 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; | ||||
|  | ||||
| 	trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID]; | ||||
| 	if (!trans_id) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, | ||||
| 			"Unset transaction id in mgcp send request\n"); | ||||
| 		talloc_free(msg); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/* 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, | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, | ||||
| 			"Cannot send, MGCP message too large: %u\n", | ||||
| 			msgb_l2len(msg)); | ||||
| 		msgb_free(msg); | ||||
| @@ -950,18 +1114,22 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg, | ||||
|  | ||||
| 	rc = osmo_wqueue_enqueue(&mgcp->wq, msg); | ||||
| 	if (rc) { | ||||
| 		LOGP(DLMGCP, LOGL_FATAL, "Could not queue message to MGCP GW\n"); | ||||
| 		LOGPMGW(mgcp, LOGL_FATAL, "Could not queue message to MGW\n"); | ||||
| 		msgb_free(msg); | ||||
| 		goto mgcp_tx_error; | ||||
| 	} else | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "Queued %u bytes for MGCP GW\n", | ||||
| 		LOGPMGW(mgcp, LOGL_DEBUG, "Queued %u bytes for MGW\n", | ||||
| 			msgb_l2len(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,10 +1146,10 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id) | ||||
| 	struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id); | ||||
| 	if (!pending) { | ||||
| 		/*! Note: it is not harmful to cancel a transaction twice. */ | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot cancel, no such transaction: %u\n", trans_id); | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Cannot cancel, no such transaction: %u\n", trans_id); | ||||
| 		return -ENOENT; | ||||
| 	} | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Canceled transaction %u\n", trans_id); | ||||
| 	LOGPMGW(mgcp, LOGL_DEBUG, "Canceled transaction %u\n", trans_id); | ||||
| 	talloc_free(pending); | ||||
| 	return 0; | ||||
| 	/*! We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent | ||||
| @@ -1019,30 +1187,40 @@ static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg) | ||||
| 	const char *codec; | ||||
| 	unsigned int pt; | ||||
|  | ||||
| 	rc += msgb_printf(msg, "L:"); | ||||
| 	rc |= msgb_printf(msg, "L:"); | ||||
|  | ||||
| 	if (mgcp_msg->ptime) | ||||
| 		rc += msgb_printf(msg, " p:%u,", mgcp_msg->ptime); | ||||
| 		rc |= msgb_printf(msg, " p:%u,", mgcp_msg->ptime); | ||||
|  | ||||
| 	if (mgcp_msg->codecs_len) { | ||||
| 		rc += msgb_printf(msg, " a:"); | ||||
| 		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(osmo_mgcpc_codec_names, pt); | ||||
|  | ||||
| 			/* Note: Use codec descriptors from enum mgcp_codecs | ||||
| 			 * in mgcp_client only! */ | ||||
| 			OSMO_ASSERT(codec); | ||||
| 			rc += msgb_printf(msg, "%s", extract_codec_name(codec)); | ||||
| 			if (!codec) { | ||||
| 				msgb_free(msg); | ||||
| 				return -EINVAL; | ||||
| 			} | ||||
|  | ||||
| 			rc |= msgb_printf(msg, "%s", extract_codec_name(codec)); | ||||
| 			if (i < mgcp_msg->codecs_len - 1) | ||||
| 				rc += msgb_printf(msg, ";"); | ||||
| 				rc |= msgb_printf(msg, ";"); | ||||
| 		} | ||||
| 		rc += msgb_printf(msg, ","); | ||||
| 		rc |= msgb_printf(msg, ","); | ||||
| 	} | ||||
|  | ||||
| 	rc += msgb_printf(msg, " nt:IN\r\n"); | ||||
| 	rc |= msgb_printf(msg, " nt:IN\r\n"); | ||||
|  | ||||
| 	return rc; | ||||
| 	if (rc != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "message buffer to small, can not generate MGCP message (LCO)\n"); | ||||
| 		msgb_free(msg); | ||||
| 		return -ENOBUFS; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */ | ||||
| @@ -1050,56 +1228,70 @@ 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; | ||||
|  | ||||
| 	/* Add separator to mark the beginning of the SDP block */ | ||||
| 	rc += msgb_printf(msg, "\r\n"); | ||||
| 	rc |= msgb_printf(msg, "\r\n"); | ||||
|  | ||||
| 	/* Add SDP protocol version */ | ||||
| 	rc += msgb_printf(msg, "v=0\r\n"); | ||||
| 	rc |= msgb_printf(msg, "v=0\r\n"); | ||||
|  | ||||
| 	/* Determine local IP-Address */ | ||||
| 	if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, | ||||
| 			"Could not determine local IP-Address!\n"); | ||||
| 		msgb_free(msg); | ||||
| 		return -2; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	local_ip_family = osmo_ip_str_type(local_ip); | ||||
| 	if (local_ip_family == AF_UNSPEC) { | ||||
| 		msgb_free(msg); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	audio_ip_family = osmo_ip_str_type(mgcp_msg->audio_ip); | ||||
| 	if (audio_ip_family == AF_UNSPEC) { | ||||
| 		msgb_free(msg); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/* 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"); | ||||
| 	rc |= msgb_printf(msg, "s=-\r\n"); | ||||
|  | ||||
| 	/* Add RTP address and port */ | ||||
| 	if (mgcp_msg->audio_port == 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, | ||||
| 			"Invalid port number, can not generate MGCP message\n"); | ||||
| 		msgb_free(msg); | ||||
| 		return -2; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	if (strlen(mgcp_msg->audio_ip) <= 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, | ||||
| 			"Empty ip address, can not generate MGCP message\n"); | ||||
| 		msgb_free(msg); | ||||
| 		return -2; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	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"); | ||||
| 	rc |= msgb_printf(msg, "t=0 0\r\n"); | ||||
|  | ||||
| 	rc += msgb_printf(msg, "m=audio %u RTP/AVP", mgcp_msg->audio_port); | ||||
| 	rc |= msgb_printf(msg, "m=audio %u RTP/AVP", mgcp_msg->audio_port); | ||||
| 	for (i = 0; i < mgcp_msg->codecs_len; i++) { | ||||
| 		pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]); | ||||
| 		rc += msgb_printf(msg, " %u", pt); | ||||
| 		rc |= msgb_printf(msg, " %u", pt); | ||||
|  | ||||
| 	} | ||||
| 	rc += msgb_printf(msg, "\r\n"); | ||||
| 	rc |= msgb_printf(msg, "\r\n"); | ||||
|  | ||||
| 	/* Add optional codec parameters (fmtp) */ | ||||
| 	if (mgcp_msg->param_present) { | ||||
| @@ -1109,9 +1301,9 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie | ||||
| 				   continue; | ||||
| 			pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]); | ||||
| 			if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned) | ||||
| 				rc += msgb_printf(msg, "a=fmtp:%u octet-align=1\r\n", pt); | ||||
| 				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); | ||||
| 				rc |= msgb_printf(msg, "a=fmtp:%u octet-align=0\r\n", pt); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -1126,16 +1318,25 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie | ||||
|  | ||||
| 			/* Note: Use codec descriptors from enum mgcp_codecs | ||||
| 			 * in mgcp_client only! */ | ||||
| 			OSMO_ASSERT(codec); | ||||
| 			if (!codec) { | ||||
| 				msgb_free(msg); | ||||
| 				return -EINVAL; | ||||
| 			} | ||||
|  | ||||
| 			rc += msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec); | ||||
| 			rc |= msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (mgcp_msg->ptime) | ||||
| 		rc += msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime); | ||||
| 		rc |= msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime); | ||||
|  | ||||
| 	return rc; | ||||
| 	if (rc != 0) { | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Message buffer to small, can not generate MGCP message (SDP)\n"); | ||||
| 		msgb_free(msg); | ||||
| 		return -ENOBUFS; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! Generate an MGCP message | ||||
| @@ -1148,7 +1349,6 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) | ||||
| 	uint32_t mandatory_mask; | ||||
| 	struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx"); | ||||
| 	int rc = 0; | ||||
| 	int rc_sdp; | ||||
| 	bool use_sdp = false; | ||||
| 	char buf[32]; | ||||
|  | ||||
| @@ -1159,34 +1359,33 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) | ||||
| 	switch (mgcp_msg->verb) { | ||||
| 	case MGCP_VERB_CRCX: | ||||
| 		mandatory_mask = MGCP_CRCX_MANDATORY; | ||||
| 		rc += msgb_printf(msg, "CRCX %u", trans_id); | ||||
| 		rc |= msgb_printf(msg, "CRCX %u", trans_id); | ||||
| 		break; | ||||
| 	case MGCP_VERB_MDCX: | ||||
| 		mandatory_mask = MGCP_MDCX_MANDATORY; | ||||
| 		rc += msgb_printf(msg, "MDCX %u", trans_id); | ||||
| 		rc |= msgb_printf(msg, "MDCX %u", trans_id); | ||||
| 		break; | ||||
| 	case MGCP_VERB_DLCX: | ||||
| 		mandatory_mask = MGCP_DLCX_MANDATORY; | ||||
| 		rc += msgb_printf(msg, "DLCX %u", trans_id); | ||||
| 		rc |= msgb_printf(msg, "DLCX %u", trans_id); | ||||
| 		break; | ||||
| 	case MGCP_VERB_AUEP: | ||||
| 		mandatory_mask = MGCP_AUEP_MANDATORY; | ||||
| 		rc += msgb_printf(msg, "AUEP %u", trans_id); | ||||
| 		rc |= msgb_printf(msg, "AUEP %u", trans_id); | ||||
| 		break; | ||||
| 	case MGCP_VERB_RSIP: | ||||
| 		mandatory_mask = MGCP_RSIP_MANDATORY; | ||||
| 		rc += msgb_printf(msg, "RSIP %u", trans_id); | ||||
| 		rc |= msgb_printf(msg, "RSIP %u", trans_id); | ||||
| 		break; | ||||
| 	default: | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Invalid command verb, can not generate MGCP message\n"); | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Invalid command verb, can not generate MGCP message\n"); | ||||
| 		msgb_free(msg); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if mandatory fields are missing */ | ||||
| 	if (!((mgcp_msg->presence & mandatory_mask) == mandatory_mask)) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, | ||||
| 			"One or more missing mandatory fields, can not generate MGCP message\n"); | ||||
| 		msgb_free(msg); | ||||
| 		return NULL; | ||||
| @@ -1195,38 +1394,37 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) | ||||
| 	/* Add endpoint name */ | ||||
| 	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_ENDPOINT) { | ||||
| 		if (strlen(mgcp_msg->endpoint) <= 0) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Empty endpoint name, can not generate MGCP message\n"); | ||||
| 			LOGPMGW(mgcp, LOGL_ERROR, "Empty endpoint name, can not generate MGCP message\n"); | ||||
| 			msgb_free(msg); | ||||
| 			return NULL; | ||||
| 		} | ||||
|  | ||||
| 		if (strstr(mgcp_msg->endpoint, "@") == NULL) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			LOGPMGW(mgcp, LOGL_ERROR, | ||||
| 				"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); | ||||
| 		rc |= msgb_printf(msg, " %s", mgcp_msg->endpoint); | ||||
| 	} | ||||
|  | ||||
| 	/* Add protocol version */ | ||||
| 	rc += msgb_printf(msg, " MGCP 1.0\r\n"); | ||||
| 	rc |= msgb_printf(msg, " MGCP 1.0\r\n"); | ||||
|  | ||||
| 	/* Add call id */ | ||||
| 	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CALL_ID) | ||||
| 		rc += msgb_printf(msg, "C: %x\r\n", mgcp_msg->call_id); | ||||
| 		rc |= msgb_printf(msg, "C: %x\r\n", mgcp_msg->call_id); | ||||
|  | ||||
| 	/* Add connection id */ | ||||
| 	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID) { | ||||
| 		if (strlen(mgcp_msg->conn_id) <= 0) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| 			     "Empty connection id, can not generate MGCP message\n"); | ||||
| 			LOGPMGW(mgcp, LOGL_ERROR, "Empty connection id, can not generate MGCP message\n"); | ||||
| 			msgb_free(msg); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 		rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id); | ||||
| 		rc |= msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id); | ||||
| 	} | ||||
|  | ||||
| 	/* Using SDP makes sense when a valid IP/Port combination is specifiec, | ||||
| @@ -1238,33 +1436,34 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) | ||||
| 	/* Add local connection options (LCO) */ | ||||
| 	if (!use_sdp | ||||
| 	    && (mgcp_msg->verb == MGCP_VERB_CRCX | ||||
| 		|| mgcp_msg->verb == MGCP_VERB_MDCX)) | ||||
| 		rc += add_lco(msg, mgcp_msg); | ||||
| 		|| mgcp_msg->verb == MGCP_VERB_MDCX)) { | ||||
| 	        if (add_lco(msg, mgcp_msg) < 0) | ||||
| 			return NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* Add mode */ | ||||
| 	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE) | ||||
| 		rc += | ||||
| 		rc |= | ||||
| 		    msgb_printf(msg, "M: %s\r\n", | ||||
| 				mgcp_client_cmode_name(mgcp_msg->conn_mode)); | ||||
|  | ||||
| 	/* Add X-Osmo-IGN */ | ||||
| 	if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_IGN) | ||||
| 	    && (mgcp_msg->x_osmo_ign != 0)) | ||||
| 		rc += | ||||
| 		rc |= | ||||
| 		    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", | ||||
| 			LOGPMGW(mgcp, LOGL_ERROR, "Wrong Osmux CID %d, can not generate MGCP message\n", | ||||
| 				mgcp_msg->x_osmo_osmux_cid); | ||||
| 			msgb_free(msg); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 		snprintf(buf, sizeof(buf), " %d", mgcp_msg->x_osmo_osmux_cid); | ||||
| 		rc += | ||||
| 		rc |= | ||||
| 		    msgb_printf(msg, MGCP_X_OSMO_OSMUX_HEADER "%s\r\n", | ||||
| 				mgcp_msg->x_osmo_osmux_cid == -1 ? " *": buf); | ||||
| 	} | ||||
| @@ -1274,16 +1473,12 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) | ||||
| 	if (use_sdp | ||||
| 	    && (mgcp_msg->verb == MGCP_VERB_CRCX | ||||
| 		|| mgcp_msg->verb == MGCP_VERB_MDCX)) { | ||||
| 		rc_sdp = add_sdp(msg, mgcp_msg, mgcp); | ||||
| 		if (rc_sdp == -2) | ||||
| 	        if (add_sdp(msg, mgcp_msg, mgcp) < 0) | ||||
| 			return NULL; | ||||
| 		else | ||||
| 			rc += rc_sdp; | ||||
| 	} | ||||
|  | ||||
| 	if (rc != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "message buffer to small, can not generate MGCP message\n"); | ||||
| 		LOGPMGW(mgcp, LOGL_ERROR, "Message buffer to small, can not generate MGCP message\n"); | ||||
| 		msgb_free(msg); | ||||
| 		msg = NULL; | ||||
| 	} | ||||
| @@ -1299,7 +1494,7 @@ mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg) | ||||
| 	return (mgcp_trans_id_t)msg->cb[MSGB_CB_MGCP_TRANS_ID]; | ||||
| } | ||||
|  | ||||
| /*! Get the configuration parameters a given MGCP client instance | ||||
| /*! Get the configuration parameters for a given MGCP client instance | ||||
|  *  \param[in] mgcp MGCP client descriptor. | ||||
|  *  \returns configuration */ | ||||
| struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp) | ||||
| @@ -1315,3 +1510,22 @@ const struct value_string mgcp_client_connection_mode_strs[] = { | ||||
| 	{ MGCP_CONN_LOOPBACK, "loopback" }, | ||||
| 	{ 0, NULL } | ||||
| }; | ||||
|  | ||||
| /*! Get MGCP client instance name (VTY). | ||||
|  *  \param[in] mgcp MGCP client descriptor. | ||||
|  *  \returns MGCP client name. | ||||
|  * | ||||
|  *  The user can only modify the name of an MGCP client instance when it is | ||||
|  *  part of a pool. For single MGCP client instances and MGCP client instance | ||||
|  *  where no description is set via the VTY, the MGW domain name will be used | ||||
|  *  as name. */ | ||||
| const char *mgcp_client_name(const struct mgcp_client *mgcp) | ||||
| { | ||||
| 	if (!mgcp) | ||||
| 		return "(null)"; | ||||
|  | ||||
| 	if (mgcp->actual.description) | ||||
| 		return mgcp->actual.description; | ||||
| 	else | ||||
| 		return mgcp_client_endpoint_domain(mgcp); | ||||
| } | ||||
|   | ||||
| @@ -27,12 +27,13 @@ | ||||
| #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); \ | ||||
| 		LOGP(DLMGCP, level, "(unknown MGW endpoint) " fmt, ## args); \ | ||||
| 	else \ | ||||
| 		LOG_MGCPC_EP(ci->ep, level, "CI[%d] %s%s%s: " fmt, \ | ||||
| 			(int)(ci - ci->ep->ci), \ | ||||
| @@ -215,6 +216,13 @@ const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci) | ||||
| 	return ci->mgcp_ci_str; | ||||
| } | ||||
|  | ||||
| struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep) | ||||
| { | ||||
| 	if (!ep) | ||||
| 		return NULL; | ||||
| 	return ep->mgcp_client; | ||||
| } | ||||
|  | ||||
| static struct value_string osmo_mgcpc_ep_fsm_event_names[33] = {}; | ||||
|  | ||||
| static char osmo_mgcpc_ep_fsm_event_name_bufs[32][32] = {}; | ||||
| @@ -235,8 +243,6 @@ static void fill_event_names() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* T_defs is used to obtain an (Osmocom specific) T2427001: timeout for an MGCP response (note, 2427 corresponds to the | ||||
|  * default MGCP port in osmo-mgw). */ | ||||
| static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init() | ||||
| { | ||||
| 	OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0); | ||||
| @@ -468,14 +474,12 @@ static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data) | ||||
|  | ||||
| 	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. */ | ||||
| 		rtp_info = data; | ||||
| 		OSMO_ASSERT(rtp_info); | ||||
| 		ci->got_port_info = true; | ||||
| 		ci->rtp_info = *rtp_info; | ||||
| 		osmo_strlcpy(ci->mgcp_ci_str, mgcp_conn_get_ci(ci->mgcp_client_fi), | ||||
| 			sizeof(ci->mgcp_ci_str)); | ||||
| 		if (rtp_info->endpoint[0]) { | ||||
| @@ -485,6 +489,15 @@ static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data) | ||||
| 				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: | ||||
| @@ -502,7 +515,8 @@ static void on_success(struct osmo_mgcpc_ep_ci *ci, void *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. */ | ||||
| /*! Return the MGW's local RTP port information for this connection, i.e. the local port that MGW is receiving on, as | ||||
|  * returned by the last CRCX-OK / 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); | ||||
| @@ -513,21 +527,47 @@ const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgc | ||||
| 	return &ci->rtp_info; | ||||
| } | ||||
|  | ||||
| /*! Return the MGW's remote RTP port information for this connection, i.e. the remote RTP port that the MGW is sending | ||||
|  * to, as sent to the MGW by the last CRCX / MDCX message. */ | ||||
| const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_remote_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; | ||||
| 	return &ci->verb_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_addr.s_addr = inet_addr(rtp_info->addr); | ||||
| 		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; | ||||
| } | ||||
|  | ||||
| @@ -547,7 +587,7 @@ bool osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(const struct osmo_mgcpc_ep_ci * | ||||
| } | ||||
|  | ||||
| static const struct osmo_tdef_state_timeout osmo_mgcpc_ep_fsm_timeouts[32] = { | ||||
| 	[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = { .T=2427001 }, | ||||
| 	[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = { .T=-2427 }, | ||||
| }; | ||||
|  | ||||
| /* Transition to a state, using the T timer defined in assignment_fsm_timeouts. | ||||
| @@ -583,7 +623,7 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci, | ||||
| 	ci = osmo_mgcpc_ep_check_ci(ci); | ||||
|  | ||||
| 	if (!ci) { | ||||
| 		LOGP(DLGLOBAL, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n"); | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n"); | ||||
| 		goto dispatch_error; | ||||
| 	} | ||||
| 	if (!verb_info && verb != MGCP_VERB_DLCX) { | ||||
| @@ -957,6 +997,34 @@ static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) | ||||
| { | ||||
| 	int i; | ||||
| 	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi); | ||||
|  | ||||
| 	/* We want the mgcp_client_fsm to still stick around until it received the DLCX "OK" responses from the MGW. So | ||||
| 	 * it should not dealloc along with this ep_fsm instance. Instead, signal DLCX for each conn on the endpoint, | ||||
| 	 * and detach the mgcp_client_fsm from being a child-fsm. | ||||
| 	 * | ||||
| 	 * After mgcp_conn_delete(), an mgcp_client_fsm instance goes into ST_DLCX_RESP, which waits up to 4 seconds for | ||||
| 	 * a DLCX OK. If none is received in that time, the instance terminates. So cleanup of the instance is | ||||
| 	 * guaranteed. */ | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) { | ||||
| 		struct osmo_mgcpc_ep_ci *ci = &ep->ci[i]; | ||||
|  | ||||
| 		if (!ci->occupied || !ci->mgcp_client_fi) | ||||
| 			continue; | ||||
|  | ||||
| 		/* mgcp_conn_delete() unlinks itself from this parent FSM implicitly and waits for the DLCX OK. */ | ||||
| 		mgcp_conn_delete(ci->mgcp_client_fi); | ||||
| 		/* Forget all about this ci */ | ||||
| 		*ci = (struct osmo_mgcpc_ep_ci){ | ||||
| 			.ep = ep, | ||||
| 		}; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static struct osmo_fsm osmo_mgcpc_ep_fsm = { | ||||
| 	.name = "mgw-endp", | ||||
| 	.states = osmo_mgcpc_ep_fsm_states, | ||||
| @@ -964,5 +1032,5 @@ static struct osmo_fsm osmo_mgcpc_ep_fsm = { | ||||
| 	.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. */ | ||||
| 	.pre_term = osmo_mgcpc_ep_fsm_pre_term, | ||||
| }; | ||||
|   | ||||
| @@ -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 | ||||
| @@ -251,6 +252,18 @@ const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi) | ||||
| 	return mgcp_ctx->conn_id; | ||||
| } | ||||
|  | ||||
| /* Get the mgcp_client that is used with this mgcp_client_fsm instance */ | ||||
| struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi) | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx; | ||||
|  | ||||
| 	if (!fi) | ||||
| 		return NULL; | ||||
|  | ||||
| 	mgcp_ctx = fi->priv; | ||||
| 	return mgcp_ctx->mgcp; | ||||
| } | ||||
|  | ||||
| static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv) | ||||
| { | ||||
| 	struct osmo_fsm_inst *fi = priv; | ||||
| @@ -520,10 +533,12 @@ static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca | ||||
| 	/* Should the FSM be terminated while there are still open connections | ||||
| 	 * on the MGW, we send an unconditional DLCX to terminate the | ||||
| 	 * connection. This is not the normal case. The user should always use | ||||
| 	 * mgcp_conn_delete() to instruct the FSM to perform a graceful exit */ | ||||
| 	if (strlen(mgcp_ctx->conn_id)) { | ||||
| 		LOGPFSML(fi, LOGL_ERROR, | ||||
| 			 "MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n"); | ||||
| 	 * mgcp_conn_delete() to instruct the FSM to perform a graceful exit. | ||||
| 	 * If in ST_DLCX_RESP, a DLCX was already sent and we did not get a | ||||
| 	 * response. No point in sending another one. */ | ||||
| 	if (fi->state != ST_DLCX_RESP && strlen(mgcp_ctx->conn_id)) { | ||||
| 		LOGPFSML(fi, LOGL_INFO, "Conn cleanup, sending DLCX for %s %s\n", mgcp_ctx->conn_peer_remote.endpoint, | ||||
| 			 mgcp_ctx->conn_id); | ||||
| 		msg = make_dlcx_msg(mgcp_ctx); | ||||
| 		if (!msg) | ||||
| 			LOGPFSML(fi, LOGL_ERROR, "MGW/DLCX: Error composing DLCX message\n"); | ||||
| @@ -593,6 +608,7 @@ static struct osmo_fsm fsm_mgcp_client = { | ||||
| 	.timer_cb = fsm_timeout_cb, | ||||
| 	.cleanup = fsm_cleanup_cb, | ||||
| 	.event_names = fsm_mgcp_client_evt_names, | ||||
| 	.log_subsys = DLMGCP, | ||||
| }; | ||||
|  | ||||
| /*! allocate FSM, and create a new connection on the MGW. | ||||
| @@ -607,14 +623,16 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm | ||||
| { | ||||
| 	struct mgcp_ctx *mgcp_ctx; | ||||
| 	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; | ||||
|  | ||||
| 	/* Allocate and configure a new fsm instance */ | ||||
| @@ -644,7 +662,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); | ||||
| @@ -668,8 +686,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; | ||||
| 	} | ||||
|  | ||||
| @@ -704,8 +722,8 @@ void mgcp_conn_delete(struct osmo_fsm_inst *fi) | ||||
| 	if (fi->proc.terminating) | ||||
| 		return; | ||||
|  | ||||
| 	/* Unlink FSM from parent */ | ||||
| 	osmo_fsm_inst_unlink_parent(fi, NULL); | ||||
| 	/* Unlink FSM from parent, set the struct mgcp_client as new talloc ctx. */ | ||||
| 	osmo_fsm_inst_unlink_parent(fi, mgcp_ctx->mgcp); | ||||
|  | ||||
| 	/* An error situation where the parent FSM must be killed immediately | ||||
| 	 * may lead into a situation where the DLCX can not be executed right | ||||
|   | ||||
							
								
								
									
										216
									
								
								src/libosmo-mgcp-client/mgcp_client_pool.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								src/libosmo-mgcp-client/mgcp_client_pool.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | ||||
| /* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Philipp Maier | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <osmocom/mgcp_client/mgcp_client.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_internal.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_pool_internal.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_pool.h> | ||||
| #include <stddef.h> | ||||
|  | ||||
| #define LOGPPMGW(pool_member, level, fmt, args...) \ | ||||
| LOGP(DLMGCP, level, "MGW-pool(%s) " fmt, mgcp_client_pool_member_name(pool_member), ## args) | ||||
|  | ||||
| /* Get a human readable name for a given pool member. */ | ||||
| const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member) | ||||
| { | ||||
| 	const struct mgcp_client *mpcp_client; | ||||
| 	struct mgcp_client mpcp_client_dummy; | ||||
| 	static char name[512]; | ||||
| 	const char *description; | ||||
|  | ||||
| 	if (!pool_member) | ||||
| 		return "(null)"; | ||||
|  | ||||
| 	/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the | ||||
| 	 * initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */ | ||||
| 	if (!pool_member->client) { | ||||
| 		memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual)); | ||||
| 		mpcp_client = &mpcp_client_dummy; | ||||
| 	} else { | ||||
| 		mpcp_client = pool_member->client; | ||||
| 	} | ||||
|  | ||||
| 	description = mgcp_client_name(mpcp_client); | ||||
| 	snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description); | ||||
|  | ||||
| 	return name; | ||||
| } | ||||
|  | ||||
| /*! Allocate MGCP client pool. This is called once on startup and before the pool is used with | ||||
|  *  mgcp_client_pool_vty_init(). Since the pool is linked with the VTY it must exist througout the entire runtime. | ||||
|  *  \param[in] talloc_ctx talloc context. */ | ||||
| struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx) | ||||
| { | ||||
| 	struct mgcp_client_pool *pool; | ||||
|  | ||||
| 	pool = talloc_zero(talloc_ctx, struct mgcp_client_pool); | ||||
| 	if (!pool) | ||||
| 		return NULL; | ||||
|  | ||||
| 	INIT_LLIST_HEAD(&pool->pool); | ||||
|  | ||||
| 	return pool; | ||||
| } | ||||
|  | ||||
| /*! Initialize and connect an mcgp client pool. | ||||
|  *  \param[in,out] mgcp MGCP client pool descriptor. | ||||
|  *  \returns number of successfully initialized pool members. */ | ||||
| unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool) | ||||
| { | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
| 	unsigned int pool_members_initialized = 0; | ||||
|  | ||||
| 	llist_for_each_entry(pool_member, &pool->pool, list) { | ||||
|  | ||||
| 		/* Initialize client */ | ||||
| 		pool_member->client = mgcp_client_init(pool_member, &pool_member->conf); | ||||
| 		if (!pool_member->client) { | ||||
| 			LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n"); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		/* Set backpointer so that we can detect later that this MGCP client is managed | ||||
| 		 * by this pool. */ | ||||
| 		pool_member->client->pool = pool; | ||||
|  | ||||
| 		/* Connect client */ | ||||
| 		if (mgcp_client_connect2(pool_member->client, 0)) { | ||||
| 			LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n", | ||||
| 				 pool_member->conf.remote_addr, pool_member->conf.remote_port); | ||||
| 			talloc_free(pool_member->client); | ||||
| 			pool_member->client = NULL; | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		pool_members_initialized++; | ||||
| 	} | ||||
|  | ||||
| 	return pool_members_initialized; | ||||
| } | ||||
|  | ||||
| /*! register a single mgcp_client instance to the pool. | ||||
|  *  \param[out] pool MGCP client pool descriptor. | ||||
|  *  \param[in] mgcp MGCP client descriptor. */ | ||||
| void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client) | ||||
| { | ||||
| 	/*! Some applications still support the non-pooled MGW VTY configuration variant provided by | ||||
| 	 *  mgcp_client_vty_init(). If this is the case the mgcp_client instance created by mgcp_client_init() | ||||
| 	 *  can be registered here so that it will appear as if it were part of the pool. When the user actively | ||||
| 	 *  configures MGW pool members, the MGCP client registered here will be ignored. (The registration of | ||||
| 	 *  multiple singe mgcp_client instances is not possible.) */ | ||||
| 	pool->mgcp_client_single = mgcp_client; | ||||
| } | ||||
|  | ||||
| /* Not every pool member may have a functional MGCP client, we will run through the pool once until we meet a | ||||
|  * pool member that is suitable (has a client, is not blocked, has a low load). */ | ||||
| static struct mgcp_client_pool_member *mgcp_client_pool_pick(struct mgcp_client_pool *pool) | ||||
| { | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
| 	struct mgcp_client_pool_member *pool_member_picked = NULL; | ||||
| 	unsigned int n_pool_members = llist_count(&pool->pool); | ||||
|  | ||||
| 	llist_for_each_entry(pool_member, &pool->pool, list) { | ||||
| 		if (pool_member->blocked == false && pool_member->client) { | ||||
| 			if (!pool_member_picked) | ||||
| 				pool_member_picked = pool_member; | ||||
| 			else if (pool_member_picked->refcount > pool_member->refcount) | ||||
| 				pool_member_picked = pool_member; | ||||
| 		} else { | ||||
| 			LOGPPMGW(pool_member, LOGL_DEBUG, "MGW pool has %u members -- MGW %u is unusable\n", n_pool_members, | ||||
| 				 pool_member->nr); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (pool_member_picked) { | ||||
| 		LOGPPMGW(pool_member_picked, LOGL_DEBUG, "MGW pool has %u members -- using MGW %u (active calls: %u)\n", | ||||
| 			 n_pool_members, pool_member_picked->nr, pool_member_picked->refcount); | ||||
| 		return pool_member_picked; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, | ||||
| 	     "MGW pool has %u members, but no functional MGW pool member found -- check configuration!\n", | ||||
| 	     n_pool_members); | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /*! get an MGCP client from the pool (increment reference counter). | ||||
|  *  \param[in,out] pool MGCP client pool descriptor. | ||||
|  *  \returns MGCP client descriptor, NULL if no member was found (empty pool). */ | ||||
| struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool) | ||||
| { | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
|  | ||||
| 	/*! When an MGCP client is taken from the pool it is still available for other calls. In fact only a reference | ||||
| 	 *  counter is incremented to keep track on how many references to a specific MGCP client are currently used | ||||
| 	 *  by the application code. */ | ||||
|  | ||||
| 	/* When the pool is empty, return a single MGCP client if it is registered. */ | ||||
| 	if (llist_empty(&pool->pool) && pool->mgcp_client_single) { | ||||
| 		LOGP(DLMGCP, LOGL_DEBUG, "MGW pool is empty -- using (single) MGW %s\n", | ||||
| 		     mgcp_client_name(pool->mgcp_client_single)); | ||||
| 		return pool->mgcp_client_single; | ||||
| 	} | ||||
|  | ||||
| 	/* Abort when the pool is empty */ | ||||
| 	if (llist_empty(&pool->pool)) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "MGW pool is empty -- no MGW available!\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* Pick a suitable pool member */ | ||||
| 	pool_member = mgcp_client_pool_pick(pool); | ||||
| 	if (pool_member) { | ||||
| 		pool_member->refcount++; | ||||
| 		return pool_member->client; | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /*! put an MGCP client back into the pool (decrement reference counter). | ||||
|  *  \param[in,out] pool MGCP client pool descriptor. | ||||
|  *  \param[in] mgcp MGCP client descriptor. | ||||
|  * | ||||
|  * This function is able to detect automatically to which pool the mgcp_client belongs. If the mgcp_client does | ||||
|  * not belong to a pool at all, the function call will have no effect. */ | ||||
| void mgcp_client_pool_put(struct mgcp_client *mgcp_client) | ||||
| { | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
| 	struct mgcp_client_pool *pool; | ||||
|  | ||||
| 	if (!mgcp_client) | ||||
| 		return; | ||||
|  | ||||
| 	if (mgcp_client->pool) | ||||
| 		pool = mgcp_client->pool; | ||||
| 	else | ||||
| 		return; | ||||
|  | ||||
| 	llist_for_each_entry(pool_member, &pool->pool, list) { | ||||
| 		if (pool_member->client == mgcp_client) { | ||||
| 			if (pool_member->refcount == 0) { | ||||
| 				LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n"); | ||||
| 				return; | ||||
| 			} | ||||
| 			pool_member->refcount--; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -24,27 +24,47 @@ | ||||
| #include <stdlib.h> | ||||
| #include <talloc.h> | ||||
|  | ||||
| #include <osmocom/vty/vty.h> | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/vty/misc.h> | ||||
| #include <osmocom/core/utils.h> | ||||
|  | ||||
| #include <osmocom/mgcp_client/mgcp_client.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_internal.h> | ||||
| #include <osmocom/mgcp_client/mgcp_client_pool_internal.h> | ||||
|  | ||||
| #define MGW_STR MGCP_CLIENT_MGW_STR | ||||
|  | ||||
| void *global_mgcp_client_ctx = NULL; | ||||
| struct mgcp_client_conf *global_mgcp_client_conf = NULL; | ||||
| /* Only common (non-pooled) VTY connands will use this talloc context. All | ||||
|  * pooled VTY commands will use the pool (global_mgcp_client_pool) as | ||||
|  * talloc context. */ | ||||
| static void *global_mgcp_client_ctx = NULL; | ||||
|  | ||||
| /* MGCP Client configuration used with mgcp_client_vty_init(). (This pointer | ||||
|  * points to user provided memory, so it cannot be used as talloc context.) */ | ||||
| static struct mgcp_client_conf *global_mgcp_client_conf = NULL; | ||||
|  | ||||
| /* Pointer to the MGCP pool that is managed by mgcp_client_pool_vty_init() */ | ||||
| static struct mgcp_client_pool *global_mgcp_client_pool = NULL; | ||||
|  | ||||
| struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty) | ||||
| { | ||||
| 	if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node) | ||||
| 		return vty->index; | ||||
| 	else | ||||
| 		return global_mgcp_client_conf; | ||||
| } | ||||
|  | ||||
| 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; | ||||
| 	OSMO_ASSERT(global_mgcp_client_ctx); | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
|  | ||||
| 	osmo_talloc_replace_string(global_mgcp_client_ctx, | ||||
| 				   (char**)&global_mgcp_client_conf->local_addr, | ||||
| 				   (char **)&conf->local_addr, | ||||
| 				   argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| @@ -58,9 +78,9 @@ DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd, | ||||
|       MGW_STR "local port to connect to MGW from\n" | ||||
|       "local bind port\n") | ||||
| { | ||||
| 	if (!global_mgcp_client_conf) | ||||
| 		return CMD_ERR_NOTHING_TODO; | ||||
| 	global_mgcp_client_conf->local_port = atoi(argv[0]); | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
|  | ||||
| 	conf->local_port = atoi(argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd, | ||||
| @@ -69,16 +89,15 @@ 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; | ||||
| 	OSMO_ASSERT(global_mgcp_client_ctx); | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
|  | ||||
| 	osmo_talloc_replace_string(global_mgcp_client_ctx, | ||||
| 				   (char**)&global_mgcp_client_conf->remote_addr, | ||||
| 				   argv[0]); | ||||
| 				   (char **)&conf->remote_addr, argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd, | ||||
| @@ -91,9 +110,9 @@ DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd, | ||||
|       MGW_STR "remote port to reach the MGW at\n" | ||||
|       "remote port\n") | ||||
| { | ||||
| 	if (!global_mgcp_client_conf) | ||||
| 		return CMD_ERR_NOTHING_TODO; | ||||
| 	global_mgcp_client_conf->remote_port = atoi(argv[0]); | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
|  | ||||
| 	conf->remote_port = atoi(argv[0]); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
| ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd, | ||||
| @@ -143,66 +162,396 @@ DEFUN(cfg_mgw_endpoint_domain_name, | ||||
|       MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n" | ||||
|       "Domain name, should be alphanumeric.\n") | ||||
| { | ||||
| 	if (osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0], | ||||
| 			 sizeof(global_mgcp_client_conf->endpoint_domain_name)) | ||||
| 	    >= sizeof(global_mgcp_client_conf->endpoint_domain_name)) { | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
|  | ||||
| 	if (osmo_strlcpy(conf->endpoint_domain_name, argv[0], sizeof(conf->endpoint_domain_name)) | ||||
| 	    >= sizeof(conf->endpoint_domain_name)) { | ||||
| 		vty_out(vty, "%% Error: 'mgw endpoint-domain' name too long, max length is %zu: '%s'%s", | ||||
| 			sizeof(global_mgcp_client_conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE); | ||||
| 			sizeof(conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| int mgcp_client_config_write(struct vty *vty, const char *indent) | ||||
| DEFUN(cfg_mgw_reset_ep_name, | ||||
|       cfg_mgw_reset_ep_name_cmd, | ||||
|       "mgw reset-endpoint NAME", | ||||
|       MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list," | ||||
|       "e.g. 'rtpbridge/*'\n" | ||||
|       "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n") | ||||
| { | ||||
| 	const char *addr; | ||||
| 	int port; | ||||
| 	int rc; | ||||
| 	struct reset_ep *reset_ep; | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
|  | ||||
| 	addr = global_mgcp_client_conf->local_addr; | ||||
| 	if (addr) | ||||
| 		vty_out(vty, "%smgw local-ip %s%s", indent, addr, | ||||
| 			VTY_NEWLINE); | ||||
| 	port = global_mgcp_client_conf->local_port; | ||||
| 	if (port >= 0) | ||||
| 		vty_out(vty, "%smgw local-port %u%s", indent, | ||||
| 			(uint16_t)port, VTY_NEWLINE); | ||||
| 	/* stop when the address is already in the list */ | ||||
| 	llist_for_each_entry(reset_ep, &conf->reset_epnames, list) { | ||||
| 		if (strcmp(argv[0], reset_ep->name) == 0) { | ||||
| 			vty_out(vty, "%% duplicate endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||
| 			return CMD_WARNING; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	addr = global_mgcp_client_conf->remote_addr; | ||||
| 	if (addr) | ||||
| 		vty_out(vty, "%smgw remote-ip %s%s", indent, addr, | ||||
| 			VTY_NEWLINE); | ||||
| 	port = global_mgcp_client_conf->remote_port; | ||||
| 	if (port >= 0) | ||||
| 		vty_out(vty, "%smgw remote-port %u%s", indent, | ||||
| 			(uint16_t)port, VTY_NEWLINE); | ||||
| 	/* the domain name is not part of the actual endpoint name */ | ||||
| 	if (strchr(argv[0], '@')) { | ||||
| 		vty_out(vty, "%% the endpoint name must be given without domain name ('%s')%s", | ||||
| 			argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	if (global_mgcp_client_conf->endpoint_domain_name[0]) | ||||
| 		vty_out(vty, "%smgw endpoint-domain %s%s", indent, | ||||
| 			global_mgcp_client_conf->endpoint_domain_name, VTY_NEWLINE); | ||||
| 	reset_ep = talloc_zero(global_mgcp_client_ctx, struct reset_ep); | ||||
| 	OSMO_ASSERT(reset_ep); | ||||
|  | ||||
| 	rc = osmo_strlcpy(reset_ep->name, argv[0], sizeof(reset_ep->name)); | ||||
| 	if (rc >= sizeof(reset_ep->name)) { | ||||
| 		vty_out(vty, "%% Error: 'mgw reset-endpoint' name too long, max length is %zu: '%s'%s", | ||||
| 			sizeof(reset_ep->name) - 1, argv[0], VTY_NEWLINE); | ||||
| 		talloc_free(reset_ep); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	llist_add_tail(&reset_ep->list, &conf->reset_epnames); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf) | ||||
| DEFUN(cfg_mgw_no_reset_ep_name, | ||||
|       cfg_mgw_no_reset_ep_name_cmd, | ||||
|       "no mgw reset-endpoint NAME", | ||||
|       NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n" | ||||
|       "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n") | ||||
| { | ||||
| 	struct reset_ep *reset_ep; | ||||
| 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||
|  | ||||
| 	llist_for_each_entry(reset_ep, &conf->reset_epnames, list) { | ||||
| 		if (strcmp(argv[0], reset_ep->name) == 0) { | ||||
| 			llist_del(&reset_ep->list); | ||||
| 			talloc_free(reset_ep); | ||||
| 			return CMD_SUCCESS; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	vty_out(vty, "%% no such endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||
| 	return CMD_WARNING; | ||||
| } | ||||
|  | ||||
| static int config_write(struct vty *vty, const char *indent, struct mgcp_client_conf *conf) | ||||
| { | ||||
| 	const char *addr; | ||||
| 	int port; | ||||
| 	struct reset_ep *reset_ep; | ||||
|  | ||||
| 	if (conf->description) | ||||
| 		vty_out(vty, "%sdescription %s%s", indent, conf->description, VTY_NEWLINE); | ||||
|  | ||||
| 	addr = conf->local_addr; | ||||
| 	if (addr) | ||||
| 		vty_out(vty, "%smgw local-ip %s%s", indent, addr, | ||||
| 			VTY_NEWLINE); | ||||
| 	port = conf->local_port; | ||||
| 	if (port >= 0) | ||||
| 		vty_out(vty, "%smgw local-port %u%s", indent, | ||||
| 			(uint16_t)port, VTY_NEWLINE); | ||||
|  | ||||
| 	addr = conf->remote_addr; | ||||
| 	if (addr) | ||||
| 		vty_out(vty, "%smgw remote-ip %s%s", indent, addr, | ||||
| 			VTY_NEWLINE); | ||||
| 	port = conf->remote_port; | ||||
| 	if (port >= 0) | ||||
| 		vty_out(vty, "%smgw remote-port %u%s", indent, | ||||
| 			(uint16_t)port, VTY_NEWLINE); | ||||
|  | ||||
| 	if (conf->endpoint_domain_name[0]) | ||||
| 		vty_out(vty, "%smgw endpoint-domain %s%s", indent, | ||||
| 			conf->endpoint_domain_name, VTY_NEWLINE); | ||||
|  | ||||
| 	llist_for_each_entry(reset_ep, &conf->reset_epnames, list) | ||||
| 		vty_out(vty, "%smgw reset-endpoint %s%s", indent, reset_ep->name, VTY_NEWLINE); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| /*! Write out MGCP client config to VTY. | ||||
|  *  \param[in] vty VTY to which we should print. | ||||
|  *  \param[in] string used for indentation (e.g. " "). | ||||
|  *  \returns CMD_SUCCESS on success, CMD_WARNING on error */ | ||||
| int mgcp_client_config_write(struct vty *vty, const char *indent) | ||||
| { | ||||
| 	return config_write(vty, indent, global_mgcp_client_conf); | ||||
| } | ||||
|  | ||||
| static void vty_init_common(void *talloc_ctx, int node) | ||||
| { | ||||
| 	global_mgcp_client_ctx = talloc_ctx; | ||||
| 	global_mgcp_client_conf = conf; | ||||
|  | ||||
| 	install_element(node, &cfg_mgw_local_ip_cmd); | ||||
| 	install_element(node, &cfg_mgw_local_port_cmd); | ||||
| 	install_element(node, &cfg_mgw_remote_ip_cmd); | ||||
| 	install_element(node, &cfg_mgw_remote_port_cmd); | ||||
| 	install_element(node, &cfg_mgw_endpoint_range_cmd); | ||||
| 	install_element(node, &cfg_mgw_rtp_bts_base_port_cmd); | ||||
| 	install_element(node, &cfg_mgw_endpoint_domain_name_cmd); | ||||
|  | ||||
| 	/* deprecated 'mgcpgw' commands */ | ||||
| 	install_element(node, &cfg_mgcpgw_local_ip_cmd); | ||||
| 	install_element(node, &cfg_mgcpgw_local_port_cmd); | ||||
| 	install_element(node, &cfg_mgcpgw_remote_ip_cmd); | ||||
| 	install_element(node, &cfg_mgcpgw_remote_port_cmd); | ||||
| 	install_element(node, &cfg_mgcpgw_endpoint_range_cmd); | ||||
| 	install_element(node, &cfg_mgcpgw_rtp_bts_base_port_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); | ||||
| 	install_lib_element(node, &cfg_mgw_reset_ep_name_cmd); | ||||
| 	install_lib_element(node, &cfg_mgw_no_reset_ep_name_cmd); | ||||
|  | ||||
| 	osmo_fsm_vty_add_cmds(); | ||||
| } | ||||
|  | ||||
| /*! Set up MGCP client VTY | ||||
|  *  (called once at startup by the application process). | ||||
|  *  \param[in] talloc_ctx talloc context to be used by the VTY for allocating memory. | ||||
|  *  \param[in] node identifier of the node on which the VTY commands should be installed. | ||||
|  *  \param[in] conf user provided memory to to store the MGCP client configuration data. */ | ||||
| void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf) | ||||
| { | ||||
| 	global_mgcp_client_conf = conf; | ||||
|  | ||||
| 	/* deprecated 'mgcpgw' commands */ | ||||
| 	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); | ||||
|  | ||||
| 	vty_init_common(talloc_ctx, node); | ||||
| } | ||||
|  | ||||
| static int config_write_pool(struct vty *vty) | ||||
| { | ||||
| 	struct mgcp_client_pool *pool = global_mgcp_client_pool; | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
| 	unsigned int indent_buf_len = strlen(pool->vty_indent) + 1 + 1; | ||||
| 	char *indent = talloc_zero_size(vty, indent_buf_len); | ||||
|  | ||||
| 	snprintf(indent, indent_buf_len, "%s ", pool->vty_indent); | ||||
|  | ||||
| 	llist_for_each_entry(pool_member, &pool->pool, list) { | ||||
| 		vty_out(vty, "%smgw %u%s", pool->vty_indent, pool_member->nr, VTY_NEWLINE); | ||||
| 		config_write(vty, indent, &pool_member->conf); | ||||
| 	} | ||||
|  | ||||
| 	talloc_free(indent); | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| /* Lookup the selected MGCP client config by its reference number */ | ||||
| static struct mgcp_client_pool_member *pool_member_by_nr(unsigned int nr) | ||||
| { | ||||
| 	struct mgcp_client_pool_member *pool_member = NULL; | ||||
| 	struct mgcp_client_pool_member *pool_member_tmp; | ||||
|  | ||||
| 	llist_for_each_entry(pool_member_tmp, &global_mgcp_client_pool->pool, list) { | ||||
| 		if (pool_member_tmp->nr == nr) { | ||||
| 			pool_member = pool_member_tmp; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return pool_member; | ||||
| } | ||||
|  | ||||
| DEFUN_ATTR(cfg_mgw, | ||||
| 	   cfg_mgw_cmd, "mgw <0-255>", "Select a MGCP client config to setup\n" "reference number\n", CMD_ATTR_IMMEDIATE) | ||||
| { | ||||
| 	int nr = atoi(argv[0]); | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
|  | ||||
| 	pool_member = pool_member_by_nr(nr); | ||||
| 	if (!pool_member) { | ||||
| 		pool_member = talloc_zero(global_mgcp_client_pool, struct mgcp_client_pool_member); | ||||
| 		OSMO_ASSERT(pool_member); | ||||
| 		mgcp_client_conf_init(&pool_member->conf); | ||||
| 		pool_member->nr = nr; | ||||
| 		llist_add_tail(&pool_member->list, &global_mgcp_client_pool->pool); | ||||
| 	} | ||||
|  | ||||
| 	vty->index = &pool_member->conf; | ||||
| 	vty->index_sub = &pool_member->conf.description; | ||||
| 	vty->node = global_mgcp_client_pool->vty_node->node; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN_ATTR(cfg_no_mgw, | ||||
| 	   cfg_no_mgw_cmd, | ||||
| 	   "no mgw <0-255>", NO_STR "Select a MGCP client config to remove\n" "reference number\n", CMD_ATTR_IMMEDIATE) | ||||
| { | ||||
| 	int nr = atoi(argv[0]); | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
|  | ||||
| 	pool_member = pool_member_by_nr(nr); | ||||
| 	if (!pool_member) { | ||||
| 		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	/* Make sure that there are no ongoing calls */ | ||||
| 	if (pool_member->refcount > 0) { | ||||
| 		vty_out(vty, "%% MGCP client (MGW %s) is still serving ongoing calls -- can't remove it now!%s", | ||||
| 			mgcp_client_pool_member_name(pool_member), VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	llist_del(&pool_member->list); | ||||
| 	if (pool_member->client) { | ||||
| 		mgcp_client_disconnect(pool_member->client); | ||||
| 		talloc_free(pool_member->client); | ||||
| 	} | ||||
| 	talloc_free(pool_member); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd, | ||||
| 	   "mgw <0-255> reconnect", | ||||
| 	   MGW_STR "reference number\n" "reconfigure and reconnect MGCP client\n", CMD_ATTR_IMMEDIATE) | ||||
| { | ||||
| 	int nr = atoi(argv[0]); | ||||
| 	struct mgcp_client_pool_member *pool_member = NULL; | ||||
|  | ||||
| 	pool_member = pool_member_by_nr(nr); | ||||
| 	if (!pool_member) { | ||||
| 		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	/* Make sure that there are no ongoing calls */ | ||||
| 	if (pool_member->refcount > 0) { | ||||
| 		vty_out(vty, "%% MGCP client (MGW %s) is still serving ongoing calls -- can't reconnect it now!%s", | ||||
| 			mgcp_client_pool_member_name(pool_member), VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	/* Get rid of a possibly existing old MGCP client instance first */ | ||||
| 	if (pool_member->client) { | ||||
| 		mgcp_client_disconnect(pool_member->client); | ||||
| 		talloc_free(pool_member->client); | ||||
| 	} | ||||
|  | ||||
| 	/* Create a new MGCP client instance with the current config */ | ||||
| 	pool_member->client = mgcp_client_init(pool_member, &pool_member->conf); | ||||
| 	if (!pool_member->client) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s initalization failed\n", | ||||
| 		     mgcp_client_pool_member_name(pool_member)); | ||||
| 		vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s", | ||||
| 			mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */ | ||||
| 	pool_member->client->pool = global_mgcp_client_pool; | ||||
|  | ||||
| 	/* Connect client */ | ||||
| 	if (mgcp_client_connect(pool_member->client)) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s connect failed at (%s:%u)\n", | ||||
| 		     mgcp_client_pool_member_name(pool_member), pool_member->conf.remote_addr, | ||||
| 		     pool_member->conf.remote_port); | ||||
| 		talloc_free(pool_member->client); | ||||
| 		pool_member->client = NULL; | ||||
| 		vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s", | ||||
| 			mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN_ATTR(mgw_block, mgw_block_cmd, | ||||
| 	   "mgw <0-255> block", | ||||
| 	   MGW_STR "reference number\n" "block MGCP client so that it won't be used for new calls\n", CMD_ATTR_IMMEDIATE) | ||||
| { | ||||
| 	int nr = atoi(argv[0]); | ||||
| 	struct mgcp_client_pool_member *pool_member = NULL; | ||||
|  | ||||
| 	pool_member = pool_member_by_nr(nr); | ||||
| 	if (!pool_member) { | ||||
| 		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	pool_member->blocked = true; | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd, | ||||
| 	   "mgw <0-255> unblock", | ||||
| 	   MGW_STR "reference number\n" "unblock MGCP client so that it will be available for new calls\n", CMD_ATTR_IMMEDIATE) | ||||
| { | ||||
| 	int nr = atoi(argv[0]); | ||||
| 	struct mgcp_client_pool_member *pool_member = NULL; | ||||
|  | ||||
| 	pool_member = pool_member_by_nr(nr); | ||||
| 	if (!pool_member) { | ||||
| 		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	pool_member->blocked = false; | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(mgw_show, mgw_snow_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n") | ||||
| { | ||||
| 	vty_out(vty, "%% MGW-Pool:%s", VTY_NEWLINE); | ||||
| 	struct mgcp_client_pool_member *pool_member; | ||||
|  | ||||
| 	if (llist_empty(&global_mgcp_client_pool->pool) && global_mgcp_client_pool->mgcp_client_single) { | ||||
| 		vty_out(vty, "%%  (pool is empty, single MGCP client will be used)%s", VTY_NEWLINE); | ||||
| 		return CMD_SUCCESS; | ||||
| 	} else if (llist_empty(&global_mgcp_client_pool->pool)) { | ||||
| 		vty_out(vty, "%%  (pool is empty)%s", VTY_NEWLINE); | ||||
| 		return CMD_SUCCESS; | ||||
| 	} | ||||
|  | ||||
| 	llist_for_each_entry(pool_member, &global_mgcp_client_pool->pool, list) { | ||||
| 		vty_out(vty, "%%  MGW %s%s", mgcp_client_pool_member_name(pool_member), VTY_NEWLINE); | ||||
| 		vty_out(vty, "%%   mgcp-client:   %s%s", pool_member->client ? "connected" : "disconnected", | ||||
| 			VTY_NEWLINE); | ||||
| 		vty_out(vty, "%%   service:       %s%s", pool_member->blocked ? "blocked" : "unblocked", VTY_NEWLINE); | ||||
| 		vty_out(vty, "%%   ongoing calls: %u%s", pool_member->refcount, VTY_NEWLINE); | ||||
| 	} | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| /*! Set up MGCP client VTY (pooled) | ||||
|  *  (called once at startup by the application process). | ||||
|  *  \param[in] parent_node identifier of the parent node on which the mgw node appears. | ||||
|  *  \param[in] mgw_node identifier that should be used with the newly installed MGW node. | ||||
|  *  \param[in] indent indentation string to match the indentation in the VTY config | ||||
|  *  \param[in] pool user provided memory to store the configured MGCP client (MGW) pool. */ | ||||
| void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool) | ||||
| { | ||||
| 	/* A pool must be allocated before this function can be called */ | ||||
| 	OSMO_ASSERT(pool); | ||||
|  | ||||
| 	/* Never allow this function to be called twice on the same pool */ | ||||
| 	OSMO_ASSERT(!pool->vty_indent); | ||||
| 	OSMO_ASSERT(!pool->vty_node); | ||||
|  | ||||
| 	pool->vty_indent = talloc_strdup(pool, indent); | ||||
| 	OSMO_ASSERT(pool->vty_indent); | ||||
| 	pool->vty_node = talloc_zero(pool, struct cmd_node); | ||||
| 	OSMO_ASSERT(pool->vty_node); | ||||
| 	pool->vty_node->node = mgw_node; | ||||
| 	pool->vty_node->vtysh = 1; | ||||
| 	pool->vty_node->prompt = talloc_strdup(pool->vty_node, "%s(config-mgw)# "); | ||||
|  | ||||
| 	install_lib_element(parent_node, &cfg_mgw_cmd); | ||||
| 	install_lib_element(parent_node, &cfg_no_mgw_cmd); | ||||
|  | ||||
| 	install_node(pool->vty_node, config_write_pool); | ||||
| 	vty_init_common(pool, mgw_node); | ||||
|  | ||||
| 	install_element(mgw_node, &cfg_description_cmd); | ||||
|  | ||||
| 	install_lib_element(ENABLE_NODE, &mgw_reconnect_cmd); | ||||
| 	install_lib_element(ENABLE_NODE, &mgw_block_cmd); | ||||
| 	install_lib_element(ENABLE_NODE, &mgw_unblock_cmd); | ||||
|  | ||||
| 	install_lib_element_ve(&mgw_snow_cmd); | ||||
|  | ||||
| 	global_mgcp_client_pool = pool; | ||||
| } | ||||
|   | ||||
| @@ -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,9 @@ 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 \ | ||||
| 	mgcp_iuup.c \ | ||||
| 	$(NULL) | ||||
|   | ||||
| @@ -14,10 +14,6 @@ | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| static inline int val_seg(int val) | ||||
|   | ||||
| @@ -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 | ||||
| @@ -84,15 +89,13 @@ static void codec_init(struct mgcp_rtp_codec *codec) | ||||
| 		.frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN, | ||||
| 		.rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE, | ||||
| 		.channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS, | ||||
| 		.subtype_name = "", | ||||
| 		.audio_name = "", | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| static void codec_free(struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	if (codec->subtype_name) | ||||
| 		talloc_free(codec->subtype_name); | ||||
| 	if (codec->audio_name) | ||||
| 		talloc_free(codec->audio_name); | ||||
| 	*codec = (struct mgcp_rtp_codec){}; | ||||
| } | ||||
|  | ||||
| @@ -119,10 +122,8 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud | ||||
| { | ||||
| 	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. */ | ||||
| @@ -155,16 +156,16 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud | ||||
| 	if (!audio_name) { | ||||
| 		switch (payload_type) { | ||||
| 		case 0: | ||||
| 			audio_name = talloc_strdup(ctx, "PCMU/8000/1"); | ||||
| 			strcpy(codec->audio_name, "PCMU/8000/1"); | ||||
| 			break; | ||||
| 		case 3: | ||||
| 			audio_name = talloc_strdup(ctx, "GSM/8000/1"); | ||||
| 			strcpy(codec->audio_name, "GSM/8000/1"); | ||||
| 			break; | ||||
| 		case 8: | ||||
| 			audio_name = talloc_strdup(ctx, "PCMA/8000/1"); | ||||
| 			strcpy(codec->audio_name, "PCMA/8000/1"); | ||||
| 			break; | ||||
| 		case 18: | ||||
| 			audio_name = talloc_strdup(ctx, "G729/8000/1"); | ||||
| 			strcpy(codec->audio_name, "G729/8000/1"); | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* The given payload type is not known to us, or it | ||||
| @@ -174,36 +175,36 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud | ||||
| 			     payload_type); | ||||
| 			goto error; | ||||
| 		} | ||||
| 	} else { | ||||
| 		OSMO_STRLCPY_ARRAY(codec->audio_name, audio_name); | ||||
| 	} | ||||
|  | ||||
| 	/* Now we extract the codec subtype name, rate and channels. The latter | ||||
| 	 * two are optional. If they are not present we use the safe defaults | ||||
| 	 * above. */ | ||||
| 	if (strlen(audio_name) >= sizeof(audio_codec)) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(audio_name, -1)); | ||||
| 	if (strlen(codec->audio_name) >= sizeof(codec->subtype_name)) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(codec->audio_name, -1)); | ||||
| 		goto error; | ||||
| 	} | ||||
| 	channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS; | ||||
| 	rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; | ||||
| 	if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(audio_name, -1)); | ||||
| 	if (sscanf(codec->audio_name, "%63[^/]/%d/%d", codec->subtype_name, &rate, &channels) < 1) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(codec->audio_name, -1)); | ||||
| 		goto error; | ||||
| 	} | ||||
|  | ||||
| 	/* Note: We only accept configurations with one audio channel! */ | ||||
| 	if (channels != 1) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot handle audio codec with more than one channel: %s\n", | ||||
| 		     osmo_quote_str(audio_name, -1)); | ||||
| 		     osmo_quote_str(codec->audio_name, -1)); | ||||
| 		goto error; | ||||
| 	} | ||||
|  | ||||
| 	codec->rate = rate; | ||||
| 	codec->channels = channels; | ||||
| 	codec->subtype_name = talloc_strdup(ctx, audio_codec); | ||||
| 	codec->audio_name = talloc_strdup(ctx, audio_name); | ||||
| 	codec->payload_type = payload_type; | ||||
|  | ||||
| 	if (!strcmp(audio_codec, "G729")) { | ||||
| 	if (!strcmp(codec->subtype_name, "G729")) { | ||||
| 		codec->frame_duration_num = 10; | ||||
| 		codec->frame_duration_den = 1000; | ||||
| 	} else { | ||||
| @@ -279,36 +280,16 @@ error: | ||||
|  * 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) | ||||
| 	if (!strlen(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; | ||||
| } | ||||
|  | ||||
| /*! Decide for one suitable codec | ||||
| @@ -335,7 +316,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; | ||||
| @@ -374,7 +355,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn) | ||||
|  * | ||||
|  * https://tools.ietf.org/html/rfc4867 | ||||
|  */ | ||||
| static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec) | ||||
| bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec) | ||||
| { | ||||
| 	if (!codec->param_present) | ||||
| 		return false; | ||||
| @@ -397,10 +378,10 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c | ||||
| 		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; | ||||
| 	} | ||||
|  | ||||
| 	/* Note: AMR allows to set the RTP payload format to octet-aligned or bandwith-efficient (octet-aligned=0) | ||||
| 	 * via SDP. This difference concerns payload format only, but not the actual codec. It is not a difference | ||||
| 	 * within the meaning of this function. */ | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
| @@ -436,7 +417,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp | ||||
| 	if (!codec_src) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	/* Use the codec infrmation from the source and try to find the | ||||
| 	/* Use the codec information from the source and try to find the | ||||
| 	 * equivalent of it on the destination side */ | ||||
| 	codecs_assigned = rtp_dst->codecs_assigned; | ||||
| 	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS); | ||||
| @@ -451,3 +432,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; | ||||
| } | ||||
|   | ||||
| @@ -21,12 +21,17 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdatomic.h> | ||||
| #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/mgcp/mgcp_iuup.h> | ||||
|  | ||||
| #include <osmocom/gsm/gsm_utils.h> | ||||
| #include <osmocom/core/rate_ctr.h> | ||||
| #include <osmocom/core/timer.h> | ||||
| @@ -87,7 +92,7 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn * | ||||
| 	/* FIXME: Each new rate counter group requires an unique index. At the | ||||
| 	 * moment we generate this index using this counter, but perhaps there | ||||
| 	 * is a more concious way to assign the indexes. */ | ||||
| 	static unsigned int rate_ctr_index = 0; | ||||
| 	static atomic_uint rate_ctr_index = 0; | ||||
|  | ||||
| 	conn_rtp->type = MGCP_RTP_DEFAULT; | ||||
| 	conn_rtp->osmux.cid_allocated = false; | ||||
| @@ -105,16 +110,15 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn * | ||||
| 	/* Set default values */ | ||||
| 	end->frames_per_packet = 0;	/* unknown */ | ||||
| 	end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS; | ||||
| 	end->output_enabled = 0; | ||||
| 	end->output_enabled = false; | ||||
| 	end->maximum_packet_time = -1; | ||||
|  | ||||
| 	conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index); | ||||
| 	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++; | ||||
| 	conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, IN_STREAM_ERR_TSTMP_CTR); | ||||
| 	conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, OUT_STREAM_ERR_TSTMP_CTR); | ||||
|  | ||||
| 	/* Make sure codec table is reset */ | ||||
| 	mgcp_codec_reset_all(conn_rtp); | ||||
| @@ -127,6 +131,8 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp) | ||||
| { | ||||
| 	if (mgcp_conn_rtp_is_osmux(conn_rtp)) | ||||
| 		conn_osmux_disable(conn_rtp); | ||||
| 	if (mgcp_conn_rtp_is_iuup(conn_rtp)) | ||||
| 		mgcp_conn_iuup_cleanup(conn_rtp); | ||||
| 	mgcp_free_rtp_port(&conn_rtp->end); | ||||
| 	rate_ctr_group_free(conn_rtp->rate_ctr_group); | ||||
| 	mgcp_codec_reset_all(conn_rtp); | ||||
| @@ -141,7 +147,7 @@ void mgcp_conn_watchdog_cb(void *data) | ||||
|  | ||||
| void mgcp_conn_watchdog_kick(struct mgcp_conn *conn) | ||||
| { | ||||
| 	int timeout = conn->endp->cfg->conn_timeout; | ||||
| 	int timeout = conn->endp->trunk->cfg->conn_timeout; | ||||
| 	if (!timeout) | ||||
| 		return; | ||||
|  | ||||
| @@ -197,7 +203,7 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, | ||||
| 	/* Initialize watchdog */ | ||||
| 	osmo_timer_setup(&conn->watchdog, mgcp_conn_watchdog_cb, conn); | ||||
| 	mgcp_conn_watchdog_kick(conn); | ||||
| 	llist_add(&conn->entry, &endp->conns); | ||||
| 	mgcp_endp_add_conn(endp, conn); | ||||
|  | ||||
| 	return conn; | ||||
| } | ||||
| @@ -254,12 +260,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; | ||||
| @@ -269,10 +273,13 @@ 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(rate_ctr_group_get_ctr(all_stats, IN_STREAM_ERR_TSTMP_CTR), | ||||
| 		     rate_ctr_group_get_ctr(conn_stats, IN_STREAM_ERR_TSTMP_CTR)->current); | ||||
| 	rate_ctr_add(rate_ctr_group_get_ctr(all_stats, OUT_STREAM_ERR_TSTMP_CTR), | ||||
| 		     rate_ctr_group_get_ctr(conn_stats, OUT_STREAM_ERR_TSTMP_CTR)->current); | ||||
|  | ||||
| 	rate_ctr_inc(&all_stats->ctr[RTP_NUM_CONNECTIONS]); | ||||
| 	rate_ctr_inc(rate_ctr_group_get_ctr(all_stats, RTP_NUM_CONNECTIONS)); | ||||
| } | ||||
|  | ||||
| /*! free a connection by its ID. | ||||
| @@ -286,15 +293,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: | ||||
| @@ -305,7 +306,8 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id) | ||||
| 	} | ||||
|  | ||||
| 	osmo_timer_del(&conn->watchdog); | ||||
| 	llist_del(&conn->entry); | ||||
| 	mgcp_endp_remove_conn(endp, conn); | ||||
| 	/* WARN: endp may have be freed after call to mgcp_endp_remove_conn */ | ||||
| 	talloc_free(conn); | ||||
| } | ||||
|  | ||||
| @@ -327,15 +329,18 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp) | ||||
|  | ||||
| /*! free all connections at once. | ||||
|  *  \param[in] endp associated endpoint */ | ||||
| #if defined(__has_attribute) | ||||
| #if __has_attribute(no_sanitize) | ||||
| __attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */ | ||||
| #endif | ||||
| #endif | ||||
| void mgcp_conn_free_all(struct mgcp_endpoint *endp) | ||||
| { | ||||
| 	struct mgcp_conn *conn; | ||||
| 	struct mgcp_conn *conn_tmp; | ||||
|  | ||||
| 	/* Drop all items in the list */ | ||||
| 	llist_for_each_entry_safe(conn, conn_tmp, &endp->conns, entry) { | ||||
| 	/* Drop all items in the list, might be consecutive! */ | ||||
| 	while ((conn = llist_first_entry_or_null(&endp->conns, struct mgcp_conn, entry))) | ||||
| 		mgcp_conn_free(endp, conn->id); | ||||
| 	} | ||||
|  | ||||
| 	return; | ||||
| } | ||||
| @@ -346,6 +351,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)"); | ||||
| @@ -359,7 +365,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; | ||||
| @@ -395,3 +401,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_c(endp->trunk, 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_ctr_group_get_ctr(rate_ctrs, 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) | ||||
| { | ||||
| 	unsigned int rtp_hdr_len = sizeof(struct rtp_hdr); | ||||
| 	struct mgcp_endpoint *endp = user_data; | ||||
| 	struct msgb *msg = msgb_alloc_c(endp->trunk, RTP_BUF_SIZE, "RTP-rx-from-E1"); | ||||
| 	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_ctr_group_get_ctr(rate_ctrs, 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_c(trunk, 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_ERROR, "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_INFO, "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_ERROR, "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_INFO, "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_INFO, "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_c(endp->trunk, 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_ctr_group_get_ctr(rate_ctrs, 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,17 +21,95 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #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> | ||||
| #include <osmocom/core/stat_item.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, unsigned int 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->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) | ||||
| @@ -44,6 +122,12 @@ void mgcp_endp_release(struct mgcp_endpoint *endp) | ||||
| 	 * RSIP is executed), free them all at once. */ | ||||
| 	mgcp_conn_free_all(endp); | ||||
|  | ||||
| 	/* We must only decrement the stat item when the endpoint as actually | ||||
| 	 * claimed. An endpoint is claimed when a call-id is set */ | ||||
| 	if (endp->callid) | ||||
| 		osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common, | ||||
| 								 TRUNK_STAT_ENDPOINTS_USED), 1); | ||||
|  | ||||
| 	/* Reset endpoint parameters and states */ | ||||
| 	talloc_free(endp->callid); | ||||
| 	endp->callid = NULL; | ||||
| @@ -51,5 +135,543 @@ void mgcp_endp_release(struct mgcp_endpoint *endp) | ||||
| 	endp->local_options.string = NULL; | ||||
| 	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) | ||||
|  *  \param[out] epname_stripped pointer to store the stripped ep name. | ||||
|  *  \param[in] epname endpoint name to lookup. | ||||
|  *  \param[in] trunk where the endpoint is located. */ | ||||
| void mgcp_endp_strip_name(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 of a trunk specified by its name. | ||||
|  *  \param[in] epname endpoint name to check. | ||||
|  *  \param[in] trunk mgcp_trunk that might have this endpoint. | ||||
|  *  \returns NULL if no ep found, else endpoint. */ | ||||
| struct mgcp_endpoint *mgcp_endp_find_specific(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 */ | ||||
| 	mgcp_endp_strip_name(epname_stripped, epname, trunk); | ||||
|  | ||||
| 	for (i = 0; i < trunk->number_endpoints; i++) { | ||||
| 		endp = trunk->endpoints[i]; | ||||
| 		mgcp_endp_strip_name(epname_stripped_endp, endp->name, trunk); | ||||
| 		if (strcmp(epname_stripped_endp, epname_stripped) == 0) | ||||
| 			return endp; | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /*! Check if the given epname refers to a wildcarded request or to a specific | ||||
|  *  endpoint. | ||||
|  *  \param[in] epname endpoint name to check | ||||
|  *  \returns true if epname refers to wildcarded request, else false. */ | ||||
| bool mgcp_endp_is_wildcarded(const char *epname) | ||||
| { | ||||
| 	if (strstr(epname, "*")) | ||||
| 		return true; | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| /*! Find an endpoint by its name on a specified trunk. | ||||
|  *  \param[out] cause pointer to store cause code, can be NULL. | ||||
|  *  \param[in] epname endpoint name to lookup. | ||||
|  *  \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 (mgcp_endp_is_wildcarded(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); | ||||
| 			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 = mgcp_endp_find_specific(epname, trunk); | ||||
| 	if (endp) { | ||||
| 		LOGPENDP(endp, DLMGCP, LOGL_DEBUG, | ||||
| 			 "(trunk:%d) found endpoint: %s\n", | ||||
| 			 trunk->trunk_nr, endp->name); | ||||
| 		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; | ||||
| } | ||||
|  | ||||
| /*! 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; | ||||
|  | ||||
| 	/* 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 = mgcp_endp_find_specific(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); | ||||
| 	osmo_stat_item_inc(osmo_stat_item_group_get_item(endp->trunk->stats.common, | ||||
| 							 TRUNK_STAT_ENDPOINTS_USED), 1); | ||||
|  | ||||
| 	/* 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); | ||||
| } | ||||
|   | ||||
							
								
								
									
										720
									
								
								src/libosmo-mgcp/mgcp_iuup.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										720
									
								
								src/libosmo-mgcp/mgcp_iuup.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,720 @@ | ||||
| /* | ||||
|  * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All rights not specifically granted under this license are reserved. | ||||
|  * | ||||
|  * Author: Pau Espin Pedrol | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU Affero General Public License as published by the | ||||
|  * Free Software Foundation; either version 3 of the License, or (at your | ||||
|  * option) any later version. | ||||
|  */ | ||||
|  | ||||
| #include <stdint.h> | ||||
|  | ||||
| #include <osmocom/core/byteswap.h> | ||||
|  | ||||
| #include <osmocom/gsm/iuup.h> | ||||
|  | ||||
| #include <osmocom/netif/rtp.h> | ||||
| #include <osmocom/netif/amr.h> | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
| #include <osmocom/mgcp/mgcp_iuup.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
| #include <osmocom/mgcp/mgcp_codec.h> | ||||
| #include <osmocom/mgcp/mgcp_network.h> | ||||
| #include <osmocom/mgcp/debug.h> | ||||
|  | ||||
| #define MGW_IUUP_MSGB_SIZE 4096 | ||||
|  | ||||
| static const struct osmo_iuup_rnl_config def_configure_req = { | ||||
| 	.transparent = false, | ||||
| 	.active = true, | ||||
| 	.supported_versions_mask = 0x0003, | ||||
| 	.num_rfci = 0, | ||||
| 	.num_subflows = 0, | ||||
| 	.IPTIs_present = false, | ||||
| 	.t_init = { .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT }, | ||||
| 	.t_ta = { .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT }, | ||||
| 	.t_rc = { .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT }, | ||||
| }; | ||||
|  | ||||
| /* Find a destination connection. */ | ||||
| static struct mgcp_conn *_find_dst_conn(struct mgcp_conn *conn) | ||||
| { | ||||
| 	/* NOTE: This code path runs every time an RTP packet is received. The | ||||
| 	 * function mgcp_find_dst_conn() we use to determine the detination | ||||
| 	 * connection will iterate the connection list inside the endpoint. | ||||
| 	 * Since list iterations are quite costly, we will figure out the | ||||
| 	 * destination only once and use the optional private data pointer of | ||||
| 	 * the connection to cache the destination connection pointer. */ | ||||
|  | ||||
| 	struct mgcp_conn *conn_dst; | ||||
| 	if (!conn->priv) { | ||||
| 		conn_dst = mgcp_find_dst_conn(conn); | ||||
| 		conn->priv = conn_dst; | ||||
| 	} else { | ||||
| 		conn_dst = (struct mgcp_conn *)conn->priv; | ||||
| 	} | ||||
| 	return conn_dst; | ||||
| } | ||||
|  | ||||
| /* Find RFCI containing all 0 sizes, -1 if not found. irp is an Initialization.ind prim */ | ||||
| static int _find_rfci_no_data(struct osmo_iuup_rnl_prim *irp) | ||||
| { | ||||
| 	int i; | ||||
| 	/* Find RFCI containing NO_DATA: */ | ||||
| 	for (i = 0; i < irp->u.status.u.initialization.num_rfci; i++) { | ||||
| 		int j; | ||||
| 		bool is_no_data = true; | ||||
| 		for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++) { | ||||
| 			if (irp->u.status.u.initialization.subflow_sizes[i][j]) { | ||||
| 				is_no_data = false; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		if (is_no_data) { | ||||
| 			return i; | ||||
| 		} | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /* Lookup RFCI to use for specific AMR codec type. -1 if none found */ | ||||
| static int8_t _conn_iuup_amr_ft_2_rfci(struct mgcp_conn_rtp *conn_rtp, uint8_t ft) | ||||
| { | ||||
| 	int8_t i; | ||||
| 	unsigned match_bytes = (unsigned)osmo_amr_bytes(ft); | ||||
| 	struct osmo_iuup_rnl_prim *irp = conn_rtp->iuup.init_ind; | ||||
| 	OSMO_ASSERT(irp); | ||||
|  | ||||
| 	/* TODO: cache this somehow */ | ||||
| 	for (i = 0; i < irp->u.status.u.initialization.num_rfci; i++) { | ||||
| 		int j; | ||||
| 		unsigned num_bits = 0; | ||||
| 		for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++) | ||||
| 			num_bits += irp->u.status.u.initialization.subflow_sizes[i][j]; | ||||
| 		if (match_bytes == (num_bits + 7)/8) | ||||
| 			return i; | ||||
| 	} | ||||
|  | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /* Helper function to configure IuUP layer FSM as Init-Passive, based on default config */ | ||||
| static int _conn_iuup_configure_as_passive(struct mgcp_conn_rtp *conn_rtp) | ||||
| { | ||||
| 	struct osmo_iuup_rnl_prim *irp; | ||||
| 	int rc; | ||||
|  | ||||
| 	conn_rtp->iuup.active_init = false; | ||||
|  | ||||
| 	/* Tx CONFIG.req */ | ||||
| 	irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE); | ||||
| 	irp->u.config = def_configure_req; | ||||
| 	irp->u.config.active = conn_rtp->iuup.active_init; | ||||
| 	if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) == 0) | ||||
| 		conn_rtp->iuup.configured = true; | ||||
| 	else | ||||
| 		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n"); | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /* Helper function to configure IuUP layer FSM as Init-Active, based on received | ||||
|  * RNL Status-Init primitive from the sister IuUP connection we will bridge to. */ | ||||
| static int _conn_iuup_configure_as_active(struct mgcp_conn_rtp *conn_rtp, struct osmo_iuup_rnl_prim *init_ind) | ||||
| { | ||||
| 	struct osmo_iuup_rnl_prim *irp = init_ind; | ||||
| 	struct osmo_iuup_rnl_prim *irp2; | ||||
| 	struct msgb *msg; | ||||
| 	bool prev_output_enabled; | ||||
| 	int rc; | ||||
|  | ||||
| 	conn_rtp->iuup.active_init = true; | ||||
|  | ||||
| 	/* Find RFCI containing NO_DATA: */ | ||||
| 	conn_rtp->iuup.rfci_idx_no_data = _find_rfci_no_data(init_ind); | ||||
|  | ||||
| 	/* Copy over the rfci_idx_no_data, since we reuse the same subflow set: */ | ||||
| 	msg = msgb_copy_c(conn_rtp->conn, irp->oph.msg, "iuup-init-copy"); | ||||
| 	conn_rtp->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg); | ||||
| 	conn_rtp->iuup.init_ind->oph.msg = msg; | ||||
|  | ||||
| 	/* Tx CONFIG.req */ | ||||
| 	irp2 = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE); | ||||
| 	irp2->u.config.transparent = false; | ||||
| 	irp2->u.config.active = conn_rtp->iuup.active_init; | ||||
| 	irp2->u.config.data_pdu_type = irp->u.status.u.initialization.data_pdu_type; | ||||
| 	irp2->u.config.supported_versions_mask = def_configure_req.supported_versions_mask; | ||||
| 	irp2->u.config.num_rfci = irp->u.status.u.initialization.num_rfci; | ||||
| 	irp2->u.config.num_subflows = irp->u.status.u.initialization.num_subflows; | ||||
| 	memcpy(irp2->u.config.subflow_sizes, irp->u.status.u.initialization.subflow_sizes, | ||||
| 	       IUUP_MAX_RFCIS * IUUP_MAX_SUBFLOWS * sizeof(irp2->u.config.subflow_sizes[0][0])); | ||||
| 	irp2->u.config.IPTIs_present = irp->u.status.u.initialization.IPTIs_present; | ||||
| 	if (irp->u.status.u.initialization.IPTIs_present) | ||||
| 		memcpy(irp2->u.config.IPTIs, irp->u.status.u.initialization.IPTIs, | ||||
| 		       IUUP_MAX_RFCIS * sizeof(irp2->u.config.IPTIs[0])); | ||||
| 	irp2->u.config.t_init = def_configure_req.t_init; | ||||
| 	irp2->u.config.t_ta = def_configure_req.t_ta; | ||||
| 	irp2->u.config.t_rc = def_configure_req.t_rc; | ||||
|  | ||||
| 	/* We need to force allowance of RTP containing Init-ACK back: */ | ||||
| 	prev_output_enabled = conn_rtp->end.output_enabled; | ||||
| 	conn_rtp->end.output_enabled = true; | ||||
|  | ||||
| 	if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp2)) == 0) | ||||
| 		conn_rtp->iuup.configured = true; | ||||
| 	else | ||||
| 		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n"); | ||||
|  | ||||
| 	conn_rtp->end.output_enabled = prev_output_enabled; | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /* Helper function to push an RTP+IuUP pkt up to the IuUP layer FSM through the | ||||
|  * TNL primitive interface. */ | ||||
| static int _conn_iuup_rtp_pl_up(struct mgcp_conn_rtp *conn_rtp, struct msgb *msg) | ||||
| { | ||||
| 	/* Send RTP payload (IuUP) up the stack: */ | ||||
| 	struct osmo_iuup_tnl_prim *itp; | ||||
| 	int rc; | ||||
|  | ||||
| 	msg->l2h = msgb_data(msg) + sizeof(struct rtp_hdr); | ||||
|  | ||||
| 	itp = osmo_iuup_tnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, MGW_IUUP_MSGB_SIZE); | ||||
| 	itp->oph.msg->l2h = msgb_put(itp->oph.msg, msgb_l2len(msg)); | ||||
| 	memcpy(itp->oph.msg->l2h, msgb_l2(msg), msgb_l2len(msg)); | ||||
| 	if ((rc = osmo_iuup_tnl_prim_up(conn_rtp->iuup.iui, itp)) != 0) { | ||||
| 		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed passing IuUP-Init to IuUP layer\n"); | ||||
| 	} | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| static int check_rtp_iuup(const struct mgcp_conn_rtp *conn_rtp, struct msgb *msg) | ||||
| { | ||||
| 	size_t min_size = sizeof(struct rtp_hdr); | ||||
| 	/* Check there's at least 2 bytes of RTP payload (IuUP header). This is | ||||
| 	 ** mainly to avoid 0-byte payload copy cases */ | ||||
| 	if (msgb_length(msg) < sizeof(struct rtp_hdr) + 2) { | ||||
| 		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "RTP-IuUP packet too short (%u < %zu)\n", | ||||
| 			     msgb_length(msg), min_size); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an IuUP sister | ||||
|  * conn in the endpoint. The function takes ownsership of the irp */ | ||||
| static int bridge_iuup_to_iuup_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp) | ||||
| { | ||||
| 	int rc; | ||||
|  | ||||
| 	/* If we are not configured and we received bridged data, it means | ||||
| 	 * conn_rtp_src is already configured and INITed, and we can infer | ||||
| 	 * conn_rtp_src is Init-passive (RNC side), so conn_rtp_dst needs to be | ||||
| 	 * configured as INIT-active: */ | ||||
| 	if (!conn_rtp_dst->iuup.configured) { | ||||
| 		OSMO_ASSERT(conn_rtp_src->iuup.init_ind); | ||||
| 		rc = _conn_iuup_configure_as_active(conn_rtp_dst, conn_rtp_src->iuup.init_ind); | ||||
| 		if (rc < 0) { | ||||
| 			msgb_free(irp->oph.msg); | ||||
| 			return rc; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* We simply forward the msg, without freeing it: */ | ||||
| 	talloc_steal(conn_rtp_dst->conn, irp->oph.msg); | ||||
| 	irp->oph.operation = PRIM_OP_REQUEST; | ||||
| 	if ((rc = osmo_iuup_rnl_prim_down(conn_rtp_dst->iuup.iui, irp)) != 0) | ||||
| 		LOG_CONN_RTP(conn_rtp_dst, LOGL_ERROR, "Failed Tx data down to IuUP layer\n"); | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an RTP (no IuUP) | ||||
|  * sister conn in the endpoint. The function takes ownsership of the irp */ | ||||
| static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp) | ||||
| { | ||||
| 	/* FIXME: We probably need transcoding here?! Or at least look up AMR modes and translate to related RFCI */ | ||||
| 	uint8_t frame_nr = irp->u.data.frame_nr; | ||||
| 	uint8_t fqc = irp->u.data.fqc; | ||||
| 	struct msgb *msg = irp->oph.msg; | ||||
| 	ssize_t amr_length = 0; | ||||
| 	int ft; | ||||
| 	uint8_t *amr_data; | ||||
| 	struct rtp_hdr *rtp_hdr; | ||||
| 	struct amr_hdr *amr_hdr; | ||||
| 	int rc; | ||||
|  | ||||
| 	ft = osmo_amr_bytes_to_ft(msgb_l3len(msg)); | ||||
| 	if (ft < 0) { | ||||
| 		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR, | ||||
| 			 "Unknown AMR format for size %u\n", msgb_l3len(msg)); | ||||
| 		msgb_free(msg); | ||||
| 		return ft; | ||||
| 	} | ||||
| 	msgb_pull_to_l3(msg); | ||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Convert Iuup -> AMR: ft %d, len %d\n", ft, msgb_l3len(msg)); | ||||
|  | ||||
| 	if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.codec)) { | ||||
| 		amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr)); | ||||
| 		amr_hdr->cmr = 15; /* no change */ | ||||
| 		amr_hdr->f = 0; | ||||
| 		amr_hdr->q = !fqc; | ||||
| 		amr_hdr->ft = ft & 0xff; | ||||
| 		amr_hdr->pad1 = 0; | ||||
| 		amr_hdr->pad2 = 0; | ||||
| 	} else { | ||||
| 		OSMO_ASSERT(msgb_tailroom(msg) >= 2); | ||||
| 		msgb_put(msg, 2); | ||||
| 		osmo_amr_iuup_to_bwe(msgb_data(msg), msgb_length(msg) - 2, msgb_length(msg) + 2); | ||||
| 		/* fill bwe header */ | ||||
| 		amr_data = msgb_data(msg); | ||||
| 		/* CMR no change      | follow bit | ft (3 of 4 bits) */ | ||||
| 		amr_data[0] = 15 << 4 | (0 << 3) | (ft >> 1); | ||||
| 		amr_data[1] |= ((ft & 0x1) << 7) | (((!fqc) & 0x1) << 6); | ||||
| 		amr_length = (osmo_amr_bits(ft) + 10 + 7) / 8; | ||||
| 		msgb_trim(msg, amr_length); | ||||
| 	} | ||||
| 	rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(*rtp_hdr)); | ||||
| 	*rtp_hdr = (struct rtp_hdr){ | ||||
| 		.csrc_count = 0, | ||||
| 		.extension = 0, | ||||
| 		.padding = 0, | ||||
| 		.version = 0, | ||||
| 		.payload_type = conn_rtp_dst->end.codec->payload_type, | ||||
| 		.marker = 0, | ||||
| 		.sequence = frame_nr, | ||||
| 		.timestamp = 0, | ||||
| 		.ssrc = 0 | ||||
| 	}; | ||||
|  | ||||
| 	rc = mgcp_send(conn_rtp_dst->conn->endp, true, NULL, msg, conn_rtp_src, conn_rtp_dst); | ||||
| 	msgb_free(msg); | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /* Handle RNL Data primitive received from the IuUP layer FSM: Bridge it to the | ||||
|  * sister connection in the endpoint: */ | ||||
| static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp) | ||||
| { | ||||
| 	struct mgcp_conn *conn_dst; | ||||
| 	struct mgcp_conn_rtp *conn_rtp_dst; | ||||
| 	int rc; | ||||
|  | ||||
| 	conn_dst = _find_dst_conn(conn_rtp_src->conn); | ||||
|  | ||||
| 	/* There is no destination conn, stop here */ | ||||
| 	if (!conn_dst) { | ||||
| 		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_DEBUG, | ||||
| 			 "no connection to forward an incoming IuUP payload to\n"); | ||||
| 		rc = -1; | ||||
| 		goto free_ret; | ||||
| 	} | ||||
|  | ||||
| 	/* The destination conn is not an RTP/IuUP connection */ | ||||
| 	if (conn_dst->type != MGCP_CONN_TYPE_RTP) { | ||||
| 		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR, | ||||
| 			 "unable to find suitable destination conn\n"); | ||||
| 		 rc = -1; | ||||
| 		goto free_ret; | ||||
| 	} | ||||
| 	conn_rtp_dst = &conn_dst->u.rtp; | ||||
|  | ||||
| 	switch (conn_rtp_dst->type) { | ||||
| 	case MGCP_RTP_IUUP: | ||||
| 		return bridge_iuup_to_iuup_peer(conn_rtp_src, conn_rtp_dst, irp); | ||||
| 	case MGCP_RTP_DEFAULT: | ||||
| 		return bridge_iuup_to_rtp_peer(conn_rtp_src, conn_rtp_dst, irp); | ||||
| 	case MGCP_OSMUX_BSC: | ||||
| 	case MGCP_OSMUX_BSC_NAT: | ||||
| 	default: | ||||
| 		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR, | ||||
| 			 "Forward of IuUP payload to RTP connection type %u not supported!\n", | ||||
| 			 conn_rtp_dst->type); | ||||
| 		rc = 0; | ||||
| 	} | ||||
|  | ||||
| free_ret: | ||||
| 	msgb_free(irp->oph.msg); | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /* Handle RNL Status-Init primitive received from the IuUP layer FSM. | ||||
|  * Potentially configure sister conn as IuUP Init-Active: */ | ||||
| static int _conn_iuup_rx_rnl_status_init(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp) | ||||
| { | ||||
| 	struct mgcp_conn *conn_dst; | ||||
| 	struct mgcp_conn_rtp *conn_rtp_dst; | ||||
| 	int rc = 0; | ||||
| 	struct msgb *msg; | ||||
|  | ||||
| 	/* Find RFCI containing NO_DATA: */ | ||||
| 	conn_rtp_src->iuup.rfci_idx_no_data = _find_rfci_no_data(irp); | ||||
|  | ||||
| 	msg = msgb_copy_c(conn_rtp_src->conn, irp->oph.msg, "iuup-init-copy"); | ||||
| 	conn_rtp_src->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg); | ||||
| 	conn_rtp_src->iuup.init_ind->oph.msg = msg; | ||||
|  | ||||
| 	conn_dst = _find_dst_conn(conn_rtp_src->conn); | ||||
| 	/* If not yet there, peer will potentially be IuUP-Initialized later | ||||
| 	 * when we attempt to bridge audio towards it. See bridge_iuup_to_iuup_peer() */ | ||||
| 	if (!conn_dst) | ||||
| 		return 0; | ||||
| 	conn_rtp_dst = &conn_dst->u.rtp; | ||||
| 	if (!mgcp_conn_rtp_is_iuup(conn_rtp_dst)) | ||||
| 		return 0; /* Nothing to do */ | ||||
|  | ||||
| 	/* We received IuUP parameters on the peer (RNC), Init actively this conn (against CN): */ | ||||
| 	if (!conn_rtp_dst->iuup.configured) | ||||
| 		rc = _conn_iuup_configure_as_active(conn_rtp_dst, irp); | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /* Handle RNL Status primitives received from the IuUP layer FSM: */ | ||||
| static int _conn_iuup_rx_rnl_status(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp) | ||||
| { | ||||
| 	int rc; | ||||
|  | ||||
| 	switch (irp->u.status.procedure) { | ||||
| 	case IUUP_PROC_INIT: | ||||
| 		rc = _conn_iuup_rx_rnl_status_init(conn_rtp_src, irp); | ||||
| 		break; | ||||
| 	case IUUP_PROC_RATE_CTRL: | ||||
| 	case IUUP_PROC_TIME_ALIGN: | ||||
| 	case IUUP_PROC_ERR_EVENT: | ||||
| 	default: | ||||
| 		LOG_CONN_RTP(conn_rtp_src, LOGL_ERROR, | ||||
| 			     "Received IuUP RNL STATUS procedure type %u not handled\n", | ||||
| 			     irp->u.status.procedure); | ||||
| 		rc = 0; | ||||
| 	} | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /* Received RNL primitive from the IuUP layer FSM containing IuUP Status or | ||||
|  * data. Continue pushing it up the stack, either IuUP Status or Data: */ | ||||
| static int _conn_iuup_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx) | ||||
| { | ||||
| 	struct mgcp_conn_rtp *conn_rtp_src = ctx; | ||||
| 	struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph; | ||||
| 	struct msgb *msg = oph->msg; | ||||
| 	int rc; | ||||
|  | ||||
| 	switch (OSMO_PRIM_HDR(&irp->oph)) { | ||||
| 	case OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION): | ||||
| 		/* we pass ownsership of msg here: */ | ||||
| 		rc = _conn_iuup_rx_rnl_data(conn_rtp_src, irp); | ||||
| 		break; | ||||
| 	case OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION): | ||||
| 		rc = _conn_iuup_rx_rnl_status(conn_rtp_src, irp); | ||||
| 		msgb_free(msg); | ||||
| 		break; | ||||
| 	default: | ||||
| 		msgb_free(msg); | ||||
| 		OSMO_ASSERT(false); | ||||
| 	} | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /*! Send |RTP+IuUP| data down the stack of the specified destination connection. | ||||
|  *  \param[in] endp associated endpoint (for configuration, logging). | ||||
|  *  \param[in] buf buffer that contains the |RTP+IuUP| data. | ||||
|  *  \param[in] len length of the buffer that contains the |RTP+IuUP| data. | ||||
|  *  \param[in] conn_src associated source connection. | ||||
|  *  \param[in] conn_dst associated destination connection. | ||||
|  *  \returns 0 on success, -1 on ERROR. */ | ||||
| static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg, | ||||
| 		   struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst) | ||||
| { | ||||
| 	/*! When no destination connection is available (e.g. when only one | ||||
| 	 *  connection in loopback mode exists), then the source connection | ||||
| 	 *  shall be specified as destination connection */ | ||||
|  | ||||
| 	struct mgcp_rtp_end *rtp_end; | ||||
| 	struct mgcp_rtp_state *rtp_state; | ||||
| 	char ipbuf[INET6_ADDRSTRLEN]; | ||||
| 	struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg); | ||||
| 	int buflen = msgb_length(msg); | ||||
| 	char *dest_name; | ||||
| 	int len; | ||||
|  | ||||
| 	OSMO_ASSERT(conn_src); | ||||
| 	OSMO_ASSERT(conn_dst); | ||||
|  | ||||
| 	LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering IuUP packet...\n"); | ||||
|  | ||||
| 	/* Note: In case of loopback configuration, both, the source and the | ||||
| 	 * destination will point to the same connection. */ | ||||
| 	rtp_end = &conn_dst->end; | ||||
| 	rtp_state = &conn_src->state; | ||||
| 	dest_name = conn_dst->conn->name; | ||||
|  | ||||
| 	/* Ensure we have an alternative SSRC in case we need it, see also | ||||
| 	 * gen_rtp_header() */ | ||||
| 	if (rtp_state->alt_rtp_tx_ssrc == 0) | ||||
| 		rtp_state->alt_rtp_tx_ssrc = rand(); | ||||
|  | ||||
| 	if (!rtp_end->output_enabled) { | ||||
| 		rtpconn_rate_ctr_add(conn_dst, endp, RTP_DROPPED_PACKETS_CTR, 1); | ||||
| 		LOGPENDP(endp, DRTP, LOGL_DEBUG, | ||||
| 			 "output disabled, drop to %s %s " | ||||
| 			 "rtp_port:%u rtcp_port:%u\n", | ||||
| 			 dest_name, | ||||
| 			 osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf), | ||||
| 			 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port) | ||||
| 			); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Specs say, in IuUP, the RTP seqnum and timestamp should actually be | ||||
| 	 * ignored by the receiver, but still it's useful for debug purposes | ||||
| 	 * to set it. Moreover, it seems ip.access nano3g produces much worse | ||||
| 	 * audio output on the air side if timestamp is not set properly. */ | ||||
| 	hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate)); | ||||
| 	hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence); | ||||
| 	hdr->ssrc = rtp_state->alt_rtp_tx_ssrc; | ||||
|  | ||||
| 	LOGPENDP(endp, DRTP, LOGL_DEBUG, | ||||
| 		 "process/send IuUP to %s %s rtp_port:%u rtcp_port:%u\n", | ||||
| 		 dest_name, osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf), | ||||
| 		 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)); | ||||
|  | ||||
| 	/* Forward a copy of the RTP data to a debug ip/port */ | ||||
| 	forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out, | ||||
| 		     msg); | ||||
|  | ||||
| 	len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port, | ||||
| 			    (char *)hdr, buflen); | ||||
|  | ||||
| 	if (len <= 0) | ||||
| 		return len; | ||||
|  | ||||
| 	rtpconn_rate_ctr_add(conn_dst, endp, RTP_PACKETS_TX_CTR, 1); | ||||
| 	rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len); | ||||
| 	rtp_state->alt_rtp_tx_sequence++; | ||||
|  | ||||
| 	return len; | ||||
| } | ||||
|  | ||||
| /* Received TNL primitive from IuUP layer FSM, transmit it further down to the | ||||
|  * socket towards destination peer. */ | ||||
| static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx) | ||||
| { | ||||
| 	struct mgcp_conn_rtp *conn_rtp_dst = ctx; | ||||
| 	struct mgcp_conn *conn_dst = conn_rtp_dst->conn; | ||||
| 	struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph; | ||||
| 	struct mgcp_conn *conn_src; | ||||
| 	struct msgb *msg; | ||||
| 	struct rtp_hdr *rtph; | ||||
|  | ||||
| 	OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST)); | ||||
|  | ||||
| 	msg = oph->msg; | ||||
| 	talloc_steal(conn_rtp_dst->conn, msg); | ||||
|  | ||||
| 	msgb_pull_to_l2(msg); | ||||
| 	rtph = (struct rtp_hdr *)msgb_push(msg, sizeof(*rtph)); | ||||
| 	/* TODO: fill rtph properly: */ | ||||
| 	*rtph = (struct rtp_hdr){ | ||||
| 		.csrc_count = 0, | ||||
| 		.extension = 0, | ||||
| 		.padding = 0, | ||||
| 		.version = 2, | ||||
| 		.payload_type = conn_rtp_dst->end.codec->payload_type, | ||||
| 		.marker = 0, | ||||
| 		.sequence = 0, | ||||
| 		.timestamp = 0, | ||||
| 		.ssrc = 0 | ||||
| 	}; | ||||
|  | ||||
| 	/* The destination of the destination conn is the source conn, right? */ | ||||
| 	conn_src = _find_dst_conn(conn_dst); | ||||
| 	if (!conn_src) { | ||||
| 		LOG_CONN_RTP(conn_rtp_dst, LOGL_NOTICE, | ||||
| 			     "Couldn't find source conn for IuUP dst conn\n"); | ||||
| 		/* If there's no sister connection we are either still | ||||
| 		 * initializing (so we want to send back Init (ACK)), or we are | ||||
| 		 * probably in loopback mode anyway, so use dst as src. */ | ||||
| 		conn_src = conn_dst; | ||||
| 	} | ||||
|  | ||||
| 	return mgcp_send_iuup(conn_dst->endp, msg, &conn_src->u.rtp, conn_rtp_dst); | ||||
| } | ||||
|  | ||||
| /* Used to upgrade a regular RTP connection (MGCP_RTP_DEFAULT) to become a IuUP | ||||
|  * connection (MGCP_RTP_IUUP) */ | ||||
| int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp) | ||||
| { | ||||
| 	conn_rtp->type = MGCP_RTP_IUUP; | ||||
| 	conn_rtp->iuup.iui = osmo_iuup_instance_alloc(conn_rtp->conn, conn_rtp->conn->id); | ||||
| 	OSMO_ASSERT(conn_rtp->iuup.iui); | ||||
| 	osmo_iuup_instance_set_user_prim_cb(conn_rtp->iuup.iui, _conn_iuup_user_prim_cb, conn_rtp); | ||||
| 	osmo_iuup_instance_set_transport_prim_cb(conn_rtp->iuup.iui, _conn_iuup_transport_prim_cb, conn_rtp); | ||||
| 	conn_rtp->iuup.rfci_idx_no_data = -1; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Cleanup specific IuUP connection (MGCP_RTP_IUUP) state, allocated by mgcp_conn_iuup_init() */ | ||||
| void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp) | ||||
| { | ||||
| 	osmo_iuup_instance_free(conn_rtp->iuup.iui); | ||||
| 	conn_rtp->iuup.iui = NULL; | ||||
| } | ||||
|  | ||||
| /* Received RTP+IuUP pkt from socket of conn_rtp_src, build a TNL primitive to | ||||
|  * push it further up the stack to the IuUP layer FSM to handle and/or bridge it */ | ||||
| int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg) | ||||
| { | ||||
| 	struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg); | ||||
| 	struct mgcp_conn_rtp *conn_rtp_src = mc->conn_src; | ||||
| 	int rc = 0; | ||||
| 	bool force_output_enabled = false; | ||||
| 	bool prev_output_enabled; | ||||
| 	struct osmo_sockaddr prev_rem_addr; | ||||
| 	uint16_t prev_rem_rtp_port; | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_conn_rtp_is_iuup(conn_rtp_src)); | ||||
|  | ||||
| 	if ((rc = check_rtp_iuup(conn_rtp_src, msg)) < 0) | ||||
| 		goto free_ret; | ||||
|  | ||||
| 	if (!conn_rtp_src->iuup.configured) { | ||||
| 		/* We received the first message without sending any, the peer is the active side (RNC). */ | ||||
| 		rc = _conn_iuup_configure_as_passive(conn_rtp_src); | ||||
| 		if (rc < 0) | ||||
| 			goto free_ret; | ||||
| 		/* We need to force allowance of RTP containing Init-ACK back: */ | ||||
| 		prev_output_enabled = conn_rtp_src->end.output_enabled; | ||||
| 		conn_rtp_src->end.output_enabled = true; | ||||
| 		force_output_enabled = true; | ||||
| 		/* Fill in the peer address so that we can send Init-ACK back: */ | ||||
| 		prev_rem_addr = conn_rtp_src->end.addr; | ||||
| 		prev_rem_rtp_port = conn_rtp_src->end.rtp_port; | ||||
| 		conn_rtp_src->end.addr = *mc->from_addr; | ||||
| 		conn_rtp_src->end.rtp_port = htons(osmo_sockaddr_port(&mc->from_addr->u.sa)); | ||||
| 	} | ||||
|  | ||||
| 	rc = _conn_iuup_rtp_pl_up(conn_rtp_src, msg); | ||||
|  | ||||
| 	if (force_output_enabled) { | ||||
| 		conn_rtp_src->end.output_enabled = prev_output_enabled; | ||||
| 		conn_rtp_src->end.addr = prev_rem_addr; | ||||
| 		conn_rtp_src->end.rtp_port = prev_rem_rtp_port; | ||||
| 	} | ||||
|  | ||||
| 	return rc; | ||||
| free_ret: | ||||
| 	msgb_free(msg); | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /* Build IuUP RNL Data primitive from msg containing an incoming RTP pkt from | ||||
|  * peer and send it down the IuUP layer towards the destination as IuUP/RTP: */ | ||||
| int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg) | ||||
| { | ||||
| 	struct osmo_iuup_rnl_prim *irp; | ||||
| 	struct rtp_hdr *rtph; | ||||
| 	int rc = -1; | ||||
| 	int iuup_length = 0; | ||||
| 	int8_t rfci; | ||||
|  | ||||
| 	/* Tx RNL-DATA.req */ | ||||
| 	rtph = (struct rtp_hdr *)msgb_data(msg); | ||||
| 	msgb_pull(msg, sizeof(*rtph)); | ||||
|  | ||||
| 	/* FIXME: validate amr packets */ | ||||
| 	irp = osmo_iuup_rnl_prim_alloc(conn_dest_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE); | ||||
| 	irp->u.data.frame_nr = htons(rtph->sequence) % 16; | ||||
|  | ||||
| 	/* TODO: CMR handling & multiple frames handling */ | ||||
|  | ||||
| 	if (strcmp(conn_src_rtp->end.codec->subtype_name, "AMR") != 0) { | ||||
| 		LOG_CONN_RTP(conn_src_rtp, LOGL_ERROR, | ||||
| 			     "Bridge RTP=>IuUP: Bridging src codec %s to IuUP AMR not supported\n", | ||||
| 			     conn_src_rtp->end.codec->subtype_name); | ||||
| 		goto free_ret; | ||||
| 	} | ||||
| 	if (mgcp_codec_amr_is_octet_aligned(conn_src_rtp->end.codec)) { | ||||
| 		struct amr_hdr *amr_hdr = (struct amr_hdr *) msgb_data(msg); | ||||
| 		if (msgb_length(msg) < (sizeof(*amr_hdr))) { | ||||
| 			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, | ||||
| 				     "Bridge RTP=>IuUP: too short for AMR OA hdr (%u)\n", msgb_length(msg)); | ||||
| 			goto free_ret; | ||||
| 		} | ||||
| 		if (amr_hdr->ft >= AMR_FT_MAX) { | ||||
| 			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR OA ft=%u\n", amr_hdr->ft); | ||||
| 			goto free_ret; | ||||
| 		} | ||||
| 		if ((rfci =  _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, amr_hdr->ft)) < 0) { | ||||
| 			LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR OA ft=%u\n", amr_hdr->ft); | ||||
| 			goto free_ret; | ||||
| 		} | ||||
| 		irp->u.data.fqc = amr_hdr->q; | ||||
| 		irp->u.data.rfci = rfci; | ||||
| 		msgb_pull(msg, 2); | ||||
| 	} else { | ||||
| 		uint8_t *amr_bwe_hdr = (uint8_t *) msgb_data(msg); | ||||
| 		int8_t ft; | ||||
| 		if (msgb_length(msg) < 2) { | ||||
| 			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, | ||||
| 				     "Bridge RTP=>IuUP: too short for AMR BE hdr (%u)\n", msgb_length(msg)); | ||||
| 			goto free_ret; | ||||
| 		} | ||||
| 		ft = ((amr_bwe_hdr[0] & 0x07) << 1) | ((amr_bwe_hdr[1] & 0x80) >> 7); | ||||
| 		if (ft >= AMR_FT_MAX) { | ||||
| 			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR BE ft=%u\n", ft); | ||||
| 			goto free_ret; | ||||
| 		} | ||||
| 		if ((rfci =  _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, ft)) < 0) { | ||||
| 			LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR BE ft=%u\n", ft); | ||||
| 			goto free_ret; | ||||
| 		} | ||||
| 		irp->u.data.fqc = ((amr_bwe_hdr[1] & 0x40) >> 6); | ||||
| 		irp->u.data.rfci = rfci; | ||||
| 		rc = iuup_length = osmo_amr_bwe_to_iuup(msgb_data(msg), msgb_length(msg)); | ||||
| 		if (rc < 0) { | ||||
| 			LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed convert the RTP/AMR to IuUP payload\n"); | ||||
| 			return rc; | ||||
| 		} | ||||
| 		msgb_trim(msg, iuup_length); | ||||
| 	} | ||||
|  | ||||
| 	irp->oph.msg->l3h = msgb_put(irp->oph.msg, msgb_length(msg)); | ||||
| 	memcpy(irp->oph.msg->l3h, msgb_data(msg), msgb_length(msg)); | ||||
| 	if ((rc = osmo_iuup_rnl_prim_down(conn_dest_rtp->iuup.iui, irp)) != 0) | ||||
| 		LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed Tx RTP payload down the IuUP layer\n"); | ||||
| 	return rc; | ||||
|  | ||||
| free_ret: | ||||
| 	msgb_free(irp->oph.msg); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /* Build IuUP RNL Data primitive from msg containing dummy content and send it | ||||
|  * down the IuUP layer towards the destination as IuUP/RTP: */ | ||||
| int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp) | ||||
| { | ||||
| 	struct osmo_iuup_rnl_prim *irp; | ||||
| 	int rc; | ||||
|  | ||||
| 	if (conn_rtp->iuup.rfci_idx_no_data == -1) { | ||||
| 		LOG_CONN_RTP(conn_rtp, LOGL_NOTICE, "No RFCI NO_DATA found, unable to send dummy packet\n"); | ||||
| 		return -ENOTSUP; | ||||
| 	} | ||||
|  | ||||
| 	irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE); | ||||
| 	irp->u.data.frame_nr = 0; | ||||
| 	irp->u.data.fqc = IUUP_FQC_FRAME_GOOD; | ||||
| 	irp->u.data.rfci = conn_rtp->iuup.rfci_idx_no_data; | ||||
| 	irp->oph.msg->l3h = irp->oph.msg->tail; | ||||
| 	if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) != 0) { | ||||
| 		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed Tx RTP dummy payload down the IuUP layer\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
| @@ -24,11 +24,14 @@ | ||||
|  | ||||
| #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> | ||||
| #include <osmocom/mgcp/mgcp_endp.h> | ||||
| #include <osmocom/mgcp/mgcp_trunk.h> | ||||
|  | ||||
| /*! Display an mgcp message on the log output. | ||||
|  *  \param[in] message mgcp message string | ||||
| @@ -105,10 +108,9 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp, | ||||
| 		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; | ||||
| 		conn->u.rtp.end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY); | ||||
| 	} | ||||
|  | ||||
| 	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn)); | ||||
| @@ -118,7 +120,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp, | ||||
|  | ||||
| 	/* Special handling für RTP connections */ | ||||
| 	if (conn->type == MGCP_CONN_TYPE_RTP) { | ||||
| 		LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n", | ||||
| 		LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n", | ||||
| 			 conn->u.rtp.end.output_enabled); | ||||
| 	} | ||||
|  | ||||
| @@ -129,185 +131,20 @@ 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]; | ||||
| 			LOGPENDP(endp, DLMGCP, LOGL_DEBUG, | ||||
| 			     "found free endpoint\n"); | ||||
| 			endp->wildcarded_req = true; | ||||
| 			return endp; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n"); | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /* Check if the domain name, which is supplied with the endpoint name | ||||
|  * matches the configuration. */ | ||||
| static int check_domain_name(struct mgcp_config *cfg, const char *mgcp) | ||||
| { | ||||
| 	char *domain_to_check; | ||||
|  | ||||
| 	domain_to_check = strstr(mgcp, "@"); | ||||
| 	if (!domain_to_check) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	/* Accept any domain if configured as "*" */ | ||||
| 	if (!strcmp(cfg->domain, "*")) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (strcmp(domain_to_check+1, cfg->domain) != 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Search the endpoint pool for the endpoint that had been selected via the | ||||
|  * MGCP message (helper function for mgcp_analyze_header()) */ | ||||
| static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, | ||||
| 					   const char *mgcp, | ||||
| 					   int *cause) | ||||
| { | ||||
| 	char *endptr = NULL; | ||||
| 	unsigned int gw = INT_MAX; | ||||
| 	const char *endpoint_number_str; | ||||
| 	struct mgcp_endpoint *endp; | ||||
|  | ||||
| 	*cause = 0; | ||||
|  | ||||
| 	/* Check if the domainname in the request is correct */ | ||||
| 	if (check_domain_name(cfg, mgcp)) { | ||||
| 		*cause = -500; | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if the E1 trunk is requested */ | ||||
| 	if (strncmp(mgcp, "ds/e1", 5) == 0) { | ||||
| 		endp = find_e1_endpoint(cfg, mgcp); | ||||
| 		if (!endp) | ||||
| 			*cause = -500; | ||||
| 		return endp; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if the virtual trunk is addressed (new, correct way with prefix) */ | ||||
| 	if (strncmp | ||||
| 	    (mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, | ||||
| 	     strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) { | ||||
| 		endpoint_number_str = | ||||
| 		    mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK); | ||||
| 		if (endpoint_number_str[0] == '*') { | ||||
| 			endp = find_free_endpoint(cfg->trunk.endpoints, | ||||
| 						  cfg->trunk.number_endpoints); | ||||
| 			if (!endp) | ||||
| 				*cause = -403; | ||||
| 			return endp; | ||||
| 		} | ||||
| 		gw = strtoul(endpoint_number_str, &endptr, 16); | ||||
| 		if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') { | ||||
| 			endp = &cfg->trunk.endpoints[gw]; | ||||
| 			endp->wildcarded_req = false; | ||||
| 			return endp; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Deprecated method without prefix */ | ||||
| 	LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 	     "Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n", | ||||
| 	     MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp); | ||||
| 	gw = strtoul(mgcp, &endptr, 16); | ||||
| 	if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') { | ||||
| 		endp = &cfg->trunk.endpoints[gw]; | ||||
| 		endp->wildcarded_req = false; | ||||
| 		return endp; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp); | ||||
| 	*cause = -500; | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /*! Analyze and parse the the hader of an MGCP messeage string. | ||||
|  *  \param[out] pdata caller provided memory to store the parsing results | ||||
|  *  \param[in] data mgcp message string | ||||
|  *  \returns when the status line was complete and transaction_id and | ||||
|  *  endp out parameters are set, -1 on error */ | ||||
|  *  \param[out] pdata caller provided memory to store the parsing results. | ||||
|  *  \param[in] data mgcp message string. | ||||
|  *  \returns 0 when the status line was complete and parseable, negative (MGCP | ||||
|  *  cause code) on error. */ | ||||
| int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data) | ||||
| { | ||||
| 	int i = 0; | ||||
| 	char *elem, *save = NULL; | ||||
| 	int cause; | ||||
|  | ||||
| 	/*! This function will parse the header part of the received | ||||
| 	 *  MGCP message. The parsing results are stored in pdata. | ||||
| 	 *  The function will also automatically search the pool with | ||||
| 	 *  available endpoints in order to find an endpoint that matches | ||||
| 	 *  the endpoint string in in the header */ | ||||
| 	 *  MGCP message. The parsing results are stored in pdata. */ | ||||
|  | ||||
| 	OSMO_ASSERT(data); | ||||
| 	pdata->trans = "000000"; | ||||
|  | ||||
| 	for (elem = strtok_r(data, " ", &save); elem; | ||||
| 	     elem = strtok_r(NULL, " ", &save)) { | ||||
| @@ -316,13 +153,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); | ||||
| 			if (!pdata->endp) { | ||||
| 				LOGP(DLMGCP, LOGL_ERROR, | ||||
| 				     "Unable to find Endpoint `%s'\n", elem); | ||||
| 				OSMO_ASSERT(cause < 0); | ||||
| 				return cause; | ||||
| 			} | ||||
| 			pdata->epname = elem; | ||||
| 			break; | ||||
| 		case 2: | ||||
| 			if (strcasecmp("MGCP", elem)) { | ||||
| @@ -332,11 +163,8 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data) | ||||
| 			} | ||||
| 			break; | ||||
| 		case 3: | ||||
| 			if (strcmp("1.0", elem)) { | ||||
| 				LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' " | ||||
| 				     "not supported\n", elem); | ||||
| 			if (strcmp("1.0", elem)) | ||||
| 				return -528; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		i++; | ||||
| @@ -344,8 +172,6 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data) | ||||
|  | ||||
| 	if (i != 4) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n"); | ||||
| 		pdata->trans = "000000"; | ||||
| 		pdata->endp = NULL; | ||||
| 		return -510; | ||||
| 	} | ||||
|  | ||||
| @@ -382,28 +208,30 @@ int mgcp_parse_osmux_cid(const char *line) | ||||
| } | ||||
|  | ||||
| /*! Check MGCP parameter line (string) for plausibility. | ||||
|  *  \param[in] endp pointer to endpoint (only used for log output) | ||||
|  *  \param[in] endp pointer to endpoint (only used for log output, may be NULL) | ||||
|  *  \param[in] trunk pointer to trunk (only used for log output, may be NULL if endp is not NULL) | ||||
|  *  \param[in] line single parameter line from the MGCP message | ||||
|  *  \returns 1 when line seems plausible, 0 on error */ | ||||
| int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line) | ||||
|  *  \returns true when line seems plausible, false on error */ | ||||
| bool mgcp_check_param(const struct mgcp_endpoint *endp, struct mgcp_trunk *trunk, const char *line) | ||||
| { | ||||
| 	const size_t line_len = strlen(line); | ||||
| 	if (line[0] != '\0' && line_len < 2) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, | ||||
| 		     "Wrong MGCP option format: '%s' on 0x%x\n", | ||||
| 		     line, ENDPOINT_NUMBER(endp)); | ||||
| 		return 0; | ||||
| 		if (endp) | ||||
| 			LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "wrong MGCP option format: '%s'\n", line); | ||||
| 		else | ||||
| 			LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE, "wrong MGCP option format: '%s'\n", line); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	/* FIXME: A couple more checks wouldn't hurt... */ | ||||
|  | ||||
| 	return 1; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /*! 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 | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -23,10 +23,11 @@ | ||||
| #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; | ||||
|  | ||||
| @@ -34,6 +35,7 @@ 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; /* network byte order */ | ||||
| @@ -46,14 +48,17 @@ static void *osmux; | ||||
| static void osmux_deliver_cb(struct msgb *batch_msg, void *data) | ||||
| { | ||||
| 	struct osmux_handle *handle = data; | ||||
| 	struct mgcp_conn_rtp *conn = handle->conn; | ||||
|  | ||||
| 	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); | ||||
| } | ||||
|  | ||||
| @@ -108,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->trunk->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++; | ||||
| @@ -147,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; | ||||
|  | ||||
| @@ -197,14 +209,15 @@ static struct mgcp_conn_rtp* | ||||
| osmux_conn_lookup(struct mgcp_config *cfg, uint8_t cid, | ||||
| 		struct in_addr *from_addr) | ||||
| { | ||||
| 	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; | ||||
|  | ||||
| 	for (i=0; i<cfg->trunk.number_endpoints; i++) { | ||||
| 	for (i = 0; i < trunk->number_endpoints; i++) { | ||||
|  | ||||
| 		endp = &cfg->trunk.endpoints[i]; | ||||
| 		endp = trunk->endpoints[i]; | ||||
|  | ||||
| 		llist_for_each_entry(conn, &endp->conns, entry) { | ||||
| 			if (conn->type != MGCP_CONN_TYPE_RTP) | ||||
| @@ -234,13 +247,15 @@ static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data) | ||||
| { | ||||
| 	struct mgcp_conn_rtp *conn = data; | ||||
| 	struct mgcp_endpoint *endp = conn->conn->endp; | ||||
| 	struct sockaddr_in addr = { | ||||
| 		.sin_addr = conn->end.addr, | ||||
| 		.sin_port = conn->end.rtp_port, | ||||
| 	}; /* FIXME: not set/used in cb */ | ||||
| 	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, | ||||
| 	}; | ||||
|  | ||||
|  | ||||
| 	endp->type->dispatch_rtp_cb(MGCP_PROTO_RTP, &addr, (char *)msg->data, msg->len, conn->conn); | ||||
| 	endp->type->dispatch_rtp_cb(msg); | ||||
| 	msgb_free(msg); | ||||
| } | ||||
|  | ||||
| @@ -271,6 +286,8 @@ 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, conn->end.rtp_port) < 0) { | ||||
| @@ -283,7 +300,8 @@ static int endp_osmux_state_check(struct mgcp_endpoint *endp, struct mgcp_conn_r | ||||
| 		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, | ||||
| 			 "Osmux %s CID %u towards %s:%u is now enabled\n", | ||||
| 			 sending ? "sent" : "received", | ||||
| 			 conn->osmux.cid, inet_ntoa(conn->end.addr), | ||||
| 			 conn->osmux.cid, | ||||
| 			 osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), | ||||
| 			 ntohs(conn->end.rtp_port)); | ||||
| 		return 0; | ||||
| 	case OSMUX_STATE_ENABLED: | ||||
| @@ -357,7 +375,7 @@ static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| 	} | ||||
|  | ||||
| 	/* not any further processing dummy messages */ | ||||
| 	if (msg->data[0] == MGCP_DUMMY_LOAD) | ||||
| 	if (mgcp_is_rtp_dummy_payload(msg)) | ||||
| 		return osmux_handle_dummy(cfg, &addr, msg); | ||||
|  | ||||
| 	rem = msg->len; | ||||
| @@ -372,6 +390,8 @@ static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what) | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		mgcp_conn_watchdog_kick(conn_src->conn); | ||||
|  | ||||
| 		/*conn_dst = mgcp_find_dst_conn(conn_src->conn); | ||||
| 		if (!conn_dst) { | ||||
| 			LOGP(DLMGCP, LOGL_ERROR, | ||||
| @@ -396,17 +416,15 @@ int osmux_init(int role, struct mgcp_config *cfg) | ||||
| { | ||||
| 	int ret; | ||||
|  | ||||
| 	osmux_fd.cb = osmux_read_fd_cb; | ||||
| 	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); | ||||
| 	ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port, | ||||
| 				cfg->endp_dscp, cfg->endp_priority); | ||||
| 	if (ret < 0) { | ||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket to %s:%u\n", | ||||
| 		     cfg->osmux_addr, cfg->osmux_port); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp); | ||||
| 	osmux_fd.when |= BSC_FD_READ; | ||||
|  | ||||
| 	ret = osmo_fd_register(&osmux_fd); | ||||
| 	if (ret < 0) { | ||||
| @@ -429,7 +447,7 @@ int osmux_init(int role, struct mgcp_config *cfg) | ||||
|  *  \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 | ||||
| @@ -440,9 +458,9 @@ 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 in_addr addr_unset = {}; | ||||
| 	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; | ||||
| 	uint16_t osmux_dummy = endp->trunk->cfg->osmux_dummy; | ||||
|  | ||||
| 	/* Check if osmux is enabled for the specified connection */ | ||||
| 	if (conn->osmux.state != OSMUX_STATE_ACTIVATING) { | ||||
| @@ -453,13 +471,16 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn, | ||||
| 	} | ||||
|  | ||||
| 	/* Wait until we have the connection information from MDCX */ | ||||
| 	if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0) { | ||||
| 	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; | ||||
| 	} | ||||
|  | ||||
| 	conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port); | ||||
| 	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", | ||||
| @@ -555,6 +576,7 @@ int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid) | ||||
|  *  \returns bytes sent, -1 on error */ | ||||
| int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) | ||||
| { | ||||
| 	char ipbuf[INET6_ADDRSTRLEN]; | ||||
| 	struct osmux_hdr *osmuxh; | ||||
| 	int buf_len; | ||||
| 	struct in_addr addr_unset = {}; | ||||
| @@ -584,7 +606,8 @@ int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) | ||||
|  | ||||
| 	LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, | ||||
| 		 "sending OSMUX dummy load to %s:%u CID %u\n", | ||||
| 		 inet_ntoa(conn->end.addr), ntohs(conn->end.rtp_port), conn->osmux.cid); | ||||
| 		 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, | ||||
| 			     conn->end.rtp_port, (char*)osmuxh, buf_len); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										325
									
								
								src/libosmo-mgcp/mgcp_ratectr.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										325
									
								
								src/libosmo-mgcp/mgcp_ratectr.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,325 @@ | ||||
| /* 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 <stdatomic.h> | ||||
| #include <errno.h> | ||||
| #include <osmocom/core/stats.h> | ||||
| #include <osmocom/core/stat_item.h> | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
| #include <osmocom/mgcp/mgcp_trunk.h> | ||||
| #include <osmocom/mgcp/mgcp_protocol.h> | ||||
| #include <osmocom/mgcp/mgcp_endp.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_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_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_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_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 | ||||
| }; | ||||
|  | ||||
| /*! allocate global rate counters | ||||
|  *  (called once at startup). | ||||
|  *  \param[in] cfg mgw configuration for which the rate counters are allocated. | ||||
|  *  \returns 0 on success, -EINVAL on failure. */ | ||||
| int mgcp_ratectr_global_alloc(struct mgcp_config *cfg) | ||||
| { | ||||
| 	struct mgcp_ratectr_global *ratectr = &cfg->ratectr; | ||||
| 	static atomic_uint general_rate_ctr_index = 0; | ||||
| 	char ctr_name[512]; | ||||
|  | ||||
| 	if (ratectr->mgcp_general_ctr_group == NULL) { | ||||
| 		ratectr->mgcp_general_ctr_group = | ||||
| 		    rate_ctr_group_alloc(cfg, &mgcp_general_ctr_group_desc, general_rate_ctr_index++); | ||||
| 		if (!ratectr->mgcp_general_ctr_group) | ||||
| 			return -EINVAL; | ||||
| 		snprintf(ctr_name, sizeof(ctr_name), "%s:general", cfg->domain); | ||||
| 		rate_ctr_group_set_name(ratectr->mgcp_general_ctr_group, ctr_name); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! free global rate counters | ||||
|  *  (called once at process shutdown). | ||||
|  *  \param[in] cfg mgw configuration for which the rate counters are allocated. */ | ||||
| void mgcp_ratectr_global_free(struct mgcp_config *cfg) | ||||
| { | ||||
| 	struct mgcp_ratectr_global *ratectr = &cfg->ratectr; | ||||
|  | ||||
| 	if (ratectr->mgcp_general_ctr_group) { | ||||
| 		rate_ctr_group_free(ratectr->mgcp_general_ctr_group); | ||||
| 		ratectr->mgcp_general_ctr_group = NULL; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /*! allocate trunk specific rate counters | ||||
|  *  (called once on trunk initialization). | ||||
|  *  \param[in] trunk mgw trunk for which the rate counters are allocated. | ||||
|  *  \returns 0 on success, -EINVAL on failure */ | ||||
| int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk) | ||||
| { | ||||
| 	struct mgcp_ratectr_trunk *ratectr = &trunk->ratectr; | ||||
| 	static atomic_uint crcx_rate_ctr_index = 0; | ||||
| 	static atomic_uint mdcx_rate_ctr_index = 0; | ||||
| 	static atomic_uint dlcx_rate_ctr_index = 0; | ||||
| 	static atomic_uint all_rtp_conn_rate_ctr_index = 0; | ||||
| 	char ctr_name[256]; | ||||
|  | ||||
| 	if (ratectr->mgcp_crcx_ctr_group == NULL) { | ||||
| 		ratectr->mgcp_crcx_ctr_group = | ||||
| 		    rate_ctr_group_alloc(trunk, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index++); | ||||
| 		if (!ratectr->mgcp_crcx_ctr_group) | ||||
| 			return -EINVAL; | ||||
| 		snprintf(ctr_name, sizeof(ctr_name), "%s-%u:crcx", mgcp_trunk_type_strs_str(trunk->trunk_type), | ||||
| 			 trunk->trunk_nr); | ||||
| 		rate_ctr_group_set_name(ratectr->mgcp_crcx_ctr_group, ctr_name); | ||||
| 	} | ||||
| 	if (ratectr->mgcp_mdcx_ctr_group == NULL) { | ||||
| 		ratectr->mgcp_mdcx_ctr_group = | ||||
| 		    rate_ctr_group_alloc(trunk, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index++); | ||||
| 		if (!ratectr->mgcp_mdcx_ctr_group) | ||||
| 			return -EINVAL; | ||||
| 		snprintf(ctr_name, sizeof(ctr_name), "%s-%u:mdcx", mgcp_trunk_type_strs_str(trunk->trunk_type), | ||||
| 			 trunk->trunk_nr); | ||||
| 		rate_ctr_group_set_name(ratectr->mgcp_mdcx_ctr_group, ctr_name); | ||||
| 	} | ||||
| 	if (ratectr->mgcp_dlcx_ctr_group == NULL) { | ||||
| 		ratectr->mgcp_dlcx_ctr_group = | ||||
| 		    rate_ctr_group_alloc(trunk, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index++); | ||||
| 		if (!ratectr->mgcp_dlcx_ctr_group) | ||||
| 			return -EINVAL; | ||||
| 		snprintf(ctr_name, sizeof(ctr_name), "%s-%u:dlcx", mgcp_trunk_type_strs_str(trunk->trunk_type), | ||||
| 			 trunk->trunk_nr); | ||||
| 		rate_ctr_group_set_name(ratectr->mgcp_dlcx_ctr_group, ctr_name); | ||||
| 	} | ||||
| 	if (ratectr->all_rtp_conn_stats == NULL) { | ||||
| 		ratectr->all_rtp_conn_stats = rate_ctr_group_alloc(trunk, &all_rtp_conn_rate_ctr_group_desc, | ||||
| 								   all_rtp_conn_rate_ctr_index++); | ||||
| 		if (!ratectr->all_rtp_conn_stats) | ||||
| 			return -EINVAL; | ||||
| 		snprintf(ctr_name, sizeof(ctr_name), "%s-%u:rtp_conn", mgcp_trunk_type_strs_str(trunk->trunk_type), | ||||
| 			 trunk->trunk_nr); | ||||
| 		rate_ctr_group_set_name(ratectr->all_rtp_conn_stats, ctr_name); | ||||
| 	} | ||||
|  | ||||
| 	/* E1 specific */ | ||||
| 	if (trunk->trunk_type == MGCP_TRUNK_E1 && ratectr->e1_stats == NULL) { | ||||
| 		ratectr->e1_stats = rate_ctr_group_alloc(trunk, &e1_rate_ctr_group_desc, mdcx_rate_ctr_index++); | ||||
| 		if (!ratectr->e1_stats) | ||||
| 			return -EINVAL; | ||||
| 		snprintf(ctr_name, sizeof(ctr_name), "%s-%u:e1", mgcp_trunk_type_strs_str(trunk->trunk_type), | ||||
| 			 trunk->trunk_nr); | ||||
| 		rate_ctr_group_set_name(ratectr->e1_stats, ctr_name); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! free trunk specific rate counters | ||||
|  *  (called once when trunk is freed). | ||||
|  *  \param[in] trunk mgw trunk on which the rate counters are allocated. */ | ||||
| void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk) | ||||
| { | ||||
| 	struct mgcp_ratectr_trunk *ratectr = &trunk->ratectr; | ||||
|  | ||||
| 	if (ratectr->mgcp_crcx_ctr_group) { | ||||
| 		rate_ctr_group_free(ratectr->mgcp_crcx_ctr_group); | ||||
| 		ratectr->mgcp_crcx_ctr_group = NULL; | ||||
| 	} | ||||
| 	if (ratectr->mgcp_mdcx_ctr_group) { | ||||
| 		rate_ctr_group_free(ratectr->mgcp_mdcx_ctr_group); | ||||
| 		ratectr->mgcp_mdcx_ctr_group = NULL; | ||||
| 	} | ||||
| 	if (ratectr->mgcp_dlcx_ctr_group) { | ||||
| 		rate_ctr_group_free(ratectr->mgcp_dlcx_ctr_group); | ||||
| 		ratectr->mgcp_dlcx_ctr_group = NULL; | ||||
| 	} | ||||
| 	if (ratectr->all_rtp_conn_stats) { | ||||
| 		rate_ctr_group_free(ratectr->all_rtp_conn_stats); | ||||
| 		ratectr->all_rtp_conn_stats = NULL; | ||||
| 	} | ||||
|  | ||||
| 	/* E1 specific */ | ||||
| 	if (ratectr->e1_stats) { | ||||
| 		rate_ctr_group_free(ratectr->e1_stats); | ||||
| 		ratectr->e1_stats = NULL; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| const struct osmo_stat_item_desc trunk_stat_desc[] = { | ||||
| 	[TRUNK_STAT_ENDPOINTS_TOTAL] = { "endpoints:total", | ||||
| 					 "Number of endpoints that exist on the trunk", | ||||
| 					 "", 60, 0 }, | ||||
| 	[TRUNK_STAT_ENDPOINTS_USED] = { "endpoints:used", | ||||
| 					"Number of endpoints in use", | ||||
| 					"", 60, 0 }, | ||||
| }; | ||||
|  | ||||
| const struct osmo_stat_item_group_desc trunk_statg_desc = { | ||||
| 	.group_name_prefix = "trunk", | ||||
| 	.group_description = "mgw trunk", | ||||
| 	.class_id = OSMO_STATS_CLASS_GLOBAL, | ||||
| 	.num_items = ARRAY_SIZE(trunk_stat_desc), | ||||
| 	.item_desc = trunk_stat_desc, | ||||
| }; | ||||
|  | ||||
| /*! allocate trunk specific stat items | ||||
|  *  (called once on trunk initialization). | ||||
|  *  \param[in] trunk for which the stat items are allocated. | ||||
|  *  \returns 0 on success, -EINVAL on failure. */ | ||||
| int mgcp_stat_trunk_alloc(struct mgcp_trunk *trunk) | ||||
| { | ||||
| 	struct mgcp_stat_trunk *stats = &trunk->stats; | ||||
| 	static unsigned int common_stat_index = 0; | ||||
| 	char stat_name[256]; | ||||
|  | ||||
| 	stats->common = osmo_stat_item_group_alloc(trunk, &trunk_statg_desc, common_stat_index); | ||||
| 	if (!stats->common) | ||||
| 		return -EINVAL; | ||||
| 	snprintf(stat_name, sizeof(stat_name), "%s-%u:common", mgcp_trunk_type_strs_str(trunk->trunk_type), | ||||
| 		 trunk->trunk_nr); | ||||
| 	osmo_stat_item_group_set_name(stats->common, stat_name); | ||||
| 	common_stat_index++; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! free trunk specific stat items | ||||
|  *  (called once when trunk is freed). | ||||
|  *  \param[in] trunk on which the stat items are allocated. */ | ||||
| void mgcp_stat_trunk_free(struct mgcp_trunk *trunk) | ||||
| { | ||||
| 	struct mgcp_stat_trunk *stats = &trunk->stats; | ||||
|  | ||||
| 	if (stats->common) { | ||||
| 		osmo_stat_item_group_free(stats->common); | ||||
| 		stats->common = NULL; | ||||
| 	} | ||||
| } | ||||
| @@ -21,12 +21,19 @@ | ||||
|  */ | ||||
|  | ||||
| #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> | ||||
| @@ -91,7 +98,7 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used) | ||||
| /* Helper function to update codec map information with additional data from | ||||
|  * SDP, called from: mgcp_parse_sdp_data() */ | ||||
| static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, | ||||
| 			  int payload, const char *audio_name) | ||||
| 			  int payload_type, const char *audio_name) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| @@ -103,7 +110,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, | ||||
| 		/* Note: We can only update payload codecs that already exist | ||||
| 		 * in our codec list. If we get an unexpected payload type, | ||||
| 		 * we just drop it */ | ||||
| 		if (codecs[i].payload_type != payload) | ||||
| 		if (codecs[i].payload_type != payload_type) | ||||
| 			continue; | ||||
|  | ||||
| 		if (sscanf(audio_name, "%63[^/]/%d/%d", | ||||
| @@ -120,7 +127,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, | ||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload_type, | ||||
| 	     audio_name); | ||||
| } | ||||
|  | ||||
| @@ -255,6 +262,42 @@ error: | ||||
| 	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) | ||||
| @@ -285,16 +328,16 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp, | ||||
| 	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); | ||||
| 	struct mgcp_rtp_end *rtp; | ||||
|  | ||||
| 	int payload; | ||||
| 	int payload_type; | ||||
| 	int ptime, ptime2 = 0; | ||||
| 	char audio_name[64]; | ||||
| 	int port, rc; | ||||
| 	char ipv4[16]; | ||||
|  | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	OSMO_ASSERT(conn); | ||||
| @@ -312,8 +355,8 @@ 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); | ||||
| 			if (sscanf(line, "a=rtpmap:%d %63s", &payload_type, audio_name) == 2) { | ||||
| 				codecs_update(tmp_ctx, codecs, codecs_used, payload_type, audio_name); | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| @@ -351,17 +394,18 @@ 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) { | ||||
| 				talloc_free(tmp_ctx); | ||||
| 				return -1; | ||||
| 			} | ||||
| 			break; | ||||
| 		default: | ||||
| 			if (p->endp) | ||||
| 			if (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", | ||||
| @@ -381,21 +425,21 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp, | ||||
| 		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); | ||||
|  | ||||
| 	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"); | ||||
| 	for (i = 0; i < codecs_used; i++) { | ||||
| 		LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s", | ||||
| 		      rtp->codecs[i].payload_type, | ||||
| 		      rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown"); | ||||
| 		      strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown"); | ||||
| 		LOGPC(DLMGCP, LOGL_NOTICE, " "); | ||||
| 	} | ||||
| 	LOGPC(DLMGCP, LOGL_NOTICE, "\n"); | ||||
| @@ -476,6 +520,8 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign | ||||
|  | ||||
| 	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) { | ||||
| @@ -494,7 +540,7 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign | ||||
| 				return -EINVAL; | ||||
| 		} | ||||
|  | ||||
| 		rc = msgb_printf(sdp, "\r\n", fmtp_params[i].payload_type); | ||||
| 		rc = msgb_printf(sdp, "\r\n"); | ||||
| 		if (rc < 0) | ||||
| 			return -EINVAL; | ||||
| 	} | ||||
| @@ -522,6 +568,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp, | ||||
| 	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); | ||||
| @@ -529,19 +576,23 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp, | ||||
| 	OSMO_ASSERT(addr); | ||||
|  | ||||
| 	/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */ | ||||
| 	endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp, | ||||
| 	endp->trunk->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp, | ||||
| 					      &codec, &fmtp_extra, | ||||
| 					      (struct mgcp_conn_rtp *)conn); | ||||
|  | ||||
| 	audio_name = codec->audio_name; | ||||
| 	payload_type = codec->payload_type; | ||||
|  | ||||
| 	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; | ||||
| @@ -550,14 +601,14 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp, | ||||
|  | ||||
| 		payload_types[0] = payload_type; | ||||
| 		if (mgcp_conn_rtp_is_osmux(conn)) | ||||
| 			local_port = endp->cfg->osmux_port; | ||||
| 			local_port = endp->trunk->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; | ||||
| @@ -573,7 +624,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp, | ||||
| 		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) | ||||
|   | ||||
| @@ -22,16 +22,24 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #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> | ||||
| #include <osmocom/mgcp/mgcp_trunk.h> | ||||
|  | ||||
| /* Helper function for mgcp_format_stats_rtp() to calculate packet loss */ | ||||
| #if defined(__has_attribute) | ||||
| #if __has_attribute(no_sanitize) | ||||
| __attribute__((no_sanitize("undefined"))) | ||||
| #endif | ||||
| #endif | ||||
| void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss) | ||||
| { | ||||
| 	struct mgcp_rtp_state *state = &conn->state; | ||||
| 	struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]; | ||||
| 	struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR); | ||||
|  | ||||
| 	*expected = state->stats.cycles + state->stats.max_seq; | ||||
| 	*expected = *expected - state->stats.base_seq + 1; | ||||
| @@ -72,10 +80,10 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len, | ||||
| 	int ploss; | ||||
| 	int nchars; | ||||
|  | ||||
| 	struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]; | ||||
| 	struct rate_ctr *octets_rx = &conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR]; | ||||
| 	struct rate_ctr *packets_tx = &conn->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]; | ||||
| 	struct rate_ctr *octets_tx = &conn->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR]; | ||||
| 	struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR); | ||||
| 	struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR); | ||||
| 	struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR); | ||||
| 	struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR); | ||||
|  | ||||
| 	calc_loss(conn, &expected, &ploss); | ||||
| 	jitter = calc_jitter(&conn->state); | ||||
| @@ -91,7 +99,7 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len, | ||||
| 	str += nchars; | ||||
| 	str_len -= nchars; | ||||
|  | ||||
| 	if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) { | ||||
| 	if (conn->conn->endp->trunk->cfg->osmux != OSMUX_USAGE_OFF) { | ||||
| 		/* Error Counter */ | ||||
| 		nchars = snprintf(str, str_len, | ||||
| 				  "\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64, | ||||
|   | ||||
							
								
								
									
										207
									
								
								src/libosmo-mgcp/mgcp_threads_queue.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								src/libosmo-mgcp/mgcp_threads_queue.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,207 @@ | ||||
| /* | ||||
|  * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * Author: Eric Wild | ||||
|  * | ||||
|  * 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 <stdatomic.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/eventfd.h> | ||||
| #include <sys/types.h> | ||||
| #include <unistd.h> | ||||
| #include <talloc.h> | ||||
|  | ||||
| #include <osmocom/mgcp/mgcp_threads_queue.h> | ||||
|  | ||||
| /* | ||||
| classic lamport circular lockfree spsc queue: | ||||
| every "side" only writes its own ptr, but may read the other sides ptr | ||||
|  | ||||
| notify reader using eventfd as soon as element is added, reader then reads until | ||||
| read fails | ||||
| -> reader pops in a loop until FALSE and might get spurious events because it | ||||
| read before it was notified, which is fine | ||||
| -> writing pushes *the same data* in a loop until TRUE, blocks | ||||
|  | ||||
| shutting this down requires | ||||
| 1) to stop reading and pushing | ||||
| 2) ONE side to take care of the eventfds | ||||
| */ | ||||
|  | ||||
| static struct spsc *spsc_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr, bool blockw) | ||||
| { | ||||
| 	struct spsc *q = talloc_zero_size(talloc_ctx, sizeof(struct spsc) + sizeof(uintptr_t) * count); | ||||
| 	atomic_init(&q->readptr, 0); | ||||
| 	atomic_init(&q->writeptr, 0); | ||||
| 	q->efd_r = eventfd(0, blockr ? 0 : EFD_NONBLOCK); | ||||
| 	q->efd_w = eventfd(1, blockw ? 0 : EFD_NONBLOCK); | ||||
| 	q->count = count; | ||||
| 	q->size_per_buf = size_per_buf; | ||||
| 	q->buf = talloc_zero_size(q, size_per_buf * count); | ||||
|  | ||||
| 	for (int i = 0; i < count; i++) | ||||
| 		q->data[i] = (uintptr_t)q->buf + i * size_per_buf; | ||||
| 	return q; | ||||
| } | ||||
|  | ||||
| static void spsc_deinit(struct spsc *q) | ||||
| { | ||||
| 	talloc_free(q->buf); | ||||
| 	close(q->efd_r); | ||||
| 	close(q->efd_w); | ||||
| 	talloc_free(q); | ||||
| } | ||||
|  | ||||
| static ssize_t spsc_check_r(struct spsc *q) | ||||
| { | ||||
| 	uint64_t efdr; | ||||
| 	return read(q->efd_r, &efdr, sizeof(uint64_t)); | ||||
| } | ||||
| static ssize_t spsc_check_w(struct spsc *q) | ||||
| { | ||||
| 	uint64_t efdr; | ||||
| 	return read(q->efd_w, &efdr, sizeof(uint64_t)); | ||||
| } | ||||
| static void spsc_notify_r(struct spsc *q) | ||||
| { | ||||
| 	uint64_t efdu = 1; | ||||
| 	write(q->efd_r, &efdu, sizeof(uint64_t)); | ||||
| } | ||||
| static void spsc_notify_w(struct spsc *q) | ||||
| { | ||||
| 	uint64_t efdu = 1; | ||||
| 	write(q->efd_w, &efdu, sizeof(uint64_t)); | ||||
| } | ||||
|  | ||||
| /*! Adds element to the queue by copying the data. | ||||
|  *  \param[in] q queue. | ||||
|  *  \param[in] elem input buffer, must match the originally configured queue buffer size!. | ||||
|  *  \returns true if queue was not full and element was successfully pushed */ | ||||
| bool spsc_push(struct spsc *q, void *elem) | ||||
| { | ||||
| 	size_t cur_wp, cur_rp; | ||||
| 	cur_wp = atomic_load_explicit(&q->writeptr, memory_order_relaxed); | ||||
| 	cur_rp = atomic_load_explicit(&q->readptr, memory_order_acquire); | ||||
| 	if ((cur_wp + 1) % q->count == cur_rp) { | ||||
| 		spsc_check_w(q); /* blocks, ensures next (!) call succeeds */ | ||||
| 		return false; | ||||
| 	} | ||||
| 	memcpy((void *)q->data[cur_wp], elem, q->size_per_buf); | ||||
| 	atomic_store_explicit(&q->writeptr, (cur_wp + 1) % q->count, memory_order_release); | ||||
| 	spsc_notify_r(q); /* fine after release */ | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /*! Reads the read-fd of the queue, which, depending on settings passed on queue creation, blocks. | ||||
|  * This function can be used to deliberately wait for a non-empty queue on the read side. | ||||
|  *  \param[in] q queue. | ||||
|  *  \returns result of reading the fd. */ | ||||
| ssize_t spsc_prep_pop(struct spsc *q) | ||||
| { | ||||
| 	return spsc_check_r(q); | ||||
| } | ||||
|  | ||||
| /*! Removes element from the queue by copying the data. | ||||
|  *  \param[in] q queue. | ||||
|  *  \param[in] elem output buffer, must match the originally configured queue buffer size!. | ||||
|  *  \returns true if queue was not empty and element was successfully removed */ | ||||
| bool spsc_pop(struct spsc *q, void *elem) | ||||
| { | ||||
| 	size_t cur_wp, cur_rp; | ||||
| 	cur_wp = atomic_load_explicit(&q->writeptr, memory_order_acquire); | ||||
| 	cur_rp = atomic_load_explicit(&q->readptr, memory_order_relaxed); | ||||
|  | ||||
| 	if (cur_wp == cur_rp) /* blocks via prep_pop */ | ||||
| 		return false; | ||||
| 	memcpy(elem, (void *)q->data[cur_rp], q->size_per_buf); | ||||
| 	atomic_store_explicit(&q->readptr, (cur_rp + 1) % q->count, memory_order_release); | ||||
| 	spsc_notify_w(q); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /*! Creates a bidirectional queue channel that consists of two queues, one in each direction, | ||||
|  *  commonly referred to as a and b side. | ||||
|  *  \param[in] talloc_ctx allocation context. | ||||
|  *  \param[in] count number of buffers per queue. | ||||
|  *  \param[in] size_per_buf size of buffers per queue. | ||||
|  *  \param[in] blockr_a should reading the a-side read fd block?. | ||||
|  *  \param[in] blockw_a should reading the a-side write fd block?. | ||||
|  *  \param[in] blockr_b should reading the b-side read fd block?. | ||||
|  *  \param[in] blockw_b should reading the b-side write fd block?. | ||||
|   *  \returns queue channel */ | ||||
| struct qchan spsc_chan_init_ex(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr_a, | ||||
| 			       bool blockw_a, bool blockr_b, bool blockw_b) | ||||
| { | ||||
| 	struct qchan q; | ||||
| 	q.a = spsc_init(talloc_ctx, count, size_per_buf, blockr_a, blockw_a); | ||||
| 	q.b = spsc_init(talloc_ctx, count, size_per_buf, blockr_b, blockw_b); | ||||
| 	return q; | ||||
| } | ||||
|  | ||||
| /*! Creates a bidirectional queue channel that consists of two queues, one in each direction, | ||||
|  *  commonly referred to as a and b side. | ||||
|  *  \param[in] talloc_ctx allocation context. | ||||
|  *  \param[in] count number of buffers per queue. | ||||
|  *  \param[in] size_per_buf size of buffers per queue. | ||||
|  *  \returns queue channel */ | ||||
| struct qchan spsc_chan_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf) | ||||
| { | ||||
| 	return spsc_chan_init_ex(talloc_ctx, count, size_per_buf, false, true, false, true); | ||||
| } | ||||
|  | ||||
| /*! Closes a bidirectional queue channel. | ||||
|  *  \param[in] q queue */ | ||||
| void spsc_chan_close(struct qchan *q) | ||||
| { | ||||
| 	spsc_deinit(q->a); | ||||
| 	spsc_deinit(q->b); | ||||
| 	free(q); | ||||
| } | ||||
|  | ||||
| /*! Gets queue channel read/write fd for a/b side according to function name. | ||||
|  *  \param[in] q queue channel. | ||||
|  *  \returns fd */ | ||||
| int spsc_get_a_rdfd(struct qchan *q) | ||||
| { | ||||
| 	return q->a->efd_r; | ||||
| } | ||||
| /*! Gets queue channel read/write fd for a/b side according to function name. | ||||
|  *  \param[in] q queue channel. | ||||
|  *  \returns fd */ | ||||
| int spsc_get_b_rdfd(struct qchan *q) | ||||
| { | ||||
| 	return q->b->efd_r; | ||||
| } | ||||
| /*! Gets queue channel read/write fd for a/b side according to function name. | ||||
|  *  \param[in] q queue channel. | ||||
|  *  \returns fd */ | ||||
| int spsc_get_a_wrfd(struct qchan *q) | ||||
| { | ||||
| 	return q->a->efd_w; | ||||
| } | ||||
| /*! Gets queue channel read/write fd for a/b side according to function name. | ||||
|  *  \param[in] q queue channel. | ||||
|  *  \returns fd */ | ||||
| int spsc_get_b_wrfd(struct qchan *q) | ||||
| { | ||||
| 	return q->b->efd_w; | ||||
| } | ||||
							
								
								
									
										308
									
								
								src/libosmo-mgcp/mgcp_trunk.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										308
									
								
								src/libosmo-mgcp/mgcp_trunk.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,308 @@ | ||||
| /* 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> | ||||
| #include <osmocom/core/stat_item.h> | ||||
|  | ||||
| const struct value_string mgcp_trunk_type_strs[] = { | ||||
| 	{ MGCP_TRUNK_VIRTUAL,		"virtual" }, | ||||
| 	{ MGCP_TRUNK_E1,		"e1" }, | ||||
| 	{ 0, NULL } | ||||
| }; | ||||
|  | ||||
| /* Free trunk, this function is automatically called by talloc_free when the trunk is freed. It does not free the | ||||
|  * endpoints on the trunk, this must be done separately before freeing the trunk. */ | ||||
| static int trunk_free_talloc_destructor(struct mgcp_trunk *trunk) | ||||
| { | ||||
| 	llist_del(&trunk->entry); | ||||
| 	mgcp_ratectr_trunk_free(trunk); | ||||
| 	mgcp_stat_trunk_free(trunk); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! allocate trunk and add it 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, unsigned 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(trunk); | ||||
| 	mgcp_stat_trunk_alloc(trunk); | ||||
| 	talloc_set_destructor(trunk, trunk_free_talloc_destructor); | ||||
|  | ||||
| 	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, 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; | ||||
| 	osmo_stat_item_set(osmo_stat_item_group_get_item(trunk->stats.common, TRUNK_STAT_ENDPOINTS_TOTAL), | ||||
| 			   trunk->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, unsigned 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(unsigned int *trunk_nr, const char *epname) | ||||
| { | ||||
| 	unsigned long trunk_nr_temp; | ||||
| 	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_temp = strtoul(epname + prefix_len, &str_trunk_nr_end, 10); | ||||
| 	if (errno == ERANGE || trunk_nr_temp > 64 | ||||
| 	    || epname + prefix_len == str_trunk_nr_end | ||||
| 	    || str_trunk_nr_end[0] != '/') | ||||
| 		return -EINVAL; | ||||
| 	else { | ||||
| 		*trunk_nr = (unsigned int)trunk_nr_temp; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Check if the domain name, which is supplied with the endpoint name | ||||
|  * matches the configuration. */ | ||||
| static int check_domain_name(const char *epname, const 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 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]; | ||||
| 	unsigned int trunk_nr; | ||||
| 	int rc; | ||||
|  | ||||
| 	osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname); | ||||
| 	epname = epname_lc; | ||||
|  | ||||
| 	/* All endpoint names require a domain as suffix */ | ||||
| 	if (check_domain_name(epname, cfg)) | ||||
| 		return NULL; | ||||
|  | ||||
| 	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); | ||||
| 	} | ||||
|  | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, epname); | ||||
| 	if (rc == 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); | ||||
| @@ -140,20 +189,20 @@ static void handle_options(int argc, char **argv) | ||||
|  | ||||
| /* 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; | ||||
| @@ -179,14 +228,14 @@ 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); | ||||
| 	} | ||||
|  | ||||
| 	/* reset endpoints */ | ||||
| 	if (reset_endpoints) { | ||||
| 		LOGP(DLMGCP, LOGL_NOTICE, | ||||
| 		     "Asked to reset endpoints: %d/%d\n", | ||||
| 		     "Asked to reset endpoints: %u/%d\n", | ||||
| 		     reset_trunk->trunk_nr, reset_trunk->trunk_type); | ||||
|  | ||||
| 		/* reset flag */ | ||||
| @@ -194,8 +243,8 @@ 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]); | ||||
| 		for (i = 0; i < reset_trunk->number_endpoints; ++i) | ||||
| 			mgcp_endp_release(reset_trunk->endpoints[i]); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| @@ -248,6 +297,12 @@ static const struct log_info_cat log_categories[] = { | ||||
| 		  .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 = { | ||||
| @@ -260,52 +315,66 @@ 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(); | ||||
| 	osmo_talloc_vty_add_cmds(); | ||||
| 	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; | ||||
|  | ||||
| 	/* we need to bind a socket */ | ||||
| 	flags = OSMO_SOCK_F_BIND; | ||||
| 	if (cfg->call_agent_addr) | ||||
| 	if (strlen(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); | ||||
| 				cfg->call_agent_addr, strlen(cfg->call_agent_addr) ? 2727 : 0, flags); | ||||
| 	if (rc < 0) { | ||||
| 		perror("Gateway failed to bind"); | ||||
| 		return -1; | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -22,16 +22,20 @@ | ||||
| #include <osmocom/mgcp/mgcp.h> | ||||
| #include <osmocom/mgcp/vty.h> | ||||
| #include <osmocom/mgcp/mgcp_common.h> | ||||
| #include <osmocom/mgcp/mgcp_internal.h> | ||||
| #include <osmocom/mgcp/mgcp_conn.h> | ||||
| #include <osmocom/mgcp/mgcp_protocol.h> | ||||
| #include <osmocom/mgcp/mgcp_stat.h> | ||||
| #include <osmocom/mgcp/mgcp_msg.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/mgcp/mgcp_network.h> | ||||
|  | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/core/talloc.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/socket.h> | ||||
| #include <string.h> | ||||
| #include <limits.h> | ||||
| #include <dlfcn.h> | ||||
| @@ -70,7 +74,7 @@ static void test_strline(void) | ||||
| } | ||||
|  | ||||
| #define AUEP1	"AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0\r\n" | ||||
| #define AUEP1_RET "200 158663169 OK\r\n" | ||||
| #define AUEP1_RET "500 158663169 FAIL\r\n" | ||||
| #define AUEP2	"AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0\r\n" | ||||
| #define AUEP2_RET "500 18983213 FAIL\r\n" | ||||
| #define EMPTY	"\r\n" | ||||
| @@ -81,7 +85,7 @@ static void test_strline(void) | ||||
| #define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0\r\n" | ||||
| #define MDCX_ERR_RET "500 18983213 FAIL\r\n" | ||||
| #define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0\r\n" | ||||
| #define MDCX_RET "400 18983214 FAIL\r\n" | ||||
| #define MDCX_RET "500 18983214 FAIL\r\n" | ||||
|  | ||||
| #define MDCX3 \ | ||||
| 	"MDCX 18983215 1@mgw MGCP 1.0\r\n" \ | ||||
| @@ -124,7 +128,7 @@ static void test_strline(void) | ||||
| 	"a=fmtp:126 0/1/2\r\n" \ | ||||
| 	"a=ptime:40\r\n" | ||||
|  | ||||
| #define MDCX4 \ | ||||
| #define MDCX4_ADDR0000 \ | ||||
| 	"MDCX 18983216 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"M: sendrecv\r" \ | ||||
| 	"C: 2\r\n" \ | ||||
| @@ -139,6 +143,24 @@ static void test_strline(void) | ||||
| 	"a=rtpmap:99 AMR/8000\r\n" \ | ||||
| 	"a=ptime:40\r\n" | ||||
|  | ||||
| #define MDCX4_ADDR0000_RET \ | ||||
| 	"527 18983216 FAIL\r\n" | ||||
|  | ||||
| #define MDCX4 \ | ||||
| 	"MDCX 18983217 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"M: sendrecv\r" \ | ||||
| 	"C: 2\r\n" \ | ||||
| 	"I: %s\r\n" \ | ||||
| 	"L: p:20, a:AMR, nt:IN\r\n" \ | ||||
| 	"\n" \ | ||||
| 	"v=0\r\n" \ | ||||
| 	"o=- %s 23 IN IP4 5.6.7.8\r\n" \ | ||||
| 	"c=IN IP4 5.6.7.8\r\n" \ | ||||
| 	"t=0 0\r\n" \ | ||||
| 	"m=audio 4441 RTP/AVP 99\r\n" \ | ||||
| 	"a=rtpmap:99 AMR/8000\r\n" \ | ||||
| 	"a=ptime:40\r\n" | ||||
|  | ||||
| #define MDCX4_RET(Ident) \ | ||||
| 	"200 " Ident " OK\r\n" \ | ||||
| 	"\r\n" \ | ||||
| @@ -164,45 +186,45 @@ static void test_strline(void) | ||||
| 	"a=ptime:40\r\n" | ||||
|  | ||||
| #define MDCX4_PT1 \ | ||||
| 	"MDCX 18983217 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"MDCX 18983218 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"M: SENDRECV\r" \ | ||||
| 	"C: 2\r\n" \ | ||||
| 	"I: %s\r\n" \ | ||||
| 	"L: p:20-40, a:AMR, nt:IN\r\n" \ | ||||
| 	"\n" \ | ||||
| 	"v=0\r\n" \ | ||||
| 	"o=- %s 23 IN IP4 0.0.0.0\r\n" \ | ||||
| 	"c=IN IP4 0.0.0.0\r\n" \ | ||||
| 	"o=- %s 23 IN IP4 5.6.7.8\r\n" \ | ||||
| 	"c=IN IP4 5.6.7.8\r\n" \ | ||||
| 	"t=0 0\r\n" \ | ||||
| 	"m=audio 4441 RTP/AVP 99\r\n" \ | ||||
| 	"a=rtpmap:99 AMR/8000\r\n" \ | ||||
| 	"a=ptime:40\r\n" | ||||
|  | ||||
| #define MDCX4_PT2 \ | ||||
| 	"MDCX 18983218 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"MDCX 18983219 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"M: sendrecv\r" \ | ||||
| 	"C: 2\r\n" \ | ||||
| 	"I: %s\r\n" \ | ||||
| 	"L: p:20-20, a:AMR, nt:IN\r\n" \ | ||||
| 	"\n" \ | ||||
| 	"v=0\r\n" \ | ||||
| 	"o=- %s 23 IN IP4 0.0.0.0\r\n" \ | ||||
| 	"c=IN IP4 0.0.0.0\r\n" \ | ||||
| 	"o=- %s 23 IN IP4 5.6.7.8\r\n" \ | ||||
| 	"c=IN IP4 5.6.7.8\r\n" \ | ||||
| 	"t=0 0\r\n" \ | ||||
| 	"m=audio 4441 RTP/AVP 99\r\n" \ | ||||
| 	"a=rtpmap:99 AMR/8000\r\n" \ | ||||
| 	"a=ptime:40\r\n" | ||||
|  | ||||
| #define MDCX4_PT3 \ | ||||
| 	"MDCX 18983219 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"MDCX 18983220 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"M: sendrecv\r" \ | ||||
| 	"C: 2\r\n" \ | ||||
| 	"I: %s\r\n" \ | ||||
| 	"L: a:AMR, nt:IN\r\n" \ | ||||
| 	"\n" \ | ||||
| 	"v=0\r\n" \ | ||||
| 	"o=- %s 23 IN IP4 0.0.0.0\r\n" \ | ||||
| 	"c=IN IP4 0.0.0.0\r\n" \ | ||||
| 	"o=- %s 23 IN IP4 5.6.7.8\r\n" \ | ||||
| 	"c=IN IP4 5.6.7.8\r\n" \ | ||||
| 	"t=0 0\r\n" \ | ||||
| 	"m=audio 4441 RTP/AVP 99\r\n" \ | ||||
| 	"a=rtpmap:99 AMR/8000\r\n" \ | ||||
| @@ -210,47 +232,47 @@ static void test_strline(void) | ||||
|  | ||||
| /* Test different upper/lower case in options */ | ||||
| #define MDCX4_PT4 \ | ||||
| 	"MDCX 18983220 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"MDCX 18983221 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"m: sendrecv\r" \ | ||||
| 	"c: 2\r\n" \ | ||||
| 	"i: %s\r\n" \ | ||||
| 	"l: A:amr, NT:IN\r\n" \ | ||||
| 	"\n" \ | ||||
| 	"v=0\r\n" \ | ||||
| 	"o=- %s 23 IN IP4 0.0.0.0\r\n" \ | ||||
| 	"c=IN IP4 0.0.0.0\r\n" \ | ||||
| 	"o=- %s 23 IN IP4 5.6.7.8\r\n" \ | ||||
| 	"c=IN IP4 5.6.7.8\r\n" \ | ||||
| 	"t=0 0\r\n" \ | ||||
| 	"m=audio 4441 RTP/AVP 99\r\n" \ | ||||
| 	"a=rtpmap:99 AMR/8000\r\n" \ | ||||
| 	"a=ptime:40\r\n" | ||||
|  | ||||
| #define MDCX4_SO \ | ||||
| 	"MDCX 18983221 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"MDCX 18983222 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"M: sendonly\r" \ | ||||
| 	"C: 2\r\n" \ | ||||
| 	"I: %s\r\n" \ | ||||
| 	"L: p:20, a:AMR, nt:IN\r\n" \ | ||||
| 	"\n" \ | ||||
| 	"v=0\r\n" \ | ||||
| 	"o=- %s 23 IN IP4 0.0.0.0\r\n" \ | ||||
| 	"c=IN IP4 0.0.0.0\r\n" \ | ||||
| 	"o=- %s 23 IN IP4 5.6.7.8\r\n" \ | ||||
| 	"c=IN IP4 5.6.7.8\r\n" \ | ||||
| 	"t=0 0\r\n" \ | ||||
| 	"m=audio 4441 RTP/AVP 99\r\n" \ | ||||
| 	"a=rtpmap:99 AMR/8000\r\n" \ | ||||
| 	"a=ptime:40\r\n" | ||||
|  | ||||
| #define MDCX4_RO \ | ||||
| 	"MDCX 18983222 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"MDCX 18983223 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"M: recvonly\r" \ | ||||
| 	"C: 2\r\n" \ | ||||
| 	"I: %s\r\n" \ | ||||
| 	"L: p:20, a:AMR, nt:IN\r\n" | ||||
|  | ||||
| #define MDCX_TOO_LONG_CI \ | ||||
| 	"MDCX 18983223 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"MDCX 18983224 1@mgw MGCP 1.0\r\n" \ | ||||
| 	"I: 123456789012345678901234567890123\n" | ||||
|  | ||||
| #define MDCX_TOO_LONG_CI_RET "510 18983223 FAIL\r\n" | ||||
| #define MDCX_TOO_LONG_CI_RET "510 18983224 FAIL\r\n" | ||||
|  | ||||
| #define SHORT2	"CRCX 1" | ||||
| #define SHORT2_RET "510 000000 FAIL\r\n" | ||||
| @@ -538,13 +560,14 @@ static const struct mgcp_test tests[] = { | ||||
| 	{"MDCX2", MDCX_UNALLOCATED, MDCX_RET}, | ||||
| 	{"CRCX", CRCX, CRCX_RET, 97}, | ||||
| 	{"MDCX3", MDCX3, MDCX3_RET, PTYPE_IGNORE}, | ||||
| 	{"MDCX4", MDCX4, MDCX4_RET("18983216"), 99}, | ||||
| 	{"MDCX4_PT1", MDCX4_PT1, MDCX4_RET("18983217"), 99}, | ||||
| 	{"MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983218"), 99}, | ||||
| 	{"MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983219"), 99}, | ||||
| 	{"MDCX4_PT4", MDCX4_PT4, MDCX4_RET("18983220"), 99}, | ||||
| 	{"MDCX4_SO", MDCX4_SO, MDCX4_RET("18983221"), 99}, | ||||
| 	{"MDCX4_RO", MDCX4_RO, MDCX4_RO_RET("18983222"), PTYPE_IGNORE}, | ||||
| 	{"MDCX4_ADDR000", MDCX4_ADDR0000, MDCX4_ADDR0000_RET}, | ||||
| 	{"MDCX4", MDCX4, MDCX4_RET("18983217"), 99}, | ||||
| 	{"MDCX4_PT1", MDCX4_PT1, MDCX4_RET("18983218"), 99}, | ||||
| 	{"MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983219"), 99}, | ||||
| 	{"MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983220"), 99}, | ||||
| 	{"MDCX4_PT4", MDCX4_PT4, MDCX4_RET("18983221"), 99}, | ||||
| 	{"MDCX4_SO", MDCX4_SO, MDCX4_RET("18983222"), 99}, | ||||
| 	{"MDCX4_RO", MDCX4_RO, MDCX4_RO_RET("18983223"), PTYPE_IGNORE}, | ||||
| 	{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE}, | ||||
| 	{"CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97}, | ||||
| 	{"EMPTY", EMPTY, EMPTY_RET}, | ||||
| @@ -591,18 +614,6 @@ static struct msgb *create_msg(const char *str, const char *conn_id) | ||||
| 	return msg; | ||||
| } | ||||
|  | ||||
| static int last_endpoint = -1; | ||||
|  | ||||
| static int mgcp_test_policy_cb(struct mgcp_trunk_config *cfg, int endpoint, | ||||
| 			       int state, const char *transactio_id) | ||||
| { | ||||
| 	fprintf(stderr, "Policy CB got state %d on endpoint 0x%x\n", | ||||
| 		state, endpoint); | ||||
| 	last_endpoint = endpoint; | ||||
| 	return MGCP_POLICY_CONT; | ||||
| } | ||||
|  | ||||
| #define MGCP_DUMMY_LOAD 0x23 | ||||
| static int dummy_packets = 0; | ||||
| /* override and forward */ | ||||
| ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, | ||||
| @@ -612,13 +623,18 @@ ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, | ||||
| 	    htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr); | ||||
| 	int dest_port = htons(((struct sockaddr_in *)dest_addr)->sin_port); | ||||
|  | ||||
| 	if (len == 1 && ((const char *)buf)[0] == MGCP_DUMMY_LOAD) { | ||||
| 	if (len == sizeof(rtp_dummy_payload) | ||||
| 	    && memcmp(buf, rtp_dummy_payload, sizeof(rtp_dummy_payload)) == 0) { | ||||
| 		fprintf(stderr, | ||||
| 			"Dummy packet to 0x%08x:%d, msg length %zu\n%s\n\n", | ||||
| 			dest_host, dest_port, len, osmo_hexdump(buf, len)); | ||||
| 		dummy_packets += 1; | ||||
| 	} | ||||
|  | ||||
| 	/* Make sure address+port are valid */ | ||||
| 	OSMO_ASSERT(dest_host); | ||||
| 	OSMO_ASSERT(dest_port); | ||||
|  | ||||
| 	return len; | ||||
| } | ||||
|  | ||||
| @@ -641,11 +657,11 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp) | ||||
| 	return real_clock_gettime(clk_id, tp); | ||||
| } | ||||
|  | ||||
| static void mgcp_endpoints_release(struct mgcp_trunk_config *trunk) | ||||
| static void mgcp_endpoints_release(struct mgcp_trunk *trunk) | ||||
| { | ||||
| 	int i; | ||||
| 	for (i = 1; i < trunk->number_endpoints; i++) | ||||
| 		mgcp_endp_release(&trunk->endpoints[i]); | ||||
| 	for (i = 0; i < trunk->number_endpoints; i++) | ||||
| 		mgcp_endp_release(trunk->endpoints[i]); | ||||
| } | ||||
|  | ||||
| #define CONN_UNMODIFIED (0x1000) | ||||
| @@ -749,23 +765,21 @@ static void test_messages(void) | ||||
| { | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	struct mgcp_trunk_config *trunk2; | ||||
| 	struct mgcp_trunk *trunk; | ||||
| 	int i; | ||||
| 	struct mgcp_conn_rtp *conn = NULL; | ||||
| 	char last_conn_id[256]; | ||||
| 	int rc; | ||||
| 	char *last_endpoint = mgcp_debug_get_last_endpoint_name(); | ||||
|  | ||||
| 	cfg = mgcp_config_alloc(); | ||||
| 	trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID); | ||||
|  | ||||
| 	cfg->trunk.vty_number_endpoints = 64; | ||||
| 	mgcp_endpoints_allocate(&cfg->trunk); | ||||
| 	cfg->policy_cb = mgcp_test_policy_cb; | ||||
| 	trunk->v.vty_number_endpoints = 64; | ||||
|         mgcp_trunk_equip(trunk); | ||||
|  | ||||
| 	memset(last_conn_id, 0, sizeof(last_conn_id)); | ||||
|  | ||||
| 	trunk2 = mgcp_trunk_alloc(cfg, 1); | ||||
| 	mgcp_endpoints_allocate(trunk2); | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(tests); i++) { | ||||
| 		const struct mgcp_test *t = &tests[i]; | ||||
| 		struct msgb *inp; | ||||
| @@ -774,10 +788,9 @@ static void test_messages(void) | ||||
| 		printf("\n================================================\n"); | ||||
| 		printf("Testing %s\n", t->name); | ||||
|  | ||||
| 		last_endpoint = -1; | ||||
| 		dummy_packets = 0; | ||||
|  | ||||
| 		osmo_talloc_replace_string(cfg, &cfg->trunk.audio_fmtp_extra, | ||||
| 		osmo_talloc_replace_string(cfg, &trunk->audio_fmtp_extra, | ||||
| 					   t->extra_fmtp); | ||||
|  | ||||
| 		inp = create_msg(t->req, last_conn_id); | ||||
| @@ -809,8 +822,9 @@ static void test_messages(void) | ||||
| 		if (dummy_packets) | ||||
| 			printf("Dummy packets: %d\n", dummy_packets); | ||||
|  | ||||
| 		if (last_endpoint != -1) { | ||||
| 			endp = &cfg->trunk.endpoints[last_endpoint]; | ||||
| 		if (last_endpoint[0] != '\0') { | ||||
| 			endp = mgcp_endp_by_name(NULL, last_endpoint, cfg); | ||||
| 			OSMO_ASSERT(endp); | ||||
|  | ||||
| 			conn = mgcp_conn_get_rtp(endp, "1"); | ||||
| 			if (conn) { | ||||
| @@ -854,7 +868,7 @@ static void test_messages(void) | ||||
| 					printf("Connection mode not set\n"); | ||||
|  | ||||
| 				OSMO_ASSERT(conn->end.output_enabled | ||||
| 					    == (conn->conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0)); | ||||
| 					    == !!(conn->conn->mode & MGCP_CONN_SEND_ONLY)); | ||||
|  | ||||
| 				conn->conn->mode |= CONN_UNMODIFIED; | ||||
|  | ||||
| @@ -865,10 +879,9 @@ static void test_messages(void) | ||||
|  | ||||
| 		/* Check detected payload type */ | ||||
| 		if (conn && t->ptype != PTYPE_IGNORE) { | ||||
| 			OSMO_ASSERT(last_endpoint != -1); | ||||
| 			endp = &cfg->trunk.endpoints[last_endpoint]; | ||||
| 			OSMO_ASSERT(last_endpoint[0] != '\0'); | ||||
|  | ||||
| 			fprintf(stderr, "endpoint 0x%x: " | ||||
| 			fprintf(stderr, "endpoint:%s: " | ||||
| 				"payload type %d (expected %d)\n", | ||||
| 				last_endpoint, | ||||
| 				conn->end.codec->payload_type, t->ptype); | ||||
| @@ -882,29 +895,26 @@ static void test_messages(void) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	mgcp_endpoints_release(trunk2); | ||||
| 	mgcp_endpoints_release(&cfg->trunk); | ||||
| 	mgcp_endpoints_release(trunk); | ||||
| 	talloc_free(cfg); | ||||
| } | ||||
|  | ||||
| static void test_retransmission(void) | ||||
| { | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct mgcp_trunk_config *trunk2; | ||||
| 	struct mgcp_trunk *trunk; | ||||
| 	int i; | ||||
| 	char last_conn_id[256]; | ||||
| 	int rc; | ||||
|  | ||||
| 	cfg = mgcp_config_alloc(); | ||||
| 	trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID); | ||||
|  | ||||
| 	cfg->trunk.vty_number_endpoints = 64; | ||||
| 	mgcp_endpoints_allocate(&cfg->trunk); | ||||
| 	trunk->v.vty_number_endpoints = 64; | ||||
|         mgcp_trunk_equip(trunk); | ||||
|  | ||||
| 	memset(last_conn_id, 0, sizeof(last_conn_id)); | ||||
|  | ||||
| 	trunk2 = mgcp_trunk_alloc(cfg, 1); | ||||
| 	mgcp_endpoints_allocate(trunk2); | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(retransmit); i++) { | ||||
| 		const struct mgcp_test *t = &retransmit[i]; | ||||
| 		struct msgb *inp; | ||||
| @@ -943,33 +953,30 @@ static void test_retransmission(void) | ||||
| 		msgb_free(msg); | ||||
| 	} | ||||
|  | ||||
| 	mgcp_endpoints_release(trunk2); | ||||
| 	mgcp_endpoints_release(&cfg->trunk); | ||||
| 	mgcp_endpoints_release(trunk); | ||||
| 	talloc_free(cfg); | ||||
| } | ||||
|  | ||||
| static int rqnt_cb(struct mgcp_endpoint *endp, char _tone) | ||||
| { | ||||
| 	ptrdiff_t tone = _tone; | ||||
| 	endp->cfg->data = (void *)tone; | ||||
| 	endp->trunk->cfg->data = (void *)tone; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void test_rqnt_cb(void) | ||||
| { | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct mgcp_trunk_config *trunk2; | ||||
| 	struct mgcp_trunk *trunk; | ||||
| 	struct msgb *inp, *msg; | ||||
| 	char conn_id[256]; | ||||
|  | ||||
| 	cfg = mgcp_config_alloc(); | ||||
| 	trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID); | ||||
| 	cfg->rqnt_cb = rqnt_cb; | ||||
|  | ||||
| 	cfg->trunk.vty_number_endpoints = 64; | ||||
| 	mgcp_endpoints_allocate(&cfg->trunk); | ||||
|  | ||||
| 	trunk2 = mgcp_trunk_alloc(cfg, 1); | ||||
| 	mgcp_endpoints_allocate(trunk2); | ||||
| 	trunk->v.vty_number_endpoints = 64; | ||||
|         mgcp_trunk_equip(trunk); | ||||
|  | ||||
| 	inp = create_msg(CRCX, NULL); | ||||
| 	msg = mgcp_handle_message(cfg, inp); | ||||
| @@ -998,8 +1005,7 @@ static void test_rqnt_cb(void) | ||||
| 	inp = create_msg(DLCX, conn_id); | ||||
| 	msgb_free(mgcp_handle_message(cfg, inp)); | ||||
| 	msgb_free(inp); | ||||
| 	mgcp_endpoints_release(trunk2); | ||||
| 	mgcp_endpoints_release(&cfg->trunk); | ||||
| 	mgcp_endpoints_release(trunk); | ||||
| 	talloc_free(cfg); | ||||
| } | ||||
|  | ||||
| @@ -1035,19 +1041,20 @@ static void test_packet_loss_calc(void) | ||||
| { | ||||
| 	int i; | ||||
| 	struct mgcp_endpoint endp; | ||||
| 	struct mgcp_config cfg = {0}; | ||||
| 	struct mgcp_trunk_config trunk; | ||||
| 	struct mgcp_endpoint *endpoints[1]; | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct mgcp_trunk *trunk; | ||||
|  | ||||
| 	printf("Testing packet loss calculation.\n"); | ||||
|  | ||||
| 	memset(&endp, 0, sizeof(endp)); | ||||
| 	memset(&trunk, 0, sizeof(trunk)); | ||||
|  | ||||
| 	endp.cfg = &cfg; | ||||
| 	cfg = mgcp_config_alloc(); | ||||
| 	trunk = mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID); | ||||
| 	endp.type = &ep_typeset.rtp; | ||||
| 	trunk.vty_number_endpoints = 1; | ||||
| 	trunk.endpoints = &endp; | ||||
| 	endp.tcfg = &trunk; | ||||
| 	trunk->v.vty_number_endpoints = 1; | ||||
| 	trunk->endpoints = endpoints; | ||||
| 	trunk->endpoints[0] = &endp; | ||||
| 	endp.trunk = trunk; | ||||
| 	INIT_LLIST_HEAD(&endp.conns); | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) { | ||||
| @@ -1064,7 +1071,7 @@ static void test_packet_loss_calc(void) | ||||
| 				    "test-connection"); | ||||
| 		conn = mgcp_conn_get_rtp(&endp, _conn->id); | ||||
| 		state = &conn->state; | ||||
| 		packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]; | ||||
| 		packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR); | ||||
|  | ||||
| 		state->stats.initialized = 1; | ||||
| 		state->stats.base_seq = pl_test_dat[i].base_seq; | ||||
| @@ -1085,6 +1092,8 @@ static void test_packet_loss_calc(void) | ||||
| 		mgcp_conn_free_all(&endp); | ||||
| 	} | ||||
|  | ||||
| 	talloc_free(trunk); | ||||
| 	talloc_free(cfg); | ||||
| } | ||||
|  | ||||
| int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os, | ||||
| @@ -1251,24 +1260,28 @@ struct rtp_packet_info test_rtp_packets1[] = { | ||||
| 	/* RTP: SeqNo=1002, TS=160320 */ | ||||
| 	{2.040000, 20, "\x80\x62\x03\xEA\x00\x02\x72\x40\x50\x60\x70\x80" | ||||
| 	 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, | ||||
| 	/* RTP: SeqNo=1003, TS=180320, Marker */ | ||||
| 	{2.060000, 20, "\x80\xE2\x03\xEB\x00\x02\xC0\x60\x50\x60\x70\x80" | ||||
| 	 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, | ||||
| 	 /* RTP: SeqNo=1004, TS=180480 */ | ||||
| 	{2.080000, 20, "\x80\x62\x03\xEC\x00\x02\xC1\x00\x50\x60\x70\x80" | ||||
| 	 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, | ||||
| 	 /* RTP: SeqNo=1005, TS=180480, 10ms too late */ | ||||
| 	{2.110000, 20, "\x80\x62\x03\xED\x00\x02\xC1\xA0\x50\x60\x70\x80" | ||||
| 	 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, | ||||
| }; | ||||
|  | ||||
| void mgcp_patch_and_count(struct mgcp_endpoint *endp, | ||||
| 			  struct mgcp_rtp_state *state, | ||||
| 			  struct mgcp_rtp_end *rtp_end, | ||||
| 			  struct sockaddr_in *addr, char *data, int len); | ||||
|  | ||||
| static void test_packet_error_detection(int patch_ssrc, int patch_ts) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	struct mgcp_trunk_config trunk; | ||||
| 	struct mgcp_trunk *trunk; | ||||
| 	struct mgcp_endpoint endp; | ||||
| 	struct mgcp_config cfg = {0}; | ||||
| 	struct mgcp_endpoint *endpoints[1]; | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct mgcp_rtp_state state; | ||||
| 	struct mgcp_rtp_end *rtp; | ||||
| 	struct sockaddr_in addr = { 0 }; | ||||
| 	char buffer[4096]; | ||||
| 	struct osmo_sockaddr addr = { 0 }; | ||||
| 	uint32_t last_ssrc = 0; | ||||
| 	uint32_t last_timestamp = 0; | ||||
| 	uint32_t last_seqno = 0; | ||||
| @@ -1283,7 +1296,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) | ||||
| 	       patch_ssrc ? ", patch SSRC" : "", | ||||
| 	       patch_ts ? ", patch timestamps" : ""); | ||||
|  | ||||
| 	memset(&trunk, 0, sizeof(trunk)); | ||||
| 	cfg = mgcp_config_alloc(); | ||||
| 	trunk = mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID); | ||||
| 	memset(&endp, 0, sizeof(endp)); | ||||
| 	memset(&state, 0, sizeof(state)); | ||||
|  | ||||
| @@ -1292,15 +1306,15 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) | ||||
| 	state.in_stream.err_ts_ctr = &test_ctr_in; | ||||
| 	state.out_stream.err_ts_ctr = &test_ctr_out; | ||||
|  | ||||
| 	endp.cfg = &cfg; | ||||
| 	endp.type = &ep_typeset.rtp; | ||||
|  | ||||
| 	trunk.vty_number_endpoints = 1; | ||||
| 	trunk.endpoints = &endp; | ||||
| 	trunk.force_constant_ssrc = patch_ssrc; | ||||
| 	trunk.force_aligned_timing = patch_ts; | ||||
| 	trunk->v.vty_number_endpoints = 1; | ||||
| 	trunk->endpoints = endpoints; | ||||
| 	trunk->endpoints[0] = &endp; | ||||
| 	trunk->force_constant_ssrc = patch_ssrc; | ||||
| 	trunk->force_aligned_timing = patch_ts; | ||||
|  | ||||
| 	endp.tcfg = &trunk; | ||||
| 	endp.trunk = trunk; | ||||
|  | ||||
| 	INIT_LLIST_HEAD(&endp.conns); | ||||
| 	_conn = mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP, | ||||
| @@ -1316,16 +1330,17 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) { | ||||
| 		struct rtp_packet_info *info = test_rtp_packets1 + i; | ||||
| 		struct msgb *msg = msgb_alloc(4096, __func__); | ||||
|  | ||||
| 		force_monotonic_time_us = round(1000000.0 * info->txtime); | ||||
|  | ||||
| 		OSMO_ASSERT(info->len <= sizeof(buffer)); | ||||
| 		OSMO_ASSERT(info->len <= msgb_tailroom(msg)); | ||||
| 		OSMO_ASSERT(info->len >= 0); | ||||
| 		memmove(buffer, info->data, info->len); | ||||
| 		msg->l3h = msgb_put(msg, info->len); | ||||
| 		memcpy((char*)msgb_l3(msg), info->data, info->len); | ||||
| 		mgcp_rtp_end_config(&endp, 1, rtp); | ||||
|  | ||||
| 		mgcp_patch_and_count(&endp, &state, rtp, &addr, | ||||
| 				     buffer, info->len); | ||||
| 		mgcp_patch_and_count(&endp, &state, rtp, &addr, msg); | ||||
|  | ||||
| 		if (state.out_stream.ssrc != last_ssrc) { | ||||
| 			printf("Output SSRC changed to %08x\n", | ||||
| @@ -1352,34 +1367,36 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) | ||||
| 		last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current; | ||||
| 		last_timestamp = state.out_stream.last_timestamp; | ||||
| 		last_seqno = state.out_stream.last_seq; | ||||
|  | ||||
| 		msgb_free(msg); | ||||
| 	} | ||||
|  | ||||
| 	force_monotonic_time_us = -1; | ||||
| 	mgcp_conn_free_all(&endp); | ||||
| 	talloc_free(trunk); | ||||
| 	talloc_free(cfg); | ||||
| } | ||||
|  | ||||
| static void test_multilple_codec(void) | ||||
| { | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct mgcp_trunk_config *trunk2; | ||||
| 	struct mgcp_trunk *trunk; | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	struct msgb *inp, *resp; | ||||
| 	struct in_addr addr; | ||||
| 	struct mgcp_conn_rtp *conn = NULL; | ||||
| 	char conn_id[256]; | ||||
| 	char *last_endpoint = mgcp_debug_get_last_endpoint_name(); | ||||
|  | ||||
| 	printf("Testing multiple payload types\n"); | ||||
|  | ||||
| 	cfg = mgcp_config_alloc(); | ||||
| 	cfg->trunk.vty_number_endpoints = 64; | ||||
| 	mgcp_endpoints_allocate(&cfg->trunk); | ||||
| 	cfg->policy_cb = mgcp_test_policy_cb; | ||||
|  | ||||
| 	trunk2 = mgcp_trunk_alloc(cfg, 1); | ||||
| 	mgcp_endpoints_allocate(trunk2); | ||||
| 	trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID); | ||||
| 	trunk->v.vty_number_endpoints = 64; | ||||
|         mgcp_trunk_equip(trunk); | ||||
|  | ||||
| 	/* Allocate endpoint 1@mgw with two codecs */ | ||||
| 	last_endpoint = -1; | ||||
| 	last_endpoint[0] = '\0'; | ||||
| 	inp = create_msg(CRCX_MULT_1, NULL); | ||||
| 	resp = mgcp_handle_message(cfg, inp); | ||||
| 	OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, | ||||
| @@ -1387,14 +1404,15 @@ static void test_multilple_codec(void) | ||||
| 	msgb_free(inp); | ||||
| 	msgb_free(resp); | ||||
|  | ||||
| 	OSMO_ASSERT(last_endpoint == 1); | ||||
| 	endp = &cfg->trunk.endpoints[last_endpoint]; | ||||
| 	OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/1@mgw") == 0); | ||||
| 	endp = mgcp_endp_by_name(NULL, last_endpoint, cfg); | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	conn = mgcp_conn_get_rtp(endp, conn_id); | ||||
| 	OSMO_ASSERT(conn); | ||||
| 	OSMO_ASSERT(conn->end.codec->payload_type == 18); | ||||
|  | ||||
| 	/* Allocate 2@mgw with three codecs, last one ignored */ | ||||
| 	last_endpoint = -1; | ||||
| 	last_endpoint[0] = '\0'; | ||||
| 	inp = create_msg(CRCX_MULT_2, NULL); | ||||
| 	resp = mgcp_handle_message(cfg, inp); | ||||
| 	OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, | ||||
| @@ -1402,8 +1420,9 @@ static void test_multilple_codec(void) | ||||
| 	msgb_free(inp); | ||||
| 	msgb_free(resp); | ||||
|  | ||||
| 	OSMO_ASSERT(last_endpoint == 2); | ||||
| 	endp = &cfg->trunk.endpoints[last_endpoint]; | ||||
| 	OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/2@mgw") == 0); | ||||
| 	endp = mgcp_endp_by_name(NULL, last_endpoint, cfg); | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	conn = mgcp_conn_get_rtp(endp, conn_id); | ||||
| 	OSMO_ASSERT(conn); | ||||
| 	OSMO_ASSERT(conn->end.codec->payload_type == 18); | ||||
| @@ -1414,7 +1433,7 @@ static void test_multilple_codec(void) | ||||
| 	 * it makes and since we already decided in OS#2658 that a missing | ||||
| 	 * LCO should pick a sane default codec, it makes sense to expect | ||||
| 	 * the same behaviour if SDP lacks proper payload type information */ | ||||
| 	last_endpoint = -1; | ||||
| 	last_endpoint[0] = '\0'; | ||||
| 	inp = create_msg(CRCX_MULT_3, NULL); | ||||
| 	resp = mgcp_handle_message(cfg, inp); | ||||
| 	OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, | ||||
| @@ -1422,14 +1441,15 @@ static void test_multilple_codec(void) | ||||
| 	msgb_free(inp); | ||||
| 	msgb_free(resp); | ||||
|  | ||||
| 	OSMO_ASSERT(last_endpoint == 3); | ||||
| 	endp = &cfg->trunk.endpoints[last_endpoint]; | ||||
| 	OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/3@mgw") == 0); | ||||
| 	endp = mgcp_endp_by_name(NULL, last_endpoint, cfg); | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	conn = mgcp_conn_get_rtp(endp, conn_id); | ||||
| 	OSMO_ASSERT(conn); | ||||
| 	OSMO_ASSERT(conn->end.codec->payload_type == 0); | ||||
|  | ||||
| 	/* Allocate 4@mgw with a single codec */ | ||||
| 	last_endpoint = -1; | ||||
| 	last_endpoint[0] = '\0'; | ||||
| 	inp = create_msg(CRCX_MULT_4, NULL); | ||||
| 	resp = mgcp_handle_message(cfg, inp); | ||||
| 	OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, | ||||
| @@ -1437,44 +1457,46 @@ static void test_multilple_codec(void) | ||||
| 	msgb_free(inp); | ||||
| 	msgb_free(resp); | ||||
|  | ||||
| 	OSMO_ASSERT(last_endpoint == 4); | ||||
| 	endp = &cfg->trunk.endpoints[last_endpoint]; | ||||
| 	OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/4@mgw") == 0); | ||||
| 	endp = mgcp_endp_by_name(NULL, last_endpoint, cfg); | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	conn = mgcp_conn_get_rtp(endp, conn_id); | ||||
| 	OSMO_ASSERT(conn); | ||||
| 	OSMO_ASSERT(conn->end.codec->payload_type == 18); | ||||
|  | ||||
| 	/* Allocate 5@mgw at select GSM.. */ | ||||
| 	last_endpoint = -1; | ||||
| 	/* Allocate 5@mgw and let osmo-mgw pick a codec from the list */ | ||||
| 	last_endpoint[0] = '\0'; | ||||
| 	inp = create_msg(CRCX_MULT_GSM_EXACT, NULL); | ||||
| 	talloc_free(cfg->trunk.audio_name); | ||||
| 	cfg->trunk.audio_name = "GSM/8000"; | ||||
| 	cfg->trunk.no_audio_transcoding = 1; | ||||
| 	trunk->no_audio_transcoding = 1; | ||||
| 	resp = mgcp_handle_message(cfg, inp); | ||||
| 	OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, | ||||
| 					      sizeof(conn_id)) == 0); | ||||
| 	msgb_free(inp); | ||||
| 	msgb_free(resp); | ||||
|  | ||||
| 	OSMO_ASSERT(last_endpoint == 5); | ||||
| 	endp = &cfg->trunk.endpoints[last_endpoint]; | ||||
| 	OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0); | ||||
| 	endp = mgcp_endp_by_name(NULL, last_endpoint, cfg); | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	conn = mgcp_conn_get_rtp(endp, conn_id); | ||||
| 	OSMO_ASSERT(conn); | ||||
| 	OSMO_ASSERT(conn->end.codec->payload_type == 3); | ||||
| 	OSMO_ASSERT(conn->end.codec->payload_type == 0); | ||||
|  | ||||
| 	inp = create_msg(MDCX_NAT_DUMMY, conn_id); | ||||
| 	last_endpoint = -1; | ||||
| 	last_endpoint[0] = '\0'; | ||||
| 	resp = mgcp_handle_message(cfg, inp); | ||||
| 	msgb_free(inp); | ||||
| 	msgb_free(resp); | ||||
| 	OSMO_ASSERT(last_endpoint == 5); | ||||
| 	endp = &cfg->trunk.endpoints[last_endpoint]; | ||||
| 	OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0); | ||||
| 	endp = mgcp_endp_by_name(NULL, last_endpoint, cfg); | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	conn = mgcp_conn_get_rtp(endp, conn_id); | ||||
| 	OSMO_ASSERT(conn); | ||||
| 	OSMO_ASSERT(conn->end.codec->payload_type == 3); | ||||
| 	OSMO_ASSERT(conn->end.rtp_port == htons(16434)); | ||||
| 	memset(&addr, 0, sizeof(addr)); | ||||
| 	inet_aton("8.8.8.8", &addr); | ||||
| 	OSMO_ASSERT(conn->end.addr.s_addr == addr.s_addr); | ||||
| 	OSMO_ASSERT(conn->end.addr.u.sa.sa_family == AF_INET); | ||||
| 	OSMO_ASSERT(conn->end.addr.u.sin.sin_addr.s_addr == addr.s_addr); | ||||
|  | ||||
| 	/* Check what happens without that flag */ | ||||
|  | ||||
| @@ -1487,23 +1509,23 @@ static void test_multilple_codec(void) | ||||
| 	conn = mgcp_conn_get_rtp(endp, conn_id); | ||||
| 	OSMO_ASSERT(!conn); | ||||
|  | ||||
| 	last_endpoint = -1; | ||||
| 	last_endpoint[0] = '\0'; | ||||
| 	inp = create_msg(CRCX_MULT_GSM_EXACT, NULL); | ||||
| 	cfg->trunk.no_audio_transcoding = 0; | ||||
| 	trunk->no_audio_transcoding = 0; | ||||
| 	resp = mgcp_handle_message(cfg, inp); | ||||
| 	OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, | ||||
| 					      sizeof(conn_id)) == 0); | ||||
| 	msgb_free(inp); | ||||
| 	msgb_free(resp); | ||||
|  | ||||
| 	OSMO_ASSERT(last_endpoint == 5); | ||||
| 	endp = &cfg->trunk.endpoints[last_endpoint]; | ||||
| 	OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0); | ||||
| 	endp = mgcp_endp_by_name(NULL, last_endpoint, cfg); | ||||
| 	OSMO_ASSERT(endp); | ||||
| 	conn = mgcp_conn_get_rtp(endp, conn_id); | ||||
| 	OSMO_ASSERT(conn); | ||||
| 	OSMO_ASSERT(conn->end.codec->payload_type == 0); | ||||
|  | ||||
| 	mgcp_endpoints_release(trunk2); | ||||
| 	mgcp_endpoints_release(&cfg->trunk); | ||||
| 	mgcp_endpoints_release(trunk); | ||||
| 	talloc_free(cfg); | ||||
| } | ||||
|  | ||||
| @@ -1513,14 +1535,17 @@ static void test_no_cycle(void) | ||||
| 	struct mgcp_endpoint *endp; | ||||
| 	struct mgcp_conn_rtp *conn = NULL; | ||||
| 	struct mgcp_conn *_conn = NULL; | ||||
| 	struct mgcp_trunk *trunk; | ||||
|  | ||||
| 	printf("Testing no sequence flow on initial packet\n"); | ||||
|  | ||||
| 	cfg = mgcp_config_alloc(); | ||||
| 	cfg->trunk.vty_number_endpoints = 64; | ||||
| 	mgcp_endpoints_allocate(&cfg->trunk); | ||||
| 	trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID); | ||||
| 	trunk->v.vty_number_endpoints = 64; | ||||
|         mgcp_trunk_equip(trunk); | ||||
|  | ||||
| 	endp = &cfg->trunk.endpoints[1]; | ||||
| 	endp = mgcp_endp_by_name(NULL, "rtpbridge/1@mgw", cfg); | ||||
| 	OSMO_ASSERT(endp); | ||||
|  | ||||
| 	_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, | ||||
| 				"test-connection"); | ||||
| @@ -1530,49 +1555,45 @@ static void test_no_cycle(void) | ||||
|  | ||||
| 	OSMO_ASSERT(conn->state.stats.initialized == 0); | ||||
|  | ||||
| 	mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342); | ||||
| 	mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342, false); | ||||
| 	OSMO_ASSERT(conn->state.stats.initialized == 1); | ||||
| 	OSMO_ASSERT(conn->state.stats.cycles == 0); | ||||
| 	OSMO_ASSERT(conn->state.stats.max_seq == 0); | ||||
|  | ||||
| 	mgcp_rtp_annex_count(endp, &conn->state, 1, 0, 2342); | ||||
| 	mgcp_rtp_annex_count(endp, &conn->state, 1, 0, 2342, false); | ||||
| 	OSMO_ASSERT(conn->state.stats.initialized == 1); | ||||
| 	OSMO_ASSERT(conn->state.stats.cycles == 0); | ||||
| 	OSMO_ASSERT(conn->state.stats.max_seq == 1); | ||||
|  | ||||
| 	/* now jump.. */ | ||||
| 	mgcp_rtp_annex_count(endp, &conn->state, UINT16_MAX, 0, 2342); | ||||
| 	mgcp_rtp_annex_count(endp, &conn->state, UINT16_MAX, 0, 2342, false); | ||||
| 	OSMO_ASSERT(conn->state.stats.initialized == 1); | ||||
| 	OSMO_ASSERT(conn->state.stats.cycles == 0); | ||||
| 	OSMO_ASSERT(conn->state.stats.max_seq == UINT16_MAX); | ||||
|  | ||||
| 	/* and wrap */ | ||||
| 	mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342); | ||||
| 	mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342, false); | ||||
| 	OSMO_ASSERT(conn->state.stats.initialized == 1); | ||||
| 	OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1); | ||||
| 	OSMO_ASSERT(conn->state.stats.max_seq == 0); | ||||
|  | ||||
| 	mgcp_endpoints_release(&cfg->trunk); | ||||
| 	mgcp_endpoints_release(trunk); | ||||
| 	talloc_free(cfg); | ||||
| } | ||||
|  | ||||
| static void test_no_name(void) | ||||
| { | ||||
| 	struct mgcp_trunk_config *trunk2; | ||||
| 	struct mgcp_trunk *trunk; | ||||
| 	struct mgcp_config *cfg; | ||||
| 	struct msgb *inp, *msg; | ||||
|  | ||||
| 	printf("Testing no rtpmap name\n"); | ||||
| 	cfg = mgcp_config_alloc(); | ||||
| 	trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID); | ||||
|  | ||||
| 	cfg->trunk.vty_number_endpoints = 64; | ||||
| 	cfg->trunk.audio_send_name = 0; | ||||
| 	mgcp_endpoints_allocate(&cfg->trunk); | ||||
|  | ||||
| 	cfg->policy_cb = mgcp_test_policy_cb; | ||||
|  | ||||
| 	trunk2 = mgcp_trunk_alloc(cfg, 1); | ||||
| 	mgcp_endpoints_allocate(trunk2); | ||||
| 	trunk->v.vty_number_endpoints = 64; | ||||
| 	trunk->audio_send_name = 0; | ||||
|         mgcp_trunk_equip(trunk); | ||||
|  | ||||
| 	inp = create_msg(CRCX, NULL); | ||||
| 	msg = mgcp_handle_message(cfg, inp); | ||||
| @@ -1585,8 +1606,7 @@ static void test_no_name(void) | ||||
| 	msgb_free(inp); | ||||
| 	msgb_free(msg); | ||||
|  | ||||
| 	mgcp_endpoints_release(trunk2); | ||||
| 	mgcp_endpoints_release(&cfg->trunk); | ||||
| 	mgcp_endpoints_release(trunk); | ||||
| 	talloc_free(cfg); | ||||
| } | ||||
|  | ||||
| @@ -1876,16 +1896,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | ||||
| 		.codecs = { | ||||
| 			{ | ||||
| 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||
| 				{ 112, "AMR/8000", &amr_param_octet_aligned_false, }, | ||||
| 			}, | ||||
| 			{ | ||||
| 				{ 122, "AMR/8000", &amr_param_octet_aligned_false, }, | ||||
| 				{ 121, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {111, 121}, }, | ||||
| 			{ .payload_type_map = {112, 122} }, | ||||
| 			{ .payload_type_map = {111, 122}, }, | ||||
| 			{ .end = true }, | ||||
| 		}, | ||||
| 	}, | ||||
| @@ -1894,15 +1911,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | ||||
| 		.codecs = { | ||||
| 			{ | ||||
| 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||
| 				{ 112, "AMR/8000", &amr_param_octet_aligned_false, }, | ||||
| 			}, | ||||
| 			{ | ||||
| 				{ 122, "AMR/8000", &amr_param_octet_aligned_unset, }, | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {111, -EINVAL}, }, | ||||
| 			{ .payload_type_map = {112, 122} }, | ||||
| 			{ .payload_type_map = {111, 122}, }, | ||||
| 			{ .end = true }, | ||||
| 		}, | ||||
| 	}, | ||||
| @@ -1911,15 +1926,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | ||||
| 		.codecs = { | ||||
| 			{ | ||||
| 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||
| 				{ 112, "AMR/8000", &amr_param_octet_aligned_false, }, | ||||
| 			}, | ||||
| 			{ | ||||
| 				{ 122, "AMR/8000", NULL, }, | ||||
| 			}, | ||||
| 		}, | ||||
| 		.expect = { | ||||
| 			{ .payload_type_map = {111, -EINVAL}, }, | ||||
| 			{ .payload_type_map = {112, 122} }, | ||||
| 			{ .payload_type_map = {111, 122}, }, | ||||
| 			{ .end = true }, | ||||
| 		}, | ||||
| 	}, | ||||
| @@ -2105,6 +2118,97 @@ void test_conn_id_matching() | ||||
| 	talloc_free(conn); | ||||
| } | ||||
|  | ||||
| void test_e1_trunk_nr_from_epname() | ||||
| { | ||||
| 	unsigned int trunk_nr; | ||||
| 	int rc; | ||||
|  | ||||
| 	/* Note: e1_trunk_nr_from_epname does not check the text | ||||
| 	 * after the E1 trunk number, after the delimiter | ||||
| 	 * character "/" arbitrary text may follow. */ | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-0/s-1/su16-0"); | ||||
| 	OSMO_ASSERT(trunk_nr == 0); | ||||
| 	OSMO_ASSERT(rc == 0); | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-1/s-1/su16-0"); | ||||
| 	OSMO_ASSERT(trunk_nr == 1); | ||||
| 	OSMO_ASSERT(rc == 0); | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-2/s-2/su16-0"); | ||||
| 	OSMO_ASSERT(trunk_nr == 2); | ||||
| 	OSMO_ASSERT(rc == 0); | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-3/s-23/su32-0"); | ||||
| 	OSMO_ASSERT(trunk_nr == 3); | ||||
| 	OSMO_ASSERT(rc == 0); | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-3/xxxxxxx"); | ||||
| 	OSMO_ASSERT(trunk_nr == 3); | ||||
| 	OSMO_ASSERT(rc == 0); | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-24/s-1/su16-0"); | ||||
| 	OSMO_ASSERT(trunk_nr == 24); | ||||
| 	OSMO_ASSERT(rc == 0); | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-64/s-1/su16-0"); | ||||
| 	OSMO_ASSERT(trunk_nr == 64); | ||||
| 	OSMO_ASSERT(rc == 0); | ||||
|  | ||||
| 	/* The following endpoint strings should fail, either the | ||||
| 	 * trunk number exceeds the valid range or the trunk prefix | ||||
| 	 * is wrong. Also when the delimiter character "/" at the | ||||
| 	 * end of the trunk is wrong the parsing should fail. */ | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-65/s-1/su16-0"); | ||||
| 	OSMO_ASSERT(rc == -EINVAL); | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1--1/s-1/su16-0"); | ||||
| 	OSMO_ASSERT(rc == -EINVAL); | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "xxxxxx4zyz"); | ||||
| 	OSMO_ASSERT(rc == -EINVAL); | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1+2/s-1/su16-0"); | ||||
| 	OSMO_ASSERT(rc == -EINVAL); | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e2-24/s-1/su16-0"); | ||||
| 	OSMO_ASSERT(rc == -EINVAL); | ||||
| 	rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-24s-1/su16-0"); | ||||
| 	OSMO_ASSERT(rc == -EINVAL); | ||||
|  | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| void test_mgcp_is_rtp_dummy_payload() | ||||
| { | ||||
| 	/* realistic rtp packet */ | ||||
| 	static const char rtp_payload[] = | ||||
| 	    { 0x80, 0x03, 0xca, 0xd7, 0x62, 0x12, 0x75, 0xc4, 0x43, 0x4b, 0x3e, | ||||
| 	      0x72, 0xd2, 0x57, 0x7a, 0x1c, 0xda, 0x50, 0x00, 0x49, 0x24, 0x92, | ||||
| 	      0x49, 0x24, 0x50, 0x00, 0x49, 0x24, 0x92, 0x49, 0x24, 0x50, 0x00, | ||||
| 	      0x49, 0x24, 0x92, 0x49, 0x24, 0x50, 0x00, 0x49, 0x23, 0x92, 0x49, | ||||
| 	      0x24 }; | ||||
|  | ||||
| 	struct msgb *msg_dummy = msgb_alloc(RTP_BUF_SIZE, "RTP-msg_dummy"); | ||||
| 	struct msgb *msg_rtp = msgb_alloc(RTP_BUF_SIZE, "RTP-msg_rtp"); | ||||
| 	struct msgb *msg_dummy_rtp = | ||||
| 	    msgb_alloc(RTP_BUF_SIZE, "RTP-msg_dummy_rtp"); | ||||
|  | ||||
| 	uint8_t *buf; | ||||
|  | ||||
| 	/* Dummy RTP packet */ | ||||
| 	buf = msgb_put(msg_dummy, ARRAY_SIZE(rtp_dummy_payload)); | ||||
| 	memcpy(buf, rtp_dummy_payload, ARRAY_SIZE(rtp_dummy_payload)); | ||||
|  | ||||
| 	/* Normal RTP packet */ | ||||
| 	buf = msgb_put(msg_rtp, ARRAY_SIZE(rtp_payload)); | ||||
| 	memcpy(buf, rtp_payload, ARRAY_SIZE(rtp_payload)); | ||||
|  | ||||
| 	/* Dummy RTP packet with normal RTP packet attached, this must not be | ||||
| 	 * recognized as Dummy RTP packet */ | ||||
| 	buf = msgb_put(msg_dummy_rtp, ARRAY_SIZE(rtp_dummy_payload)); | ||||
| 	memcpy(buf, rtp_dummy_payload, ARRAY_SIZE(rtp_dummy_payload)); | ||||
| 	buf = msgb_put(msg_dummy_rtp, ARRAY_SIZE(rtp_payload)); | ||||
| 	memcpy(buf, rtp_payload, ARRAY_SIZE(rtp_payload)); | ||||
|  | ||||
| 	OSMO_ASSERT(mgcp_is_rtp_dummy_payload(msg_dummy) == true); | ||||
| 	OSMO_ASSERT(mgcp_is_rtp_dummy_payload(msg_rtp) == false); | ||||
| 	OSMO_ASSERT(mgcp_is_rtp_dummy_payload(msg_dummy_rtp) == false); | ||||
|  | ||||
| 	msgb_free(msg_dummy); | ||||
| 	msgb_free(msg_rtp); | ||||
| 	msgb_free(msg_dummy_rtp); | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	void *ctx = talloc_named_const(NULL, 0, "mgcp_test"); | ||||
| @@ -2130,6 +2234,8 @@ int main(int argc, char **argv) | ||||
| 	test_check_local_cx_options(ctx); | ||||
| 	test_mgcp_codec_pt_translate(); | ||||
| 	test_conn_id_matching(); | ||||
| 	test_e1_trunk_nr_from_epname(); | ||||
| 	test_mgcp_is_rtp_dummy_payload(); | ||||
|  | ||||
| 	OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0); | ||||
| 	OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1); | ||||
|   | ||||
| @@ -97,7 +97,7 @@ Response matches our expectations. | ||||
| Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX4 | ||||
| Testing MDCX4_ADDR000 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983216 1@mgw MGCP 1.0 | ||||
| @@ -113,6 +113,29 @@ m=audio 4441 RTP/AVP 99 | ||||
| m=audio 4441 RTP/AVP 99 | ||||
| a=rtpmap:99 AMR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message as statically defined for comparison | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX4 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983217 1@mgw MGCP 1.0 | ||||
| M: sendrecv | ||||
| C: 2 | ||||
| I: %s | ||||
| L: p:20, a:AMR, nt:IN | ||||
|  | ||||
| v=0 | ||||
| o=- %s 23 IN IP4 5.6.7.8 | ||||
| c=IN IP4 5.6.7.8 | ||||
| t=0 0 | ||||
| m=audio 4441 RTP/AVP 99 | ||||
| a=rtpmap:99 AMR/8000 | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| @@ -124,14 +147,14 @@ Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX4_PT1 | ||||
| ---------8<--------- | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983218 1@mgw MGCP 1.0 | ||||
| M: SENDRECV | ||||
| C: 2 | ||||
| I: %s | ||||
|  | ||||
| v=0 | ||||
| L: p:20-40, a:AMR, nt:IN | ||||
|  | ||||
| v=0 | ||||
| o=- %s 23 IN IP4 5.6.7.8 | ||||
| c=IN IP4 5.6.7.8 | ||||
| @@ -148,14 +171,14 @@ Dummy packets: 2 | ||||
| Dummy packets: 2 | ||||
|  | ||||
| ================================================ | ||||
| creating message from statically defined input: | ||||
| Testing MDCX4_PT2 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983219 1@mgw MGCP 1.0 | ||||
| M: sendrecv | ||||
| C: 2 | ||||
| L: p:20-20, a:AMR, nt:IN | ||||
|  | ||||
| I: %s | ||||
| L: p:20-20, a:AMR, nt:IN | ||||
|  | ||||
| v=0 | ||||
| o=- %s 23 IN IP4 5.6.7.8 | ||||
| @@ -172,14 +195,14 @@ Dummy packets: 2 | ||||
| (response contains a connection id) | ||||
| Dummy packets: 2 | ||||
|  | ||||
| Testing MDCX4_PT3 | ||||
| ================================================ | ||||
| Testing MDCX4_PT3 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983220 1@mgw MGCP 1.0 | ||||
| M: sendrecv | ||||
| I: %s | ||||
| L: a:AMR, nt:IN | ||||
| C: 2 | ||||
| I: %s | ||||
| L: a:AMR, nt:IN | ||||
|  | ||||
| v=0 | ||||
| @@ -196,14 +219,14 @@ Dummy packets: 2 | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
| Dummy packets: 2 | ||||
| ================================================ | ||||
|  | ||||
| ================================================ | ||||
| Testing MDCX4_PT4 | ||||
| creating message from statically defined input: | ||||
| ---------8<--------- | ||||
| MDCX 18983221 1@mgw MGCP 1.0 | ||||
| c: 2 | ||||
| i: %s | ||||
| m: sendrecv | ||||
| c: 2 | ||||
| i: %s | ||||
| l: A:amr, NT:IN | ||||
|  | ||||
| @@ -220,14 +243,14 @@ Dummy packets: 2 | ||||
| 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: | ||||
| ---------8<--------- | ||||
| M: sendonly | ||||
| C: 2 | ||||
| MDCX 18983222 1@mgw MGCP 1.0 | ||||
| M: sendonly | ||||
| C: 2 | ||||
| I: %s | ||||
| L: p:20, a:AMR, nt:IN | ||||
| @@ -243,7 +266,7 @@ Response matches our expectations. | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| using message with patched conn_id for comparison | ||||
| (response contains a connection id) | ||||
| Response matches our expectations. | ||||
| (response contains a connection id) | ||||
|  | ||||
| ================================================ | ||||
| @@ -441,7 +464,6 @@ checking response: | ||||
| Response matches our expectations. | ||||
| (response does not contain a connection id) | ||||
|  | ||||
| Testing CRCX | ||||
| ================================================ | ||||
| Testing CRCX | ||||
| creating message from statically defined input: | ||||
| @@ -470,7 +492,7 @@ Dummy packets: 2 | ||||
| c=IN IP4 123.12.12.123 | ||||
| m=audio 5904 RTP/AVP 97 | ||||
| a=rtpmap:97 GSM-EFR/8000 | ||||
|  | ||||
| a=ptime:40 | ||||
|  | ||||
| ---------8<--------- | ||||
| checking response: | ||||
| @@ -770,6 +792,15 @@ Stats: Jitter = 0, Transit = -144000 | ||||
| Stats: Jitter = 24, Transit = -32888 | ||||
| In TS: 36728, dTS: 160, Seq: 24 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 22, Transit = -32888 | ||||
| In TS: 36888, dTS: 160, Seq: 25 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 21, Transit = -32888 | ||||
| In TS: 160000, dTS: 0, Seq: 1000 | ||||
| Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160160, dTS: 160, Seq: 1001 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160320, dTS: 160, Seq: 1002 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| @@ -864,6 +895,15 @@ Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 36728, dTS: 160, Seq: 24 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 22, Transit = -32888 | ||||
| In TS: 36888, dTS: 160, Seq: 25 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 21, Transit = -32888 | ||||
| Output SSRC changed to 50607080 | ||||
| In TS: 160000, dTS: 0, Seq: 1000 | ||||
| Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160160, dTS: 160, Seq: 1001 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160320, dTS: 160, Seq: 1002 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| @@ -958,6 +998,15 @@ Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 36728, dTS: 160, Seq: 24 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 22, Transit = -32888 | ||||
| In TS: 36888, dTS: 160, Seq: 25 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 21, Transit = -32888 | ||||
| Output SSRC changed to 50607080 | ||||
| In TS: 160000, dTS: 0, Seq: 1000 | ||||
| Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160160, dTS: 160, Seq: 1001 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160320, dTS: 160, Seq: 1002 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| @@ -1050,6 +1099,15 @@ Stats: Jitter = 0, Transit = -144000 | ||||
| Stats: Jitter = 24, Transit = -32888 | ||||
| In TS: 36728, dTS: 160, Seq: 24 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 22, Transit = -32888 | ||||
| In TS: 36888, dTS: 160, Seq: 25 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 21, Transit = -32888 | ||||
| In TS: 160000, dTS: 0, Seq: 1000 | ||||
| Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160160, dTS: 160, Seq: 1001 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| Stats: Jitter = 0, Transit = -144000 | ||||
| In TS: 160320, dTS: 160, Seq: 1002 | ||||
| Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 | ||||
| @@ -1289,32 +1347,24 @@ Testing mgcp_codec_pt_translate() | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22 | ||||
|  - add codecs on conn0: | ||||
| #4: conn1 has no codecs | ||||
|  - add codecs on conn0: | ||||
|     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 | ||||
|     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) | ||||
|     0: 111 AMR/8000 octet-aligned=1  -> rc=0 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22 | ||||
|     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(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 | ||||
|  - add codecs on conn1: | ||||
| #6: test AMR with missing octet-aligned settings (defaults to 0) | ||||
|     0: 122 AMR/8000 octet-aligned=0  -> rc=0 | ||||
|  - mgcp_codec_pt_translate(conn0, conn1, 111) -> 122 | ||||
|     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(conn1, conn0, 122) -> 111 | ||||
| #6: test AMR with missing octet-aligned settings (defaults to 0) | ||||
|  - add codecs on conn0: | ||||
|     0: 111 AMR/8000 octet-aligned=1  -> rc=0 | ||||
|  - add codecs on conn1: | ||||
|   | ||||
| @@ -35,6 +35,7 @@ mgcp_client_test_LDADD = \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) \ | ||||
| 	$(LIBRARY_DL) \ | ||||
| 	$(LIBRARY_DLSYM) \ | ||||
| 	$(LIBOSMONETIF_LIBS) \ | ||||
| 	$(NULL) | ||||
|  | ||||
|   | ||||
| @@ -294,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); | ||||
| } | ||||
| @@ -413,6 +425,66 @@ static struct sdp_section_start_test sdp_section_start_tests[] = { | ||||
| 			"m=audio 23\r\n", | ||||
| 		.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, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| void test_sdp_section_start() | ||||
| @@ -443,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++; | ||||
| @@ -547,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[] = { | ||||
| }; | ||||
|  | ||||
| @@ -561,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); | ||||
| @@ -575,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"); | ||||
|   | ||||
| @@ -1,75 +1,138 @@ | ||||
| DLMGCP MGCP client: using endpoint domain '@mgw' | ||||
| DLMGCP message buffer to small, can not generate MGCP message | ||||
| DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw' | ||||
| DLMGCP MGW(mgw) Message buffer to small, can not generate MGCP message (SDP) | ||||
|  | ||||
| test_mgcp_client_cancel(): | ||||
| DLMGCP MGCP client: using endpoint domain '@mgw' | ||||
| DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw' | ||||
| - composed msg with trans_id=1 | ||||
| - not in queue yet, cannot cancel yet | ||||
| DLMGCP Cannot cancel, no such transaction: 1 | ||||
| DLMGCP MGW(mgw) Cannot cancel, no such transaction: 1 | ||||
| - enqueue | ||||
| - cancel succeeds | ||||
| DLMGCP Canceled transaction 1 | ||||
| DLMGCP MGW(mgw) Canceled transaction 1 | ||||
| - late response gets discarded | ||||
| DLMGCP Cannot find matching MGCP transaction for trans_id 1 | ||||
| DLMGCP MGW(mgw) MGCP client: Rx 200 1 OK | ||||
| DLMGCP MGW(mgw) Cannot find matching MGCP transaction for trans_id 1 | ||||
| - canceling again does nothing | ||||
| DLMGCP Cannot cancel, no such transaction: 1 | ||||
| DLMGCP MGW(mgw) Cannot cancel, no such transaction: 1 | ||||
| test_mgcp_client_cancel() done | ||||
|  | ||||
| test_sdp_section_start() test [0]: | ||||
| body: "" | ||||
| 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 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 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 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 MGW(mgw) MGCP client: using endpoint domain '@mgw' | ||||
| DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw), rate(128)/offset(0) combination is invalid! | ||||
| DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid! | ||||
| DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid! | ||||
| DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid! | ||||
| Done | ||||
|   | ||||
| @@ -109,6 +109,20 @@ 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(): | ||||
| @@ -149,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 | ||||
| @@ -178,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