mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-03 21:43:32 +00:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			rhizomatic
			...
			36c3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					fbe05d8b1f | 
							
								
								
									
										118
									
								
								.clang-format
									
									
									
									
									
								
							
							
						
						
									
										118
									
								
								.clang-format
									
									
									
									
									
								
							@@ -1,118 +0,0 @@
 | 
			
		||||
# 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,7 +16,6 @@ src/osmo-mgw/osmo-mgw
 | 
			
		||||
*.gcda
 | 
			
		||||
*.gcno
 | 
			
		||||
*.pc
 | 
			
		||||
*~
 | 
			
		||||
 | 
			
		||||
#configure
 | 
			
		||||
aclocal.m4
 | 
			
		||||
@@ -62,10 +61,3 @@ 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,13 +22,7 @@ pkgconfig_DATA = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
BUILT_SOURCES = $(top_srcdir)/.version
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	     .version \
 | 
			
		||||
	     README.md \
 | 
			
		||||
	     debian \
 | 
			
		||||
	     git-version-gen \
 | 
			
		||||
	     osmoappdesc.py \
 | 
			
		||||
	     $(NULL)
 | 
			
		||||
EXTRA_DIST = git-version-gen osmoappdesc.py .version
 | 
			
		||||
 | 
			
		||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
 | 
			
		||||
	--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								README
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
About OsmoMGW
 | 
			
		||||
=============
 | 
			
		||||
 | 
			
		||||
OsmoMGW originated from the OpenBSC project, which started as a minimalistic
 | 
			
		||||
all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
 | 
			
		||||
maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
 | 
			
		||||
IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
 | 
			
		||||
approach to fully independent separate programs as in typical GSM networks.
 | 
			
		||||
 | 
			
		||||
OsmoMGW was one of the parts split off from the old openbsc.git. It originated
 | 
			
		||||
as a solution to merely navigate RTP streams through a NAT, but has since
 | 
			
		||||
matured to a Media Gateway implementation that is capable of streaming RTP for
 | 
			
		||||
2G (AoIP) and 3G (IuCS) GSM networks as well as (still not implemented at time
 | 
			
		||||
of writing) transcoding between TRAU, various RTP payloads and IuUP.
 | 
			
		||||
 | 
			
		||||
The OsmoMGW program exposes an MGCP interface towards clients like OsmoMSC and
 | 
			
		||||
OsmoBSC, and receives and sends RTP streams as configured via MGCP.
 | 
			
		||||
 | 
			
		||||
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC (found
 | 
			
		||||
in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
 | 
			
		||||
 | 
			
		||||
Find OsmoMGW issue tracker and wiki online at
 | 
			
		||||
https://osmocom.org/projects/osmo-mgw
 | 
			
		||||
https://osmocom.org/projects/osmo-mgw/wiki
 | 
			
		||||
							
								
								
									
										102
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								README.md
									
									
									
									
									
								
							@@ -1,102 +0,0 @@
 | 
			
		||||
osmo-mgw - Osmocom MGW (Media GateWay) Implementation
 | 
			
		||||
=====================================================
 | 
			
		||||
 | 
			
		||||
This repository contains a C-language implementation of an MGW (Media
 | 
			
		||||
GateWay) for use [not only] within the 2G (GSM) and/or 3G (UMTS)
 | 
			
		||||
Cellular Network built using Osmocom CNI (Cellular Network
 | 
			
		||||
Infrastructure) software.
 | 
			
		||||
 | 
			
		||||
The OsmoMGW program provides an MGCP interface towards an MGCP call agent
 | 
			
		||||
(client) like OsmoMSC and OsmoBSC, and receives and sends RTP streams as
 | 
			
		||||
configured via the MGCP control plane.
 | 
			
		||||
 | 
			
		||||
This Media Gateway implementation is capable of
 | 
			
		||||
 | 
			
		||||
* streaming RTP for 2G (3GPP AoIP and Abis-over-IP)
 | 
			
		||||
* streaming RTP for 3G (IuCS including the IuFP protocol)
 | 
			
		||||
* TDM (E1/T1) based Abis interface with TRAU frames on 16k sub-slots
 | 
			
		||||
* basic support for LCLS (Local Call, Local Switch) related features
 | 
			
		||||
* various built-in translation capabilities
 | 
			
		||||
  * between Abis TRAU frames and RTP formats
 | 
			
		||||
  * between 2G AMR/RTP and 3G AMR/IuFP/RTP
 | 
			
		||||
  * between bandwidth-efficient and octet-aligned AMR
 | 
			
		||||
  * between different standards for encapsulating GSM HR codec frames in RTP
 | 
			
		||||
 | 
			
		||||
osmo-mgw is typically co-located with
 | 
			
		||||
 | 
			
		||||
 * osmo-bsc (GSM BSC)
 | 
			
		||||
 * osmo-msc (GSM/UMTS MSC)
 | 
			
		||||
 * osmo-hnbgw (UMTS HNBGW); osmo-mgw implements RTP relay between Iuh
 | 
			
		||||
   and IuCS interfaces
 | 
			
		||||
 | 
			
		||||
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC
 | 
			
		||||
(found in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
 | 
			
		||||
 | 
			
		||||
Homepage
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
You can find the OsmoMGW issue tracker and wiki online at
 | 
			
		||||
<https://osmocom.org/projects/osmo-mgw> and <https://osmocom.org/projects/osmo-mgw/wiki>.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GIT Repository
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
You can clone from the official osmo-mgw.git repository using
 | 
			
		||||
 | 
			
		||||
        git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
 | 
			
		||||
 | 
			
		||||
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Documentation
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
User Manuals and VTY reference manuals are [optionally] built in PDF form
 | 
			
		||||
as part of the build process.
 | 
			
		||||
 | 
			
		||||
Pre-rendered PDF version of the current "master" can be found at
 | 
			
		||||
[User Manual](https://ftp.osmocom.org/docs/latest/osmomgw-usermanual.pdf)
 | 
			
		||||
as well as the [VTY Reference Manual](https://ftp.osmocom.org/docs/latest/osmomgw-vty-reference.pdf)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Mailing List
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
Discussions related to osmo-mgw are happening on the
 | 
			
		||||
openbsc@lists.osmocom.org mailing list, please see
 | 
			
		||||
<https://lists.osmocom.org/mailman/listinfo/openbsc> for subscription
 | 
			
		||||
options and the list archive.
 | 
			
		||||
 | 
			
		||||
Please observe the [Osmocom Mailing List
 | 
			
		||||
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
 | 
			
		||||
when posting.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Contributing
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
Our coding standards are described at
 | 
			
		||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
 | 
			
		||||
 | 
			
		||||
We use a gerrit based patch submission/review process for managing
 | 
			
		||||
contributions.  Please see
 | 
			
		||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for
 | 
			
		||||
more details
 | 
			
		||||
 | 
			
		||||
The current patch queue for osmo-mgw can be seen at
 | 
			
		||||
<https://gerrit.osmocom.org/#/q/project:osmo-mgw+status:open>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
History
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
OsmoMGW originated from the OpenBSC project, which started as a minimalistic
 | 
			
		||||
all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
 | 
			
		||||
maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
 | 
			
		||||
IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
 | 
			
		||||
approach to fully independent separate programs as in typical GSM networks.
 | 
			
		||||
 | 
			
		||||
OsmoMGW was one of the parts split off from the old openbsc.git. It originated
 | 
			
		||||
as a solution to merely navigate RTP streams through a NAT, but has since
 | 
			
		||||
matured.
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
To run the configuration parsing and output (VTY) test suite, first install
 | 
			
		||||
 | 
			
		||||
  https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests
 | 
			
		||||
  git://git.osmocom.org/python/osmo-python-tests
 | 
			
		||||
 | 
			
		||||
and pass the following configure options here:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								TODO-RELEASE
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								TODO-RELEASE
									
									
									
									
									
								
							@@ -1,11 +1,26 @@
 | 
			
		||||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
 | 
			
		||||
# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
 | 
			
		||||
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
 | 
			
		||||
# LIBVERSION=c:r:a
 | 
			
		||||
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
 | 
			
		||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a.
 | 
			
		||||
# If any interfaces have been added since the last public release: c:r:a + 1.
 | 
			
		||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
 | 
			
		||||
# When cleaning up this file upon a release:
 | 
			
		||||
#
 | 
			
		||||
# - Note that the release version number is entirely unrelated to the API
 | 
			
		||||
#   versions. A release version 5.2.3 may happily have an API version of 42:7:5.
 | 
			
		||||
#
 | 
			
		||||
# - Bump API version in src/lib*/Makefile.am files according to chapter
 | 
			
		||||
#   "Library interface versions" of the libtool documentation.
 | 
			
		||||
#   https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
 | 
			
		||||
#
 | 
			
		||||
# - Iff the 'current' API version has changed, rename debian/lib*.install
 | 
			
		||||
#
 | 
			
		||||
# API version bumping for the impatient:
 | 
			
		||||
# LIBVERSION=c:r:a  (current:revision_of_current:backwards_compat_age)
 | 
			
		||||
# 5:2:4 means that
 | 
			
		||||
# - this implements version 5 of the API;
 | 
			
		||||
# - this is the 2nd (compatible) revision of API version 5;
 | 
			
		||||
# - this is backwards compatible to all APIs since 4 versions ago,
 | 
			
		||||
#   i.e. callers that need API versions from 1 to 5 can use this.
 | 
			
		||||
#
 | 
			
		||||
# Bumping API versions recipe:
 | 
			
		||||
# If the library source code has changed at all since the last update, r++;
 | 
			
		||||
# If any interfaces have been added, removed, or changed since the last update, c++, r=0;
 | 
			
		||||
# If any interfaces have been added since the last public release, a++;
 | 
			
		||||
# If any interfaces have been removed or changed since the last public release, a=0.
 | 
			
		||||
#
 | 
			
		||||
#library		what		description / commit summary line
 | 
			
		||||
libosmocore		bump_dep; workaround	Bump libosmocore version dependency after I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5
 | 
			
		||||
						has been merged to libosmocore.git; then remove my_msgb_copy_c wrapper function.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								configure.ac
									
									
									
									
									
								
							@@ -9,8 +9,6 @@ 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])])
 | 
			
		||||
 | 
			
		||||
@@ -24,11 +22,6 @@ 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
 | 
			
		||||
@@ -36,25 +29,20 @@ 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.11.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.11.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.11.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.11.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.6.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 2.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 2.0.0)
 | 
			
		||||
 | 
			
		||||
CFLAGS="$CFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
 | 
			
		||||
CPPFLAGS="$CPPFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
 | 
			
		||||
LDFLAGS="$LDFLAGS -pthread"
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.6.0)
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(sanitize,
 | 
			
		||||
	[AS_HELP_STRING(
 | 
			
		||||
@@ -80,13 +68,21 @@ AC_ARG_ENABLE(werror,
 | 
			
		||||
if test x"$werror" = x"yes"
 | 
			
		||||
then
 | 
			
		||||
	WERROR_FLAGS="-Werror"
 | 
			
		||||
	WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
 | 
			
		||||
	WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
 | 
			
		||||
	WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
 | 
			
		||||
	CFLAGS="$CFLAGS $WERROR_FLAGS"
 | 
			
		||||
	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,
 | 
			
		||||
@@ -114,7 +110,7 @@ if test "x$enable_ext_tests" = "xyes" ; then
 | 
			
		||||
	AM_PATH_PYTHON
 | 
			
		||||
	AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
 | 
			
		||||
	 if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
 | 
			
		||||
		AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
 | 
			
		||||
		AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
 | 
			
		||||
	fi
 | 
			
		||||
fi
 | 
			
		||||
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
 | 
			
		||||
@@ -199,6 +195,7 @@ AC_OUTPUT(
 | 
			
		||||
    tests/atlocal
 | 
			
		||||
    tests/mgcp_client/Makefile
 | 
			
		||||
    tests/mgcp/Makefile
 | 
			
		||||
    tests/iuup/Makefile
 | 
			
		||||
    doc/Makefile
 | 
			
		||||
    doc/examples/Makefile
 | 
			
		||||
    doc/manuals/Makefile
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,10 @@
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ export deps inst
 | 
			
		||||
osmo-clean-workspace.sh
 | 
			
		||||
 | 
			
		||||
mkdir "$deps" || true
 | 
			
		||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
 | 
			
		||||
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
 | 
			
		||||
 | 
			
		||||
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
 | 
			
		||||
 | 
			
		||||
@@ -30,12 +30,13 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
 | 
			
		||||
export LD_LIBRARY_PATH="$inst/lib"
 | 
			
		||||
export PATH="$inst/bin:$PATH"
 | 
			
		||||
 | 
			
		||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
 | 
			
		||||
osmo-build-dep.sh libosmo-abis
 | 
			
		||||
osmo-build-dep.sh libosmo-netif
 | 
			
		||||
 | 
			
		||||
# Additional configure options and depends
 | 
			
		||||
CONFIG=""
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ]; then
 | 
			
		||||
	osmo-build-dep.sh osmo-gsm-manuals
 | 
			
		||||
	CONFIG="--enable-manuals"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
@@ -49,18 +50,18 @@ set -x
 | 
			
		||||
 | 
			
		||||
cd "$base"
 | 
			
		||||
autoreconf --install --force
 | 
			
		||||
./configure --enable-sanitize --enable-vty-tests --enable-external-tests --enable-werror $CONFIG
 | 
			
		||||
./configure --enable-vty-tests --enable-external-tests --enable-werror $CONFIG
 | 
			
		||||
$MAKE $PARALLEL_MAKE
 | 
			
		||||
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
 | 
			
		||||
  || cat-testlogs.sh
 | 
			
		||||
LD_LIBRARY_PATH="$inst/lib" \
 | 
			
		||||
  DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests $CONFIG" \
 | 
			
		||||
  $MAKE $PARALLEL_MAKE distcheck \
 | 
			
		||||
  $MAKE distcheck \
 | 
			
		||||
  || cat-testlogs.sh
 | 
			
		||||
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
 | 
			
		||||
	make -C "$base/doc/manuals" publish
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
$MAKE $PARALLEL_MAKE maintainer-clean
 | 
			
		||||
$MAKE maintainer-clean
 | 
			
		||||
osmo-clean-workspace.sh
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +1,11 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Osmocom Media Gateway (MGW)
 | 
			
		||||
After=network-online.target
 | 
			
		||||
Wants=network-online.target
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
LimitNOFILE=65536
 | 
			
		||||
StateDirectory=osmocom
 | 
			
		||||
WorkingDirectory=%S/osmocom
 | 
			
		||||
Restart=always
 | 
			
		||||
User=osmocom
 | 
			
		||||
Group=osmocom
 | 
			
		||||
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
 | 
			
		||||
RestartSec=2
 | 
			
		||||
AmbientCapabilities=CAP_SYS_NICE
 | 
			
		||||
# CPU scheduling policy:
 | 
			
		||||
CPUSchedulingPolicy=rr
 | 
			
		||||
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
 | 
			
		||||
CPUSchedulingPriority=1
 | 
			
		||||
# See sched(7) for further details on real-time policies and priorities
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										721
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										721
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,724 +1,3 @@
 | 
			
		||||
osmo-mgw (1.14.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * jenkins.sh: libosmo-netif no longer depends on libosmo-abis
 | 
			
		||||
  * jenkins.sh: Use --disable-doxygen configure param
 | 
			
		||||
  * mgcp-client: Fix wrong value passed to strerror()
 | 
			
		||||
  * mgcp_client_internal.h: Add missing header dependency
 | 
			
		||||
  * mgcp-cli: Mark iofd ptr as NULL when freed
 | 
			
		||||
  * mgcp-cli: Improve error handling around mgcp_msg_gen() return
 | 
			
		||||
  * mgcp-cli: Fix filling in wrong local IP address of SDP Origin o=
 | 
			
		||||
  * mgw: Drop own MGCP extension 'noanswer'
 | 
			
		||||
  * cosmetic: mgw: Fix indentation whitespace
 | 
			
		||||
  * mgcp-client: Fix regression checking null ptr
 | 
			
		||||
  * mgw: Rename and move code freeing endp connection
 | 
			
		||||
  * mgw: Rename and move several get_conn funcs acting on endp object
 | 
			
		||||
  * mgw: mgcp_network.c: Simplify use of conn_rtp ptr
 | 
			
		||||
  * mgw: Clean up access to conn_rtp from conn
 | 
			
		||||
  * mgw: Avoid 2nd lookup of conn in endp during CRCX
 | 
			
		||||
  * cosmetic: mgw: iuup: Update comment
 | 
			
		||||
  * mgw: constify mgcp_endp_avail() param
 | 
			
		||||
  * mgcp_endp: Add helpers accessing endp connections
 | 
			
		||||
  * mgw: mgcp_protocol: assert freeing last conn allows creating new conn
 | 
			
		||||
  * mgw: Split conn mode parsing and applying into conn
 | 
			
		||||
  * mgw: CRCXMDCX/DLCX: rename conn and conn_rtp variables
 | 
			
		||||
  * mgw: Use bool instead of int in local var
 | 
			
		||||
  * Rename mgcp_free_rtp_port() to mgcp_rtp_end_free_port()
 | 
			
		||||
  * mgw: Split mgcp_rtp_end to its own file
 | 
			
		||||
  * mgw: Introduce mgcp_rtp_end_init()
 | 
			
		||||
  * mgw: Introduce struct mgcp_codecset struct
 | 
			
		||||
  * mgw: Cleanup rtp_endp fields in its own function
 | 
			
		||||
  * mgw: Move force_ptime logic outside of main CRCX func handler
 | 
			
		||||
  * mgw: Move several params setting to mgcp_rtp_end_init()
 | 
			
		||||
  * mgw: Simplify and redo code around ssrc patch feature
 | 
			
		||||
  * mgw: Clean up code allocating conn_rtp rtp/rtcp sockets
 | 
			
		||||
  * mgw: Rename and cleanup code allocating rtp/rtcp ports in trunk
 | 
			
		||||
  * mgw: CRCX: Split mgcp header pars parsing into a previous step
 | 
			
		||||
  * mgw: MDCX: Split mgcp header pars parsing into a previous step
 | 
			
		||||
  * mgw: DLCX: Split mgcp header pars parsing into a previous step
 | 
			
		||||
  * mgw: Decouple SDP parsing step from conn obj update
 | 
			
		||||
  * mgw: Remove wrong TODO comment
 | 
			
		||||
  * mgw: MDCX: Simplify early return code paths
 | 
			
		||||
 | 
			
		||||
  [ Mychaela N. Falconia ]
 | 
			
		||||
  * E1 cosmetic: reduce white space in hard-coded TRAU-DL frames
 | 
			
		||||
  * E1: replace idle_tf_efr[] with a better version
 | 
			
		||||
  * E1: replace idle_tf_fr[] with a better version
 | 
			
		||||
 | 
			
		||||
 -- Oliver Smith <osmith@sysmocom.de>  Wed, 12 Feb 2025 12:30:33 +0100
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.13.1) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * mgcp_network: use an uint16_t to store the port number
 | 
			
		||||
  * mgcp_network: add missing ntohs
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * tests/mgcp/mgcp_test: Add some extra asserts in code
 | 
			
		||||
 | 
			
		||||
 -- Oliver Smith <osmith@sysmocom.de>  Thu, 12 Sep 2024 13:53:19 +0200
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.13.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * add mgcp_conn_rtp_type_names[]
 | 
			
		||||
  * mgcp_parse_audio_port_pt(): fix buffer overflow
 | 
			
		||||
  * client: replace two assertions with graceful error handling
 | 
			
		||||
  * systemd,manual: set LimitNOFILE=65536
 | 
			
		||||
  * IuUP: allow Initialization from any address if not yet set
 | 
			
		||||
  * check_rtp_origin: drop special case for legacy IuUP hack
 | 
			
		||||
  * mgcp_client_test: fix function name
 | 
			
		||||
  * fix possible NULL deref on early media
 | 
			
		||||
  * client: move some items to internal header
 | 
			
		||||
  * client: safely handle dealloc on event dispatch
 | 
			
		||||
  * build: move mgcp/*.h to noinst_HEADERS, drop RPM libosmo-mgcp-devel
 | 
			
		||||
  * client: deprecate legacy API
 | 
			
		||||
  * client: collapse codecs[] and ptmap[]; allow codec variants
 | 
			
		||||
  * client: allow MGCP_MAX_CODECS entries
 | 
			
		||||
  * client SDP: more verbose error logging
 | 
			
		||||
  * mgcp_client_test: add test_parse_response()
 | 
			
		||||
  * drop (now) unused code
 | 
			
		||||
  * tests/mgcp: add update_exp target
 | 
			
		||||
  * drop get_net_downlink_format_cb
 | 
			
		||||
  * drop cfg 'sdp audio fmtp-extra'
 | 
			
		||||
  * mgcp_codec_decide: remove redundant lookup
 | 
			
		||||
  * tweak DEBUG log
 | 
			
		||||
  * mgcp_test: fix false negatives in test output
 | 
			
		||||
  * mgw: do not fail MGCP on codec mismatch
 | 
			
		||||
  * mgcp-client: always send 'm=audio' line
 | 
			
		||||
  * mgcp_test.c: verify osmo-mgw accepts m=audio 0
 | 
			
		||||
  * mgcp_test.c: fix various missing '\r' and '\n'
 | 
			
		||||
  * mgcp_test: test a=ptime:20, not 40
 | 
			
		||||
  * mgcp_test: add CRCX for IUFP in sendrecv
 | 
			
		||||
  * do not FAIL on CRCX in sendrecv mode
 | 
			
		||||
 | 
			
		||||
  [ Keith Whyte ]
 | 
			
		||||
  * vty and log: also show local port for RTP conns
 | 
			
		||||
 | 
			
		||||
  [ Andreas Eversberg ]
 | 
			
		||||
  * Use uniform log format for default config files
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * mgcp_network: Improve err logging when rtp pkt from unexpected origin comes in
 | 
			
		||||
  * cosmetic: Fix line indentation
 | 
			
		||||
  * IuUP: Allow Initialization with set rem IP address and unset rem port
 | 
			
		||||
  * mgcp-client: Transmit remote IP addr in CRCX if known and port=0
 | 
			
		||||
  * Fix IuUP RTP hdr seqnr field not incremented
 | 
			
		||||
  * iuup: Increment RTP hdr seqnr even if Tx over UDP fails
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * mgcp: simplify getting msgb tail in mgcp_msg_terminate_nul()
 | 
			
		||||
  * mgcp: reserve once byte for '\0' in mgcp_do_read()
 | 
			
		||||
  * mgcp: correctly put NUL character in mgcp_msg_terminate_nul()
 | 
			
		||||
  * build: include README into the release tarball
 | 
			
		||||
 | 
			
		||||
  [ neels ]
 | 
			
		||||
  * Revert "drop (now) unused code"
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * Convert README to README.md and expand like in other projects
 | 
			
		||||
  * migrate mgcp_client from osmo_wqueue to osmo_io
 | 
			
		||||
  * mgw: Add our usual SIGABRT, SIGUSR1 signal handlers
 | 
			
		||||
  * don't log useless "transcoding disabled" message
 | 
			
		||||
  * remove strange loop for non-existant transcoding support
 | 
			
		||||
  * simplify unused transcoding/processing call-back
 | 
			
		||||
  * remove osmo_fd from mgcp_create_bind()
 | 
			
		||||
  * Change msgb ownership in processing of received msgb
 | 
			
		||||
  * cosmetic: make linter happy
 | 
			
		||||
  * Convert RTP/RTCP/OSMUX I/O from osmo_fd to osmo_io
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * contrib: remove rpm spec file
 | 
			
		||||
  * contrib/systemd: run as osmocom user
 | 
			
		||||
  * doc: example configs: fix deprecation warnings
 | 
			
		||||
 | 
			
		||||
  [ Mychaela N. Falconia ]
 | 
			
		||||
  * E1: support HRv1 codec on both 16k and 8k subslots
 | 
			
		||||
  * fix E1 TS output when used with osmo-e1d
 | 
			
		||||
 | 
			
		||||
 -- Oliver Smith <osmith@sysmocom.de>  Wed, 24 Jul 2024 15:44:47 +0200
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.12.1) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * mgcp-client: Fix missing include in mgcp_client_pool.h
 | 
			
		||||
  * mgcp-client: Introduce API osmo_mgcpc_ep_local_name()
 | 
			
		||||
  * mgw: Configure IuUP if codec set during MDCX
 | 
			
		||||
 | 
			
		||||
 -- Oliver Smith <osmith@sysmocom.de>  Thu, 28 Sep 2023 15:58:17 +0200
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.12.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * mgcp_sdp: add spec reference
 | 
			
		||||
  * mgcp_sdp: cosmetic: remove newline
 | 
			
		||||
  * mgcp_endp: cosmetic move mgcp_endp_release to the end
 | 
			
		||||
  * mgcp_endp: cosmetic: remove unnecessary new line
 | 
			
		||||
  * mgcp_e1: fix log output
 | 
			
		||||
  * mgcp_e1: be more frugal withe E1 line resources
 | 
			
		||||
  * mgcp_client: fix sourcecode formatting
 | 
			
		||||
  * mgcp_e1: fix typo
 | 
			
		||||
  * mgcp_e1: rewrite comment
 | 
			
		||||
  * mgcp_e1: rename e1_send to e1_send_ts_frame
 | 
			
		||||
  * mgcp_e1: cosmetic: rewrite comment
 | 
			
		||||
  * Revert "mgcp_codec: do not differentiate between oa and bwe when comparing codec"
 | 
			
		||||
  * mgcp_codec: fix oa/bwe comparison in mgcp_codec_pt_translate()
 | 
			
		||||
  * mgcp_codec: refactor payload type converstion
 | 
			
		||||
  * mgcp_codec: cosmetic: remove line break in api-doc
 | 
			
		||||
  * mgcp_network: fix apidoc
 | 
			
		||||
  * mgcp_vty: add warnings for deprecated config options
 | 
			
		||||
  * mgcp_codec: move mgcp_codec_decide down
 | 
			
		||||
  * mgcp_codec: fix codec decision
 | 
			
		||||
  * mgcp_network: do not deliver RTP packets with unpatched PT
 | 
			
		||||
  * mgcp_codec: be sensitive about IuFP when checking codecs
 | 
			
		||||
  * mgcp_client.h: also add spec ref to the other 3gpp defined payload types
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * cosmetic: Fix grammar suggesting reading _the_ user manual
 | 
			
		||||
 | 
			
		||||
  [ arehbein ]
 | 
			
		||||
  * Transition to use of 'telnet_init_default'
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * mgcp_client: mgcp_msg_gen: add more error logs
 | 
			
		||||
  * mgcp_client_pool: add mgcp_client_pool_empty()
 | 
			
		||||
  * Cosmetic: mgcp_client: fix typo
 | 
			
		||||
  * debian: set compat level to 10
 | 
			
		||||
  * systemd: depend on networking-online.target
 | 
			
		||||
  * Cosmetic: fix a typo
 | 
			
		||||
  * mgcp_client: check rc of map_str_to_codec
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * mgcp_network: Unregister osmo_fd before closing fd
 | 
			
		||||
  * mgcp-client: Call osmo_fd_unregister() before closing and changing bfd->fd
 | 
			
		||||
  * mgcp-client: Drop unused struct mgcp_client field
 | 
			
		||||
  * mgcp_client: Introduce mgcp_client_conf_alloc(), deprecate mgcp_client_conf_init()
 | 
			
		||||
  * mgcp-client: Move some static functions further above
 | 
			
		||||
  * mgw: Allow auditing speciall 'null' endpoint
 | 
			
		||||
  * mgcp-client: Add keepalive feature
 | 
			
		||||
  * mgcp_client: pool: Only pick clients with an MGCP link considered to be UP
 | 
			
		||||
  * mgcp-client: Always mark client as UP if keepalive request-interval disabled
 | 
			
		||||
  * mgcp-client: Mark client as UP when keepalive request-interval/timeout is disabled through VTY
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * */Makefile.am: libraries shall not be in AM_LDFLAGS
 | 
			
		||||
  * tests: use -no-install libtool flag to avoid ./lt-* scripts
 | 
			
		||||
  * tests: $(BUILT_SOURCES) is not defined, depend on osmo-mgw
 | 
			
		||||
  * copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * mgcp_find_section_end(): skip spaces at start of SDP
 | 
			
		||||
  * mgcp_client: simpler error handling
 | 
			
		||||
  * mgcp: fix "L: a:" header parsing: heed ";" separator
 | 
			
		||||
  * mgcp_client: tweak extract_codec_name() implementation
 | 
			
		||||
 | 
			
		||||
  [ Andreas Eversberg ]
 | 
			
		||||
  * ASCI: Add new mode for voice group/broadcast call
 | 
			
		||||
  * ASCI: Support conference briding with 1..n connections
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 12 Sep 2023 14:48:51 +0200
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.11.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * mgcp-client: Remove impossible code path
 | 
			
		||||
  * mgcp_osmux: Drop duplicated conn_osmux_release_cid() in code path
 | 
			
		||||
  * Use Osmux default port define from libosmo-netif
 | 
			
		||||
  * osmux: Use new osmux APIs to let libosmo-netif alloc struct osmux_out_handle
 | 
			
		||||
  * mgw: Fix osmux conn local IP selection
 | 
			
		||||
  * cosmetic: osmux: Fix wrong indentation
 | 
			
		||||
  * mgw: Use X-Osmux string define
 | 
			
		||||
  * Add Osmux log category
 | 
			
		||||
  * cosmetic: main: Properly format log_info_cat
 | 
			
		||||
  * osmux: Use better name for function which may allocate a new struct
 | 
			
		||||
  * osmux: Use osmo_sockaddr wherever possible
 | 
			
		||||
  * osmux: Log refcounting of osmux_handle_list
 | 
			
		||||
  * osmux: Fix memleak on error code path
 | 
			
		||||
  * osmux: don't store conn ptr inside shared osmux_handle
 | 
			
		||||
  * osmux: set log level of expected code path to INFO
 | 
			
		||||
  * cosmetic: vty: Fix indentation whitespace
 | 
			
		||||
  * osmux: Log sendto() error
 | 
			
		||||
  * cosmetic: mgcp_conn.h: fix indentation whitespace
 | 
			
		||||
  * osmux: Attach osmux to virtual trunk
 | 
			
		||||
  * osmux: Clean up helper macro osmux_chunk_length()
 | 
			
		||||
  * osmux: Get rid of static NULL talloc context
 | 
			
		||||
  * Fix typo in ratectr description
 | 
			
		||||
  * mgcp_conn: rename field s/rate_ctr_group/ctrg/g
 | 
			
		||||
  * Use 'static const' instead of 'const static' everywhere
 | 
			
		||||
  * osmux: Add connection and global rate counters
 | 
			
		||||
  * cosmetic: osmux: Fix formatting of if-else brackets
 | 
			
		||||
  * osmux: Rename field osmux usage policy and define it with proper type
 | 
			
		||||
  * Use bool type instead of int in config field
 | 
			
		||||
  * osmux: Support local CID != remote CID
 | 
			
		||||
  * osmux: Fix incorrect rate_ctr_group used in mgcp_osmux.c
 | 
			
		||||
  * osmux: Improve per-conn tx rate counters
 | 
			
		||||
  * vty: show per-connection Osmux VTY stats
 | 
			
		||||
  * osmux: Rename field s/init/initialized
 | 
			
		||||
  * osmux: Allocate rate counters during initialization of osmux conn
 | 
			
		||||
  * cosmetic: osmux: Drop extra empty line
 | 
			
		||||
  * osmux: Drop unneeded comment block
 | 
			
		||||
  * osmux: Lower log level when osmux batch received for unknown CID
 | 
			
		||||
  * osmux: Keep decoding osmux pkt if a batch contains an unknown CID
 | 
			
		||||
  * Allocate struct osmux_in_handle through new libosmo-netif APIs
 | 
			
		||||
  * osmux: Drop logging of osmux internal counters
 | 
			
		||||
  * osmux: cleanup misleading code calling rtp_bridge_cb
 | 
			
		||||
  * osmux: Unify rtp_conn osmux type into a single type
 | 
			
		||||
  * osmux: Match remote address in osmux_conn_lookup()
 | 
			
		||||
  * osmux: Fill in from_addr in struct osmo_rtp_msg_ctx
 | 
			
		||||
  * osmux: Log remote address upon rx of osmux pkt
 | 
			
		||||
  * send_dummy: Use proper condition to test if conn is osmux
 | 
			
		||||
  * Fix regression in detection of legacy dummy packets
 | 
			
		||||
  * Use new libosmocore API osmo_sockaddr_is_any()
 | 
			
		||||
  * Get rid of separate rtp_port field
 | 
			
		||||
  * osmux: Use available API to check if remote end is known
 | 
			
		||||
  * Add Osmux IPv6 support
 | 
			
		||||
  * Clean up local var pointers in mgcp_get_local_addr()
 | 
			
		||||
  * osmux: Change couple log lines to OSMUX category
 | 
			
		||||
  * osmux: Add square brackets around IPv6 address to distinguish port in log line
 | 
			
		||||
  * osmux: Drop unused role parameter
 | 
			
		||||
  * osmux: cosmetic: Fix indentation
 | 
			
		||||
  * Check once if remote addr is available when sending dummy packet
 | 
			
		||||
  * osmux: Simplify and constify param passing
 | 
			
		||||
  * osmux: Set conn->type during osmux_init_conn()
 | 
			
		||||
  * osmux: Make conn_osmux_{allocate,release}_local_cid() APIs static
 | 
			
		||||
  * osmux: Define osmux_dummy cfg as boolean
 | 
			
		||||
  * osmux: Move setting OSMUX_STATE_DISABLED to initializer function
 | 
			
		||||
  * osmux: Erase references to bsc-nat
 | 
			
		||||
  * mgcp_conn_dump(): Separate dump for osmux and iuup connections
 | 
			
		||||
  * osmux: Introduce osmux peer-behind-nat (on|off) and rework conn activation
 | 
			
		||||
  * osmux: Clean up mgcp_config osmux fields
 | 
			
		||||
  * cosmetic: Fix typo in comment
 | 
			
		||||
  * mgcp-client: pool: Improve documentation of some internal fields
 | 
			
		||||
  * mgcp-client: Avoid double iteration picking client from pool
 | 
			
		||||
  * mgcp-client: Fix typo in internal function name
 | 
			
		||||
  * mgcp-client: Rename internal field in mgcp_client_pool
 | 
			
		||||
  * mgcp-client: Move & rename helper function outside of vty code
 | 
			
		||||
  * mgcp-client: Create alloc() and free() internal APIs for mgcp_client_pool_member
 | 
			
		||||
  * mgcp-client: Move internal API acting on mgcp_client_pool to the correct file section
 | 
			
		||||
  * mgcp-client: Refactor reinit of mgcp clients
 | 
			
		||||
  * mgcp-client: Rearrange order of structs and APIs in header
 | 
			
		||||
  * mgcp-client: Rearrange internal backpointers
 | 
			
		||||
  * mgcp-client: Introduce APIs to manually select mgcp_client from pool
 | 
			
		||||
  * mgcp-client: vty: Write deprecation warning using non-mgw nodes
 | 
			
		||||
  * mgcp-client: Use random free local port by default
 | 
			
		||||
  * mgcp-client: Convert users supporting new MGW Pool VTY node during write-config
 | 
			
		||||
  * mgcp-client: Introduce API mgcp_client_pool_config_write
 | 
			
		||||
  * mgcp-client: Add new VTY commands under mgw node without mgw prefix
 | 
			
		||||
  * mgcp-client: Fix 'mgw endpoint-range' command dropped from old VTY node
 | 
			
		||||
  * mgcp-client: Refactor system keeping old users not calling mgcp_client_pool_config_write() working
 | 
			
		||||
  * mgcp-client: Fix no 'mgw ' prefix written in old VTY node
 | 
			
		||||
  * Fix misleading error log
 | 
			
		||||
  * mgcp_send: Use mgcp_conn_rtp_is_iuup() helper
 | 
			
		||||
  * cosmetic: Clarify and fix typos in comment
 | 
			
		||||
  * mgw: Log unexpected RTP AMR OA-vs-BE payload
 | 
			
		||||
  * Rename and move func checking if amr mode is explicitly configured
 | 
			
		||||
  * mgw: rx_rtp(): reorder checks and handlings
 | 
			
		||||
  * mgw: Rename s/mgcp_send_rtp/mgcp_conn_rtp_dispatch_rtp/
 | 
			
		||||
  * osmux: Rename function and pass msgb directly to it
 | 
			
		||||
  * osmux: Make sure RTP AMR feed to osmux is in octet-aligned mode
 | 
			
		||||
  * Improve logging on AMR OA<->BWE conversion failure
 | 
			
		||||
  * osmux: Rotate over available Osmux CID when allocating a new one
 | 
			
		||||
  * osmux: Use new osmux_xfrm_input API to set name on each link
 | 
			
		||||
  * iuup: Use osmo_amr_ft_valid() API
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * libosmo-mgcp-client: add -no-undefined to *_la_LDFLAGS
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * mgcp_e1: fix apidoc
 | 
			
		||||
  * mgcp_network: improve coment
 | 
			
		||||
 | 
			
		||||
  [ Max ]
 | 
			
		||||
  * Set working directory in systemd service file
 | 
			
		||||
  * Add realtime scheduling and set priority in service file
 | 
			
		||||
  * ctrl: take both address and port from vty config
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * Make osmo_mgcpc_ep_fsm_pre_term() static
 | 
			
		||||
  * Support building with -Werror=strict-prototypes / -Werror=old-style-definition
 | 
			
		||||
  * Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
 | 
			
		||||
  * update outdated vty copyright statement
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * mgcp_client_pool.h: add missing stdbool include
 | 
			
		||||
  * Fix various typos
 | 
			
		||||
  * mgcp_client: add new clearmode codec
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * AMR->IuUP: do not crash on AMR data before IuUP Init
 | 
			
		||||
  * AMR->IuUP: properly translate Q -> FQC
 | 
			
		||||
  * IuUP->AMR: do not patch payload type a second time
 | 
			
		||||
  * IuUP->AMR: log whether converting to AMR OA or BE
 | 
			
		||||
  * AMR->IuUP: log conversion, like for the flipside
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 07 Feb 2023 16:58:41 +0100
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.10.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Eric ]
 | 
			
		||||
  * adjust mgcp response context
 | 
			
		||||
  * rework message handling
 | 
			
		||||
  * add a lock-free bounded spsc interthread queue
 | 
			
		||||
  * clang-format: remove foreach macros
 | 
			
		||||
  * fix mgcp_conn_free_all ubsan complaints
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * configuration: point out difference between trunk-nr and e1 line nr
 | 
			
		||||
  * mgcp_client: add new codec IUFP as VND.3GPP.IUFP
 | 
			
		||||
  * mgcp_codec: do not differentiate between oa and bwe when comparing codec
 | 
			
		||||
  * mgcp_network: do not try to convert RTCP packets
 | 
			
		||||
  * mgcp_network: fix typo RTPC -> RTCP
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * treewide: remove FSF address
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * mgcp_network.c: Set proper CRC Header for ACK Initialization
 | 
			
		||||
  * cosmetic: Rename variable payload=>payload_type
 | 
			
		||||
  * mgcp_network.c: Fix byte alignment of CRC Header for ACK Initialization
 | 
			
		||||
  * Define mgcp_rtp_end.output_enabled as bool
 | 
			
		||||
  * cosmetic: mgcp_codec.c: Fix typo in comment
 | 
			
		||||
  * Drop unneeded ax_check_compile_flag.m4
 | 
			
		||||
  * Make function amr_is_octet_aligned publicly available
 | 
			
		||||
  * Initial IuUP support using proper FSMs
 | 
			
		||||
  * IuUP: Support RFCI ID != RFCI Index
 | 
			
		||||
  * iuup: Fix caps in logging message
 | 
			
		||||
  * iuup: Check for IuUP Initialization retrans
 | 
			
		||||
 | 
			
		||||
  [ Alexander Couzens ]
 | 
			
		||||
  * doc/overview: fix wrong project page link
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
 | 
			
		||||
  * libosmo-mgcp: e1: fix memleaks in e1_recv_cb()
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * update git URLs (git -> https; gitea)
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 28 Jun 2022 18:50:25 +0200
 | 
			
		||||
 | 
			
		||||
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 ]
 | 
			
		||||
  * rename codecs_cmp() to codecs_same()
 | 
			
		||||
  * mgcp_codec: constify 'param' arg
 | 
			
		||||
  * fix crashes: don't assert on incoming RTP packet size
 | 
			
		||||
  * mgcp_send(): stop looping on conversion error
 | 
			
		||||
  * mgcp_codec: split codec_free() off of codec_init()
 | 
			
		||||
  * fix memleak: actually free strings in mgcp_codec_reset_all()
 | 
			
		||||
  * mgcp_test: extend / rewrite test_mgcp_codec_pt_translate()
 | 
			
		||||
  * test_mgcp_codec_pt_translate(): more tests
 | 
			
		||||
  * differentiate AMR octet-aligned=0 vs =1
 | 
			
		||||
  * ptmap: implicitly match  '/8000' and '/8000/1'
 | 
			
		||||
  * mgcp_codec: codec_set(): log about all possible errors
 | 
			
		||||
  * mgcp_codec_add: fix audio_name size check
 | 
			
		||||
  * explicitly free codecs in mgcp_rtp_conn_cleanup()
 | 
			
		||||
  * tweak mgcp_parse_audio_ptime_rtpmap()
 | 
			
		||||
  * SDP: store all ptmap entries
 | 
			
		||||
  * mgcp_client_fsm cleanup: Do not assert on DLCX failure
 | 
			
		||||
  * clear pending requests on MGCP failure
 | 
			
		||||
  * client: endp fsm: add notify struct, prep for cancel-notify
 | 
			
		||||
  * client: endp fsm: clear ci[] before dispatching DLCX success
 | 
			
		||||
  * client: endp fsm: allow cancelling a notify event
 | 
			
		||||
  * client: endp fsm: add osmo_mgcpc_ep_ci_ep()
 | 
			
		||||
  * accept MGCP without SDP
 | 
			
		||||
  * fix use-after-free: require new fsm deferred dealloc, check for term
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * mgcp_test: Correctly release all endpoints allocated
 | 
			
		||||
  * mgw: Allocate mgcp_conn instance under tcfg->endpoints
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * manual: Fix copy+paste error
 | 
			
		||||
  * mgcp_client: Check for osmo_fsm_register() error return value
 | 
			
		||||
  * Move fsm_mgcp_client regstration to __attribute__((contructor))
 | 
			
		||||
  * exit(2) on unsupported positional arguments on command line
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * osmoappdesc.py: switch to python 3
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Fri, 03 Jan 2020 13:35:09 +0100
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.6.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							@@ -1 +1 @@
 | 
			
		||||
10
 | 
			
		||||
9
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -1,17 +1,17 @@
 | 
			
		||||
Source: osmo-mgw
 | 
			
		||||
Section: net
 | 
			
		||||
Priority: extra
 | 
			
		||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
 | 
			
		||||
Build-Depends: debhelper (>= 10),
 | 
			
		||||
Maintainer: Alexander Couzens <lynxis@fe80.eu>
 | 
			
		||||
Build-Depends: debhelper (>=9),
 | 
			
		||||
               dh-autoreconf,
 | 
			
		||||
               pkg-config,
 | 
			
		||||
               autotools-dev,
 | 
			
		||||
               libosmocore-dev (>= 1.11.0),
 | 
			
		||||
               libosmo-netif-dev (>= 1.6.0),
 | 
			
		||||
               libosmo-abis-dev (>= 2.0.0),
 | 
			
		||||
               libosmocore-dev,
 | 
			
		||||
               libosmo-netif-dev,
 | 
			
		||||
               osmo-gsm-manuals-dev
 | 
			
		||||
Standards-Version: 3.9.8
 | 
			
		||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
 | 
			
		||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
 | 
			
		||||
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
 | 
			
		||||
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
 | 
			
		||||
Homepage: https://osmocom.org/projects/osmo-mgw
 | 
			
		||||
 | 
			
		||||
Package: osmo-mgw
 | 
			
		||||
@@ -20,7 +20,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-client14
 | 
			
		||||
Package: libosmo-mgcp-client6
 | 
			
		||||
Section: libs
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
@@ -32,6 +32,14 @@ Package: libosmo-mgcp-client-dev
 | 
			
		||||
Section: libdevel
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
Depends: libosmo-mgcp-client14 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Depends: libosmo-mgcp-client6 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
 | 
			
		||||
 | 
			
		||||
Package: osmo-mgw-doc
 | 
			
		||||
Architecture: all
 | 
			
		||||
Section: doc
 | 
			
		||||
Priority: optional
 | 
			
		||||
Depends: ${misc:Depends}
 | 
			
		||||
Description: ${misc:Package} PDF documentation
 | 
			
		||||
 Various manuals: user manual, VTY reference manual and/or
 | 
			
		||||
 protocol/interface manuals.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							@@ -1,12 +1,12 @@
 | 
			
		||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 | 
			
		||||
Upstream-Name: osmo-mgw
 | 
			
		||||
Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
 | 
			
		||||
Source: git://git.osmocom.org/osmo-mgw
 | 
			
		||||
 | 
			
		||||
Files:     *
 | 
			
		||||
Copyright: 2009-2014 On-Waves
 | 
			
		||||
           2009-2015 Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
           2013 Jacob Erlbeck <jerlbeck@sysmocom.de>
 | 
			
		||||
           2016-2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
           2016-2017 sysmocom s.m.f.c. GmbH <info@sysmocom.de>
 | 
			
		||||
License:   AGPL-3.0+
 | 
			
		||||
 This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
@@ -22,7 +22,7 @@ License:   AGPL-3.0+
 | 
			
		||||
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
Files:     src/libosmo-mgcp-client/* include/osmocom/mgcp_client/*
 | 
			
		||||
Copyright: 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
Copyright: 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
 | 
			
		||||
           Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
 | 
			
		||||
           2009 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
           2009-2011 by Holger Hans Peter Freyther
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							@@ -2,4 +2,3 @@ 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										38
									
								
								debian/postinst
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								debian/postinst
									
									
									
									
										vendored
									
									
								
							@@ -1,38 +0,0 @@
 | 
			
		||||
#!/bin/sh -e
 | 
			
		||||
case "$1" in
 | 
			
		||||
	configure)
 | 
			
		||||
		# Create the osmocom group and user (if it doesn't exist yet)
 | 
			
		||||
		if ! getent group osmocom >/dev/null; then
 | 
			
		||||
			groupadd --system osmocom
 | 
			
		||||
		fi
 | 
			
		||||
		if ! getent passwd osmocom >/dev/null; then
 | 
			
		||||
			useradd \
 | 
			
		||||
				--system \
 | 
			
		||||
				--gid osmocom \
 | 
			
		||||
				--home-dir /var/lib/osmocom \
 | 
			
		||||
				--shell /sbin/nologin \
 | 
			
		||||
				--comment "Open Source Mobile Communications" \
 | 
			
		||||
				osmocom
 | 
			
		||||
		fi
 | 
			
		||||
 | 
			
		||||
		# Fix permissions of previous (root-owned) install (OS#4107)
 | 
			
		||||
		if dpkg --compare-versions "$2" le "1.13.0"; then
 | 
			
		||||
			if [ -e /etc/osmocom/osmo-mgw.cfg ]; then
 | 
			
		||||
				chown -v osmocom:osmocom /etc/osmocom/osmo-mgw.cfg
 | 
			
		||||
				chmod -v 0660 /etc/osmocom/osmo-mgw.cfg
 | 
			
		||||
			fi
 | 
			
		||||
 | 
			
		||||
			if [ -d /etc/osmocom ]; then
 | 
			
		||||
				chown -v root:osmocom /etc/osmocom
 | 
			
		||||
				chmod -v 2775 /etc/osmocom
 | 
			
		||||
			fi
 | 
			
		||||
 | 
			
		||||
			mkdir -p /var/lib/osmocom
 | 
			
		||||
			chown -R -v osmocom:osmocom /var/lib/osmocom
 | 
			
		||||
		fi
 | 
			
		||||
		;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
# dh_installdeb(1) will replace this with shell code automatically
 | 
			
		||||
# generated by other debhelper scripts.
 | 
			
		||||
#DEBHELPER#
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							@@ -30,7 +30,7 @@ override_dh_auto_test:
 | 
			
		||||
	dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
 | 
			
		||||
 | 
			
		||||
override_dh_auto_configure:
 | 
			
		||||
	dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system
 | 
			
		||||
	dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
 | 
			
		||||
 | 
			
		||||
# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
 | 
			
		||||
override_dh_compress:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
!
 | 
			
		||||
! MGCP configuration example
 | 
			
		||||
!
 | 
			
		||||
log stderr
 | 
			
		||||
 logging color 1
 | 
			
		||||
 logging print category-hex 0
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 0
 | 
			
		||||
 logging print file basename last
 | 
			
		||||
 logging print level 1
 | 
			
		||||
 | 
			
		||||
e1_input
 | 
			
		||||
 e1_line 0 driver dahdi
 | 
			
		||||
 e1_line 0 port 0
 | 
			
		||||
mgcp
 | 
			
		||||
  bind ip 127.0.0.1
 | 
			
		||||
  rtp port-range 4002 16001
 | 
			
		||||
  rtp bind-ip 127.0.0.1
 | 
			
		||||
  rtp ip-probing
 | 
			
		||||
  rtp ip-dscp 46
 | 
			
		||||
  bind port 2427
 | 
			
		||||
  number endpoints 512
 | 
			
		||||
  force-realloc 1
 | 
			
		||||
  rtcp-omit
 | 
			
		||||
  rtp-patch ssrc
 | 
			
		||||
  rtp-patch timestamp
 | 
			
		||||
  trunk 1
 | 
			
		||||
   rtp keep-alive once
 | 
			
		||||
   no rtp keep-alive
 | 
			
		||||
   line 0
 | 
			
		||||
@@ -1,22 +1,17 @@
 | 
			
		||||
!
 | 
			
		||||
! MGCP configuration example
 | 
			
		||||
!
 | 
			
		||||
log stderr
 | 
			
		||||
 logging color 1
 | 
			
		||||
 logging print category-hex 0
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 0
 | 
			
		||||
 logging print file basename last
 | 
			
		||||
 logging print level 1
 | 
			
		||||
 | 
			
		||||
mgcp
 | 
			
		||||
  bind ip 127.0.0.1
 | 
			
		||||
  rtp port-range 4002 16001
 | 
			
		||||
  rtp bind-ip 127.0.0.1
 | 
			
		||||
  rtp port-range 4002 16000
 | 
			
		||||
  rtp bind-ip 10.9.1.122
 | 
			
		||||
  rtp ip-probing
 | 
			
		||||
  rtp ip-dscp 46
 | 
			
		||||
  rtp ip-tos 184
 | 
			
		||||
  bind port 2427
 | 
			
		||||
  number endpoints 512
 | 
			
		||||
  sdp audio payload number 98
 | 
			
		||||
  sdp audio payload name GSM
 | 
			
		||||
  number endpoints 31
 | 
			
		||||
  loop 0
 | 
			
		||||
  force-realloc 1
 | 
			
		||||
  rtcp-omit
 | 
			
		||||
  rtp-patch ssrc
 | 
			
		||||
 
 | 
			
		||||
@@ -13,11 +13,6 @@ 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,13 +23,14 @@ OsmoMGW(config-mgcp)# local ip 127.0.0.1
 | 
			
		||||
 | 
			
		||||
=== Configuring the trunk
 | 
			
		||||
 | 
			
		||||
The first trunk is considered a virtual trunk in OsmoMGW. All
 | 
			
		||||
The first trunk (trunk 0) is considered a virtual trunk in OsmoMGW. All
 | 
			
		||||
endpoints of type "rtpbridge" are routed here. The virtual trunk is configured
 | 
			
		||||
in the config-mgcp context.
 | 
			
		||||
 | 
			
		||||
All other trunks are configured in the config-mgcp-trunk context, but the
 | 
			
		||||
commands used are identical. Right now trunks are considered only for "ds/e1"
 | 
			
		||||
type endpoints.
 | 
			
		||||
commands used are identical. Right now trunks are considered only for ds/e1
 | 
			
		||||
type endpoints which are not yet implemented. Don't use trunks other than the
 | 
			
		||||
"virtual" trunk 0.
 | 
			
		||||
 | 
			
		||||
.Example: MGCP trunk configuration
 | 
			
		||||
----
 | 
			
		||||
@@ -53,77 +54,4 @@ 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.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,100 +0,0 @@
 | 
			
		||||
== 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)
 | 
			
		||||
 | 
			
		||||
=== The `null` endpoint
 | 
			
		||||
 | 
			
		||||
OsmoMGW offers a special `null@<domain>` endpoint which can be audited at all times.
 | 
			
		||||
This is useful for MGCP clients who wish to submit requests to OsmoMGW
 | 
			
		||||
periodically to find out whether it is still reachable and in a working state.
 | 
			
		||||
@@ -21,10 +21,10 @@ Protocol.
 | 
			
		||||
digraph G {
 | 
			
		||||
    rankdir = LR;
 | 
			
		||||
    OsmoBTS -> OsmoBSC [label="Abis/IP"];
 | 
			
		||||
    OsmoBSC -> "core-network" [label="3GPP AoIP"];
 | 
			
		||||
    OsmoBSC -> OsmoMSC [label="3GPP AoIP"];
 | 
			
		||||
    OsmoBSC -> OsmoMGW [label="MGCP"];
 | 
			
		||||
    OsmoBTS -> OsmoMGW [label="RTP",dir=both];
 | 
			
		||||
    OsmoMGW -> "core-network" [label="RTP",dir=both];
 | 
			
		||||
    OsmoMGW -> OsmoMSC [label="RTP",dir=both];
 | 
			
		||||
    {rank=same OsmoBSC OsmoMGW}
 | 
			
		||||
    OsmoMGW [color=red];
 | 
			
		||||
}
 | 
			
		||||
@@ -36,9 +36,10 @@ digraph G {
 | 
			
		||||
----
 | 
			
		||||
digraph G {
 | 
			
		||||
    rankdir = LR;
 | 
			
		||||
    "2G BSS" -> OsmoMSC [label="3GPP AoIP"];
 | 
			
		||||
    BTS -> BSC [label="Abis"];
 | 
			
		||||
    BSC -> OsmoMSC [label="3GPP AoIP"];
 | 
			
		||||
    OsmoMSC -> OsmoMGW [label="MGCP"];
 | 
			
		||||
    "2G BSS" -> OsmoMGW [label="RTP",dir=both];
 | 
			
		||||
    BSC -> OsmoMGW [label="RTP",dir=both];
 | 
			
		||||
    OsmoMSC -> OsmoSIP [label="MNCC"];
 | 
			
		||||
    OsmoSIP -> PBX [label="SIP Trunk"];
 | 
			
		||||
    OsmoMGW -> PBX [label="RTP",dir=both];
 | 
			
		||||
@@ -52,22 +53,6 @@ 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
 | 
			
		||||
@@ -92,14 +77,14 @@ Transcoding is currently not supported in OsmoMGW.
 | 
			
		||||
 | 
			
		||||
=== Limitations
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
Osmux is not yet supported in OsmoMGW.
 | 
			
		||||
 | 
			
		||||
At the moment (July 2018), OsmoMGW only implements RTP proxy / RTP bridge
 | 
			
		||||
type endpoints, to each of which two RTP connections can be established.
 | 
			
		||||
We are planning to add endpoint types for:
 | 
			
		||||
 | 
			
		||||
- classic E1/T1 timeslots (64kBps alaw/ulaw)
 | 
			
		||||
- classic E1/T1 16k sub-slots with TRAU frames for classic BTS support
 | 
			
		||||
- announcement/playout end-points
 | 
			
		||||
- conference endpoints
 | 
			
		||||
 | 
			
		||||
@@ -107,8 +92,8 @@ We are planning to add further endpoint types for:
 | 
			
		||||
 | 
			
		||||
You can find the OsmoMGW issue tracker and wiki online at
 | 
			
		||||
 | 
			
		||||
- https://osmocom.org/projects/osmo-mgw
 | 
			
		||||
- https://osmocom.org/projects/osmo-mgw/wiki
 | 
			
		||||
- https://osmocom.org/projects/osmomgw
 | 
			
		||||
- https://osmocom.org/projects/osmomgw/wiki
 | 
			
		||||
 | 
			
		||||
RFC 3435 for MGCP is located at
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
==== 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.
 | 
			
		||||
@@ -23,20 +23,3 @@ arguments:
 | 
			
		||||
	Disable colors for logging to stderr. This has mostly been
 | 
			
		||||
	deprecated by VTY based logging configuration, see <<logging>>
 | 
			
		||||
	for more information.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== Configure limits
 | 
			
		||||
 | 
			
		||||
When servicing hundreds of media endpoints, it may be necessary to adjust the
 | 
			
		||||
operating system's limit on open file descriptors for the osmo-mgw process. A
 | 
			
		||||
typical default limit imposed by operating systems is 1024; this would be
 | 
			
		||||
exceeded by, for example, about 256 active voice calls with 4 RTP/RTPC ports
 | 
			
		||||
each, sockets for other interfaces not considered yet.
 | 
			
		||||
 | 
			
		||||
It should be ok to set an OS limit on open file descriptors as high as 65536
 | 
			
		||||
for osmo-mgw, which practically rules out failure from running out of file
 | 
			
		||||
descriptors anywhere (<16,000 active calls).
 | 
			
		||||
 | 
			
		||||
When using systemd, the file descriptor limit may be adjusted in the service
 | 
			
		||||
file by the `LimitNOFILE` setting ("Number of Open FILE descriptors"). OsmoMGW
 | 
			
		||||
ships a systemd service file with a high LimitNOFILE setting.
 | 
			
		||||
 
 | 
			
		||||
@@ -18,18 +18,12 @@ 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[]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1677
									
								
								doc/manuals/vty/mgw_vty_reference.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1677
									
								
								doc/manuals/vty/mgw_vty_reference.xml
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -3,16 +3,12 @@ SUBDIRS = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
nobase_include_HEADERS = \
 | 
			
		||||
	osmocom/mgcp_client/defs.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client_fsm.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client_pool.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	osmocom/mgcp/mgcp.h \
 | 
			
		||||
	osmocom/mgcp/mgcp_common.h \
 | 
			
		||||
	osmocom/mgcp/mgcp_internal.h \
 | 
			
		||||
	osmocom/mgcp/osmux.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,12 @@
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	vty.h \
 | 
			
		||||
	mgcp_msg.h \
 | 
			
		||||
	mgcp_codec.h \
 | 
			
		||||
	mgcp_conn.h \
 | 
			
		||||
	mgcp_stat.h \
 | 
			
		||||
	mgcp_endp.h \
 | 
			
		||||
	mgcp_sdp.h \
 | 
			
		||||
	mgcp_codec.h \
 | 
			
		||||
	mgcp_trunk.h \
 | 
			
		||||
	debug.h \
 | 
			
		||||
	mgcp_ratectr.h \
 | 
			
		||||
	mgcp_e1.h \
 | 
			
		||||
	mgcp_network.h \
 | 
			
		||||
	mgcp_protocol.h \
 | 
			
		||||
	mgcp_iuup.h \
 | 
			
		||||
	mgcp_rtp_end.h \
 | 
			
		||||
	iuup_cn_node.h \
 | 
			
		||||
	iuup_protocol.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -29,8 +29,7 @@
 | 
			
		||||
/* Debug Areas of the code */
 | 
			
		||||
enum {
 | 
			
		||||
	DRTP,
 | 
			
		||||
	DE1,
 | 
			
		||||
	DOSMUX,
 | 
			
		||||
	DIUUP,
 | 
			
		||||
	Debug_LastEntry,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								include/osmocom/mgcp/iuup_cn_node.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								include/osmocom/mgcp/iuup_cn_node.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
			
		||||
/* IuUP CN node, minimal implementation */
 | 
			
		||||
 | 
			
		||||
/*                                            _____IuUP_CN_____
 | 
			
		||||
 *                                            |               |
 | 
			
		||||
 * UE <--> RNC --PDU-> osmo_iuup_cn_rx_pdu() -+->           ---+-> rx_payload()
 | 
			
		||||
 *          |                                 |               |
 | 
			
		||||
 *          |  <-PDU-- tx_msg() <-------------+--           <-+--- osmo_iuup_cn_tx_payload()
 | 
			
		||||
 *                                            |               |
 | 
			
		||||
 *                                            -----------------
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
struct msgb;
 | 
			
		||||
 | 
			
		||||
typedef int (*osmo_iuup_data_cb_t)(struct msgb *msg, void *node_priv);
 | 
			
		||||
 | 
			
		||||
struct osmo_iuup_cn_cfg {
 | 
			
		||||
	void *node_priv;
 | 
			
		||||
 | 
			
		||||
	/* When the IuUP peer sent a voice packet, the clean RTP without the IuUP header is fed to this
 | 
			
		||||
	 * callback. */
 | 
			
		||||
	osmo_iuup_data_cb_t rx_payload;
 | 
			
		||||
 | 
			
		||||
	/* IuUP handler requests that a PDU shall be sent to the IuUP peer (e.g. the RNC).
 | 
			
		||||
	 * It is guaranteed that the msgb->dst pointer is preserved or copied from the msgb that
 | 
			
		||||
	 * originated the request. */
 | 
			
		||||
	osmo_iuup_data_cb_t tx_msg;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_iuup_cn {
 | 
			
		||||
	struct osmo_iuup_cn_cfg cfg;
 | 
			
		||||
	char *name;
 | 
			
		||||
	uint8_t next_frame_nr;
 | 
			
		||||
	int rtp_payload_type;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
bool osmo_iuup_cn_is_iuup_init(struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
 | 
			
		||||
				       const char *name_fmt, ...);
 | 
			
		||||
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn);
 | 
			
		||||
 | 
			
		||||
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *payload);
 | 
			
		||||
 | 
			
		||||
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu);
 | 
			
		||||
							
								
								
									
										117
									
								
								include/osmocom/mgcp/iuup_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								include/osmocom/mgcp/iuup_protocol.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
			
		||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
			
		||||
/* IuUP protocol handling, minimal implementation */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/endian.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
 | 
			
		||||
#define OSMO_IUUP_HEADROOM 32
 | 
			
		||||
 | 
			
		||||
enum osmo_iuup_pdu_type {
 | 
			
		||||
	OSMO_IUUP_PDU_DATA_WITH_CRC = 0,
 | 
			
		||||
	OSMO_IUUP_PDU_CONTROL_PROCEDURE = 14,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmo_iuup_acknack {
 | 
			
		||||
	OSMO_IUUP_ACKNACK_PROCEDURE = 0,
 | 
			
		||||
	OSMO_IUUP_ACKNACK_ACK = 1,
 | 
			
		||||
	OSMO_IUUP_ACKNACK_NACK = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmo_iuup_procedure {
 | 
			
		||||
	OSMO_IUUP_PROC_INITIALIZATION = 0,
 | 
			
		||||
	OSMO_IUUP_PROC_RATE_CONTROL = 1,
 | 
			
		||||
	OSMO_IUUP_PROC_TIME_ALIGNMENT = 2,
 | 
			
		||||
	OSMO_IUUP_PROC_ERROR_EVENT = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmo_iuup_frame_good {
 | 
			
		||||
	OSMO_IUUP_FRAME_GOOD = 0,
 | 
			
		||||
	OSMO_IUUP_FRAME_BAD = 1,
 | 
			
		||||
	OSMO_IUUP_FRAME_BAD_DUE_TO_RADIO = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_iuup_hdr_ctrl {
 | 
			
		||||
#if OSMO_IS_BIG_ENDIAN
 | 
			
		||||
	uint8_t pdu_type:4,
 | 
			
		||||
		ack_nack:2,
 | 
			
		||||
		frame_nr:2;
 | 
			
		||||
	uint8_t mode_version:4,
 | 
			
		||||
		procedure:4;
 | 
			
		||||
	uint8_t header_crc:6,
 | 
			
		||||
		payload_crc_hi:2;
 | 
			
		||||
	uint8_t payload_crc_lo;
 | 
			
		||||
	uint8_t payload[0];
 | 
			
		||||
#elif OSMO_IS_LITTLE_ENDIAN
 | 
			
		||||
	uint8_t frame_nr:2,
 | 
			
		||||
		ack_nack:2,
 | 
			
		||||
		pdu_type:4;
 | 
			
		||||
	uint8_t procedure:4,
 | 
			
		||||
		mode_version:4;
 | 
			
		||||
	uint8_t payload_crc_hi:2,
 | 
			
		||||
		header_crc:6;
 | 
			
		||||
	uint8_t payload_crc_lo;
 | 
			
		||||
	uint8_t payload[0];
 | 
			
		||||
#endif
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
union osmo_iuup_hdr_ctrl_payload {
 | 
			
		||||
	struct {
 | 
			
		||||
#if OSMO_IS_BIG_ENDIAN
 | 
			
		||||
	uint8_t spare:3,
 | 
			
		||||
		iptis_present:1,
 | 
			
		||||
		subflows:3,
 | 
			
		||||
		chain:1;
 | 
			
		||||
#elif OSMO_IS_LITTLE_ENDIAN
 | 
			
		||||
	uint8_t spare:3,
 | 
			
		||||
		iptis_present:1,
 | 
			
		||||
		subflows:3,
 | 
			
		||||
		chain:1;
 | 
			
		||||
#endif
 | 
			
		||||
	} initialization;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
#if OSMO_IS_BIG_ENDIAN
 | 
			
		||||
	uint8_t error_distance:2,
 | 
			
		||||
		error_cause:6;
 | 
			
		||||
#elif OSMO_IS_LITTLE_ENDIAN
 | 
			
		||||
	uint8_t error_cause:6,
 | 
			
		||||
		error_distance:2;
 | 
			
		||||
#endif
 | 
			
		||||
	} error_event;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct value_string osmo_iuup_error_cause_names[];
 | 
			
		||||
static inline const char *osmo_iuup_error_cause_name(uint8_t val)
 | 
			
		||||
{ return get_value_string(osmo_iuup_error_cause_names, val); }
 | 
			
		||||
 | 
			
		||||
struct osmo_iuup_hdr_data {
 | 
			
		||||
#if OSMO_IS_BIG_ENDIAN
 | 
			
		||||
	uint8_t pdu_type:4,
 | 
			
		||||
		frame_nr:4;
 | 
			
		||||
	uint8_t frame_good:2,
 | 
			
		||||
		rfci:6;
 | 
			
		||||
	uint8_t header_crc:6,
 | 
			
		||||
		payload_crc_hi:2;
 | 
			
		||||
	uint8_t payload_crc_lo;
 | 
			
		||||
#elif OSMO_IS_LITTLE_ENDIAN
 | 
			
		||||
	uint8_t frame_nr:4,
 | 
			
		||||
		pdu_type:4;
 | 
			
		||||
	uint8_t rfci:6,
 | 
			
		||||
		frame_good:2;
 | 
			
		||||
	uint8_t payload_crc_hi:2,
 | 
			
		||||
		header_crc:6;
 | 
			
		||||
	uint8_t payload_crc_lo;
 | 
			
		||||
#endif
 | 
			
		||||
	uint8_t payload[0];
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
int osmo_iuup_classify(bool log_errors,
 | 
			
		||||
		       const char *log_label,
 | 
			
		||||
		       struct msgb *pdu,
 | 
			
		||||
		       struct osmo_iuup_hdr_ctrl **is_ctrl,
 | 
			
		||||
		       struct osmo_iuup_hdr_data **is_data);
 | 
			
		||||
bool osmo_iuup_is_init(struct msgb *pdu);
 | 
			
		||||
void osmo_iuup_make_init_ack(struct msgb *ack);
 | 
			
		||||
void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len);
 | 
			
		||||
@@ -23,22 +23,16 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/osmo_io.h>
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#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
 | 
			
		||||
@@ -48,7 +42,7 @@
 | 
			
		||||
 */
 | 
			
		||||
struct mgcp_endpoint;
 | 
			
		||||
struct mgcp_config;
 | 
			
		||||
struct mgcp_trunk;
 | 
			
		||||
struct mgcp_trunk_config;
 | 
			
		||||
struct mgcp_rtp_end;
 | 
			
		||||
 | 
			
		||||
#define MGCP_ENDP_CRCX 1
 | 
			
		||||
@@ -65,16 +59,21 @@ struct mgcp_rtp_end;
 | 
			
		||||
#define MGCP_POLICY_REJECT	5
 | 
			
		||||
#define MGCP_POLICY_DEFER	6
 | 
			
		||||
 | 
			
		||||
typedef int (*mgcp_reset)(struct mgcp_trunk *cfg);
 | 
			
		||||
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_rqnt)(struct mgcp_endpoint *endp, char tone);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Return:
 | 
			
		||||
 *   <  0 in case no audio was processed
 | 
			
		||||
 *   >= 0 in case audio was processed.
 | 
			
		||||
 *   >= 0 in case audio was processed. The remaining payload
 | 
			
		||||
 *   length will be returned.
 | 
			
		||||
 */
 | 
			
		||||
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
 | 
			
		||||
			       struct mgcp_rtp_end *dst_end, struct msgb *msg);
 | 
			
		||||
			       struct mgcp_rtp_end *dst_end,
 | 
			
		||||
			       char *data, int *len, int buf_size);
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn_rtp;
 | 
			
		||||
 | 
			
		||||
@@ -93,10 +92,8 @@ 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_v4[INET6_ADDRSTRLEN];
 | 
			
		||||
	char bind_addr_v6[INET6_ADDRSTRLEN];
 | 
			
		||||
	char *bind_addr;
 | 
			
		||||
 | 
			
		||||
	/* dynamically allocated */
 | 
			
		||||
	int range_start;
 | 
			
		||||
@@ -122,6 +119,104 @@ 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,
 | 
			
		||||
@@ -129,63 +224,61 @@ enum mgcp_role {
 | 
			
		||||
 | 
			
		||||
struct mgcp_config {
 | 
			
		||||
	int source_port;
 | 
			
		||||
	char local_ip[INET6_ADDRSTRLEN];
 | 
			
		||||
	char source_addr[INET6_ADDRSTRLEN];
 | 
			
		||||
	char call_agent_addr[INET6_ADDRSTRLEN];
 | 
			
		||||
	char *local_ip;
 | 
			
		||||
	char *source_addr;
 | 
			
		||||
	char *call_agent_addr;
 | 
			
		||||
 | 
			
		||||
	/* RTP processing */
 | 
			
		||||
	mgcp_processing rtp_processing_cb;
 | 
			
		||||
	mgcp_processing_setup setup_rtp_processing_cb;
 | 
			
		||||
 | 
			
		||||
	mgcp_get_format get_net_downlink_format_cb;
 | 
			
		||||
 | 
			
		||||
	struct osmo_wqueue gw_fd;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	/* list holding the trunks */
 | 
			
		||||
	uint32_t last_call_id;
 | 
			
		||||
 | 
			
		||||
	/* trunk handling */
 | 
			
		||||
	struct mgcp_trunk_config trunk;
 | 
			
		||||
	struct llist_head trunks;
 | 
			
		||||
 | 
			
		||||
	enum mgcp_role role;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		/* Osmux usage policy: */
 | 
			
		||||
		enum osmux_usage usage;
 | 
			
		||||
	/* osmux translator: 0 means disabled, 1 means enabled */
 | 
			
		||||
	int osmux;
 | 
			
		||||
	/* addr to bind the server to */
 | 
			
		||||
		char *local_addr_v4;
 | 
			
		||||
		char *local_addr_v6;
 | 
			
		||||
		/* osmux port */
 | 
			
		||||
		uint16_t local_port;
 | 
			
		||||
		/* The osmux socket is allocated on demand (1st time used).
 | 
			
		||||
		* This tells us if the osmux socket is already initialized.  */
 | 
			
		||||
		bool initialized;
 | 
			
		||||
	char *osmux_addr;
 | 
			
		||||
	/* The BSC-NAT may ask for enabling osmux on demand. This tells us if
 | 
			
		||||
	 * the osmux socket is already initialized.
 | 
			
		||||
	 */
 | 
			
		||||
	int osmux_init;
 | 
			
		||||
	/* osmux batch factor: from 1 to 4 maximum */
 | 
			
		||||
		int batch_factor;
 | 
			
		||||
	int osmux_batch;
 | 
			
		||||
	/* osmux batch size (in bytes) */
 | 
			
		||||
		int batch_size;
 | 
			
		||||
		/* Pad circuit with dummy AMR frames if no payload to transmit is available */
 | 
			
		||||
		bool dummy_padding;
 | 
			
		||||
		/* Whether peer is behind NAT (Retrieve remote addr from 1st received Osmux packet) */
 | 
			
		||||
		bool peer_behind_nat;
 | 
			
		||||
	} osmux;
 | 
			
		||||
	int osmux_batch_size;
 | 
			
		||||
	/* osmux port */
 | 
			
		||||
	uint16_t osmux_port;
 | 
			
		||||
	/* Pad circuit with dummy messages until we see the first voice
 | 
			
		||||
	 * message.
 | 
			
		||||
	 */
 | 
			
		||||
	uint16_t osmux_dummy;
 | 
			
		||||
	/* domain name of the media gateway */
 | 
			
		||||
	char domain[255+1];
 | 
			
		||||
 | 
			
		||||
	/* time after which inactive connections (CIs) get closed */
 | 
			
		||||
	int conn_timeout;
 | 
			
		||||
 | 
			
		||||
	/* osmocom CTRL interface */
 | 
			
		||||
	struct ctrl_handle *ctrl;
 | 
			
		||||
 | 
			
		||||
	/* global rate counters to measure the MGWs overall performance and
 | 
			
		||||
	 * health */
 | 
			
		||||
	struct mgcp_ratectr_global ratectr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* config management */
 | 
			
		||||
@@ -193,7 +286,8 @@ 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);
 | 
			
		||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval);
 | 
			
		||||
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
 | 
			
		||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * format helper functions
 | 
			
		||||
@@ -201,9 +295,9 @@ void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval);
 | 
			
		||||
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp);
 | 
			
		||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
 | 
			
		||||
int mgcp_send_reset_all(struct mgcp_config *cfg);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t prio);
 | 
			
		||||
int mgcp_udp_send(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, const char *buf, int len);
 | 
			
		||||
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);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,48 +1,9 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
 | 
			
		||||
#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_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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec);
 | 
			
		||||
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec);
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_codecset {
 | 
			
		||||
	/* 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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void mgcp_codecset_reset(struct mgcp_rtp_codecset *cset);
 | 
			
		||||
void mgcp_codecset_summary(struct mgcp_rtp_codecset *cset, const char *prefix_str);
 | 
			
		||||
int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type,
 | 
			
		||||
			    const char *audio_name, const struct mgcp_codec_param *param);
 | 
			
		||||
int mgcp_codecset_decide(struct mgcp_rtp_codecset *cset_src, struct mgcp_rtp_codecset *cset_dst);
 | 
			
		||||
const struct mgcp_rtp_codec *mgcp_codecset_pt_find_by_subtype_name(const struct mgcp_rtp_codecset *cset,
 | 
			
		||||
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);
 | 
			
		||||
struct mgcp_rtp_codec *mgcp_codecset_find_codec_from_pt(struct mgcp_rtp_codecset *cset, int payload_type);
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,6 @@ enum mgcp_connection_mode {
 | 
			
		||||
	MGCP_CONN_SEND_ONLY = 2,
 | 
			
		||||
	MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
 | 
			
		||||
	MGCP_CONN_LOOPBACK  = 4 | MGCP_CONN_RECV_SEND,
 | 
			
		||||
	MGCP_CONN_CONFECHO  = 8 | MGCP_CONN_RECV_SEND,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:"
 | 
			
		||||
@@ -68,11 +67,11 @@ struct mgcp_codec_param {
 | 
			
		||||
/* Ensure that the msg->l2h is NUL terminated. */
 | 
			
		||||
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char *tail = msg->tail; /* char after l2 data */
 | 
			
		||||
	unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
 | 
			
		||||
	if (tail[-1] == '\0')
 | 
			
		||||
		/* nothing to do */;
 | 
			
		||||
	else if (msgb_tailroom(msg) > 0)
 | 
			
		||||
		msgb_put_u8(msg, (uint8_t)'\0');
 | 
			
		||||
		tail[0] = '\0';
 | 
			
		||||
	else if (tail[-1] == '\r' || tail[-1] == '\n')
 | 
			
		||||
		tail[-1] = '\0';
 | 
			
		||||
	else {
 | 
			
		||||
@@ -101,10 +100,6 @@ 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,132 +23,11 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_network.h>
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/gsm/iuup.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_rtp_end.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_RTP_OSMUX,
 | 
			
		||||
	MGCP_RTP_IUUP,
 | 
			
		||||
};
 | 
			
		||||
extern const struct value_string mgcp_conn_rtp_type_names[];
 | 
			
		||||
static inline const char *mgcp_conn_rtp_type_name(enum mgcp_conn_rtp_type val)
 | 
			
		||||
{
 | 
			
		||||
	return get_value_string(mgcp_conn_rtp_type_names, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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 local_cid holding valid data? is it allocated from pool? */
 | 
			
		||||
		bool local_cid_allocated;
 | 
			
		||||
		/* Allocated local Osmux circuit ID for this conn */
 | 
			
		||||
		uint8_t local_cid;
 | 
			
		||||
		/* Is remote_cid holding valid data? was it already received from client? */
 | 
			
		||||
		bool remote_cid_present;
 | 
			
		||||
		/* Received remote Osmux circuit ID for this conn */
 | 
			
		||||
		uint8_t remote_cid;
 | 
			
		||||
		/* handle to batch messages, shared (refcounted) among several conns */
 | 
			
		||||
		struct osmux_in_handle *in;
 | 
			
		||||
		/* handle to unbatch messages, one allocated and owned per conn */
 | 
			
		||||
		struct osmux_out_handle *out;
 | 
			
		||||
		/* statistics: */
 | 
			
		||||
		struct rate_ctr_group *ctrg;
 | 
			
		||||
	} osmux;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		struct osmo_iuup_instance *iui;
 | 
			
		||||
		bool active_init; /* true: Send IuUP Init */
 | 
			
		||||
		int rfci_id_no_data; /* RFCI Id for RFCI NO_DATA (-1 if not available) */
 | 
			
		||||
		bool configured;
 | 
			
		||||
		struct osmo_iuup_rnl_prim *init_ind;
 | 
			
		||||
	} iuup;
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr_group *ctrg;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! 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,
 | 
			
		||||
@@ -171,7 +50,7 @@ static const struct rate_ctr_desc mgcp_conn_rate_ctr_desc[] = {
 | 
			
		||||
	[RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."},
 | 
			
		||||
	[RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."},
 | 
			
		||||
	[RTP_OCTETS_TX_CTR] = {"rtp:octets_tx", "Outbound rtp octets."},
 | 
			
		||||
	[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "Dropped rtp packets."}
 | 
			
		||||
	[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Aggregated RTP connection stats. These are updated when an RTP connection is freed.
 | 
			
		||||
@@ -189,67 +68,13 @@ static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = {
 | 
			
		||||
	[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Osmux connection related counters */
 | 
			
		||||
enum {
 | 
			
		||||
	OSMUX_CHUNKS_RX_CTR,
 | 
			
		||||
	OSMUX_OCTETS_RX_CTR,
 | 
			
		||||
	OSMUX_RTP_PACKETS_TX_CTR,
 | 
			
		||||
	OSMUX_RTP_PACKETS_TX_DROPPED_CTR,
 | 
			
		||||
	OSMUX_AMR_OCTETS_TX_CTR,
 | 
			
		||||
	/* Only available in global stats: */
 | 
			
		||||
	OSMUX_NUM_CONNECTIONS,
 | 
			
		||||
	OSMUX_PACKETS_RX_CTR,
 | 
			
		||||
	OSMUX_PACKETS_TX_CTR,
 | 
			
		||||
	OSMUX_DROPPED_PACKETS_CTR,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* RTP per-connection statistics. Instances of the corresponding rate counter group
 | 
			
		||||
 * exist for the lifetime of an RTP connection.
 | 
			
		||||
 * Must be kept in sync with all_rtp_conn_rate_ctr_desc below */
 | 
			
		||||
static const struct rate_ctr_desc mgcp_conn_osmux_rate_ctr_desc[] = {
 | 
			
		||||
	[OSMUX_CHUNKS_RX_CTR] = {"osmux:chunks_rx", "Inbound Osmux chunks."},
 | 
			
		||||
	[OSMUX_OCTETS_RX_CTR] = {"osmux:octets_rx", "Inbound Osmux octets."},
 | 
			
		||||
	[OSMUX_RTP_PACKETS_TX_CTR] = {"osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
 | 
			
		||||
	[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
 | 
			
		||||
	[OSMUX_AMR_OCTETS_TX_CTR] = {"osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Aggregated Osmux connection stats. These are updated when an Osmux connection is freed.
 | 
			
		||||
 * Must be kept in sync with mgcp_conn_osmux_rate_ctr_desc above */
 | 
			
		||||
static const struct rate_ctr_desc all_osmux_conn_rate_ctr_desc[] = {
 | 
			
		||||
	[OSMUX_CHUNKS_RX_CTR] = {"all_osmux:chunks_rx", "Inbound Osmux chunks."},
 | 
			
		||||
	[OSMUX_OCTETS_RX_CTR] = {"all_osmux:octets_rx", "Inbound Osmux octets."},
 | 
			
		||||
	[OSMUX_RTP_PACKETS_TX_CTR] = {"all_osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
 | 
			
		||||
	[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"all_osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
 | 
			
		||||
	[OSMUX_AMR_OCTETS_TX_CTR] = {"all_osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
 | 
			
		||||
	/* These last counters below do not exist in per-connection stats, only here: */
 | 
			
		||||
	[OSMUX_NUM_CONNECTIONS] = {"all_osmux:num_closed_conns", "Total number of osmux connections closed."},
 | 
			
		||||
	[OSMUX_PACKETS_RX_CTR] = {"all_osmux:packets_rx", "Total inbound UDP/Osmux packets."},
 | 
			
		||||
	[OSMUX_PACKETS_TX_CTR] = {"all_osmux:packets_tx", "Total outbound UDP/Osmux packets."},
 | 
			
		||||
	[OSMUX_DROPPED_PACKETS_CTR] = {"all_osmux:dropped_packets", "Dropped outbound UDP/Osmux packets."}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Was conn configured to handle Osmux? */
 | 
			
		||||
static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
 | 
			
		||||
	return conn->type == MGCP_RTP_OSMUX;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct mgcp_conn_rtp *mgcp_conn_get_conn_rtp(struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(conn->type == MGCP_CONN_TYPE_RTP);
 | 
			
		||||
	return &conn->u.rtp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
				  enum mgcp_conn_type type, char *name);
 | 
			
		||||
void mgcp_conn_free(struct mgcp_conn *conn);
 | 
			
		||||
int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode);
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
 | 
			
		||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
					const char *id);
 | 
			
		||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id);
 | 
			
		||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
 | 
			
		||||
char *mgcp_conn_dump(struct mgcp_conn *conn);
 | 
			
		||||
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
 | 
			
		||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
#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, uint8_t ts);
 | 
			
		||||
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg);
 | 
			
		||||
@@ -24,36 +24,22 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/gsm/i460_mux.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
 | 
			
		||||
struct sockaddr;
 | 
			
		||||
struct sockaddr_in;
 | 
			
		||||
struct mgcp_conn;
 | 
			
		||||
struct mgcp_conn_rtp;
 | 
			
		||||
struct mgcp_endpoint;
 | 
			
		||||
 | 
			
		||||
/* 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)
 | 
			
		||||
 | 
			
		||||
enum rtp_proto {
 | 
			
		||||
	MGCP_PROTO_RTP,
 | 
			
		||||
	MGCP_PROTO_RTCP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_rtp_msg_ctx {
 | 
			
		||||
	enum rtp_proto proto;
 | 
			
		||||
	int proto;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_src;
 | 
			
		||||
	struct osmo_sockaddr *from_addr;
 | 
			
		||||
	struct sockaddr_in *from_addr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define OSMO_RTP_MSG_CTX(MSGB) ((struct osmo_rtp_msg_ctx*)(MSGB)->cb)
 | 
			
		||||
#define OSMO_RTP_MSG_CTX(MSGB) (*(struct osmo_rtp_msg_ctx**)&((MSGB)->dst))
 | 
			
		||||
 | 
			
		||||
osmo_static_assert(sizeof(((struct msgb*)0)->cb) >= sizeof(struct osmo_rtp_msg_ctx), osmo_rtp_msg_ctx_fits_in_msgb_cb);
 | 
			
		||||
#define LOG_ENDP(endp, level, fmt, args...) \
 | 
			
		||||
	LOGP(DRTP, level, "%x@ " fmt, ENDPOINT_NUMBER(endp), ## args)
 | 
			
		||||
 | 
			
		||||
/* 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. */
 | 
			
		||||
@@ -81,7 +67,6 @@ 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) */
 | 
			
		||||
@@ -90,9 +75,6 @@ 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;
 | 
			
		||||
 | 
			
		||||
@@ -102,8 +84,11 @@ struct mgcp_endpoint {
 | 
			
		||||
	/*! List of struct mgcp_conn, of the connections active on this endpoint */
 | 
			
		||||
	struct llist_head conns;
 | 
			
		||||
 | 
			
		||||
	/*! Backpointer to the trunk this endpoint belongs to */
 | 
			
		||||
	struct mgcp_trunk *trunk;
 | 
			
		||||
	/*! Backpointer to the MGW configuration */
 | 
			
		||||
	struct mgcp_config *cfg;
 | 
			
		||||
 | 
			
		||||
	/*! Backpointer to the Trunk specific configuration */
 | 
			
		||||
	struct mgcp_trunk_config *tcfg;
 | 
			
		||||
 | 
			
		||||
	/*! Endpoint properties (see above) */
 | 
			
		||||
	const struct mgcp_endpoint_type *type;
 | 
			
		||||
@@ -114,44 +99,15 @@ 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;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index);
 | 
			
		||||
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid);
 | 
			
		||||
void mgcp_endp_update(struct mgcp_endpoint *endp);
 | 
			
		||||
bool mgcp_endp_is_wildcarded(const char *epname);
 | 
			
		||||
bool mgcp_endp_is_null(const char *epname);
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
 | 
			
		||||
					      const struct mgcp_trunk *trunk);
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
 | 
			
		||||
					struct mgcp_config *cfg);
 | 
			
		||||
bool mgcp_endp_avail(const struct mgcp_endpoint *endp);
 | 
			
		||||
unsigned int mgcp_endp_num_conns(const struct mgcp_endpoint *endp);
 | 
			
		||||
bool mgcp_endp_is_full(const 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_free_conn_oldest(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_endp_free_conn_all(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_endp_strip_name(char *epname_stripped, const char *epname,
 | 
			
		||||
			 const struct mgcp_trunk *trunk);
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
 | 
			
		||||
			const struct mgcp_trunk *trunk);
 | 
			
		||||
void mgcp_endp_release(struct mgcp_endpoint *endp);
 | 
			
		||||
/*! Extract endpoint number for a given endpoint */
 | 
			
		||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id);
 | 
			
		||||
struct mgcp_conn *mgcp_endp_get_conn_oldest(struct mgcp_endpoint *endp);
 | 
			
		||||
struct mgcp_conn_rtp *mgcp_endp_get_conn_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
					const char *id);
 | 
			
		||||
void mgcp_endp_release(struct mgcp_endpoint *endp);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										365
									
								
								include/osmocom/mgcp/mgcp_internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								include/osmocom/mgcp/mgcp_internal.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,365 @@
 | 
			
		||||
/* 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"
 | 
			
		||||
 | 
			
		||||
#define LOG_CONN(conn, level, fmt, args...) \
 | 
			
		||||
	LOGP(DRTP, level, "(%d@ I:%s) " fmt, \
 | 
			
		||||
	     ENDPOINT_NUMBER((conn)->endp), (conn)->id, ## args)
 | 
			
		||||
 | 
			
		||||
#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
 | 
			
		||||
	LOG_CONN(conn_rtp->conn, level, fmt, ## args)
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
	struct osmo_iuup_cn *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 (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,
 | 
			
		||||
	      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_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)
 | 
			
		||||
 | 
			
		||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
			
		||||
			  struct mgcp_rtp_state *state,
 | 
			
		||||
			  struct mgcp_rtp_end *rtp_end,
 | 
			
		||||
			  struct sockaddr_in *addr, struct msgb *msg);
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
/* 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,25 +25,21 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.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);
 | 
			
		||||
 | 
			
		||||
enum mgcp_connection_mode mgcp_parse_conn_mode(const char *msg);
 | 
			
		||||
int mgcp_parse_conn_mode(const char *msg, struct mgcp_endpoint *endp,
 | 
			
		||||
			 struct mgcp_conn *conn);
 | 
			
		||||
 | 
			
		||||
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data);
 | 
			
		||||
int mgcp_parse_hdr_pars(struct mgcp_parse_data *pdata);
 | 
			
		||||
 | 
			
		||||
int mgcp_parse_osmux_cid(const char *line);
 | 
			
		||||
 | 
			
		||||
bool mgcp_check_param(const char *line);
 | 
			
		||||
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line);
 | 
			
		||||
 | 
			
		||||
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,125 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/osmo_io.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
 | 
			
		||||
/* The following constant defines an RTP dummy payload that is used for
 | 
			
		||||
 * "UDP Hole Punching" (NAT) */
 | 
			
		||||
#define MGCP_DUMMY_LOAD 0x23
 | 
			
		||||
static const char rtp_dummy_payload[] = { MGCP_DUMMY_LOAD };
 | 
			
		||||
 | 
			
		||||
/* Check if the data in a given message buffer matches the rtp dummy payload
 | 
			
		||||
 * defined above */
 | 
			
		||||
#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_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_conn_rtp_bind_rtp_ports(struct mgcp_conn_rtp *conn, int rtp_port);
 | 
			
		||||
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);
 | 
			
		||||
int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
 | 
			
		||||
 | 
			
		||||
/* payload processing default functions */
 | 
			
		||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
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 rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
 | 
			
		||||
				 int id);
 | 
			
		||||
void forward_data_tap(struct osmo_io_fd *iofd, struct mgcp_rtp_tap *tap, struct msgb *msg);
 | 
			
		||||
uint32_t mgcp_get_current_ts(unsigned codec_rate);
 | 
			
		||||
 | 
			
		||||
int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, bool target_is_oa);
 | 
			
		||||
@@ -1,96 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define MGCP_PARSE_SDP_PTIME_UNSET (-1)
 | 
			
		||||
#define MGCP_PARSE_SDP_MAXPTIME_UNSET (-1)
 | 
			
		||||
#define MGCP_PARSE_SDP_RTP_PORT_UNSET (0)
 | 
			
		||||
 | 
			
		||||
struct mgcp_parse_sdp {
 | 
			
		||||
	int ptime;
 | 
			
		||||
	int maxptime;
 | 
			
		||||
	int rtp_port;
 | 
			
		||||
	struct osmo_sockaddr rem_addr; /* Only IP address, port is in rtp_port above */
 | 
			
		||||
	struct mgcp_rtp_codecset cset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline void mgcp_parse_sdp_init(struct mgcp_parse_sdp *sdp)
 | 
			
		||||
{
 | 
			
		||||
	sdp->ptime = MGCP_PARSE_SDP_PTIME_UNSET;
 | 
			
		||||
	sdp->maxptime = MGCP_PARSE_SDP_MAXPTIME_UNSET;
 | 
			
		||||
	sdp->rtp_port = MGCP_PARSE_SDP_RTP_PORT_UNSET;
 | 
			
		||||
	sdp->rem_addr = (struct osmo_sockaddr){ .u.sa.sa_family = AF_UNSPEC };
 | 
			
		||||
	mgcp_codecset_reset(&sdp->cset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET (-2)
 | 
			
		||||
#define MGCP_PARSE_HDR_PARS_OSMUX_CID_WILDCARD (-1)
 | 
			
		||||
 | 
			
		||||
struct mgcp_parse_hdr_pars {
 | 
			
		||||
	const char *local_options;
 | 
			
		||||
	const char *callid;
 | 
			
		||||
	const char *connid;
 | 
			
		||||
	enum mgcp_connection_mode mode;
 | 
			
		||||
	int remote_osmux_cid;
 | 
			
		||||
	bool have_sdp;
 | 
			
		||||
	/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
 | 
			
		||||
	uint32_t x_osmo_ign;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline void mgcp_parse_hdr_pars_init(struct mgcp_parse_hdr_pars *hpars)
 | 
			
		||||
{
 | 
			
		||||
	*hpars = (struct mgcp_parse_hdr_pars){
 | 
			
		||||
		.local_options = NULL,
 | 
			
		||||
		.callid = NULL,
 | 
			
		||||
		.connid = NULL,
 | 
			
		||||
		.mode = MGCP_CONN_NONE,
 | 
			
		||||
		.remote_osmux_cid = MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET,
 | 
			
		||||
		.have_sdp = false,
 | 
			
		||||
		.x_osmo_ign = 0,
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Internal structure while parsing a request */
 | 
			
		||||
struct mgcp_parse_data {
 | 
			
		||||
	struct mgcp_config *cfg;
 | 
			
		||||
	char *save;
 | 
			
		||||
	/* MGCP Header: */
 | 
			
		||||
	char *epname;
 | 
			
		||||
	char *trans;
 | 
			
		||||
	struct mgcp_parse_hdr_pars hpars;
 | 
			
		||||
	/* MGCP Body: */
 | 
			
		||||
	struct mgcp_parse_sdp sdp;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
 | 
			
		||||
uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
 | 
			
		||||
				  const struct mgcp_rtp_end *rtp);
 | 
			
		||||
 | 
			
		||||
extern const struct value_string mgcp_connection_mode_strs[];
 | 
			
		||||
static inline const char *mgcp_cmode_name(enum mgcp_connection_mode mode)
 | 
			
		||||
{
 | 
			
		||||
	return get_value_string(mgcp_connection_mode_strs, mode);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,109 +0,0 @@
 | 
			
		||||
#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 aggregates stats of individual Osmux connections. */
 | 
			
		||||
	struct rate_ctr_group *all_osmux_conn_stats;
 | 
			
		||||
	/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
 | 
			
		||||
	struct rate_ctr_group *e1_stats;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
@@ -1,53 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/osmo_io.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
 | 
			
		||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
 | 
			
		||||
struct mgcp_rtp_end {
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp; /* backpointer */
 | 
			
		||||
	/* remote IP address of the RTP socket */
 | 
			
		||||
	struct osmo_sockaddr addr;
 | 
			
		||||
 | 
			
		||||
	/* in network byte order */
 | 
			
		||||
	uint16_t rtcp_port;
 | 
			
		||||
 | 
			
		||||
	struct mgcp_rtp_codecset cset;
 | 
			
		||||
 | 
			
		||||
	/* per endpoint data */
 | 
			
		||||
	int  frames_per_packet;
 | 
			
		||||
	uint32_t packet_duration_ms;
 | 
			
		||||
	int maximum_packet_time; /* -1: not set */
 | 
			
		||||
	/* 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 */
 | 
			
		||||
	bool force_constant_ssrc;
 | 
			
		||||
	/* 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_io_fd *rtp;
 | 
			
		||||
	struct osmo_io_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];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void mgcp_rtp_end_init(struct mgcp_rtp_end *end, struct mgcp_conn_rtp *conn_rtp);
 | 
			
		||||
void mgcp_rtp_end_cleanup(struct mgcp_rtp_end *end);
 | 
			
		||||
void mgcp_rtp_end_set_packet_duration_ms(struct mgcp_rtp_end *end, uint32_t packet_duration_ms);
 | 
			
		||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end);
 | 
			
		||||
void mgcp_rtp_end_free_port(struct mgcp_rtp_end *end);
 | 
			
		||||
@@ -22,9 +22,9 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
struct mgcp_parse_data;
 | 
			
		||||
 | 
			
		||||
int mgcp_parse_sdp_data(struct mgcp_parse_data *p);
 | 
			
		||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
			struct mgcp_conn_rtp *conn,
 | 
			
		||||
			struct mgcp_parse_data *p);
 | 
			
		||||
 | 
			
		||||
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
			    const struct mgcp_conn_rtp *conn, struct msgb *sdp,
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,52 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (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);
 | 
			
		||||
@@ -1,87 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/i460_mux.h>
 | 
			
		||||
#include <osmocom/abis/e1_input.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_network.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;
 | 
			
		||||
 | 
			
		||||
	int audio_send_ptime;
 | 
			
		||||
	int audio_send_name;
 | 
			
		||||
 | 
			
		||||
	int omit_rtcp;
 | 
			
		||||
	int keepalive_interval;
 | 
			
		||||
 | 
			
		||||
	/* RTP patching */
 | 
			
		||||
	bool force_constant_ssrc;
 | 
			
		||||
	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;
 | 
			
		||||
			uint8_t ts_usecount[NUM_E1_TS-1];
 | 
			
		||||
			struct osmo_i460_timeslot i460_ts[NUM_E1_TS-1];
 | 
			
		||||
			/* Note: on an E1 line TS 0 is devoted to framing and
 | 
			
		||||
			 * alignment and therefore only NUM_E1_TS-1 timeslots
 | 
			
		||||
			 * 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);
 | 
			
		||||
int mgcp_trunk_allocate_conn_rtp_ports(struct mgcp_trunk *trunk, struct mgcp_conn_rtp *conn_rtp);
 | 
			
		||||
 | 
			
		||||
/* 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,22 +1,24 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/netif/osmux.h>
 | 
			
		||||
struct mgcp_conn_rtp;
 | 
			
		||||
struct mgcp_trunk;
 | 
			
		||||
struct mgcp_endpoint;
 | 
			
		||||
struct mgcp_conn_rtp;
 | 
			
		||||
 | 
			
		||||
int osmux_init(struct mgcp_trunk *trunk);
 | 
			
		||||
int osmux_init_conn(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int conn_osmux_enable(struct mgcp_conn_rtp *conn);
 | 
			
		||||
#define OSMUX_PORT	1984
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	OSMUX_ROLE_BSC = 0,
 | 
			
		||||
	OSMUX_ROLE_BSC_NAT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int osmux_init(int role, struct mgcp_config *cfg);
 | 
			
		||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
 | 
			
		||||
		      struct in_addr *addr, uint16_t port);
 | 
			
		||||
void conn_osmux_disable(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int conn_osmux_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg);
 | 
			
		||||
int osmux_send_dummy(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid);
 | 
			
		||||
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
 | 
			
		||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
 | 
			
		||||
 | 
			
		||||
void osmux_cid_pool_get(uint8_t osmux_cid);
 | 
			
		||||
int osmux_cid_pool_get_next(void);
 | 
			
		||||
@@ -26,14 +28,10 @@ int osmux_cid_pool_count_used(void);
 | 
			
		||||
 | 
			
		||||
enum osmux_state {
 | 
			
		||||
	OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
 | 
			
		||||
	OSMUX_STATE_ACTIVATING,   /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref conn_osmux_enable. */
 | 
			
		||||
	OSMUX_STATE_ENABLED,	  /* Osmux was initialized by \ref conn_osmux_enable and can process frames */
 | 
			
		||||
	OSMUX_STATE_ACTIVATING,   /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */
 | 
			
		||||
	OSMUX_STATE_ENABLED,	  /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct value_string osmux_state_strs[];
 | 
			
		||||
static inline const char *osmux_state_str(enum osmux_state val)
 | 
			
		||||
{ return get_value_string(osmux_state_strs, val); }
 | 
			
		||||
 | 
			
		||||
enum osmux_usage {
 | 
			
		||||
	OSMUX_USAGE_OFF = 0,
 | 
			
		||||
	OSMUX_USAGE_ON = 1,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,3 @@ enum mgcp_vty_node {
 | 
			
		||||
	MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
 | 
			
		||||
	TRUNK_NODE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum mgw_vty_cmd_attr {
 | 
			
		||||
	MGW_CMD_ATTR_NEWCONN = 0,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ 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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
#include <osmocom/core/defs.h>
 | 
			
		||||
 | 
			
		||||
#if BUILDING_LIBOSMOMGCPCLIENT
 | 
			
		||||
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT(text)
 | 
			
		||||
#else
 | 
			
		||||
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT(text) OSMO_DEPRECATED(text)
 | 
			
		||||
#endif
 | 
			
		||||
@@ -3,15 +3,14 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/defs.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_common.h>
 | 
			
		||||
 | 
			
		||||
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT NULL /* INADDR(6)_ANY */
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
 | 
			
		||||
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
 | 
			
		||||
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
 | 
			
		||||
#define MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP "null"
 | 
			
		||||
 | 
			
		||||
#define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n"
 | 
			
		||||
 | 
			
		||||
struct msgb;
 | 
			
		||||
@@ -27,21 +26,6 @@ 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;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		uint32_t timeout_sec;
 | 
			
		||||
		uint32_t req_interval_sec;
 | 
			
		||||
		char req_endpoint_name[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	} keepalive;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef unsigned int mgcp_trans_id_t;
 | 
			
		||||
@@ -52,12 +36,10 @@ enum mgcp_codecs {
 | 
			
		||||
	CODEC_GSM_8000_1 = 3,
 | 
			
		||||
	CODEC_PCMA_8000_1 = 8,
 | 
			
		||||
	CODEC_G729_8000_1 = 18,
 | 
			
		||||
	CODEC_GSMEFR_8000_1 = 110,	/* 3GPP TS 48.103 table 5.4.2.2.1 */
 | 
			
		||||
	CODEC_GSMHR_8000_1 = 111,	/* 3GPP TS 48.103 table 5.4.2.2.1 */
 | 
			
		||||
	CODEC_AMR_8000_1 = 112,		/* 3GPP TS 48.103 table 5.4.2.2.1 */
 | 
			
		||||
	CODEC_AMRWB_16000_1 = 113,	/* 3GPP TS 48.103 table 5.4.2.2.1 */
 | 
			
		||||
	CODEC_IUFP = 96,
 | 
			
		||||
	CODEC_CLEARMODE = 120,		/* 3GPP TS 48.103 table 5.4.2.2.1 */
 | 
			
		||||
	CODEC_GSMEFR_8000_1 = 110,
 | 
			
		||||
	CODEC_GSMHR_8000_1 = 111,
 | 
			
		||||
	CODEC_AMR_8000_1 = 112,
 | 
			
		||||
	CODEC_AMRWB_16000_1 = 113,
 | 
			
		||||
};
 | 
			
		||||
/* Note: when new codec types are added, the corresponding value strings
 | 
			
		||||
 * in mgcp_client.c (codec_table) must be updated as well. Enumerations
 | 
			
		||||
@@ -79,7 +61,27 @@ struct ptmap {
 | 
			
		||||
	unsigned int pt;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int ptmap_cmp(const struct ptmap *a, const struct ptmap *b);
 | 
			
		||||
struct mgcp_response_head {
 | 
			
		||||
	int response_code;
 | 
			
		||||
	mgcp_trans_id_t trans_id;
 | 
			
		||||
	char comment[MGCP_COMMENT_MAXLEN];
 | 
			
		||||
	char conn_id[MGCP_CONN_ID_MAXLEN];
 | 
			
		||||
	char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	bool x_osmo_osmux_use;
 | 
			
		||||
	uint8_t x_osmo_osmux_cid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_response {
 | 
			
		||||
	char *body;
 | 
			
		||||
	struct mgcp_response_head head;
 | 
			
		||||
	uint16_t audio_port;
 | 
			
		||||
	char audio_ip[INET_ADDRSTRLEN];
 | 
			
		||||
	unsigned int ptime;
 | 
			
		||||
	enum mgcp_codecs codecs[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int codecs_len;
 | 
			
		||||
	struct ptmap ptmap[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int ptmap_len;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum mgcp_verb {
 | 
			
		||||
	MGCP_VERB_CRCX,
 | 
			
		||||
@@ -89,8 +91,38 @@ enum mgcp_verb {
 | 
			
		||||
	MGCP_VERB_RSIP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_client_conf *mgcp_client_conf_alloc(void *ctx);
 | 
			
		||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf) OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use mgcp_client_conf_alloc() (or even better, switch to the mgcp_client_pool API!)");
 | 
			
		||||
#define MGCP_MSG_PRESENCE_ENDPOINT	0x0001
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CALL_ID	0x0002
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CONN_ID	0x0004
 | 
			
		||||
#define MGCP_MSG_PRESENCE_AUDIO_IP	0x0008
 | 
			
		||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT	0x0010
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CONN_MODE	0x0020
 | 
			
		||||
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
 | 
			
		||||
#define MGCP_MSG_PRESENCE_X_OSMO_IGN	0x8000
 | 
			
		||||
 | 
			
		||||
struct mgcp_msg {
 | 
			
		||||
	enum mgcp_verb verb;
 | 
			
		||||
	/* See MGCP_MSG_PRESENCE_* constants */
 | 
			
		||||
	uint32_t presence;
 | 
			
		||||
	char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	unsigned int call_id;
 | 
			
		||||
	char *conn_id;
 | 
			
		||||
	uint16_t audio_port;
 | 
			
		||||
	char *audio_ip;
 | 
			
		||||
	enum mgcp_connection_mode conn_mode;
 | 
			
		||||
	unsigned int ptime;
 | 
			
		||||
	enum mgcp_codecs codecs[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int codecs_len;
 | 
			
		||||
	struct ptmap ptmap[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int ptmap_len;
 | 
			
		||||
	uint32_t x_osmo_ign;
 | 
			
		||||
	bool x_osmo_osmux_use;
 | 
			
		||||
	int x_osmo_osmux_cid; /* -1 is wildcard */
 | 
			
		||||
	bool param_present;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
 | 
			
		||||
void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf);
 | 
			
		||||
int mgcp_client_config_write(struct vty *vty, const char *indent);
 | 
			
		||||
struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp);
 | 
			
		||||
@@ -98,20 +130,28 @@ 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) OSMO_DEPRECATED("Use mgcp_client_connect() instead");
 | 
			
		||||
void mgcp_client_disconnect(struct mgcp_client *mgcp);
 | 
			
		||||
 | 
			
		||||
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp);
 | 
			
		||||
uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp);
 | 
			
		||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp) OSMO_DEPRECATED("deprecated, returns 0");
 | 
			
		||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp);
 | 
			
		||||
 | 
			
		||||
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. */
 | 
			
		||||
typedef void (* mgcp_response_cb_t )(struct mgcp_response *response, void *priv);
 | 
			
		||||
int mgcp_response_parse_params(struct mgcp_response *r);
 | 
			
		||||
 | 
			
		||||
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
 | 
			
		||||
		   mgcp_response_cb_t response_cb, void *priv);
 | 
			
		||||
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
 | 
			
		||||
 | 
			
		||||
enum mgcp_connection_mode;
 | 
			
		||||
 | 
			
		||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
 | 
			
		||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
extern const struct value_string mgcp_client_connection_mode_strs[];
 | 
			
		||||
static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
 | 
			
		||||
{
 | 
			
		||||
@@ -123,5 +163,3 @@ 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,8 +23,6 @@ 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,10 +43,8 @@ static inline void osmo_mgcpc_ep_ci_dlcx(struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep);
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep);
 | 
			
		||||
const char *osmo_mgcpc_ep_local_name(const struct osmo_mgcpc_ep *ep);
 | 
			
		||||
const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci);
 | 
			
		||||
const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci);
 | 
			
		||||
struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep);
 | 
			
		||||
 | 
			
		||||
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[INET6_ADDRSTRLEN];
 | 
			
		||||
	char addr[INET_ADDRSTRLEN];
 | 
			
		||||
 | 
			
		||||
	/*! RTP connection IP-Port (optional)  */
 | 
			
		||||
	uint16_t port;
 | 
			
		||||
@@ -29,11 +29,11 @@ struct mgcp_conn_peer {
 | 
			
		||||
	/*! RTP packetization interval (optional) */
 | 
			
		||||
	unsigned int ptime;
 | 
			
		||||
 | 
			
		||||
	/*! Deprecated. Use only ptmap[].codec in new code. */
 | 
			
		||||
	enum mgcp_codecs codecs[MGCP_MAX_CODECS]
 | 
			
		||||
		OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[i].codec instead");
 | 
			
		||||
	unsigned int codecs_len
 | 
			
		||||
		OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[] and ptmap_len instead");
 | 
			
		||||
	/*! RTP codec list (optional) */
 | 
			
		||||
	enum mgcp_codecs codecs[MGCP_MAX_CODECS];
 | 
			
		||||
 | 
			
		||||
	/*! Number of codecs in RTP codec list (optional) */
 | 
			
		||||
	unsigned int codecs_len;
 | 
			
		||||
 | 
			
		||||
	/*! RTP payload type map (optional, only needed when payload types are
 | 
			
		||||
	 * used that differ from what IANA/3GPP defines) */
 | 
			
		||||
@@ -64,15 +64,10 @@ struct mgcp_conn_peer {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
 | 
			
		||||
				       uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
 | 
			
		||||
	OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_alloc() and osmo_mgcpc_ep_ci_add() instead");
 | 
			
		||||
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
 | 
			
		||||
	OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci_request() instead");
 | 
			
		||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi)
 | 
			
		||||
	OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci_dlcx() instead");
 | 
			
		||||
				       uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
 | 
			
		||||
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
 | 
			
		||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
 | 
			
		||||
 | 
			
		||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
 | 
			
		||||
	OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci.mgcp_ci_str instead");
 | 
			
		||||
struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi);
 | 
			
		||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,53 +1,23 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/osmo_io.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
	struct osmo_io_fd *iofd;
 | 
			
		||||
	uint32_t remote_addr;
 | 
			
		||||
	struct osmo_wqueue wq;
 | 
			
		||||
	mgcp_trans_id_t next_trans_id;
 | 
			
		||||
	struct llist_head responses_pending;
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
	struct osmo_timer_list keepalive_tx_timer;
 | 
			
		||||
	struct osmo_timer_list keepalive_rx_timer;
 | 
			
		||||
	bool conn_up;
 | 
			
		||||
	struct llist_head inuse_endpoints;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_response_head {
 | 
			
		||||
	int response_code;
 | 
			
		||||
	mgcp_trans_id_t trans_id;
 | 
			
		||||
	char comment[MGCP_COMMENT_MAXLEN];
 | 
			
		||||
	char conn_id[MGCP_CONN_ID_MAXLEN];
 | 
			
		||||
	char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	bool x_osmo_osmux_use;
 | 
			
		||||
	uint8_t x_osmo_osmux_cid;
 | 
			
		||||
struct mgcp_inuse_endpoint {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
	uint16_t id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_response {
 | 
			
		||||
	char *body;
 | 
			
		||||
	struct mgcp_response_head head;
 | 
			
		||||
	uint16_t audio_port;
 | 
			
		||||
	char audio_ip[INET6_ADDRSTRLEN];
 | 
			
		||||
	unsigned int ptime;
 | 
			
		||||
	struct ptmap ptmap[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int ptmap_len;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Invoked when an MGCP response is received or sending failed.  When the
 | 
			
		||||
 * response is passed as NULL, this indicates failure during transmission. */
 | 
			
		||||
typedef void (*mgcp_response_cb_t)(struct mgcp_response *response, void *priv);
 | 
			
		||||
 | 
			
		||||
struct mgcp_response_pending {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
@@ -63,41 +33,3 @@ struct mgcp_response_pending * mgcp_client_pending_add(
 | 
			
		||||
					mgcp_trans_id_t trans_id,
 | 
			
		||||
					mgcp_response_cb_t response_cb,
 | 
			
		||||
					void *priv);
 | 
			
		||||
 | 
			
		||||
#define MGCP_MSG_PRESENCE_ENDPOINT	0x0001
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CALL_ID	0x0002
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CONN_ID	0x0004
 | 
			
		||||
#define MGCP_MSG_PRESENCE_AUDIO_IP	0x0008
 | 
			
		||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT	0x0010
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CONN_MODE	0x0020
 | 
			
		||||
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
 | 
			
		||||
#define MGCP_MSG_PRESENCE_X_OSMO_IGN	0x8000
 | 
			
		||||
 | 
			
		||||
struct mgcp_msg {
 | 
			
		||||
	enum mgcp_verb verb;
 | 
			
		||||
	/* See MGCP_MSG_PRESENCE_* constants */
 | 
			
		||||
	uint32_t presence;
 | 
			
		||||
	char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	unsigned int call_id;
 | 
			
		||||
	char *conn_id;
 | 
			
		||||
	uint16_t audio_port;
 | 
			
		||||
	char *audio_ip;
 | 
			
		||||
	enum mgcp_connection_mode conn_mode;
 | 
			
		||||
	unsigned int ptime;
 | 
			
		||||
	struct ptmap ptmap[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int ptmap_len;
 | 
			
		||||
	uint32_t x_osmo_ign;
 | 
			
		||||
	bool x_osmo_osmux_use;
 | 
			
		||||
	int x_osmo_osmux_cid; /* -1 is wildcard */
 | 
			
		||||
	bool param_present;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int mgcp_response_parse_params(struct mgcp_response *r);
 | 
			
		||||
 | 
			
		||||
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
 | 
			
		||||
		   mgcp_response_cb_t response_cb, void *priv);
 | 
			
		||||
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
 | 
			
		||||
 | 
			
		||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
 | 
			
		||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/vty.h>
 | 
			
		||||
 | 
			
		||||
struct mgcp_client;
 | 
			
		||||
struct mgcp_client_pool;
 | 
			
		||||
struct mgcp_client_pool_member;
 | 
			
		||||
 | 
			
		||||
struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx);
 | 
			
		||||
 | 
			
		||||
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool);
 | 
			
		||||
int mgcp_client_pool_config_write(struct vty *vty, const char *indent);
 | 
			
		||||
unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool);
 | 
			
		||||
void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client);
 | 
			
		||||
bool mgcp_client_pool_empty(const struct mgcp_client_pool *pool);
 | 
			
		||||
 | 
			
		||||
struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool);
 | 
			
		||||
void mgcp_client_pool_put(struct mgcp_client *mgcp_client);
 | 
			
		||||
 | 
			
		||||
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr);
 | 
			
		||||
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member);
 | 
			
		||||
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member);
 | 
			
		||||
@@ -1,52 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
/* Struct to handle a pool of MGWs. (Use _pool functions) */
 | 
			
		||||
struct mgcp_client_pool {
 | 
			
		||||
 | 
			
		||||
	/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using
 | 
			
		||||
	 * mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */
 | 
			
		||||
	struct mgcp_client *mgcp_client_single;
 | 
			
		||||
 | 
			
		||||
	/* A list that manages the pool members (see mgcp_client_pool_member->list above) */
 | 
			
		||||
	struct llist_head member_list;
 | 
			
		||||
 | 
			
		||||
	/* String to use for indentation when writing the configuration file to the VTY. This field is populated by
 | 
			
		||||
	 * mgcp_client_pool_vty_init() */
 | 
			
		||||
	char *vty_indent;
 | 
			
		||||
 | 
			
		||||
	/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */
 | 
			
		||||
	struct cmd_node *vty_node;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Struct to handle a member of a pool of MGWs. */
 | 
			
		||||
struct mgcp_client_pool_member {
 | 
			
		||||
	/* Entry in llist mgcp_client_pool->pool. */
 | 
			
		||||
	struct llist_head list;
 | 
			
		||||
 | 
			
		||||
	/* The pool managing this object: */
 | 
			
		||||
	struct mgcp_client_pool *pool;
 | 
			
		||||
 | 
			
		||||
	/* Reference number assinged by VTY. This number is used to manage the pool from the VTY and to identify it in
 | 
			
		||||
	 * the log. */
 | 
			
		||||
	unsigned int nr;
 | 
			
		||||
 | 
			
		||||
	/* 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 mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr);
 | 
			
		||||
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member);
 | 
			
		||||
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member);
 | 
			
		||||
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member);
 | 
			
		||||
							
								
								
									
										74
									
								
								m4/ax_check_compile_flag.m4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								m4/ax_check_compile_flag.m4
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#   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
 | 
			
		||||
@@ -13,6 +13,12 @@ AM_CFLAGS = \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# Libraries
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	libosmo-mgcp-client \
 | 
			
		||||
 
 | 
			
		||||
@@ -9,17 +9,18 @@ AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOABIS_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# This is not at all related to the release version, but a range of supported
 | 
			
		||||
# API versions. Read TODO_RELEASE in the source tree's root!
 | 
			
		||||
MGCP_CLIENT_LIBVERSION=14:1:0
 | 
			
		||||
MGCP_CLIENT_LIBVERSION=6:0:0
 | 
			
		||||
 | 
			
		||||
lib_LTLIBRARIES = \
 | 
			
		||||
	libosmo-mgcp-client.la \
 | 
			
		||||
@@ -30,16 +31,6 @@ 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) \
 | 
			
		||||
	-no-undefined \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_mgcp_client_la_LIBADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -27,13 +27,12 @@
 | 
			
		||||
#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(DLMGCP, level, "(unknown MGW endpoint) " fmt, ## args); \
 | 
			
		||||
		LOGP(DLGLOBAL, level, "(unknown MGW endpoint) " fmt, ## args); \
 | 
			
		||||
	else \
 | 
			
		||||
		LOG_MGCPC_EP(ci->ep, level, "CI[%d] %s%s%s: " fmt, \
 | 
			
		||||
			(int)(ci - ci->ep->ci), \
 | 
			
		||||
@@ -173,30 +172,6 @@ const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep)
 | 
			
		||||
	return osmo_fsm_inst_name(ep->fi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Get "local endpoint name" part of the endpoint name: (local-endpoint-name@domain-name)
 | 
			
		||||
 *
 | 
			
		||||
 * \param ep  The MGCP Endpoint
 | 
			
		||||
 * \returns the local endpoint name if found, NULL on error.
 | 
			
		||||
 */
 | 
			
		||||
const char *osmo_mgcpc_ep_local_name(const struct osmo_mgcpc_ep *ep)
 | 
			
		||||
{
 | 
			
		||||
	static char buf[1024];
 | 
			
		||||
	const char *sep;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ep);
 | 
			
		||||
	sep = strchr(ep->endpoint, '@');
 | 
			
		||||
	if (!sep) {
 | 
			
		||||
		OSMO_STRLCPY_ARRAY(buf, ep->endpoint);
 | 
			
		||||
		return buf;
 | 
			
		||||
	}
 | 
			
		||||
	if (sep - ep->endpoint >= sizeof(buf))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	memcpy(buf, ep->endpoint, sep - ep->endpoint);
 | 
			
		||||
	buf[sep - ep->endpoint] = '\0';
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *mgcp_conn_peer_name(const struct mgcp_conn_peer *info)
 | 
			
		||||
{
 | 
			
		||||
	/* I'd be fine with a smaller buffer and accept truncation, but gcc possibly refuses to build if
 | 
			
		||||
@@ -240,18 +215,11 @@ 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] = {};
 | 
			
		||||
 | 
			
		||||
static void fill_event_names(void)
 | 
			
		||||
static void fill_event_names()
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) {
 | 
			
		||||
@@ -267,7 +235,9 @@ static void fill_event_names(void)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init(void)
 | 
			
		||||
/* T_defs is used to obtain an (Osmocom specific) T2427001: timeout for an MGCP response (note, 2427 corresponds to the
 | 
			
		||||
 * default MGCP port in osmo-mgw). */
 | 
			
		||||
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init()
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0);
 | 
			
		||||
	fill_event_names();
 | 
			
		||||
@@ -498,12 +468,14 @@ 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]) {
 | 
			
		||||
@@ -513,15 +485,6 @@ 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:
 | 
			
		||||
@@ -533,46 +496,13 @@ static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data)
 | 
			
		||||
	       mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL),
 | 
			
		||||
	       ci->notify.fi ? "" : " (not sending a notification)");
 | 
			
		||||
 | 
			
		||||
	/* Below ordering is a delicate decision:
 | 
			
		||||
	 *
 | 
			
		||||
	 * We want to
 | 
			
		||||
	 * - emit the resulting event to ci->notify.fi,
 | 
			
		||||
	 * - check whether we want to tx the next pending MGCP message.
 | 
			
		||||
	 * Both these steps may terminate (=deallocate) the ep.
 | 
			
		||||
	 * So whichever one goes first may cause a use-after-free in the other.
 | 
			
		||||
	 *
 | 
			
		||||
	 * When dispatching the FSM event, we don't get an rc indicating dealloc of the FSM -- it may deallocate and we
 | 
			
		||||
	 * cannot tell. The common mechanism for that is osmo_fsm_set_dealloc_ctx(OTC_SELECT) and query the still
 | 
			
		||||
	 * allocated FSM state after termination (here we would check 'if (ci->ep != NULL)'), but we cannot assume the
 | 
			
		||||
	 * caller has actually set up an osmo_fsm_set_dealloc_ctx(). At time of writing, e.g. osmo-hnbgw does not use
 | 
			
		||||
	 * it.
 | 
			
		||||
	 *
 | 
			
		||||
	 * In osmo_mgcpc_ep_fsm_check_state_chg_after_response(), we do get an rc: false means FSM has terminated.
 | 
			
		||||
	 * On termination, the ep emits a term event to the FSM's parent.
 | 
			
		||||
	 * That may cause the notify.fi to be terminated in turn, depending on how the caller set things up.
 | 
			
		||||
	 * So: we cannot store notify.fi before, then call osmo_mgcpc_ep_fsm_check_state_chg_after_response(), and then
 | 
			
		||||
	 * emit the event, because notify.fi may have deallocated. We cannot look up whether
 | 
			
		||||
	 * osmo_mgcpc_ep_cancel_notify() has been called, because ci may have deallocated along with ci->ep.
 | 
			
		||||
	 *
 | 
			
		||||
	 * We have to skip emitting below success event in case the ep is now terminated.
 | 
			
		||||
	 * - It may be the final DLCX OK: not a problem, osmo_mgcpc_ep_ci_dlcx() has no notify args on purpose, so we do
 | 
			
		||||
	 *   make all callers not set a notify event for DLCX by design. notify.fi should always be NULL when the final
 | 
			
		||||
	 *   DLCX OK terminates the local endpoint state.
 | 
			
		||||
	 * - It may also be sudden termination due to a bad problem, in which case we shouldn't emit success.
 | 
			
		||||
	 *   The osmo_fsm_inst.parent_term_event should suffice as feedback to the caller.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	if (osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi) == false) {
 | 
			
		||||
		/* false means, the ci->ep has been terminated. */
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ci->notify.fi)
 | 
			
		||||
		osmo_fsm_inst_dispatch(ci->notify.fi, ci->notify.success, ci->notify.data);
 | 
			
		||||
 | 
			
		||||
	osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Return the MGW's 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. */
 | 
			
		||||
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
 | 
			
		||||
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	ci = osmo_mgcpc_ep_check_ci((struct osmo_mgcpc_ep_ci*)ci);
 | 
			
		||||
@@ -583,47 +513,21 @@ 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -643,7 +547,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=-2427 },
 | 
			
		||||
	[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = { .T=2427001 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Transition to a state, using the T timer defined in assignment_fsm_timeouts.
 | 
			
		||||
@@ -679,7 +583,7 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
 | 
			
		||||
	ci = osmo_mgcpc_ep_check_ci(ci);
 | 
			
		||||
 | 
			
		||||
	if (!ci) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n");
 | 
			
		||||
		LOGP(DLGLOBAL, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n");
 | 
			
		||||
		goto dispatch_error;
 | 
			
		||||
	}
 | 
			
		||||
	if (!verb_info && verb != MGCP_VERB_DLCX) {
 | 
			
		||||
@@ -718,11 +622,11 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
 | 
			
		||||
	osmo_strlcpy(cleared_ci.mgcp_ci_str, ci->mgcp_ci_str, sizeof(cleared_ci.mgcp_ci_str));
 | 
			
		||||
	*ci = cleared_ci;
 | 
			
		||||
 | 
			
		||||
	LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
 | 
			
		||||
 | 
			
		||||
	if (verb_info)
 | 
			
		||||
		ci->verb_info = *verb_info;
 | 
			
		||||
 | 
			
		||||
	LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
 | 
			
		||||
 | 
			
		||||
	if (ep->endpoint[0]) {
 | 
			
		||||
		if (ci->verb_info.endpoint[0] && strcmp(ci->verb_info.endpoint, ep->endpoint))
 | 
			
		||||
			LOG_CI(ci, LOGL_ERROR,
 | 
			
		||||
@@ -1053,34 +957,6 @@ static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
 | 
			
		||||
 | 
			
		||||
	/* 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,
 | 
			
		||||
@@ -1088,5 +964,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,
 | 
			
		||||
	.pre_term = osmo_mgcpc_ep_fsm_pre_term,
 | 
			
		||||
	/* The FSM termination will automatically trigger any mgcp_client_fsm instances to DLCX. */
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -19,14 +19,12 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/fsm.h>
 | 
			
		||||
#include <osmocom/core/byteswap.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#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
 | 
			
		||||
@@ -115,10 +113,12 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
 | 
			
		||||
		.call_id = info->call_id,
 | 
			
		||||
		.conn_mode = MGCP_CONN_RECV_ONLY,
 | 
			
		||||
		.ptime = info->ptime,
 | 
			
		||||
		.codecs_len = info->codecs_len,
 | 
			
		||||
		.ptmap_len = info->ptmap_len,
 | 
			
		||||
		.param_present = info->param_present
 | 
			
		||||
	};
 | 
			
		||||
	osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN);
 | 
			
		||||
	memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs));
 | 
			
		||||
	memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap));
 | 
			
		||||
	memcpy(&mgcp_msg->param, &info->param, sizeof(mgcp_msg->param));
 | 
			
		||||
 | 
			
		||||
@@ -135,18 +135,9 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
 | 
			
		||||
 | 
			
		||||
static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
 | 
			
		||||
{
 | 
			
		||||
	bool ip_is_set = info->addr[0] != '\0' &&
 | 
			
		||||
			 strncmp(info->addr, "::", sizeof(info->addr)) != 0 &&
 | 
			
		||||
			 strncmp(info->addr, "0.0.0.0", sizeof(info->addr)) != 0;
 | 
			
		||||
	if (ip_is_set) {
 | 
			
		||||
		mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP;
 | 
			
		||||
	mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT;
 | 
			
		||||
	mgcp_msg->audio_ip = info->addr;
 | 
			
		||||
	}
 | 
			
		||||
	if (info->port) {
 | 
			
		||||
		mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_PORT;
 | 
			
		||||
	mgcp_msg->audio_port = info->port;
 | 
			
		||||
	}
 | 
			
		||||
	if (ip_is_set && info->port)
 | 
			
		||||
	mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -157,7 +148,6 @@ static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer
 | 
			
		||||
		mgcp_msg->conn_mode = conn_mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* returns message buffer containing MGXP MDCX on success, NULL on error. */
 | 
			
		||||
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_msg mgcp_msg;
 | 
			
		||||
@@ -172,10 +162,12 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
		.audio_ip = mgcp_ctx->conn_peer_local.addr,
 | 
			
		||||
		.audio_port = mgcp_ctx->conn_peer_local.port,
 | 
			
		||||
		.ptime = mgcp_ctx->conn_peer_local.ptime,
 | 
			
		||||
		.codecs_len = mgcp_ctx->conn_peer_local.codecs_len,
 | 
			
		||||
		.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len,
 | 
			
		||||
		.param_present = mgcp_ctx->conn_peer_local.param_present
 | 
			
		||||
	};
 | 
			
		||||
	osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
 | 
			
		||||
	memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
 | 
			
		||||
	memcpy(mgcp_msg.ptmap, mgcp_ctx->conn_peer_local.ptmap, sizeof(mgcp_msg.ptmap));
 | 
			
		||||
	memcpy(&mgcp_msg.param, &mgcp_ctx->conn_peer_local.param, sizeof(mgcp_ctx->conn_peer_local.param));
 | 
			
		||||
 | 
			
		||||
@@ -193,7 +185,6 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
	return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* returns message buffer containing MGXP DLCX on success, NULL on error. */
 | 
			
		||||
struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_msg mgcp_msg;
 | 
			
		||||
@@ -229,14 +220,13 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
			 mgcp_ctx->conn_peer_local.endpoint);
 | 
			
		||||
 | 
			
		||||
		make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local);
 | 
			
		||||
		if (mgcp_ctx->conn_peer_local.port)
 | 
			
		||||
			add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
 | 
			
		||||
		set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
 | 
			
		||||
 | 
			
		||||
		msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
 | 
			
		||||
		if (!msg) {
 | 
			
		||||
			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		OSMO_ASSERT(msg);
 | 
			
		||||
 | 
			
		||||
		mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
 | 
			
		||||
		mgcp_ctx->mgw_trans_pending = true;
 | 
			
		||||
		rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi);
 | 
			
		||||
@@ -261,18 +251,6 @@ 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;
 | 
			
		||||
@@ -373,21 +351,13 @@ static void fsm_ready_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case EV_MDCX:
 | 
			
		||||
		msg = make_mdcx_msg(mgcp_ctx);
 | 
			
		||||
		if (!msg) {
 | 
			
		||||
			/* make_mdcx_msg() should already have logged the error */
 | 
			
		||||
			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		OSMO_ASSERT(msg);
 | 
			
		||||
		rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_resp_cb, fi);
 | 
			
		||||
		new_state = ST_MDCX_RESP;
 | 
			
		||||
		break;
 | 
			
		||||
	case EV_DLCX:
 | 
			
		||||
		msg = make_dlcx_msg(mgcp_ctx);
 | 
			
		||||
		if (!msg) {
 | 
			
		||||
			/* make_dlcx_msg() should already have logged the error */
 | 
			
		||||
			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		OSMO_ASSERT(msg);
 | 
			
		||||
		rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_resp_cb, fi);
 | 
			
		||||
		new_state = ST_DLCX_RESP;
 | 
			
		||||
		break;
 | 
			
		||||
@@ -550,12 +520,10 @@ 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 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);
 | 
			
		||||
	 * mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
 | 
			
		||||
	if (strlen(mgcp_ctx->conn_id)) {
 | 
			
		||||
		LOGPFSML(fi, LOGL_ERROR,
 | 
			
		||||
			 "MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n");
 | 
			
		||||
		msg = make_dlcx_msg(mgcp_ctx);
 | 
			
		||||
		if (!msg)
 | 
			
		||||
			LOGPFSML(fi, LOGL_ERROR, "MGW/DLCX: Error composing DLCX message\n");
 | 
			
		||||
@@ -625,75 +593,8 @@ 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,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Provide backwards compat for deprecated conn_peer->codecs[]: when the caller passes in an mgcp_conn_peer instance
 | 
			
		||||
 * that has codecs[] set, apply it to ptmap[] instead. */
 | 
			
		||||
static void mgcp_conn_peer_compat(struct mgcp_conn_peer *conn_peer)
 | 
			
		||||
{
 | 
			
		||||
	struct ptmap ptmap[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int ptmap_len;
 | 
			
		||||
 | 
			
		||||
	if (!conn_peer->codecs_len)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Before dropping codecs[], codecs[] would indicate the order in which the codecs should appear in SDP. ptmap[]
 | 
			
		||||
	 * would indicate payload type numbers when not using a default payload type number (may omit entries).
 | 
			
		||||
	 * Now, ptmap[] just indicates both at the same time; codecs[] should be empty, and ptmap[] lists all codecs.
 | 
			
		||||
	 * So if any codecs[] are present, recreate ptmap[] in the order of codecs[]. */
 | 
			
		||||
 | 
			
		||||
	ptmap_len = 0;
 | 
			
		||||
	for (int i = 0; i < conn_peer->codecs_len; i++) {
 | 
			
		||||
		enum mgcp_codecs codec = conn_peer->codecs[i];
 | 
			
		||||
		struct ptmap *found = NULL;
 | 
			
		||||
 | 
			
		||||
		/* Look up whether a specific pt was indicated for this codec */
 | 
			
		||||
		for (int p = 0; p < conn_peer->ptmap_len; p++) {
 | 
			
		||||
			if (conn_peer->ptmap[p].codec != codec)
 | 
			
		||||
				continue;
 | 
			
		||||
			found = &conn_peer->ptmap[p];
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		if (found) {
 | 
			
		||||
			ptmap[ptmap_len] = *found;
 | 
			
		||||
		} else {
 | 
			
		||||
			ptmap[ptmap_len] = (struct ptmap){
 | 
			
		||||
				.codec = codec,
 | 
			
		||||
				/* some enum mgcp_codecs correspond to their standard PT nr, so for compat: */
 | 
			
		||||
				.pt = codec,
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
		ptmap_len++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Are there any entries in the old ptmap that were omitted by codecs[]? */
 | 
			
		||||
	for (int p = 0; p < conn_peer->ptmap_len; p++) {
 | 
			
		||||
		bool exists = false;
 | 
			
		||||
		for (int i = 0; i < ptmap_len; i++) {
 | 
			
		||||
			if (ptmap_cmp(&ptmap[i], &conn_peer->ptmap[p]))
 | 
			
		||||
				continue;
 | 
			
		||||
			exists = true;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (exists)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (ptmap_len >= ARRAY_SIZE(ptmap))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		/* Not present yet, add it to the end */
 | 
			
		||||
		ptmap[ptmap_len] = conn_peer->ptmap[p];
 | 
			
		||||
		ptmap_len++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Use the new ptmap[], and clear out legacy codecs[]. */
 | 
			
		||||
	memcpy(conn_peer->ptmap, ptmap, sizeof(conn_peer->ptmap));
 | 
			
		||||
	conn_peer->ptmap_len = ptmap_len;
 | 
			
		||||
	conn_peer->codecs_len = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! allocate FSM, and create a new connection on the MGW.
 | 
			
		||||
 *  \param[in] mgcp MGCP client descriptor.
 | 
			
		||||
 *  \param[in] parent_fi Parent FSM instance.
 | 
			
		||||
@@ -706,17 +607,14 @@ 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 in6_addr ip_test;
 | 
			
		||||
 | 
			
		||||
	mgcp_conn_peer_compat(conn_peer);
 | 
			
		||||
	struct in_addr ip_test;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(parent_fi);
 | 
			
		||||
	OSMO_ASSERT(mgcp);
 | 
			
		||||
	OSMO_ASSERT(conn_peer);
 | 
			
		||||
 | 
			
		||||
	/* Check if IP/Port information in conn info makes sense */
 | 
			
		||||
	if (conn_peer->port && inet_pton(osmo_ip_str_type(conn_peer->addr),
 | 
			
		||||
					 conn_peer->addr, &ip_test) != 1)
 | 
			
		||||
	if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Allocate and configure a new fsm instance */
 | 
			
		||||
@@ -746,9 +644,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 in6_addr ip_test;
 | 
			
		||||
 | 
			
		||||
	mgcp_conn_peer_compat(conn_peer);
 | 
			
		||||
	struct in_addr ip_test;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(mgcp_ctx);
 | 
			
		||||
	OSMO_ASSERT(conn_peer);
 | 
			
		||||
@@ -772,8 +668,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_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);
 | 
			
		||||
	if (inet_aton(conn_peer->addr, &ip_test) == 0) {
 | 
			
		||||
		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -808,8 +704,8 @@ void mgcp_conn_delete(struct osmo_fsm_inst *fi)
 | 
			
		||||
	if (fi->proc.terminating)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Unlink FSM from parent, set the struct mgcp_client as new talloc ctx. */
 | 
			
		||||
	osmo_fsm_inst_unlink_parent(fi, mgcp_ctx->mgcp);
 | 
			
		||||
	/* Unlink FSM from parent */
 | 
			
		||||
	osmo_fsm_inst_unlink_parent(fi, NULL);
 | 
			
		||||
 | 
			
		||||
	/* An error situation where the parent FSM must be killed immediately
 | 
			
		||||
	 * may lead into a situation where the DLCX can not be executed right
 | 
			
		||||
@@ -846,7 +742,7 @@ const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info)
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init(void)
 | 
			
		||||
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init()
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,298 +0,0 @@
 | 
			
		||||
/* (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 <asm-generic/errno.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
 | 
			
		||||
#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)
 | 
			
		||||
 | 
			
		||||
/*! 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->member_list);
 | 
			
		||||
 | 
			
		||||
	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->member_list, list) {
 | 
			
		||||
 | 
			
		||||
		/* Initialize client */
 | 
			
		||||
		if (mgcp_client_pool_member_reinit_client(pool_member) == 0)
 | 
			
		||||
			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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mgcp_client_pool_empty(const struct mgcp_client_pool *pool)
 | 
			
		||||
{
 | 
			
		||||
	return llist_empty(&pool->member_list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Lookup the selected MGCP client config by its reference number */
 | 
			
		||||
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &pool->member_list, list) {
 | 
			
		||||
		if (pool_member->nr == nr)
 | 
			
		||||
			return pool_member;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Not every pool member may have a functional MGCP client, we will run through the pool once until we meet a
 | 
			
		||||
 * pool member that is suitable (is not blocked, has a client with a working link, has a low load). */
 | 
			
		||||
static struct mgcp_client_pool_member *mgcp_client_pool_pick(struct mgcp_client_pool *pool)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member_picked = NULL;
 | 
			
		||||
	unsigned int n_pool_members = 0;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &pool->member_list, list) {
 | 
			
		||||
		n_pool_members++;
 | 
			
		||||
		bool conn_up = pool_member->client && pool_member->client->conn_up;
 | 
			
		||||
		if (pool_member->blocked == false && conn_up) {
 | 
			
		||||
			if (!pool_member_picked)
 | 
			
		||||
				pool_member_picked = pool_member;
 | 
			
		||||
			else if (pool_member_picked->refcount > pool_member->refcount)
 | 
			
		||||
				pool_member_picked = pool_member;
 | 
			
		||||
		} else {
 | 
			
		||||
			LOGPPMGW(pool_member, LOGL_DEBUG, "%s -- MGW %u is unusable (blocked=%u, cli=%u, link=%u)\n",
 | 
			
		||||
				 __func__, pool_member->nr, pool_member->blocked, !!pool_member->client, conn_up);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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->member_list) && pool->mgcp_client_single) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "MGW pool is empty -- using (single) MGW %s\n",
 | 
			
		||||
		     mgcp_client_name(pool->mgcp_client_single));
 | 
			
		||||
		return pool->mgcp_client_single;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Abort when the pool is empty */
 | 
			
		||||
	if (llist_empty(&pool->member_list)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "MGW pool is empty -- no MGW available!\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Pick a suitable pool member */
 | 
			
		||||
	pool_member = mgcp_client_pool_pick(pool);
 | 
			
		||||
	if (!pool_member)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	return mgcp_client_pool_member_get(pool_member);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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;
 | 
			
		||||
 | 
			
		||||
	if (!mgcp_client)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!mgcp_client->pool_member)
 | 
			
		||||
		return;
 | 
			
		||||
	pool_member = mgcp_client->pool_member;
 | 
			
		||||
 | 
			
		||||
	if (pool_member->refcount == 0) {
 | 
			
		||||
		LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	pool_member->refcount--;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************
 | 
			
		||||
 * mgcp_client_pool_member:
 | 
			
		||||
 ***************************/
 | 
			
		||||
 | 
			
		||||
/*! Allocate an mgcp_client_pool_member.
 | 
			
		||||
 *  \param[in] pool MGCP client pool descriptor.
 | 
			
		||||
 *  \param[in] nr Reference number of the pool member.
 | 
			
		||||
 */
 | 
			
		||||
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
 | 
			
		||||
	pool_member = talloc_zero(pool, struct mgcp_client_pool_member);
 | 
			
		||||
	OSMO_ASSERT(pool_member);
 | 
			
		||||
	mgcp_client_conf_init(&pool_member->conf);
 | 
			
		||||
	pool_member->pool = pool;
 | 
			
		||||
	pool_member->nr = nr;
 | 
			
		||||
	llist_add_tail(&pool_member->list, &pool->member_list);
 | 
			
		||||
	return pool_member;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Free an mgcp_client_pool_member allocated through mgcp_client_pool_member_alloc().
 | 
			
		||||
 *  \param[in] pool_member MGCP client pool descriptor.
 | 
			
		||||
 *
 | 
			
		||||
 * It also frees the associated MGCP client if present.
 | 
			
		||||
 */
 | 
			
		||||
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member)
 | 
			
		||||
{
 | 
			
		||||
	llist_del(&pool_member->list);
 | 
			
		||||
	if (pool_member->client) {
 | 
			
		||||
		mgcp_client_disconnect(pool_member->client);
 | 
			
		||||
		talloc_free(pool_member->client);
 | 
			
		||||
	}
 | 
			
		||||
	talloc_free(pool_member);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Recreate and reconnect the MGCP client associated to the pool descriptor.
 | 
			
		||||
 *  \param[in] pool_member MGCP client pool descriptor.
 | 
			
		||||
 */
 | 
			
		||||
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member)
 | 
			
		||||
{
 | 
			
		||||
	/* Get rid of a possibly existing old MGCP client instance first */
 | 
			
		||||
	if (pool_member->client) {
 | 
			
		||||
		mgcp_client_disconnect(pool_member->client);
 | 
			
		||||
		talloc_free(pool_member->client);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Initialize client */
 | 
			
		||||
	pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
 | 
			
		||||
	if (!pool_member->client) {
 | 
			
		||||
		LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */
 | 
			
		||||
	pool_member->client->pool_member = pool_member;
 | 
			
		||||
 | 
			
		||||
	/* Connect client */
 | 
			
		||||
	if (mgcp_client_connect(pool_member->client)) {
 | 
			
		||||
		LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n",
 | 
			
		||||
				pool_member->conf.remote_addr, pool_member->conf.remote_port);
 | 
			
		||||
		talloc_free(pool_member->client);
 | 
			
		||||
		pool_member->client = NULL;
 | 
			
		||||
		return -ECONNABORTED;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get a human readable name for a given pool member. */
 | 
			
		||||
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member)
 | 
			
		||||
{
 | 
			
		||||
	const struct mgcp_client *mpcp_client;
 | 
			
		||||
	struct mgcp_client mpcp_client_dummy;
 | 
			
		||||
	static char name[512];
 | 
			
		||||
	const char *description;
 | 
			
		||||
 | 
			
		||||
	if (!pool_member)
 | 
			
		||||
		return "(null)";
 | 
			
		||||
 | 
			
		||||
	/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the
 | 
			
		||||
	 * initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */
 | 
			
		||||
	if (!pool_member->client) {
 | 
			
		||||
		memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual));
 | 
			
		||||
		mpcp_client = &mpcp_client_dummy;
 | 
			
		||||
	} else {
 | 
			
		||||
		mpcp_client = pool_member->client;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	description = mgcp_client_name(mpcp_client);
 | 
			
		||||
	snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description);
 | 
			
		||||
 | 
			
		||||
	return name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Get the MGCP client associated with the pool reference from the pool (increment reference counter).
 | 
			
		||||
 *  \param[in] pool_member MGCP client pool descriptor.
 | 
			
		||||
 *  \returns MGCP client descriptor, NULL if no member was not ready.
 | 
			
		||||
 */
 | 
			
		||||
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member)
 | 
			
		||||
{
 | 
			
		||||
	pool_member->refcount++;
 | 
			
		||||
	return pool_member->client;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Get whether the MGCP client associated with the pool reference is blocked by policy.
 | 
			
		||||
 *  \param[in] pool_member MGCP client pool descriptor.
 | 
			
		||||
 *  \returns true if blocked, false otherwise
 | 
			
		||||
 */
 | 
			
		||||
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member)
 | 
			
		||||
{
 | 
			
		||||
	return pool_member->blocked;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* MGCP client interface to quagga VTY */
 | 
			
		||||
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
 | 
			
		||||
 * (C) 2009 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 * (C) 2009-2011 by Holger Hans Peter Freyther
 | 
			
		||||
@@ -24,78 +24,27 @@
 | 
			
		||||
#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/core/timer.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_pool.h>
 | 
			
		||||
 | 
			
		||||
#define MGW_STR MGCP_CLIENT_MGW_STR
 | 
			
		||||
 | 
			
		||||
/* Only common (non-pooled) VTY commands will use this talloc context. All
 | 
			
		||||
 * pooled VTY commands will use the pool (global_mgcp_client_pool) as
 | 
			
		||||
 * talloc context. */
 | 
			
		||||
static void *global_mgcp_client_ctx = NULL;
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
 | 
			
		||||
static struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node)
 | 
			
		||||
		return vty->index;
 | 
			
		||||
 | 
			
		||||
	/* Global single MGCP config, deprecated: */
 | 
			
		||||
	vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. "
 | 
			
		||||
		"You should consider reading the User Manual and migrating to 'mgw' node.%s",
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	return global_mgcp_client_conf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct mgcp_client *get_mgcp_client(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
 | 
			
		||||
	if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node) {
 | 
			
		||||
		llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) {
 | 
			
		||||
			/* Find matching the conf pointer: */
 | 
			
		||||
			if (&pool_member->conf != conf)
 | 
			
		||||
				continue;
 | 
			
		||||
			return pool_member->client;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Global single MGCP config, deprecated: */
 | 
			
		||||
	vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. "
 | 
			
		||||
		"You should consider reading the User Manual and migrating to 'mgw' node.%s",
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	/* There's no way to obtain the struct mgcp_client in old interface, but anyway it's deprecated. */
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *global_mgcp_client_ctx = NULL;
 | 
			
		||||
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
 | 
			
		||||
      "local-ip " VTY_IPV46_CMD,
 | 
			
		||||
      "local bind to connect to MGW from\n"
 | 
			
		||||
      "local bind IPv4 address\n"
 | 
			
		||||
      "local bind IPv6 address\n")
 | 
			
		||||
      "mgw local-ip A.B.C.D",
 | 
			
		||||
      MGW_STR "local bind to connect to MGW from\n"
 | 
			
		||||
      "local bind IP address\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
 | 
			
		||||
	if (!global_mgcp_client_conf)
 | 
			
		||||
		return CMD_ERR_NOTHING_TODO;
 | 
			
		||||
	OSMO_ASSERT(global_mgcp_client_ctx);
 | 
			
		||||
	osmo_talloc_replace_string(global_mgcp_client_ctx,
 | 
			
		||||
				   (char **)&conf->local_addr,
 | 
			
		||||
				   (char**)&global_mgcp_client_conf->local_addr,
 | 
			
		||||
				   argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
@@ -103,77 +52,56 @@ ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
 | 
			
		||||
		 "mgcpgw local-ip A.B.C.D",
 | 
			
		||||
		 MGW_STR "local bind to connect to MGCP gateway with\n"
 | 
			
		||||
		 "local bind IP address\n")
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_local_ip,
 | 
			
		||||
		 cfg_mgw_mgw_local_ip_cmd,
 | 
			
		||||
		 "mgw local-ip " VTY_IPV46_CMD,
 | 
			
		||||
		 MGW_STR "local bind to connect to MGW from\n"
 | 
			
		||||
		 "local bind IPv4 address\n"
 | 
			
		||||
		 "local bind IPv6 address\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd,
 | 
			
		||||
      "local-port <0-65535>",
 | 
			
		||||
      "local port to connect to MGW from\n"
 | 
			
		||||
      "mgw local-port <0-65535>",
 | 
			
		||||
      MGW_STR "local port to connect to MGW from\n"
 | 
			
		||||
      "local bind port\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
 | 
			
		||||
	conf->local_port = atoi(argv[0]);
 | 
			
		||||
	if (!global_mgcp_client_conf)
 | 
			
		||||
		return CMD_ERR_NOTHING_TODO;
 | 
			
		||||
	global_mgcp_client_conf->local_port = atoi(argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
 | 
			
		||||
		 "mgcpgw local-port <0-65535>",
 | 
			
		||||
		 MGW_STR "local bind to connect to MGCP gateway with\n"
 | 
			
		||||
		 "local bind port\n")
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_local_port,
 | 
			
		||||
		 cfg_mgw_mgw_local_port_cmd,
 | 
			
		||||
		 "mgw local-port <0-65535>",
 | 
			
		||||
		 MGW_STR "local port to connect to MGW from\n"
 | 
			
		||||
		 "local bind port\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
 | 
			
		||||
      "remote-ip " VTY_IPV46_CMD,
 | 
			
		||||
      "remote IP address to reach the MGW at\n"
 | 
			
		||||
      "remote IPv4 address\n"
 | 
			
		||||
      "remote IPv6 address\n")
 | 
			
		||||
      "mgw remote-ip A.B.C.D",
 | 
			
		||||
      MGW_STR "remote IP address to reach the MGW at\n"
 | 
			
		||||
      "remote IP address\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
 | 
			
		||||
	if (!global_mgcp_client_conf)
 | 
			
		||||
		return CMD_ERR_NOTHING_TODO;
 | 
			
		||||
	OSMO_ASSERT(global_mgcp_client_ctx);
 | 
			
		||||
	osmo_talloc_replace_string(global_mgcp_client_ctx,
 | 
			
		||||
				   (char **)&conf->remote_addr, argv[0]);
 | 
			
		||||
				   (char**)&global_mgcp_client_conf->remote_addr,
 | 
			
		||||
				   argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
 | 
			
		||||
		 "mgcpgw remote-ip A.B.C.D",
 | 
			
		||||
		 MGW_STR "remote bind to connect to MGCP gateway with\n"
 | 
			
		||||
		 "remote bind IP address\n")
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_remote_ip,
 | 
			
		||||
		 cfg_mgw_mgw_remote_ip_cmd,
 | 
			
		||||
		 "mgw remote-ip " VTY_IPV46_CMD,
 | 
			
		||||
		 MGW_STR "remote IP address to reach the MGW at\n"
 | 
			
		||||
		 "remote IPv4 address\n"
 | 
			
		||||
		 "remote IPv6 address\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd,
 | 
			
		||||
      "remote-port <0-65535>",
 | 
			
		||||
      "remote port to reach the MGW at\n"
 | 
			
		||||
      "mgw remote-port <0-65535>",
 | 
			
		||||
      MGW_STR "remote port to reach the MGW at\n"
 | 
			
		||||
      "remote port\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
 | 
			
		||||
	conf->remote_port = atoi(argv[0]);
 | 
			
		||||
	if (!global_mgcp_client_conf)
 | 
			
		||||
		return CMD_ERR_NOTHING_TODO;
 | 
			
		||||
	global_mgcp_client_conf->remote_port = atoi(argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
 | 
			
		||||
		 "mgcpgw remote-port <0-65535>",
 | 
			
		||||
		 MGW_STR "remote bind to connect to MGCP gateway with\n"
 | 
			
		||||
		 "remote bind port\n")
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_remote_port,
 | 
			
		||||
		 cfg_mgw_mgw_remote_port_cmd,
 | 
			
		||||
		 "mgw remote-port <0-65535>",
 | 
			
		||||
		 MGW_STR "remote port to reach the MGW at\n"
 | 
			
		||||
		 "remote port\n")
 | 
			
		||||
 | 
			
		||||
DEFUN_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgw_mgw_endpoint_range_cmd,
 | 
			
		||||
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
 | 
			
		||||
      "mgw endpoint-range <1-65534> <1-65534>",
 | 
			
		||||
      MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
 | 
			
		||||
      "-\n" "-\n")
 | 
			
		||||
@@ -183,7 +111,7 @@ DEFUN_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgw_mgw_endpoint_range_cmd,
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
 | 
			
		||||
      "mgcpgw endpoint-range <1-65534> <1-65534>",
 | 
			
		||||
      MGW_STR "usable range of endpoint identifiers\n"
 | 
			
		||||
      "set first useable endpoint identifier\n"
 | 
			
		||||
@@ -211,535 +139,70 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_endpoint_domain_name,
 | 
			
		||||
      cfg_mgw_endpoint_domain_name_cmd,
 | 
			
		||||
      "endpoint-domain NAME",
 | 
			
		||||
      "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
 | 
			
		||||
      "Domain name, should be alphanumeric.\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
 | 
			
		||||
	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(conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_domain_name,
 | 
			
		||||
      cfg_mgw_mgw_endpoint_domain_name_cmd,
 | 
			
		||||
      "mgw endpoint-domain NAME",
 | 
			
		||||
      MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
 | 
			
		||||
      "Domain name, should be alphanumeric.\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_reset_ep_name,
 | 
			
		||||
      cfg_mgw_reset_ep_name_cmd,
 | 
			
		||||
      "reset-endpoint NAME",
 | 
			
		||||
      "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
 | 
			
		||||
      "e.g. 'rtpbridge/*'\n"
 | 
			
		||||
      "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct reset_ep *reset_ep;
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
 | 
			
		||||
	/* 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);
 | 
			
		||||
	if (osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0],
 | 
			
		||||
			 sizeof(global_mgcp_client_conf->endpoint_domain_name))
 | 
			
		||||
	    >= sizeof(global_mgcp_client_conf->endpoint_domain_name)) {
 | 
			
		||||
		vty_out(vty, "%% Error: 'mgw endpoint-domain' name too long, max length is %zu: '%s'%s",
 | 
			
		||||
			sizeof(global_mgcp_client_conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_reset_ep_name,
 | 
			
		||||
      cfg_mgw_mgw_reset_ep_name_cmd,
 | 
			
		||||
      "mgw reset-endpoint NAME",
 | 
			
		||||
      MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
 | 
			
		||||
      "e.g. 'rtpbridge/*'\n"
 | 
			
		||||
      "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_no_reset_ep_name,
 | 
			
		||||
      cfg_mgw_no_reset_ep_name_cmd,
 | 
			
		||||
      "no reset-endpoint NAME",
 | 
			
		||||
      NO_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
 | 
			
		||||
      "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
 | 
			
		||||
{
 | 
			
		||||
	struct reset_ep *reset_ep;
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_no_reset_ep_name,
 | 
			
		||||
      cfg_mgw_mgw_no_reset_ep_name_cmd,
 | 
			
		||||
      "no mgw reset-endpoint NAME",
 | 
			
		||||
      NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
 | 
			
		||||
      "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_mgw_keepalive_req_interval,
 | 
			
		||||
      cfg_mgw_mgw_keepalive_req_interval_cmd,
 | 
			
		||||
      "keepalive request-interval <0-4294967295>",
 | 
			
		||||
      "Monitor if the MGCP link against MGW is still usable\n"
 | 
			
		||||
      "Send an MGCP command to the MGW at given interval if no other commands are sent\n"
 | 
			
		||||
      "The interval at which send MGCP commands (s), 0 to disable\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
	struct mgcp_client *mgcp = get_mgcp_client(vty);
 | 
			
		||||
 | 
			
		||||
	conf->keepalive.req_interval_sec = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	if (!mgcp)
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
 | 
			
		||||
	/* If client already exists, apply the change immediately if possible: */
 | 
			
		||||
	mgcp->actual.keepalive.req_interval_sec = atoi(argv[0]);
 | 
			
		||||
	if (mgcp->iofd) { /* UDP MGCP socket connected */
 | 
			
		||||
		if (mgcp->actual.keepalive.req_interval_sec > 0) {
 | 
			
		||||
			/* Re-schedule: */
 | 
			
		||||
			osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0);
 | 
			
		||||
		} else {
 | 
			
		||||
			if (osmo_timer_pending(&mgcp->keepalive_tx_timer))
 | 
			
		||||
				osmo_timer_del(&mgcp->keepalive_tx_timer);
 | 
			
		||||
			/* Assume link is UP by default, so that this MGW can be selected: */
 | 
			
		||||
			mgcp->conn_up = true;
 | 
			
		||||
		}
 | 
			
		||||
	} /* else: wait until connect() to do first scheduling */
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_mgw_keepalive_req_endpoint,
 | 
			
		||||
      cfg_mgw_mgw_keepalive_req_endpoint_cmd,
 | 
			
		||||
      "keepalive request-endpoint NAME",
 | 
			
		||||
      "Monitor if the MGCP link against MGW is still usable\n"
 | 
			
		||||
      "Use a given endpoint name when sending an MGCP command to the MGW for keepalive purposes\n"
 | 
			
		||||
      "The name of the endpoint to use\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
	struct mgcp_client *mgcp = get_mgcp_client(vty);
 | 
			
		||||
 | 
			
		||||
	OSMO_STRLCPY_ARRAY(conf->keepalive.req_endpoint_name, argv[0]);
 | 
			
		||||
 | 
			
		||||
	if (!mgcp)
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
 | 
			
		||||
	/* If client already exists, apply the change immediately if possible: */
 | 
			
		||||
	OSMO_STRLCPY_ARRAY(mgcp->actual.keepalive.req_endpoint_name, argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_mgw_keepalive_timeout,
 | 
			
		||||
      cfg_mgw_mgw_keepalive_timeout_cmd,
 | 
			
		||||
      "keepalive timeout <0-4294967295>",
 | 
			
		||||
      "Monitor if the MGCP link against MGW is still usable\n"
 | 
			
		||||
      "Consider the link to the MGW to be down after time without receiving any message from it\n"
 | 
			
		||||
      "The timeout (s), 0 to disable\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
	struct mgcp_client *mgcp = get_mgcp_client(vty);
 | 
			
		||||
 | 
			
		||||
	conf->keepalive.timeout_sec = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	if (!mgcp)
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
 | 
			
		||||
	/* If client already exists, apply the change immediately if possible: */
 | 
			
		||||
	mgcp->actual.keepalive.timeout_sec = atoi(argv[0]);
 | 
			
		||||
	if (mgcp->iofd) { /* UDP MGCP socket connected */
 | 
			
		||||
		if (mgcp->actual.keepalive.timeout_sec > 0) {
 | 
			
		||||
			/* Re-schedule: */
 | 
			
		||||
			osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0);
 | 
			
		||||
		} else {
 | 
			
		||||
			if (osmo_timer_pending(&mgcp->keepalive_rx_timer))
 | 
			
		||||
				osmo_timer_del(&mgcp->keepalive_rx_timer);
 | 
			
		||||
			/* Assume link is UP by default, so that this MGW can be selected: */
 | 
			
		||||
			mgcp->conn_up = true;
 | 
			
		||||
		}
 | 
			
		||||
	} /* else: wait until connect() to do first scheduling */
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_write(struct vty *vty, const char *indent, struct mgcp_client_conf *conf)
 | 
			
		||||
int mgcp_client_config_write(struct vty *vty, const char *indent)
 | 
			
		||||
{
 | 
			
		||||
	const char *addr;
 | 
			
		||||
	int port;
 | 
			
		||||
	struct reset_ep *reset_ep;
 | 
			
		||||
 | 
			
		||||
	/* If caller doesn't the MGW pool API (mgcp_client_pool_vty_init was never called),
 | 
			
		||||
	 * then the "mgw" cmd prefix must be added since the old node always contained it.
 | 
			
		||||
	 */
 | 
			
		||||
	const char *mgw_prefix = global_mgcp_client_pool ? "" : "mgw ";
 | 
			
		||||
 | 
			
		||||
	if (conf->description) /* description never had "mgw" prefix even on old node: */
 | 
			
		||||
		vty_out(vty, "%sdescription %s%s", indent, conf->description, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	addr = conf->local_addr;
 | 
			
		||||
	addr = global_mgcp_client_conf->local_addr;
 | 
			
		||||
	if (addr)
 | 
			
		||||
		vty_out(vty, "%s%slocal-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
 | 
			
		||||
	port = conf->local_port;
 | 
			
		||||
		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, "%s%slocal-port %u%s", indent, mgw_prefix,
 | 
			
		||||
		vty_out(vty, "%smgw local-port %u%s", indent,
 | 
			
		||||
			(uint16_t)port, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	addr = conf->remote_addr;
 | 
			
		||||
	addr = global_mgcp_client_conf->remote_addr;
 | 
			
		||||
	if (addr)
 | 
			
		||||
		vty_out(vty, "%s%sremote-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
 | 
			
		||||
	port = conf->remote_port;
 | 
			
		||||
		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, "%s%sremote-port %u%s", indent, mgw_prefix,
 | 
			
		||||
		vty_out(vty, "%smgw remote-port %u%s", indent,
 | 
			
		||||
			(uint16_t)port, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (conf->endpoint_domain_name[0])
 | 
			
		||||
		vty_out(vty, "%s%sendpoint-domain %s%s", indent, mgw_prefix,
 | 
			
		||||
			conf->endpoint_domain_name, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(reset_ep, &conf->reset_epnames, list)
 | 
			
		||||
		vty_out(vty, "%s%sreset-endpoint %s%s", indent, mgw_prefix, reset_ep->name, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (conf->keepalive.req_interval_sec != 0)
 | 
			
		||||
		vty_out(vty, "%s%skeepalive request-interval %u%s", indent, mgw_prefix,
 | 
			
		||||
			conf->keepalive.req_interval_sec, VTY_NEWLINE);
 | 
			
		||||
	if (strncmp(conf->keepalive.req_endpoint_name, MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP,
 | 
			
		||||
		    sizeof(conf->keepalive.req_endpoint_name)) != 0)
 | 
			
		||||
		vty_out(vty, "%s%skeepalive request-endpoint %s%s", indent,  mgw_prefix,
 | 
			
		||||
			conf->keepalive.req_endpoint_name, VTY_NEWLINE);
 | 
			
		||||
	if (conf->keepalive.timeout_sec != 0)
 | 
			
		||||
		vty_out(vty, "%s%skeepalive timeout %u%s", indent,  mgw_prefix,
 | 
			
		||||
			conf->keepalive.timeout_sec, VTY_NEWLINE);
 | 
			
		||||
	if (global_mgcp_client_conf->endpoint_domain_name[0])
 | 
			
		||||
		vty_out(vty, "%smgw endpoint-domain %s%s", indent,
 | 
			
		||||
			global_mgcp_client_conf->endpoint_domain_name, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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)
 | 
			
		||||
{
 | 
			
		||||
	/* If caller supports MGW pool API (mgcp_client_pool_vty_init was
 | 
			
		||||
	 * called), then skip printing any config in this node and print it when
 | 
			
		||||
	 * the whole 'mgw' node is printed. */
 | 
			
		||||
	if (global_mgcp_client_pool)
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
	return config_write(vty, indent, global_mgcp_client_conf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vty_init_common(void *talloc_ctx, int node)
 | 
			
		||||
void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf)
 | 
			
		||||
{
 | 
			
		||||
	global_mgcp_client_ctx = talloc_ctx;
 | 
			
		||||
	global_mgcp_client_conf = conf;
 | 
			
		||||
 | 
			
		||||
	/* deprecated 'mgw' commands ('mgw' prepended as first arg) */
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_local_ip_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_local_port_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_remote_ip_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_remote_port_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_endpoint_range_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_endpoint_domain_name_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_reset_ep_name_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_no_reset_ep_name_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_keepalive_req_interval_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_keepalive_req_endpoint_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_keepalive_timeout_cmd);
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Mark whether user called mgcp_client_pool_config_write() and hence support new API */
 | 
			
		||||
static bool mgcp_client_pool_config_write_called = false;
 | 
			
		||||
 | 
			
		||||
static int _mgcp_client_pool_config_write(struct vty *vty, const char *indent)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_pool *pool = global_mgcp_client_pool;
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
	unsigned int subindent_buf_len;
 | 
			
		||||
	char *subindent;
 | 
			
		||||
 | 
			
		||||
	if (!indent)
 | 
			
		||||
		indent = pool->vty_indent ? : "";
 | 
			
		||||
	subindent_buf_len = strlen(indent) + 1 + 1;
 | 
			
		||||
	subindent = talloc_zero_size(vty, subindent_buf_len);
 | 
			
		||||
 | 
			
		||||
	snprintf(subindent, subindent_buf_len, "%s ", indent);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &pool->member_list, list) {
 | 
			
		||||
		vty_out(vty, "%smgw %u%s", indent, pool_member->nr, VTY_NEWLINE);
 | 
			
		||||
		config_write(vty, subindent, &pool_member->conf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* MGW pool API is supported by user (global_mgcp_client_pool is set
 | 
			
		||||
	 * because mgcp_client_pool_vty_init was called). If single MGW was
 | 
			
		||||
	 * configured through old VTY and no mgw in the new MGW pool VTY is
 | 
			
		||||
	 * replacing it, then output the single MGW converted to the new MGW
 | 
			
		||||
	 * pool VTY. */
 | 
			
		||||
	if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
 | 
			
		||||
		vty_out(vty, "%smgw 0%s", indent, VTY_NEWLINE);
 | 
			
		||||
		config_write(vty, subindent, global_mgcp_client_conf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(subindent);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Deprecated, used for backward compatibility with older users which didn't call
 | 
			
		||||
 * mgcp_client_pool_config_write(): */
 | 
			
		||||
static int config_write_pool(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	if (mgcp_client_pool_config_write_called)
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
 | 
			
		||||
	return _mgcp_client_pool_config_write(vty, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Write out MGCP client config to VTY.
 | 
			
		||||
 *  \param[in] vty VTY to which we should print.
 | 
			
		||||
 *  \param[in] indent string used for indentation (e.g. " ").
 | 
			
		||||
	       If NULL, indentation passed during mgcp_client_pool_vty_init() will be used.
 | 
			
		||||
 *  \returns CMD_SUCCESS on success, CMD_WARNING on error */
 | 
			
		||||
int mgcp_client_pool_config_write(struct vty *vty, const char *indent)
 | 
			
		||||
{
 | 
			
		||||
	/* Tell internal node write function that the user supports calling proper API: */
 | 
			
		||||
	mgcp_client_pool_config_write_called = true;
 | 
			
		||||
	return _mgcp_client_pool_config_write(vty, indent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_ATTR(cfg_mgw,
 | 
			
		||||
	   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 = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
 | 
			
		||||
	if (!pool_member) {
 | 
			
		||||
		pool_member = mgcp_client_pool_member_alloc(global_mgcp_client_pool, nr);
 | 
			
		||||
		OSMO_ASSERT(pool_member);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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 = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
 | 
			
		||||
	if (!pool_member) {
 | 
			
		||||
		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mgcp_client_pool_member_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 = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
 | 
			
		||||
	if (!pool_member) {
 | 
			
		||||
		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (mgcp_client_pool_member_reinit_client(pool_member) < 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s connect failed at (%s:%u)\n",
 | 
			
		||||
		     mgcp_client_pool_member_name(pool_member), pool_member->conf.remote_addr,
 | 
			
		||||
		     pool_member->conf.remote_port);
 | 
			
		||||
		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 = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
 | 
			
		||||
	if (!pool_member) {
 | 
			
		||||
		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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 = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
 | 
			
		||||
	if (!pool_member) {
 | 
			
		||||
		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pool_member->blocked = false;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n")
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% MGW-Pool:%s", VTY_NEWLINE);
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
 | 
			
		||||
	if (llist_empty(&global_mgcp_client_pool->member_list) && global_mgcp_client_pool->mgcp_client_single) {
 | 
			
		||||
		vty_out(vty, "%%  (pool is empty, single MGCP client will be used)%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
	} else if (llist_empty(&global_mgcp_client_pool->member_list)) {
 | 
			
		||||
		vty_out(vty, "%%  (pool is empty)%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) {
 | 
			
		||||
		const struct mgcp_client *cli = pool_member->client;
 | 
			
		||||
		vty_out(vty, "%%  MGW %s%s", mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "%%   MGCP link:     %s,%s%s",
 | 
			
		||||
			cli && cli->iofd ? "connected" : "disconnected",
 | 
			
		||||
			cli && cli->conn_up ?
 | 
			
		||||
				((cli->actual.keepalive.timeout_sec > 0) ? "UP" : "MAYBE") :
 | 
			
		||||
				"DOWN",
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "%%   service:       %s%s", pool_member->blocked ? "blocked" : "unblocked", VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "%%   ongoing calls: %u%s", pool_member->refcount, VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
	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.
 | 
			
		||||
	       If NULL, it must be passed explicitly each time mgcp_client_pool_config_write() is called.
 | 
			
		||||
 *  \param[in] pool user provided memory to store the configured MGCP client (MGW) pool. */
 | 
			
		||||
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool)
 | 
			
		||||
{
 | 
			
		||||
	/* 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_node);
 | 
			
		||||
 | 
			
		||||
	if (indent) {
 | 
			
		||||
		pool->vty_indent = talloc_strdup(pool, indent);
 | 
			
		||||
		OSMO_ASSERT(pool->vty_indent);
 | 
			
		||||
	}
 | 
			
		||||
	pool->vty_node = talloc_zero(pool, struct cmd_node);
 | 
			
		||||
	OSMO_ASSERT(pool->vty_node);
 | 
			
		||||
	pool->vty_node->node = mgw_node;
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	/* Note: config_write_pool is deprecated and user is expected to
 | 
			
		||||
	 * manually call mgcp_client_pool_config_write() when printing the VTY
 | 
			
		||||
	 * config */
 | 
			
		||||
	install_node(pool->vty_node, config_write_pool);
 | 
			
		||||
	vty_init_common(pool, mgw_node);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_local_ip_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_local_port_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_remote_ip_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_remote_port_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_rtp_bts_base_port_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_endpoint_domain_name_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_reset_ep_name_cmd);
 | 
			
		||||
	install_lib_element(mgw_node, &cfg_mgw_no_reset_ep_name_cmd);
 | 
			
		||||
 | 
			
		||||
	install_element(mgw_node, &cfg_description_cmd);
 | 
			
		||||
 | 
			
		||||
	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_show_cmd);
 | 
			
		||||
 | 
			
		||||
	global_mgcp_client_pool = pool;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,12 +10,14 @@ AM_CFLAGS = \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOABIS_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOTRAU_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
@@ -38,9 +40,6 @@ libosmo_mgcp_a_SOURCES = \
 | 
			
		||||
	mgcp_conn.c \
 | 
			
		||||
	mgcp_stat.c \
 | 
			
		||||
	mgcp_endp.c \
 | 
			
		||||
	mgcp_trunk.c \
 | 
			
		||||
	mgcp_ratectr.c \
 | 
			
		||||
	mgcp_rtp_end.c \
 | 
			
		||||
	mgcp_e1.c \
 | 
			
		||||
	mgcp_iuup.c \
 | 
			
		||||
	iuup_protocol.c \
 | 
			
		||||
	iuup_cn_node.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,10 @@
 | 
			
		||||
 *  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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										211
									
								
								src/libosmo-mgcp/iuup_cn_node.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/libosmo-mgcp/iuup_cn_node.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,211 @@
 | 
			
		||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
			
		||||
/* IuUP Core Network side protocol handling, minimal implementation */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Hofmeyr <neels@hofmeyr.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <talloc.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/netif/rtp.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/iuup_cn_node.h>
 | 
			
		||||
#include <osmocom/mgcp/iuup_protocol.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
 | 
			
		||||
#define LOG_IUUP_CN(cn, level, fmt, args...) \
 | 
			
		||||
		LOGP(DIUUP, level, "(%s) " fmt, (cn)->name, ## args)
 | 
			
		||||
 | 
			
		||||
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
 | 
			
		||||
				       const char *name_fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	struct osmo_iuup_cn *cn = talloc_zero(ctx, struct osmo_iuup_cn);
 | 
			
		||||
	OSMO_ASSERT(cn);
 | 
			
		||||
 | 
			
		||||
	cn->cfg = *cfg;
 | 
			
		||||
 | 
			
		||||
	if (!name_fmt)
 | 
			
		||||
		name_fmt = "-";
 | 
			
		||||
 | 
			
		||||
	va_start(ap, name_fmt);
 | 
			
		||||
	cn->name = talloc_vasprintf(cn, name_fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
 | 
			
		||||
	LOGP(DIUUP, LOGL_INFO, "(%s) Initializing IuUP node\n", cn->name);
 | 
			
		||||
 | 
			
		||||
	if (!osmo_identifier_valid(cn->name)) {
 | 
			
		||||
		LOGP(DIUUP, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
 | 
			
		||||
		     osmo_quote_str(cn->name, -1));
 | 
			
		||||
		talloc_free(cn);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn)
 | 
			
		||||
{
 | 
			
		||||
	talloc_free(cn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
 | 
			
		||||
		   struct osmo_iuup_hdr_data *hdr)
 | 
			
		||||
{
 | 
			
		||||
	/* Remove the IuUP bit from the middle of the buffer by writing the RTP header forward. */
 | 
			
		||||
	/* And append AMR 12.2k header "0xf03c". - AD HOC fix */
 | 
			
		||||
	unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
 | 
			
		||||
	memmove(pdu->data + sizeof(*hdr) - 2, pdu->data, pre_hdr_len);
 | 
			
		||||
	((uint8_t*)hdr)[2] = 0xf0;
 | 
			
		||||
	((uint8_t*)hdr)[3] = 0x3c;
 | 
			
		||||
	msgb_pull(pdu, sizeof(*hdr) - 2);
 | 
			
		||||
 | 
			
		||||
	LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
 | 
			
		||||
	cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
 | 
			
		||||
{
 | 
			
		||||
	/* Send Initialization Ack PDU back to the sender */
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
 | 
			
		||||
	OSMO_ASSERT(ack);
 | 
			
		||||
	ack->dst = src_pdu->dst;
 | 
			
		||||
 | 
			
		||||
	/* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
 | 
			
		||||
	memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
 | 
			
		||||
 | 
			
		||||
	osmo_iuup_make_init_ack(ack);
 | 
			
		||||
 | 
			
		||||
	LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
 | 
			
		||||
	rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
 | 
			
		||||
	msgb_free(ack);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
 | 
			
		||||
		      struct osmo_iuup_hdr_ctrl *hdr)
 | 
			
		||||
{
 | 
			
		||||
	switch (hdr->procedure) {
 | 
			
		||||
	case OSMO_IUUP_PROC_INITIALIZATION:
 | 
			
		||||
		switch (hdr->ack_nack) {
 | 
			
		||||
		case OSMO_IUUP_ACKNACK_PROCEDURE:
 | 
			
		||||
			LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
 | 
			
		||||
			cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
 | 
			
		||||
			return tx_init_ack(cn, pdu);
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
 | 
			
		||||
			     cn->name, hdr->ack_nack);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		/* Continue to log "unexpected procedure" below. */
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case OSMO_IUUP_PROC_ERROR_EVENT:
 | 
			
		||||
		{
 | 
			
		||||
			union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
 | 
			
		||||
			LOGP(DIUUP, LOGL_ERROR,
 | 
			
		||||
			     "(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
 | 
			
		||||
			     cn->name, p->error_event.error_distance, p->error_event.error_cause,
 | 
			
		||||
			     osmo_iuup_error_cause_name(p->error_event.error_cause));
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	LOG_IUUP_CN(cn, LOGL_ERROR,
 | 
			
		||||
		    "Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
 | 
			
		||||
		    hdr->procedure, hdr->ack_nack);
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
 | 
			
		||||
 * freed by the caller. */
 | 
			
		||||
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_iuup_hdr_ctrl *is_ctrl;
 | 
			
		||||
	struct osmo_iuup_hdr_data *is_data;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	rc = osmo_iuup_classify(true, cn->name, pdu, &is_ctrl, &is_data);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	if (is_ctrl)
 | 
			
		||||
		return rx_control(cn, pdu, is_ctrl);
 | 
			
		||||
	if (is_data)
 | 
			
		||||
		return rx_data(cn, pdu, is_data);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t frame_nr = cn->next_frame_nr;
 | 
			
		||||
	cn->next_frame_nr = (frame_nr + 1) & 0x0f;
 | 
			
		||||
	return frame_nr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
 | 
			
		||||
 * message to the IuUP peer.
 | 
			
		||||
 * Returns 0 on success, negative on error. */
 | 
			
		||||
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
 | 
			
		||||
{
 | 
			
		||||
	struct rtp_hdr *rtp_was, *rtp;
 | 
			
		||||
	struct osmo_iuup_hdr_data *iuup_hdr;
 | 
			
		||||
 | 
			
		||||
	/* Splice an IuUP header in between RTP header and payload data */
 | 
			
		||||
	rtp_was = (void*)pdu->data;
 | 
			
		||||
 | 
			
		||||
	/* copy the RTP header part backwards by the size needed for the IuUP header */
 | 
			
		||||
	/* also strips 2 bytes from the front of RTP payload - AMR header - AD HOC fix */
 | 
			
		||||
	rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr) - 2);
 | 
			
		||||
	memmove(rtp, rtp_was, sizeof(*rtp));
 | 
			
		||||
 | 
			
		||||
	/* The IuUP side negotiated a payload type number to use during Initialization. The RTP packet going to the IuUP
 | 
			
		||||
	 * peer should reflect this payload_type. This should already have happened in mgcp_patch_pt(), but can't hurt
 | 
			
		||||
	 * to patch over it again. */
 | 
			
		||||
	rtp->payload_type = cn->rtp_payload_type;
 | 
			
		||||
 | 
			
		||||
	iuup_hdr = (void*)rtp->data;
 | 
			
		||||
 | 
			
		||||
	*iuup_hdr = (struct osmo_iuup_hdr_data){
 | 
			
		||||
		.pdu_type = OSMO_IUUP_PDU_DATA_WITH_CRC,
 | 
			
		||||
		.frame_nr = next_frame_nr(cn),
 | 
			
		||||
		.frame_good = OSMO_IUUP_FRAME_GOOD,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
 | 
			
		||||
	LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
 | 
			
		||||
	     cn->name, iuup_hdr->frame_nr);
 | 
			
		||||
 | 
			
		||||
	return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										286
									
								
								src/libosmo-mgcp/iuup_protocol.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								src/libosmo-mgcp/iuup_protocol.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,286 @@
 | 
			
		||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
			
		||||
/* IuUP Core Network side protocol, minimal implementation */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Hofmeyr <neels@hofmeyr.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <osmocom/mgcp/iuup_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
#include <osmocom/netif/rtp.h>
 | 
			
		||||
 | 
			
		||||
/* Calculating two bytes of CRC is ok to do by a loop */
 | 
			
		||||
static uint8_t header_crc6(const uint8_t *hdr)
 | 
			
		||||
{
 | 
			
		||||
    int bit;
 | 
			
		||||
    /* Polynomial: D^6 + D^5 + D^3 + D^2 + D^1 + 1
 | 
			
		||||
     * that's 1101111 or 0x6f;
 | 
			
		||||
     * align its lowest bit with a uint16_t's highest bit: */
 | 
			
		||||
    uint32_t polynomial = 0x6f << 15; // 00110111 10000000 00000000
 | 
			
		||||
    uint32_t remainder = ( ((uint32_t)hdr[0]) << 8 | hdr[1] ) << 6;
 | 
			
		||||
 | 
			
		||||
    for (bit = 15; bit >= 0; bit--)
 | 
			
		||||
    {
 | 
			
		||||
        if (remainder & (0x40 << bit))
 | 
			
		||||
            remainder ^= polynomial;
 | 
			
		||||
        polynomial >>= 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return remainder;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Charles Michael Heard's CRC-10 code, from
 | 
			
		||||
 *
 | 
			
		||||
 *      http://web.archive.org/web/20061005231950/http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/crc10.html
 | 
			
		||||
 *
 | 
			
		||||
 * with the CRC table initialized with values computed by
 | 
			
		||||
 * his "gen_byte_crc10_table()" routine, rather than by calling that
 | 
			
		||||
 * routine at run time, and with various data type cleanups.
 | 
			
		||||
 */
 | 
			
		||||
static const uint16_t byte_crc10_table[256] = {
 | 
			
		||||
	0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
 | 
			
		||||
	0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
 | 
			
		||||
	0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
 | 
			
		||||
	0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
 | 
			
		||||
	0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
 | 
			
		||||
	0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
 | 
			
		||||
	0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
 | 
			
		||||
	0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
 | 
			
		||||
	0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
 | 
			
		||||
	0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
 | 
			
		||||
	0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
 | 
			
		||||
	0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
 | 
			
		||||
	0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
 | 
			
		||||
	0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
 | 
			
		||||
	0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
 | 
			
		||||
	0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
 | 
			
		||||
	0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
 | 
			
		||||
	0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
 | 
			
		||||
	0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
 | 
			
		||||
	0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
 | 
			
		||||
	0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
 | 
			
		||||
	0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
 | 
			
		||||
	0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
 | 
			
		||||
	0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
 | 
			
		||||
	0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
 | 
			
		||||
	0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
 | 
			
		||||
	0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
 | 
			
		||||
	0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
 | 
			
		||||
	0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
 | 
			
		||||
	0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
 | 
			
		||||
	0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
 | 
			
		||||
	0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static uint16_t crc10(uint16_t crc10_accum, const uint8_t *payload, unsigned int payload_len)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < payload_len; i++) {
 | 
			
		||||
		crc10_accum = ((crc10_accum << 8) & 0x300)
 | 
			
		||||
			^ byte_crc10_table[(crc10_accum >> 2) & 0xff]
 | 
			
		||||
			^ payload[i];
 | 
			
		||||
	}
 | 
			
		||||
	return crc10_accum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* When a payload of a multiple of bytes has run through, we need to still feed 10 bits of zeros into the
 | 
			
		||||
 * CRC10 to get the payload's checksum result that we can send to a peer. That can't be done with above
 | 
			
		||||
 * table, because it acts as if full 16 bits are fed. This stops after 10 bits. */
 | 
			
		||||
static uint16_t crc10_remainder(uint16_t crc10_accum)
 | 
			
		||||
{
 | 
			
		||||
    int bit;
 | 
			
		||||
    /* Polynomial: D^10 + D^9 + D^5 + D^4 + D^1 + 1
 | 
			
		||||
     * that's 11000110011 or 0x633;
 | 
			
		||||
     * align its lowest bit with a 10bit value's highest bit: */
 | 
			
		||||
    uint32_t polynomial = 0x633 << 9; // 1100 01100110 00000000
 | 
			
		||||
    uint32_t remainder = ((uint32_t)crc10_accum) << 10;
 | 
			
		||||
 | 
			
		||||
    /* Run on 10 bits */
 | 
			
		||||
    for (bit = 9; bit >= 0; bit--)
 | 
			
		||||
    {
 | 
			
		||||
        if (remainder & ((1 << 10) << bit))
 | 
			
		||||
            remainder ^= polynomial;
 | 
			
		||||
        polynomial >>= 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return remainder & 0x3ff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint16_t payload_crc10(const uint8_t *payload, unsigned int payload_len)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t crc10_accum = crc10(0, payload, payload_len);
 | 
			
		||||
	return crc10_remainder(crc10_accum);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Given an IuUP PDU data block, write the correct header and payload CRC checksums at the right places.
 | 
			
		||||
 */
 | 
			
		||||
void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len)
 | 
			
		||||
{
 | 
			
		||||
	/* For both data and ctrl, the checksums and payload are at the same offset */
 | 
			
		||||
	struct osmo_iuup_hdr_data *hdr = (void*)iuup_header_and_payload;
 | 
			
		||||
	uint16_t crc;
 | 
			
		||||
	unsigned int payload_len;
 | 
			
		||||
 | 
			
		||||
	hdr->header_crc = header_crc6(iuup_header_and_payload);
 | 
			
		||||
 | 
			
		||||
	payload_len = iuup_header_and_payload + header_and_payload_len - hdr->payload;
 | 
			
		||||
	crc = payload_crc10(hdr->payload, payload_len);
 | 
			
		||||
	hdr->payload_crc_hi = (crc >> 8) & 0x3;
 | 
			
		||||
	hdr->payload_crc_lo = crc & 0xff;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Validate minimum message sizes, IuUP PDU type, header- and payload checksums. If it is a Control
 | 
			
		||||
 * Procedure PDU, return the header position in is_ctrl, if it is a Data PDU, return the header position
 | 
			
		||||
 * in is_data. If log_errors is true, log on DIUUP with the given log label for context. Return NULL in
 | 
			
		||||
 * both is_ctrl and is_data, and return a negative error code if the PDU could not be identified as a
 | 
			
		||||
 * valid RTP PDU containing an IuUP part. */
 | 
			
		||||
int osmo_iuup_classify(bool log_errors,
 | 
			
		||||
		       const char *log_label,
 | 
			
		||||
		       struct msgb *pdu,
 | 
			
		||||
		       struct osmo_iuup_hdr_ctrl **is_ctrl,
 | 
			
		||||
		       struct osmo_iuup_hdr_data **is_data)
 | 
			
		||||
{
 | 
			
		||||
	struct rtp_hdr *rtp = (void*)pdu->data;
 | 
			
		||||
	struct osmo_iuup_hdr_ctrl *hdr = (void*)rtp->data;
 | 
			
		||||
	unsigned int payload_len;
 | 
			
		||||
	uint16_t crc_calculated;
 | 
			
		||||
	uint16_t crc_from_peer;
 | 
			
		||||
 | 
			
		||||
#define ERR(fmt, args...) do { \
 | 
			
		||||
			if (log_errors) \
 | 
			
		||||
				LOGP(DIUUP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
 | 
			
		||||
			return -EINVAL; \
 | 
			
		||||
		} while (0)
 | 
			
		||||
 | 
			
		||||
	if (is_ctrl)
 | 
			
		||||
		*is_ctrl = NULL;
 | 
			
		||||
	if (is_data)
 | 
			
		||||
		*is_data = NULL;
 | 
			
		||||
 | 
			
		||||
	/* We need at least a header of 4 bytes. The osmo_iuup_hdr_ctrl already includes a byte of
 | 
			
		||||
	 * payload, so use osmo_iuup_hdr_data to check the minimum here. */
 | 
			
		||||
	if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_data)))
 | 
			
		||||
		ERR("IuUP PDU too short: %u\n", pdu->len);
 | 
			
		||||
 | 
			
		||||
	/* Let's not validate checksums if the header type isn't sane */
 | 
			
		||||
	switch (hdr->pdu_type) {
 | 
			
		||||
	case OSMO_IUUP_PDU_DATA_WITH_CRC:
 | 
			
		||||
		/* If the caller isn't interested in data PDUs, cut short here. */
 | 
			
		||||
		if (!is_data)
 | 
			
		||||
			return 0;
 | 
			
		||||
		break;
 | 
			
		||||
	case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
 | 
			
		||||
		/* If the caller isn't interested in control PDUs, cut short here. */
 | 
			
		||||
		if (!is_ctrl)
 | 
			
		||||
			return 0;
 | 
			
		||||
		if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_ctrl)))
 | 
			
		||||
			ERR("IuUP control PDU too short: %u\n", pdu->len);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* For both data and ctrl, the checksums and payload are at the same offset */
 | 
			
		||||
 | 
			
		||||
	crc_calculated = header_crc6((uint8_t*)hdr);
 | 
			
		||||
	if (crc_calculated != hdr->header_crc)
 | 
			
		||||
		ERR("IuUP PDU with invalid header CRC (peer sent 0x%x, calculated 0x%x)\n",
 | 
			
		||||
		    hdr->header_crc, crc_calculated);
 | 
			
		||||
 | 
			
		||||
	payload_len = pdu->tail - hdr->payload;
 | 
			
		||||
	crc_calculated = payload_crc10(hdr->payload, payload_len);
 | 
			
		||||
	crc_from_peer = (((uint16_t)hdr->payload_crc_hi) << 8) | hdr->payload_crc_lo;
 | 
			
		||||
	if (crc_from_peer != crc_calculated)
 | 
			
		||||
		ERR("IuUP PDU with invalid payload CRC (peer sent 0x%x, calculated 0x%x)\n",
 | 
			
		||||
		    crc_from_peer, crc_calculated);
 | 
			
		||||
 | 
			
		||||
	switch (hdr->pdu_type) {
 | 
			
		||||
	case OSMO_IUUP_PDU_DATA_WITH_CRC:
 | 
			
		||||
		if (is_data)
 | 
			
		||||
			*is_data = (void*)hdr;
 | 
			
		||||
		return 0;
 | 
			
		||||
	case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
 | 
			
		||||
		if (is_ctrl)
 | 
			
		||||
			*is_ctrl = hdr;
 | 
			
		||||
		return 0;
 | 
			
		||||
	default:
 | 
			
		||||
		ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
 | 
			
		||||
	}
 | 
			
		||||
#undef ERR
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return true if this RTP packet contains an IuUP Initialization header (detect IuUP peer). */
 | 
			
		||||
bool osmo_iuup_is_init(struct msgb *pdu)
 | 
			
		||||
{
 | 
			
		||||
 	struct osmo_iuup_hdr_ctrl *is_ctrl;
 | 
			
		||||
	osmo_iuup_classify(false, NULL, pdu, &is_ctrl, NULL);
 | 
			
		||||
	return is_ctrl
 | 
			
		||||
		&& is_ctrl->procedure == OSMO_IUUP_PROC_INITIALIZATION
 | 
			
		||||
		&& is_ctrl->ack_nack == OSMO_IUUP_ACKNACK_PROCEDURE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Append an IuUP Initialization ACK message */
 | 
			
		||||
void osmo_iuup_make_init_ack(struct msgb *ack)
 | 
			
		||||
{
 | 
			
		||||
	/* Send Initialization Ack PDU back to the sender */
 | 
			
		||||
 	struct osmo_iuup_hdr_ctrl *hdr;
 | 
			
		||||
	OSMO_ASSERT(ack);
 | 
			
		||||
 | 
			
		||||
	hdr = (void*)msgb_put(ack, sizeof(*hdr));
 | 
			
		||||
 | 
			
		||||
	*hdr = (struct osmo_iuup_hdr_ctrl){
 | 
			
		||||
		.pdu_type = OSMO_IUUP_PDU_CONTROL_PROCEDURE,
 | 
			
		||||
		.ack_nack = OSMO_IUUP_ACKNACK_ACK,
 | 
			
		||||
		.procedure = OSMO_IUUP_PROC_INITIALIZATION,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	osmo_iuup_set_checksums((uint8_t*)hdr, sizeof(*hdr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct value_string osmo_iuup_error_cause_names[] = {
 | 
			
		||||
	{ 0, "CRC error of frame header" },
 | 
			
		||||
	{ 1, "CRC error of frame payload" },
 | 
			
		||||
	{ 2, "Unexpected frame number" },
 | 
			
		||||
	{ 3, "Frame loss" },
 | 
			
		||||
	{ 4, "PDU type unknown" },
 | 
			
		||||
	{ 5, "Unknown procedure" },
 | 
			
		||||
	{ 6, "Unknown reserved value" },
 | 
			
		||||
	{ 7, "Unknown field" },
 | 
			
		||||
	{ 8, "Frame too short" },
 | 
			
		||||
	{ 9, "Missing fields" },
 | 
			
		||||
	{ 16, "Unexpected PDU type" },
 | 
			
		||||
	{ 17, "spare" },
 | 
			
		||||
	{ 18, "Unexpected procedure" },
 | 
			
		||||
	{ 19, "Unexpected RFCI" },
 | 
			
		||||
	{ 20, "Unexpected value" },
 | 
			
		||||
	{ 42, "Initialisation failure" },
 | 
			
		||||
	{ 43, "Initialisation failure (network error, timer expiry)" },
 | 
			
		||||
	{ 44, "Initialisation failure (Iu UP function error, repeated NACK)" },
 | 
			
		||||
	{ 45, "Rate control failure" },
 | 
			
		||||
	{ 46, "Error event failure" },
 | 
			
		||||
	{ 47, "Time Alignment not supported" },
 | 
			
		||||
	{ 48, "Requested Time Alignment not possible" },
 | 
			
		||||
	{ 49, "Iu UP Mode version not supported" },
 | 
			
		||||
	{}
 | 
			
		||||
};
 | 
			
		||||
@@ -17,18 +17,13 @@
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#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_internal.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
 | 
			
		||||
 * string, used by dump_codec_summary() */
 | 
			
		||||
static char *mgcp_codec_dump(struct mgcp_rtp_codec *codec)
 | 
			
		||||
static char *dump_codec(struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	static char str[256];
 | 
			
		||||
	char *pt_str;
 | 
			
		||||
@@ -49,25 +44,31 @@ static char *mgcp_codec_dump(struct mgcp_rtp_codec *codec)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Dump a summary of all negotiated codecs to debug log
 | 
			
		||||
 *  \param[in] cset related codecset.
 | 
			
		||||
 *  \param[in] prefix_str Prefix string to print during logging.
 | 
			
		||||
 * */
 | 
			
		||||
void mgcp_codecset_summary(struct mgcp_rtp_codecset *cset, const char *prefix_str)
 | 
			
		||||
 *  \param[in] conn related rtp-connection. */
 | 
			
		||||
void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_end *rtp;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	struct mgcp_rtp_codec *codec;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
 | 
			
		||||
	if (cset->codecs_assigned == 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "%s no codecs available\n", prefix_str);
 | 
			
		||||
	rtp = &conn->end;
 | 
			
		||||
	endp = conn->conn->endp;
 | 
			
		||||
 | 
			
		||||
	if (rtp->codecs_assigned == 0) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR, "conn:%s no codecs available\n",
 | 
			
		||||
			 mgcp_conn_dump(conn->conn));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Store parsed codec information */
 | 
			
		||||
	for (i = 0; i < cset->codecs_assigned; i++) {
 | 
			
		||||
		struct mgcp_rtp_codec *codec = &cset->codecs[i];
 | 
			
		||||
	for (i = 0; i < rtp->codecs_assigned; i++) {
 | 
			
		||||
		codec = &rtp->codecs[i];
 | 
			
		||||
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "%s codecs[%u]:%s", prefix_str, i, mgcp_codec_dump(codec));
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s codecs[%u]:%s",
 | 
			
		||||
			 mgcp_conn_dump(conn->conn), i, dump_codec(codec));
 | 
			
		||||
 | 
			
		||||
		if (codec == cset->codec)
 | 
			
		||||
		if (codec == rtp->codec)
 | 
			
		||||
			LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
 | 
			
		||||
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_DEBUG, "\n");
 | 
			
		||||
@@ -75,7 +76,7 @@ void mgcp_codecset_summary(struct mgcp_rtp_codecset *cset, const char *prefix_st
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initalize or reset codec information with default data. */
 | 
			
		||||
static void mgcp_codec_init(struct mgcp_rtp_codec *codec)
 | 
			
		||||
static void codec_init(struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	*codec = (struct mgcp_rtp_codec){
 | 
			
		||||
		.payload_type = -1,
 | 
			
		||||
@@ -83,25 +84,27 @@ static void mgcp_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 mgcp_codec_free(struct mgcp_rtp_codec *codec)
 | 
			
		||||
static void codec_free(struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	if (codec->subtype_name)
 | 
			
		||||
		talloc_free(codec->subtype_name);
 | 
			
		||||
	if (codec->audio_name)
 | 
			
		||||
		talloc_free(codec->audio_name);
 | 
			
		||||
	*codec = (struct mgcp_rtp_codec){};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Initalize or reset codec information with default data.
 | 
			
		||||
 *  \param[out] conn related rtp-connection. */
 | 
			
		||||
void mgcp_codecset_reset(struct mgcp_rtp_codecset *cset)
 | 
			
		||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; i < cset->codecs_assigned; i++)
 | 
			
		||||
		mgcp_codec_free(&cset->codecs[i]);
 | 
			
		||||
	cset->codecs_assigned = 0;
 | 
			
		||||
	cset->codec = NULL;
 | 
			
		||||
	for (i = 0; i < conn->end.codecs_assigned; i++)
 | 
			
		||||
		codec_free(&conn->end.codecs[i]);
 | 
			
		||||
	conn->end.codecs_assigned = 0;
 | 
			
		||||
	conn->end.codec = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Add codec configuration depending on payload type and/or codec name. This
 | 
			
		||||
@@ -112,23 +115,25 @@ void mgcp_codecset_reset(struct mgcp_rtp_codecset *cset)
 | 
			
		||||
 *  \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
 | 
			
		||||
 *  \param[in] param optional codec parameters (set to NULL when unused).
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on failure. */
 | 
			
		||||
int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
 | 
			
		||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
 | 
			
		||||
{
 | 
			
		||||
	int rate;
 | 
			
		||||
	int channels;
 | 
			
		||||
	char audio_codec[64];
 | 
			
		||||
	struct mgcp_rtp_codec *codec;
 | 
			
		||||
	unsigned int pt_offset = cset->codecs_assigned;
 | 
			
		||||
	unsigned int pt_offset = conn->end.codecs_assigned;
 | 
			
		||||
	void *ctx = conn->conn;
 | 
			
		||||
 | 
			
		||||
	/* The amount of codecs we can store is limited, make sure we do not
 | 
			
		||||
	 * overrun this limit. */
 | 
			
		||||
	if (cset->codecs_assigned >= MGCP_MAX_CODECS)
 | 
			
		||||
	if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* First unused entry */
 | 
			
		||||
	codec = &cset->codecs[cset->codecs_assigned];
 | 
			
		||||
	codec = &conn->end.codecs[conn->end.codecs_assigned];
 | 
			
		||||
 | 
			
		||||
	/* Initalize the codec struct with some default data to begin with */
 | 
			
		||||
	mgcp_codec_init(codec);
 | 
			
		||||
	codec_init(codec);
 | 
			
		||||
 | 
			
		||||
	if (payload_type != PTYPE_UNDEFINED) {
 | 
			
		||||
		/* Make sure we do not get any reserved or undefined type numbers */
 | 
			
		||||
@@ -150,16 +155,16 @@ int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type, co
 | 
			
		||||
	if (!audio_name) {
 | 
			
		||||
		switch (payload_type) {
 | 
			
		||||
		case 0:
 | 
			
		||||
			strcpy(codec->audio_name, "PCMU/8000/1");
 | 
			
		||||
			audio_name = talloc_strdup(ctx, "PCMU/8000/1");
 | 
			
		||||
			break;
 | 
			
		||||
		case 3:
 | 
			
		||||
			strcpy(codec->audio_name, "GSM/8000/1");
 | 
			
		||||
			audio_name = talloc_strdup(ctx, "GSM/8000/1");
 | 
			
		||||
			break;
 | 
			
		||||
		case 8:
 | 
			
		||||
			strcpy(codec->audio_name, "PCMA/8000/1");
 | 
			
		||||
			audio_name = talloc_strdup(ctx, "PCMA/8000/1");
 | 
			
		||||
			break;
 | 
			
		||||
		case 18:
 | 
			
		||||
			strcpy(codec->audio_name, "G729/8000/1");
 | 
			
		||||
			audio_name = talloc_strdup(ctx, "G729/8000/1");
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* The given payload type is not known to us, or it
 | 
			
		||||
@@ -169,36 +174,36 @@ int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type, co
 | 
			
		||||
			     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(codec->audio_name) >= sizeof(codec->subtype_name)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(codec->audio_name, -1));
 | 
			
		||||
	if (strlen(audio_name) >= sizeof(audio_codec)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(audio_name, -1));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
	channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
 | 
			
		||||
	rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
 | 
			
		||||
	if (sscanf(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));
 | 
			
		||||
	if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(audio_name, -1));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Note: We only accept configurations with one audio channel! */
 | 
			
		||||
	if (channels != 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot handle audio codec with more than one channel: %s\n",
 | 
			
		||||
		     osmo_quote_str(codec->audio_name, -1));
 | 
			
		||||
		     osmo_quote_str(audio_name, -1));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	codec->rate = rate;
 | 
			
		||||
	codec->channels = channels;
 | 
			
		||||
	codec->subtype_name = talloc_strdup(ctx, audio_codec);
 | 
			
		||||
	codec->audio_name = talloc_strdup(ctx, audio_name);
 | 
			
		||||
	codec->payload_type = payload_type;
 | 
			
		||||
 | 
			
		||||
	if (!strcmp(codec->subtype_name, "G729")) {
 | 
			
		||||
	if (!strcmp(audio_codec, "G729")) {
 | 
			
		||||
		codec->frame_duration_num = 10;
 | 
			
		||||
		codec->frame_duration_den = 1000;
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -262,11 +267,100 @@ int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type, co
 | 
			
		||||
	} else
 | 
			
		||||
		codec->param_present = false;
 | 
			
		||||
 | 
			
		||||
	cset->codecs_assigned++;
 | 
			
		||||
	conn->end.codecs_assigned++;
 | 
			
		||||
	return 0;
 | 
			
		||||
error:
 | 
			
		||||
	/* Make sure we leave a clean codec entry on error. */
 | 
			
		||||
	mgcp_codec_free(codec);
 | 
			
		||||
	codec_free(codec);
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the given codec is applicable on the specified endpoint
 | 
			
		||||
 * Helper function for mgcp_codec_decide() */
 | 
			
		||||
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	char codec_name[64];
 | 
			
		||||
 | 
			
		||||
	/* A codec name must be set, if not, this might mean that the codec
 | 
			
		||||
	 * (payload type) that was assigned is unknown to us so we must stop
 | 
			
		||||
	 * here. */
 | 
			
		||||
	if (!codec->subtype_name)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* We now extract the codec_name (letters before the /, e.g. "GSM"
 | 
			
		||||
	 * from the audio name that is stored in the trunk configuration.
 | 
			
		||||
	 * We do not compare to the full audio_name because we expect that
 | 
			
		||||
	 * "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
 | 
			
		||||
	 * audio name of the codec is set to "GSM" */
 | 
			
		||||
	if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* Finally we check if the subtype_name we have generated from the
 | 
			
		||||
	 * audio_name in the trunc struct patches the codec_name of the
 | 
			
		||||
	 * given codec */
 | 
			
		||||
	if (strcasecmp(codec_name, codec->subtype_name) == 0)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: It is questinable that the method to pick a compatible
 | 
			
		||||
	 * codec can work properly. Since this useses tcfg->audio_name, as
 | 
			
		||||
	 * a reference, which is set to "AMR/8000" permanently.
 | 
			
		||||
	 * tcfg->audio_name must be updated by the first connection that
 | 
			
		||||
	 * has been made on an endpoint, so that the second connection
 | 
			
		||||
	 * can make a meaningful decision here */
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Decide for one suitable codec
 | 
			
		||||
 *  \param[in] conn related rtp-connection.
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on failure. */
 | 
			
		||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_end *rtp;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	bool codec_assigned = false;
 | 
			
		||||
 | 
			
		||||
	endp = conn->conn->endp;
 | 
			
		||||
	rtp = &conn->end;
 | 
			
		||||
 | 
			
		||||
	/* This function works on the results the SDP/LCO parser has extracted
 | 
			
		||||
	 * from the MGCP message. The goal is to select a suitable codec for
 | 
			
		||||
	 * the given connection. When transcoding is available, the first codec
 | 
			
		||||
	 * from the codec list is taken without further checking. When
 | 
			
		||||
	 * transcoding is not available, then the choice must be made more
 | 
			
		||||
	 * carefully. Each codec in the list is checked until one is found that
 | 
			
		||||
	 * is rated compatible. The rating is done by the helper function
 | 
			
		||||
	 * is_codec_compatible(), which does the actual checking. */
 | 
			
		||||
	for (i = 0; i < rtp->codecs_assigned; i++) {
 | 
			
		||||
		/* When no transcoding is available, avoid codecs that would
 | 
			
		||||
		 * require transcoding. */
 | 
			
		||||
		if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
 | 
			
		||||
			     rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rtp->codec = &rtp->codecs[i];
 | 
			
		||||
		codec_assigned = true;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* FIXME: To the reviewes: This is problematic. I do not get why we
 | 
			
		||||
	 * need to reset the packet_duration_ms depending on the codec
 | 
			
		||||
	 * selection. I thought it were all 20ms? Is this to address some
 | 
			
		||||
	 * cornercase. (This piece of code was in the code path before,
 | 
			
		||||
	 * together with the note: "TODO/XXX: Store this per codec and derive
 | 
			
		||||
	 * it on use" */
 | 
			
		||||
	if (codec_assigned) {
 | 
			
		||||
		if (rtp->maximum_packet_time >= 0
 | 
			
		||||
		    && rtp->maximum_packet_time * rtp->codec->frame_duration_den >
 | 
			
		||||
		    rtp->codec->frame_duration_num * 1500)
 | 
			
		||||
			rtp->packet_duration_ms = 0;
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -280,7 +374,7 @@ error:
 | 
			
		||||
 *
 | 
			
		||||
 * https://tools.ietf.org/html/rfc4867
 | 
			
		||||
 */
 | 
			
		||||
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	if (!codec->param_present)
 | 
			
		||||
		return false;
 | 
			
		||||
@@ -289,15 +383,10 @@ bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
	return codec->param.amr_octet_aligned;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Compare two codecs, all parameters must match up */
 | 
			
		||||
static bool codecs_same(const struct mgcp_rtp_codec *codec_a, const struct mgcp_rtp_codec *codec_b)
 | 
			
		||||
/* Compare two codecs, all parameters must match up, except for the payload type
 | 
			
		||||
 * number. */
 | 
			
		||||
static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
 | 
			
		||||
{
 | 
			
		||||
	/* All codec properties must match up, except the payload type number. Even though standardisd payload numbers
 | 
			
		||||
	 * exist for certain situations, the call agent may still assign them freely. Hence we must not insist on equal
 | 
			
		||||
	 * payload type numbers. Also the audio_name is not checked since it is already parsed into subtype_name, rate,
 | 
			
		||||
	 * and channels, which are checked. */
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->rate != codec_b->rate)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->channels != codec_b->channels)
 | 
			
		||||
@@ -306,210 +395,75 @@ static bool codecs_same(const struct mgcp_rtp_codec *codec_a, const struct mgcp_
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->frame_duration_den != codec_b->frame_duration_den)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* AMR payload may be formatted in two different payload formats, it is still the same codec but since the
 | 
			
		||||
	 * formatting of the payload is different, conversation is required, so we must treat it as a different
 | 
			
		||||
	 * codec here. */
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, "AMR") == 0) {
 | 
			
		||||
		if (mgcp_codec_amr_is_octet_aligned(codec_a) != mgcp_codec_amr_is_octet_aligned(codec_b))
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Compare two codecs, all parameters must match up, except parameters related to payload formatting (not checked). */
 | 
			
		||||
static bool codecs_convertible(const struct mgcp_rtp_codec *codec_a, const struct mgcp_rtp_codec *codec_b)
 | 
			
		||||
{
 | 
			
		||||
	/* OsmoMGW currently has no ability to transcode from one codec to another. However OsmoMGW is still able to
 | 
			
		||||
	 * translate between different payload formats as long as the encoded voice data itself does not change.
 | 
			
		||||
	 * Therefore we must insist on equal codecs but still allow different payload formatting. */
 | 
			
		||||
 | 
			
		||||
	/* In 3G IuUP, AMR may be encapsulated in IuFP, this means even though the codec name and negotiated rate is
 | 
			
		||||
	 * different, the formatting can still be converted by OsmoMGW. Therefore we won't insist on equal
 | 
			
		||||
	 * subtype_name and rate if we detect IuFP and AMR is used on the same tandem. */
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, "AMR") == 0 && strcmp(codec_b->subtype_name, "VND.3GPP.IUFP") == 0)
 | 
			
		||||
		goto iufp;
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, "VND.3GPP.IUFP") == 0 && strcmp(codec_b->subtype_name, "AMR") == 0)
 | 
			
		||||
		goto iufp;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->rate != codec_b->rate)
 | 
			
		||||
		return false;
 | 
			
		||||
iufp:
 | 
			
		||||
	if (codec_a->channels != codec_b->channels)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->frame_duration_num != codec_b->frame_duration_num)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->frame_duration_den != codec_b->frame_duration_den)
 | 
			
		||||
	if (!strcmp(codec_a->subtype_name, "AMR")) {
 | 
			
		||||
		if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_codec *mgcp_codecset_find_same(struct mgcp_rtp_codecset *cset, const struct mgcp_rtp_codec *codec)
 | 
			
		||||
/*! Translate a given payload type number that belongs to the packet of a
 | 
			
		||||
 *  source connection to the equivalent payload type number that matches the
 | 
			
		||||
 *  configuration of a destination connection.
 | 
			
		||||
 *  \param[in] conn_src related source rtp-connection.
 | 
			
		||||
 *  \param[in] conn_dst related destination rtp-connection.
 | 
			
		||||
 *  \param[in] payload_type number from the source packet or source connection.
 | 
			
		||||
 *  \returns translated payload type number on success, -EINVAL on failure. */
 | 
			
		||||
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_end *rtp_src;
 | 
			
		||||
	struct mgcp_rtp_end *rtp_dst;
 | 
			
		||||
	struct mgcp_rtp_codec *codec_src = NULL;
 | 
			
		||||
	struct mgcp_rtp_codec *codec_dst = NULL;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	unsigned int codecs_assigned;
 | 
			
		||||
 | 
			
		||||
	/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
 | 
			
		||||
	 * the first run we will look for an exact match. */
 | 
			
		||||
	codecs_assigned = cset->codecs_assigned;
 | 
			
		||||
	rtp_src = &conn_src->end;
 | 
			
		||||
	rtp_dst = &conn_dst->end;
 | 
			
		||||
 | 
			
		||||
	/* Find the codec information that is used on the source side */
 | 
			
		||||
	codecs_assigned = rtp_src->codecs_assigned;
 | 
			
		||||
	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
 | 
			
		||||
	for (i = 0; i < codecs_assigned; i++) {
 | 
			
		||||
		if (codecs_same(codec, &cset->codecs[i])) {
 | 
			
		||||
			return &cset->codecs[i];
 | 
			
		||||
		if (payload_type == rtp_src->codecs[i].payload_type) {
 | 
			
		||||
			codec_src = &rtp_src->codecs[i];
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (!codec_src)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* For a given codec, find a convertible codec in the given connection. */
 | 
			
		||||
static struct mgcp_rtp_codec *codecset_find_convertible(struct mgcp_rtp_codecset *cset, const struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	unsigned int codecs_assigned;
 | 
			
		||||
	struct mgcp_rtp_codec *codec_convertible = NULL;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
 | 
			
		||||
	 * the first run we will look for an exact match. */
 | 
			
		||||
	codec_convertible = mgcp_codecset_find_same(cset, codec);
 | 
			
		||||
	if (codec_convertible)
 | 
			
		||||
		return codec_convertible;
 | 
			
		||||
 | 
			
		||||
	/* In case we weren't able to find an exact match, we will try to find a match that is the same codec, but the
 | 
			
		||||
	 * payload format may be different. This alternative will require a frame format conversion (i.e. AMR bwe->oe) */
 | 
			
		||||
	codecs_assigned = cset->codecs_assigned;
 | 
			
		||||
	/* Use the codec infrmation from the source and try to find the
 | 
			
		||||
	 * equivalent of it on the destination side */
 | 
			
		||||
	codecs_assigned = rtp_dst->codecs_assigned;
 | 
			
		||||
	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
 | 
			
		||||
	for (i = 0; i < codecs_assigned; i++) {
 | 
			
		||||
		if (codecs_convertible(codec, &cset->codecs[i])) {
 | 
			
		||||
			codec_convertible = &cset->codecs[i];
 | 
			
		||||
		if (codecs_same(codec_src, &rtp_dst->codecs[i])) {
 | 
			
		||||
			codec_dst = &rtp_dst->codecs[i];
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return codec_convertible;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Decide for one suitable codec on both of the given connections. In case a destination connection is not available,
 | 
			
		||||
 *  a tentative decision is made.
 | 
			
		||||
 *  \param[in] cset_src related codec set.
 | 
			
		||||
 *  \param[inout] cset_dst related destination codec set (NULL if not present).
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on failure. */
 | 
			
		||||
int mgcp_codecset_decide(struct mgcp_rtp_codecset *cset_src, struct mgcp_rtp_codecset *cset_dst)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	/* In case no destination connection is available (yet), or in case the destination connection exists but has
 | 
			
		||||
	 * no codecs assigned, we are forced to make a simple tentative decision:
 | 
			
		||||
	 * We just use the first codec of the source connection (conn_src) */
 | 
			
		||||
	OSMO_ASSERT(cset_src->codecs_assigned <= MGCP_MAX_CODECS);
 | 
			
		||||
	if (!cset_dst || cset_dst->codecs_assigned == 0) {
 | 
			
		||||
		if (cset_src->codecs_assigned >= 1) {
 | 
			
		||||
			cset_src->codec = &cset_src->codecs[0];
 | 
			
		||||
			return 0;
 | 
			
		||||
		} else
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Compare all codecs of the source connection (conn_src) to the codecs of the destination connection (conn_dst). In case
 | 
			
		||||
	 * of a match set this codec on both connections. This would be an ideal selection since no codec conversion would be
 | 
			
		||||
	 * required. */
 | 
			
		||||
	for (i = 0; i < cset_src->codecs_assigned; i++) {
 | 
			
		||||
		struct mgcp_rtp_codec *codec_cset_src = &cset_src->codecs[i];
 | 
			
		||||
		struct mgcp_rtp_codec *codec_cset_dst = mgcp_codecset_find_same(cset_dst, codec_cset_src);
 | 
			
		||||
		if (codec_cset_dst) {
 | 
			
		||||
			/* We found the a codec that is exactly the same (same codec, same payload format etc.) on both
 | 
			
		||||
			 * sides. We now set this codec on both connections. */
 | 
			
		||||
			cset_dst->codec = codec_cset_dst;
 | 
			
		||||
			cset_src->codec = codec_cset_src;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* In case we could not find a codec that is exactly the same, let's at least try to find a codec that we are able
 | 
			
		||||
	 * to convert. */
 | 
			
		||||
	for (i = 0; i < cset_src->codecs_assigned; i++) {
 | 
			
		||||
		struct mgcp_rtp_codec *codec_cset_src = &cset_src->codecs[i];
 | 
			
		||||
		struct mgcp_rtp_codec *codec_cset_dst = codecset_find_convertible(cset_dst, codec_cset_src);
 | 
			
		||||
		if (codec_cset_dst) {
 | 
			
		||||
			/* We found the a codec that we can convert to. Set each side to its codec. */
 | 
			
		||||
			cset_dst->codec = codec_cset_dst;
 | 
			
		||||
			cset_src->codec = codec_cset_src;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cset_dst->codecs_assigned)
 | 
			
		||||
		cset_dst->codec = &cset_dst->codecs[0];
 | 
			
		||||
	else
 | 
			
		||||
	if (!codec_dst)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (cset_src->codecs_assigned)
 | 
			
		||||
		cset_src->codec = &cset_src->codecs[0];
 | 
			
		||||
	else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
	return codec_dst->payload_type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the codec has a specific AMR mode (octet-aligned or bandwith-efficient) set. */
 | 
			
		||||
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	if (codec->param_present == false)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (!codec->param.amr_octet_aligned_present)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (strcmp(codec->subtype_name, "AMR") != 0)
 | 
			
		||||
		return false;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find the payload type number configured for a specific codec by SDP.
 | 
			
		||||
 * 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_codecset_pt_find_by_subtype_name(const struct mgcp_rtp_codecset *cset,
 | 
			
		||||
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 < cset->codecs_assigned; i++) {
 | 
			
		||||
		if (!strcmp(cset->codecs[i].subtype_name, subtype_name)) {
 | 
			
		||||
	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 &cset->codecs[i];
 | 
			
		||||
			return &conn->end.codecs[i];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Lookup a codec that is assigned to a connection by its payload type number.
 | 
			
		||||
 *  \param[in] cset related codec set.
 | 
			
		||||
 *  \param[in] payload_type number of the codec to look up.
 | 
			
		||||
 *  \returns pointer to codec struct on success, NULL on failure. */
 | 
			
		||||
struct mgcp_rtp_codec *mgcp_codecset_find_codec_from_pt(struct mgcp_rtp_codecset *cset, int payload_type)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_codec *codec = NULL;
 | 
			
		||||
	size_t i;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(cset->codecs_assigned <= MGCP_MAX_CODECS);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < cset->codecs_assigned; i++) {
 | 
			
		||||
		if (payload_type == cset->codecs[i].payload_type) {
 | 
			
		||||
			codec = &cset->codecs[i];
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return codec;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,23 +21,18 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdatomic.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_network.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.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>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_group_desc rate_ctr_group_desc = {
 | 
			
		||||
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "conn_rtp",
 | 
			
		||||
	.group_description = "rtp connection statistics",
 | 
			
		||||
	.class_id = 1,
 | 
			
		||||
@@ -74,7 +69,7 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
 | 
			
		||||
 | 
			
		||||
		/* ensure that the generated conn_id is unique
 | 
			
		||||
		 * for this endpoint */
 | 
			
		||||
		if (!mgcp_endp_get_conn_rtp(endp, id_hex)) {
 | 
			
		||||
		if (!mgcp_conn_get_rtp(endp, id_hex)) {
 | 
			
		||||
			osmo_strlcpy(id, id_hex, MGCP_CONN_ID_MAXLEN);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
@@ -88,31 +83,42 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
 | 
			
		||||
/* Initialize rtp connection struct with default values */
 | 
			
		||||
static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_end *end = &conn_rtp->end;
 | 
			
		||||
	/* FIXME: Each new rate counter group requires an unique index. At the
 | 
			
		||||
	 * moment we generate this index using this counter, but perhaps there
 | 
			
		||||
	 * is a more concious way to assign the indexes. */
 | 
			
		||||
	static atomic_uint rate_ctr_index = 0;
 | 
			
		||||
	static unsigned int rate_ctr_index = 0;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->type = MGCP_RTP_DEFAULT;
 | 
			
		||||
 | 
			
		||||
	/* Osmux specific defaults, only used if conn is later on Osmux-enabled: */
 | 
			
		||||
	conn_rtp->osmux.state = OSMUX_STATE_DISABLED;
 | 
			
		||||
	conn_rtp->osmux.local_cid_allocated = false;
 | 
			
		||||
	conn_rtp->osmux.local_cid = 0;
 | 
			
		||||
	conn_rtp->osmux.remote_cid_present = false;
 | 
			
		||||
	conn_rtp->osmux.remote_cid = 0;
 | 
			
		||||
	conn_rtp->osmux.cid_allocated = false;
 | 
			
		||||
	conn_rtp->osmux.cid = 0;
 | 
			
		||||
 | 
			
		||||
	/* backpointer to the generic part of the connection */
 | 
			
		||||
	conn->u.rtp.conn = conn;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->ctrg = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
 | 
			
		||||
	if (!conn_rtp->ctrg)
 | 
			
		||||
	end->rtp.fd = -1;
 | 
			
		||||
	end->rtcp.fd = -1;
 | 
			
		||||
	end->rtp_port = end->rtcp_port = 0;
 | 
			
		||||
	talloc_free(end->fmtp_extra);
 | 
			
		||||
	end->fmtp_extra = NULL;
 | 
			
		||||
 | 
			
		||||
	/* Set default values */
 | 
			
		||||
	end->frames_per_packet = 0;	/* unknown */
 | 
			
		||||
	end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
 | 
			
		||||
	end->output_enabled = 0;
 | 
			
		||||
	end->maximum_packet_time = -1;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
 | 
			
		||||
	if (!conn_rtp->rate_ctr_group)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, IN_STREAM_ERR_TSTMP_CTR);
 | 
			
		||||
	conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, OUT_STREAM_ERR_TSTMP_CTR);
 | 
			
		||||
	conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR];
 | 
			
		||||
	conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR];
 | 
			
		||||
	rate_ctr_index++;
 | 
			
		||||
 | 
			
		||||
	/* Make sure codec table is reset */
 | 
			
		||||
	mgcp_codec_reset_all(conn_rtp);
 | 
			
		||||
 | 
			
		||||
	mgcp_rtp_end_init(&conn_rtp->end, conn_rtp);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -121,22 +127,21 @@ 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_rtp_end_cleanup(&conn_rtp->end);
 | 
			
		||||
	rate_ctr_group_free(conn_rtp->ctrg);
 | 
			
		||||
	mgcp_free_rtp_port(&conn_rtp->end);
 | 
			
		||||
	rate_ctr_group_free(conn_rtp->rate_ctr_group);
 | 
			
		||||
	mgcp_codec_reset_all(conn_rtp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_conn_watchdog_cb(void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn = data;
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_ERROR, "connection timed out!\n");
 | 
			
		||||
	mgcp_conn_free(conn);
 | 
			
		||||
	mgcp_conn_free(conn->endp, conn->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	int timeout = conn->endp->trunk->cfg->conn_timeout;
 | 
			
		||||
	int timeout = conn->endp->cfg->conn_timeout;
 | 
			
		||||
	if (!timeout)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
@@ -156,8 +161,8 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* Do not allow more than the maximum number of connections */
 | 
			
		||||
	if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns)
 | 
			
		||||
	/* Do not allow more then two connections */
 | 
			
		||||
	if (llist_count(&endp->conns) >= endp->type->max_conns)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Create new connection and add it to the list */
 | 
			
		||||
@@ -192,15 +197,69 @@ 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);
 | 
			
		||||
	mgcp_endp_add_conn(endp, conn);
 | 
			
		||||
	llist_add(&conn->entry, &endp->conns);
 | 
			
		||||
 | 
			
		||||
	return conn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
/*! find a connection by its ID.
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection
 | 
			
		||||
 *  \returns pointer to allocated connection, NULL if not found */
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
{
 | 
			
		||||
	struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
 | 
			
		||||
	struct rate_ctr_group *conn_stats = conn_rtp->ctrg;
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	const char *id_upper;
 | 
			
		||||
	const char *conn_id;
 | 
			
		||||
 | 
			
		||||
	if (!id || !*id)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Ignore leading zeros in needle */
 | 
			
		||||
	while (*id == '0')
 | 
			
		||||
		id++;
 | 
			
		||||
 | 
			
		||||
	/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
 | 
			
		||||
	 * Connections" defines the id as a hex string, so clients may return lower case hex even though
 | 
			
		||||
	 * we sent upper case hex in the CRCX response. */
 | 
			
		||||
	id_upper = osmo_str_toupper(id);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(conn, &endp->conns, entry) {
 | 
			
		||||
		/* Ignore leading zeros in haystack */
 | 
			
		||||
		for (conn_id=conn->id; *conn_id == '0'; conn_id++);
 | 
			
		||||
 | 
			
		||||
		if (strcmp(conn_id, id_upper) == 0)
 | 
			
		||||
			return conn;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! find an RTP connection by its ID.
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection
 | 
			
		||||
 *  \returns pointer to allocated connection, NULL if not found */
 | 
			
		||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
					const char *id)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	conn = mgcp_conn_get(endp, id);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP)
 | 
			
		||||
		return &conn->u.rtp;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
aggregate_rtp_conn_stats(struct mgcp_trunk_config *trunk, struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
{
 | 
			
		||||
	struct rate_ctr_group *all_stats = trunk->all_rtp_conn_stats;
 | 
			
		||||
	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (all_stats == NULL || conn_stats == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
@@ -210,30 +269,33 @@ static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_con
 | 
			
		||||
	 * All other counters in both counter groups correspond to each other. */
 | 
			
		||||
	OSMO_ASSERT(conn_stats->desc->num_ctr + 1 == all_stats->desc->num_ctr);
 | 
			
		||||
 | 
			
		||||
	/* 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);
 | 
			
		||||
	for (i = 0; i < conn_stats->desc->num_ctr; i++)
 | 
			
		||||
		rate_ctr_add(&all_stats->ctr[i], conn_stats->ctr[i].current);
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(rate_ctr_group_get_ctr(all_stats, RTP_NUM_CONNECTIONS));
 | 
			
		||||
	rate_ctr_inc(&all_stats->ctr[RTP_NUM_CONNECTIONS]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! free a connection
 | 
			
		||||
 *  \param[in] conn the conn to free. May be NULL.
 | 
			
		||||
*/
 | 
			
		||||
void mgcp_conn_free(struct mgcp_conn *conn)
 | 
			
		||||
/*! free a connection by its ID.
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection */
 | 
			
		||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp;
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	conn = mgcp_conn_get(endp, id);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Run endpoint cleanup action. By this we inform the endpoint about
 | 
			
		||||
	 * the removal of the connection and allow it to clean up its inner
 | 
			
		||||
	 * state accordingly */
 | 
			
		||||
	if (endp->type->cleanup_cb)
 | 
			
		||||
		endp->type->cleanup_cb(endp, conn);
 | 
			
		||||
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		conn_rtp = mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
		aggregate_rtp_conn_stats(conn->endp, conn_rtp);
 | 
			
		||||
		mgcp_rtp_conn_cleanup(conn_rtp);
 | 
			
		||||
		aggregate_rtp_conn_stats(endp->tcfg, &conn->u.rtp);
 | 
			
		||||
		mgcp_rtp_conn_cleanup(&conn->u.rtp);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		/* NOTE: This should never be called with an
 | 
			
		||||
@@ -243,40 +305,39 @@ void mgcp_conn_free(struct mgcp_conn *conn)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osmo_timer_del(&conn->watchdog);
 | 
			
		||||
	mgcp_endp_remove_conn(conn->endp, conn);
 | 
			
		||||
	/* WARN: endp may have be freed after call to mgcp_endp_remove_conn */
 | 
			
		||||
	llist_del(&conn->entry);
 | 
			
		||||
	talloc_free(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Parse connection mode.
 | 
			
		||||
 *  \param[in] conn Connection whose mode is being set
 | 
			
		||||
 *  \param[in] mode Mode to set
 | 
			
		||||
 *  \returns 0 on success, -1 on error */
 | 
			
		||||
int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode)
 | 
			
		||||
/*! free oldest connection in the list.
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
	if (mode == MGCP_CONN_NONE) {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "missing connection mode\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	conn->mode = mode;
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
 | 
			
		||||
		 mgcp_cmode_name(mode), conn->mode);
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	/* Special handling for RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
		conn_rtp->end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
 | 
			
		||||
			 conn_rtp->end.output_enabled);
 | 
			
		||||
	if (llist_empty(&endp->conns))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	mgcp_conn_free(endp, conn->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! free all connections at once.
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	struct mgcp_conn *conn_tmp;
 | 
			
		||||
 | 
			
		||||
	/* Drop all items in the list */
 | 
			
		||||
	llist_for_each_entry_safe(conn, conn_tmp, &endp->conns, entry) {
 | 
			
		||||
		mgcp_conn_free(endp, conn->id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* The VTY might change the connection mode at any time, so we have
 | 
			
		||||
	 * to hold a copy of the original connection mode */
 | 
			
		||||
	conn->mode_orig = conn->mode;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! dump basic connection information to human readable string.
 | 
			
		||||
@@ -285,40 +346,29 @@ int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode)
 | 
			
		||||
char *mgcp_conn_dump(struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	static char str[sizeof(conn->name)+sizeof(conn->id)+256];
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
	struct osmo_strbuf sb = { .buf = str, .len = sizeof(str) };
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp;
 | 
			
		||||
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return "NULL";
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		snprintf(str, sizeof(str), "(null connection)");
 | 
			
		||||
		return str;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		conn_rtp = mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, "(%s/%s C:%s r=%s:%u<->l=%s:%u",
 | 
			
		||||
		/* Dump RTP connection */
 | 
			
		||||
		snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
 | 
			
		||||
			 "rtp:%u rtcp:%u)",
 | 
			
		||||
			 conn->name,
 | 
			
		||||
				   mgcp_conn_rtp_type_name(conn->type),
 | 
			
		||||
			 conn->id,
 | 
			
		||||
				   osmo_sockaddr_ntop(&conn_rtp->end.addr.u.sa, ipbuf) ? : "NULL",
 | 
			
		||||
				   osmo_sockaddr_port(&conn_rtp->end.addr.u.sa),
 | 
			
		||||
				   conn_rtp->end.local_addr ? : "NULL",
 | 
			
		||||
				   conn_rtp->end.local_port);
 | 
			
		||||
 | 
			
		||||
		switch (conn_rtp->type) {
 | 
			
		||||
		case MGCP_RTP_OSMUX:
 | 
			
		||||
			OSMO_STRBUF_PRINTF(sb, " CID=%u", conn_rtp->osmux.local_cid);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, ")");
 | 
			
		||||
			 inet_ntoa(conn->u.rtp.end.addr),
 | 
			
		||||
			 ntohs(conn->u.rtp.end.rtp_port),
 | 
			
		||||
			 ntohs(conn->u.rtp.end.rtcp_port));
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		/* Should not happen, we should be able to dump
 | 
			
		||||
		 * every possible connection type. */
 | 
			
		||||
		return "(unknown connection type)";
 | 
			
		||||
		snprintf(str, sizeof(str), "(unknown connection type)");
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return str;
 | 
			
		||||
@@ -345,10 +395,3 @@ struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct value_string mgcp_conn_rtp_type_names[] = {
 | 
			
		||||
	{ MGCP_RTP_DEFAULT, "rtp" },
 | 
			
		||||
	{ MGCP_RTP_OSMUX, "osmux" },
 | 
			
		||||
	{ MGCP_RTP_IUUP, "iuup" },
 | 
			
		||||
	{}
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,781 +0,0 @@
 | 
			
		||||
/* 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,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* The following EFR TRAU-DL frame is a dummy to be transmitted in the absence
 | 
			
		||||
 * of RTP-derived TRAU-DL frames.  The payload bit content here is the decoder
 | 
			
		||||
 * homing frame (DHF) of TS 46.060 section 8.2 Table 7 - the best we can do
 | 
			
		||||
 * in the absence of a proper TFO transform for EFR - while the full TRAU-DL
 | 
			
		||||
 * frame was generated by passing said EFR DHF through osmo_rtp2trau().
 | 
			
		||||
 */
 | 
			
		||||
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, 1, 1, 1,
 | 
			
		||||
	1, 1, 0, 0, 0, 0, 1, 0,
 | 
			
		||||
	0, 0, 0, 1, 0, 1, 1, 1,
 | 
			
		||||
	1, 1, 0, 1, 0, 1, 1, 0,
 | 
			
		||||
	1, 0, 0, 1, 0, 0, 1, 0,
 | 
			
		||||
	1, 0, 0, 0, 1, 1, 1, 1,
 | 
			
		||||
	1, 0, 0, 1, 0, 1, 0, 1,
 | 
			
		||||
	1, 0, 1, 0, 1, 1, 0, 1,
 | 
			
		||||
	0, 1, 1, 0, 0, 0, 0, 0,
 | 
			
		||||
	1, 0, 0, 1, 1, 1, 1, 1,
 | 
			
		||||
	0, 0, 0, 1, 1, 1, 0, 1,
 | 
			
		||||
	1, 0, 0, 0, 0, 1, 1, 0,
 | 
			
		||||
	0, 0, 0, 1, 1, 0, 0, 0,
 | 
			
		||||
	1, 0, 0, 0, 1, 1, 1, 1,
 | 
			
		||||
	0, 1, 1, 0, 1, 1, 0, 0,
 | 
			
		||||
	1, 0, 0, 1, 1, 0, 0, 0,
 | 
			
		||||
	1, 0, 0, 0, 0, 1, 0, 1,
 | 
			
		||||
	1, 1, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
	1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
	1, 0, 1, 0, 0, 1, 0, 0,
 | 
			
		||||
	0, 0, 0, 0, 0, 0, 1, 0,
 | 
			
		||||
	1, 0, 1, 0, 1, 0, 1, 0,
 | 
			
		||||
	1, 1, 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, 1, 1, 1, 0,
 | 
			
		||||
	1, 1, 1, 0, 1, 0, 1, 1,
 | 
			
		||||
	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, 1, 1, 1, 1,
 | 
			
		||||
	1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* The following FRv1 TRAU-DL frame is a dummy to be transmitted in the absence
 | 
			
		||||
 * of RTP-derived TRAU-DL frames.  The payload bit content here is the silence
 | 
			
		||||
 * frame of TS 46.011 Table 1 - the best we can do without integrating the
 | 
			
		||||
 * TFO transform for FRv1 from Themyscira libgsmfr2 - while the full TRAU-DL
 | 
			
		||||
 * frame was generated by passing said FRv1 silence frame through
 | 
			
		||||
 * osmo_rtp2trau().
 | 
			
		||||
 */
 | 
			
		||||
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, 1, 1, 1,
 | 
			
		||||
	1, 0, 1, 0, 1, 0, 1, 1,
 | 
			
		||||
	1, 1, 0, 0, 1, 1, 0, 1,
 | 
			
		||||
	1, 0, 1, 0, 1, 0, 1, 0,
 | 
			
		||||
	1, 0, 0, 1, 0, 0, 1, 0,
 | 
			
		||||
	1, 1, 1, 0, 0, 1, 0, 0,
 | 
			
		||||
	0, 0, 1, 0, 1, 0, 0, 0,
 | 
			
		||||
	1, 1, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
	0, 1, 1, 0, 0, 0, 1, 1,
 | 
			
		||||
	1, 1, 0, 0, 0, 1, 0, 0,
 | 
			
		||||
	1, 1, 1, 0, 1, 1, 0, 1,
 | 
			
		||||
	1, 1, 0, 1, 1, 0, 0, 0,
 | 
			
		||||
	1, 0, 0, 1, 1, 1, 0, 1,
 | 
			
		||||
	1, 1, 0, 0, 0, 0, 1, 0,
 | 
			
		||||
	1, 0, 0, 0, 1, 0, 0, 0,
 | 
			
		||||
	1, 0, 0, 0, 0, 1, 1, 0,
 | 
			
		||||
	0, 0, 1, 1, 1, 0, 0, 0,
 | 
			
		||||
	1, 1, 0, 0, 1, 1, 1, 0,
 | 
			
		||||
	1, 1, 0, 1, 1, 0, 1, 1,
 | 
			
		||||
	1, 0, 0, 0, 1, 0, 0, 1,
 | 
			
		||||
	1, 1, 0, 1, 1, 0, 0, 0,
 | 
			
		||||
	1, 0, 1, 0, 1, 0, 0, 0,
 | 
			
		||||
	1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
	1, 1, 1, 0, 0, 0, 1, 1,
 | 
			
		||||
	1, 0, 0, 0, 1, 0, 0, 1,
 | 
			
		||||
	1, 1, 1, 0, 1, 1, 0, 1,
 | 
			
		||||
	1, 0, 1, 1, 0, 0, 0, 1,
 | 
			
		||||
	1, 0, 0, 1, 1, 1, 0, 1,
 | 
			
		||||
	1, 0, 0, 0, 0, 1, 0, 1,
 | 
			
		||||
	1, 0, 0, 0, 1, 0, 0, 0,
 | 
			
		||||
	0, 0, 0, 0, 1, 1, 0, 0,
 | 
			
		||||
	1, 0, 1, 1, 1, 0, 0, 0,
 | 
			
		||||
	1, 0, 0, 1, 1, 1, 0, 1,
 | 
			
		||||
	1, 1, 0, 1, 1, 0, 1, 1,
 | 
			
		||||
	0, 0, 0, 1, 0, 0, 1, 1,
 | 
			
		||||
	1, 1, 0, 1, 1, 0, 1, 1,
 | 
			
		||||
	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-multiplexer; feed output of I.460 demux into TRAU frame sync */
 | 
			
		||||
static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
 | 
			
		||||
				  unsigned int num_bits)
 | 
			
		||||
{
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
skip:
 | 
			
		||||
	rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_RX_FAIL_CTR));
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* handle outgoing E1 traffic */
 | 
			
		||||
static void e1_send_ts_frame(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = msgb_alloc_c(trunk, E1_TS_BYTES, "E1-TX-timeslot-bytes");
 | 
			
		||||
	uint8_t *ptr;
 | 
			
		||||
 | 
			
		||||
	/* 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 */
 | 
			
		||||
	e1inp_ts_send_raw(ts, 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);
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		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_frame(ts, trunk);
 | 
			
		||||
 | 
			
		||||
	/* e1inp_rx_ts(), the caller of this callback does not free() msgb. */
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int e1_open(struct mgcp_trunk *trunk, uint8_t ts_nr)
 | 
			
		||||
{
 | 
			
		||||
	/*! One E1 timeslot may serve multiple I.460 subslots. The timeslot is opened as soon as an I.460 subslot is
 | 
			
		||||
	 *  opened and will stay open until the last I.460 subslot is closed (see e1_close below). This function must
 | 
			
		||||
	 *  be called any time a new I.460 subslot is opened in order to maintain constancy of the ts_usecount counter. */
 | 
			
		||||
 | 
			
		||||
	struct e1inp_line *e1_line;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
 | 
			
		||||
	cfg = trunk->cfg;
 | 
			
		||||
 | 
			
		||||
	if (trunk->e1.ts_usecount[ts_nr - 1] > 0) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up and in use by %u subslot(s), using it as it is...\n",
 | 
			
		||||
			  ts_nr, trunk->e1.ts_usecount[ts_nr - 1]);
 | 
			
		||||
		trunk->e1.ts_usecount[ts_nr - 1]++;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Find E1 line */
 | 
			
		||||
	e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
 | 
			
		||||
	if (!e1_line) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
 | 
			
		||||
			  trunk->e1.vty_line_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	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 line %u.\n", ts_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u set up successfully.\n", ts_nr);
 | 
			
		||||
	trunk->e1.ts_usecount[ts_nr - 1]++;
 | 
			
		||||
	OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 1);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int e1_close(struct mgcp_trunk *trunk, uint8_t ts_nr)
 | 
			
		||||
{
 | 
			
		||||
	/* See also comment above (e1_open). This function must be called any time an I.460 subslot is closed */
 | 
			
		||||
 | 
			
		||||
	struct e1inp_line *e1_line;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
 | 
			
		||||
	cfg = trunk->cfg;
 | 
			
		||||
 | 
			
		||||
	if (trunk->e1.ts_usecount[ts_nr - 1] > 1) {
 | 
			
		||||
		trunk->e1.ts_usecount[ts_nr - 1]--;
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u still in use by %u other subslot(s), leaving it open...\n",
 | 
			
		||||
			  ts_nr, trunk->e1.ts_usecount[ts_nr - 1]);
 | 
			
		||||
		return 0;
 | 
			
		||||
	} else if (trunk->e1.ts_usecount[ts_nr - 1] == 0) {
 | 
			
		||||
		/* This should not be as it means we close the timeslot too often. */
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "E1 timeslot %u already closed, leaving it as it is...\n", ts_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Find E1 line */
 | 
			
		||||
	e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
 | 
			
		||||
	if (!e1_line) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
 | 
			
		||||
			  trunk->e1.vty_line_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Release E1 timeslot */
 | 
			
		||||
	rc = e1inp_ts_config_none(&e1_line->ts[ts_nr - 1], e1_line);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to disable E1 timeslot %u.\n", ts_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	rc = e1inp_line_update(e1_line);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", trunk->e1.vty_line_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u closed.\n", ts_nr);
 | 
			
		||||
	trunk->e1.ts_usecount[ts_nr - 1]--;
 | 
			
		||||
	OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 0);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
 | 
			
		||||
		return OSMO_TRAU16_FT_EFR;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0) {
 | 
			
		||||
		if (i460_rate == OSMO_I460_RATE_16k)
 | 
			
		||||
			return OSMO_TRAU16_FT_HR;
 | 
			
		||||
		if (i460_rate == OSMO_I460_RATE_8k)
 | 
			
		||||
			return OSMO_TRAU8_SPEECH;
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_ERROR,
 | 
			
		||||
			 "E1-TRAU-TX: unsupported or illegal I.460 rate for HR\n");
 | 
			
		||||
		return OSMO_TRAU_FT_NONE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
 | 
			
		||||
		return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0) {
 | 
			
		||||
		if (i460_rate == OSMO_I460_RATE_16k)
 | 
			
		||||
			return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
		if (i460_rate == OSMO_I460_RATE_8k)
 | 
			
		||||
			return OSMO_TRAU_SYNCP_8_HR;
 | 
			
		||||
		LOGPENDP(endp, DE1, LOGL_ERROR,
 | 
			
		||||
			 "E1-TRAU-TX: unsupported or illegal I.460 rate for HR\n");
 | 
			
		||||
		return OSMO_TRAU_SYNCP_16_FR_EFR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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 and E1 timeslot 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_open(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_conn_rtp *conn_rtp;
 | 
			
		||||
	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_endp_get_conn_oldest(endp);
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
	conn_rtp = mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
	codec = conn_rtp->end.cset.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.
 | 
			
		||||
 *  \param[in] ts E1 timeslot number. */
 | 
			
		||||
void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts)
 | 
			
		||||
{
 | 
			
		||||
	/* Guard against multiple calls. In case we don't see a subchannel anymore we can safely assume that all work
 | 
			
		||||
	 * is done. */
 | 
			
		||||
	if (!(endp->e1.schan || endp->e1.trau_rtp_st || endp->e1.trau_sync_fi))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
 | 
			
		||||
 | 
			
		||||
	if (endp->e1.schan)
 | 
			
		||||
		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));
 | 
			
		||||
 | 
			
		||||
	/* Close E1 timeslot */
 | 
			
		||||
	e1_close(endp->trunk, ts);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Philipp Maier
 | 
			
		||||
@@ -21,749 +21,17 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.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 = {
 | 
			
		||||
		.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,
 | 
			
		||||
	},
 | 
			
		||||
	.rtp.max_conns = 2,
 | 
			
		||||
	.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
 | 
			
		||||
	.rtp.cleanup_cb = mgcp_cleanup_rtp_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(const 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Check if the given epname refers to a "null" endpoint.
 | 
			
		||||
 *  \param[in] epname endpoint name to check
 | 
			
		||||
 *  \returns true if epname refers to "null"" endpoint, else false. */
 | 
			
		||||
bool mgcp_endp_is_null(const char *epname)
 | 
			
		||||
{
 | 
			
		||||
	if (strncasecmp(epname, "null@", 5) == 0)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Find an endpoint by its name on a specified trunk.
 | 
			
		||||
 *  \param[out] cause pointer to store cause code, can be NULL.
 | 
			
		||||
 *  \param[in] epname endpoint name to lookup.
 | 
			
		||||
 *  \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(const 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(const 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Get number of conns in an endpoint.
 | 
			
		||||
 *  \param[in] endp endpoint to check.
 | 
			
		||||
 *  \returns Number of connections present in the endpoint. */
 | 
			
		||||
unsigned int mgcp_endp_num_conns(const struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	return llist_count(&endp->conns);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! check if an endpoint can in current state allocate new conns.
 | 
			
		||||
 *  \param[in] endp endpoint to check.
 | 
			
		||||
 *  \returns true if more connections can be allowed on endpoint, false if it is already busy. */
 | 
			
		||||
bool mgcp_endp_is_full(const struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	if (endp->type->max_conns == 0)
 | 
			
		||||
		return false;
 | 
			
		||||
	return mgcp_endp_num_conns(endp) >= endp->type->max_conns;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! free oldest connection in the list.
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
void mgcp_endp_free_conn_oldest(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	if (llist_empty(&endp->conns))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
 | 
			
		||||
	mgcp_conn_free(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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_endp_free_conn_all(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	/* 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(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! find a connection by its ID.
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection
 | 
			
		||||
 *  \returns pointer to allocated connection, NULL if not found */
 | 
			
		||||
struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	const char *id_upper;
 | 
			
		||||
	const char *conn_id;
 | 
			
		||||
 | 
			
		||||
	if (!id || !*id)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Ignore leading zeros in needle */
 | 
			
		||||
	while (*id == '0')
 | 
			
		||||
		id++;
 | 
			
		||||
 | 
			
		||||
	/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
 | 
			
		||||
	 * Connections" defines the id as a hex string, so clients may return lower case hex even though
 | 
			
		||||
	 * we sent upper case hex in the CRCX response. */
 | 
			
		||||
	id_upper = osmo_str_toupper(id);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(conn, &endp->conns, entry) {
 | 
			
		||||
		/* Ignore leading zeros in haystack */
 | 
			
		||||
		for (conn_id = conn->id; *conn_id == '0'; conn_id++);
 | 
			
		||||
 | 
			
		||||
		if (strcmp(conn_id, id_upper) == 0)
 | 
			
		||||
			return conn;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! get oldest connection in the list.
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
struct mgcp_conn *mgcp_endp_get_conn_oldest(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	if (llist_empty(&endp->conns))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! find an RTP connection by its ID.
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection
 | 
			
		||||
 *  \returns pointer to allocated connection, NULL if not found */
 | 
			
		||||
struct mgcp_conn_rtp *mgcp_endp_get_conn_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
					const char *id)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	conn = mgcp_endp_get_conn(endp, id);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP)
 | 
			
		||||
		return mgcp_conn_get_conn_rtp(conn);
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! release endpoint, all open connections are closed.
 | 
			
		||||
 *  \param[in] endp endpoint to release */
 | 
			
		||||
void mgcp_endp_release(struct mgcp_endpoint *endp)
 | 
			
		||||
@@ -774,13 +42,7 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
 | 
			
		||||
	 * all connections have been removed already. In case
 | 
			
		||||
	 * that there are still connections open (e.g. when
 | 
			
		||||
	 * RSIP is executed), free them all at once. */
 | 
			
		||||
	mgcp_endp_free_conn_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);
 | 
			
		||||
	mgcp_conn_free_all(endp);
 | 
			
		||||
 | 
			
		||||
	/* Reset endpoint parameters and states */
 | 
			
		||||
	talloc_free(endp->callid);
 | 
			
		||||
@@ -789,10 +51,5 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
 | 
			
		||||
	endp->local_options.string = NULL;
 | 
			
		||||
	talloc_free(endp->local_options.codec);
 | 
			
		||||
	endp->local_options.codec = NULL;
 | 
			
		||||
 | 
			
		||||
	if (endp->trunk->trunk_type == MGCP_TRUNK_E1) {
 | 
			
		||||
		uint8_t ts = e1_ts_nr_from_epname(endp->name);
 | 
			
		||||
		mgcp_e1_endp_release(endp, ts);
 | 
			
		||||
	}
 | 
			
		||||
	endp->wildcarded_req = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,755 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (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;
 | 
			
		||||
	uint8_t rfci_cnt = 0;
 | 
			
		||||
	/* Find RFCI containing NO_DATA: */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
 | 
			
		||||
		struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i];
 | 
			
		||||
		int j;
 | 
			
		||||
		bool is_no_data;
 | 
			
		||||
		if (!rfci->used)
 | 
			
		||||
			continue;
 | 
			
		||||
		rfci_cnt++;
 | 
			
		||||
 | 
			
		||||
		is_no_data = true;
 | 
			
		||||
		for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++) {
 | 
			
		||||
			if (rfci->subflow_sizes[j]) {
 | 
			
		||||
				is_no_data = false;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (is_no_data)
 | 
			
		||||
			return rfci->id;
 | 
			
		||||
 | 
			
		||||
		/* early loop termination: */
 | 
			
		||||
		if (rfci_cnt == irp->u.status.u.initialization.num_subflows)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	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;
 | 
			
		||||
	uint8_t rfci_cnt = 0;
 | 
			
		||||
	unsigned match_bytes = (unsigned)osmo_amr_bytes(ft);
 | 
			
		||||
	struct osmo_iuup_rnl_prim *irp = conn_rtp->iuup.init_ind;
 | 
			
		||||
	if (!irp) {
 | 
			
		||||
		/* No IuUP Initialization has occured on the IuUP side yet. Return error and drop the RTP data, until
 | 
			
		||||
		 * the IuUP Initialization has configured the link. */
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* TODO: cache this somehow */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
 | 
			
		||||
		struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i];
 | 
			
		||||
		int j;
 | 
			
		||||
		unsigned num_bits;
 | 
			
		||||
		if (!rfci->used)
 | 
			
		||||
			continue;
 | 
			
		||||
		rfci_cnt++;
 | 
			
		||||
 | 
			
		||||
		num_bits = 0;
 | 
			
		||||
		for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++)
 | 
			
		||||
			num_bits += rfci->subflow_sizes[j];
 | 
			
		||||
		if (match_bytes == (num_bits + 7)/8)
 | 
			
		||||
			return rfci->id;
 | 
			
		||||
 | 
			
		||||
		/* early loop termination: */
 | 
			
		||||
		if (rfci_cnt == irp->u.status.u.initialization.num_subflows)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	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_id_no_data = _find_rfci_no_data(init_ind);
 | 
			
		||||
 | 
			
		||||
	/* Copy over the rfci_id_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;
 | 
			
		||||
	irp2->u.config.IPTIs_present = irp->u.status.u.initialization.IPTIs_present;
 | 
			
		||||
	memcpy(irp2->u.config.rfci, irp->u.status.u.initialization.rfci, sizeof(irp2->u.config.rfci));
 | 
			
		||||
	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;
 | 
			
		||||
	struct mgcp_rtp_codec *dst_codec;
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	dst_codec = conn_rtp_dst->end.cset.codec;
 | 
			
		||||
	if (mgcp_codec_amr_is_octet_aligned(dst_codec)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR OA: ft %d, len %d\n", ft, msgb_length(msg));
 | 
			
		||||
		amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr));
 | 
			
		||||
		amr_hdr->cmr = 15; /* no change */
 | 
			
		||||
		amr_hdr->f = 0;
 | 
			
		||||
		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);
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR BE: ft %d, len %zd\n", ft, amr_length);
 | 
			
		||||
	}
 | 
			
		||||
	rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(*rtp_hdr));
 | 
			
		||||
	*rtp_hdr = (struct rtp_hdr){
 | 
			
		||||
		.csrc_count = 0,
 | 
			
		||||
		.extension = 0,
 | 
			
		||||
		.padding = 0,
 | 
			
		||||
		.version = 0,
 | 
			
		||||
		.payload_type = dst_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);
 | 
			
		||||
	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_RTP_OSMUX:
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	if (conn_rtp_src->iuup.init_ind) {
 | 
			
		||||
		/* We received more than one IuUP Initialization. It's probably
 | 
			
		||||
		 * a retransmission, so simply ignore it (lower layers take care
 | 
			
		||||
		 * of ACKing it). */
 | 
			
		||||
		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_INFO,
 | 
			
		||||
		  "Ignoring potential IuUP Initialization retrans\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	/* Find RFCI containing NO_DATA: */
 | 
			
		||||
	conn_rtp_src->iuup.rfci_id_no_data = _find_rfci_no_data(irp);
 | 
			
		||||
 | 
			
		||||
	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 rc;
 | 
			
		||||
 | 
			
		||||
	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),
 | 
			
		||||
			 osmo_sockaddr_port(&rtp_end->addr.u.sa), 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->cset.codec->rate));
 | 
			
		||||
	hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence);
 | 
			
		||||
	hdr->ssrc = rtp_state->alt_rtp_tx_ssrc;
 | 
			
		||||
	rtp_state->alt_rtp_tx_sequence++;
 | 
			
		||||
 | 
			
		||||
	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),
 | 
			
		||||
		 osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port));
 | 
			
		||||
 | 
			
		||||
	/* Forward a copy of the RTP data to a debug ip/port */
 | 
			
		||||
	forward_data_tap(rtp_end->rtp, &conn_src->tap_out, msg);
 | 
			
		||||
 | 
			
		||||
	rc = mgcp_udp_send(rtp_end->rtp, &rtp_end->addr, (char *)hdr, buflen);
 | 
			
		||||
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	rtpconn_rate_ctr_add(conn_dst, endp, RTP_PACKETS_TX_CTR, 1);
 | 
			
		||||
	rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, buflen);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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));
 | 
			
		||||
	/* rtph is further filled in mgcp_send_iuup() below. */
 | 
			
		||||
	*rtph = (struct rtp_hdr){
 | 
			
		||||
		.csrc_count = 0,
 | 
			
		||||
		.extension = 0,
 | 
			
		||||
		.padding = 0,
 | 
			
		||||
		.version = 2,
 | 
			
		||||
		.payload_type = conn_rtp_dst->end.cset.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_id_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 = osmo_sockaddr_port(&conn_rtp_src->end.addr.u.sa);
 | 
			
		||||
		conn_rtp_src->end.addr = *mc->from_addr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
		osmo_sockaddr_set_port(&conn_rtp_src->end.addr.u.sa, 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. Takes ownership of msg. */
 | 
			
		||||
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;
 | 
			
		||||
	struct mgcp_rtp_codec *src_codec;
 | 
			
		||||
	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 */
 | 
			
		||||
 | 
			
		||||
	src_codec = conn_src_rtp->end.cset.codec;
 | 
			
		||||
	if (strcmp(src_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",
 | 
			
		||||
			     src_codec->subtype_name);
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	}
 | 
			
		||||
	if (mgcp_codec_amr_is_octet_aligned(src_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 (!osmo_amr_ft_valid(amr_hdr->ft)) {
 | 
			
		||||
			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR OA ft=%u\n", amr_hdr->ft);
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		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 ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
 | 
			
		||||
		irp->u.data.rfci = rfci;
 | 
			
		||||
		msgb_pull(msg, 2);
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR OA -> IuUP: ft %d -> rfci %d len %d\n",
 | 
			
		||||
		     amr_hdr->ft, rfci, msgb_length(msg));
 | 
			
		||||
	} else {
 | 
			
		||||
		uint8_t *amr_bwe_hdr = (uint8_t *) msgb_data(msg);
 | 
			
		||||
		int8_t ft;
 | 
			
		||||
		uint8_t q;
 | 
			
		||||
		if (msgb_length(msg) < 2) {
 | 
			
		||||
			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
 | 
			
		||||
				     "Bridge RTP=>IuUP: too short for AMR BE hdr (%u)\n", msgb_length(msg));
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		ft = ((amr_bwe_hdr[0] & 0x07) << 1) | ((amr_bwe_hdr[1] & 0x80) >> 7);
 | 
			
		||||
		if (!osmo_amr_ft_valid(ft)) {
 | 
			
		||||
			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR BE ft=%u\n", ft);
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		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;
 | 
			
		||||
		}
 | 
			
		||||
		q = amr_bwe_hdr[1] & 0x40;
 | 
			
		||||
		irp->u.data.fqc = q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
 | 
			
		||||
		irp->u.data.rfci = rfci;
 | 
			
		||||
		rc = iuup_length = osmo_amr_bwe_to_iuup(msgb_data(msg), msgb_length(msg));
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			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);
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR BE -> IuUP: ft %d -> rfci %d len %d\n",
 | 
			
		||||
		     ft, rfci, msgb_length(msg));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	irp->oph.msg->l3h = msgb_put(irp->oph.msg, msgb_length(msg));
 | 
			
		||||
	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_id_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_id_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;
 | 
			
		||||
}
 | 
			
		||||
@@ -23,20 +23,12 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_msg.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
 | 
			
		||||
/* (same fmt as LOGPENDP()) */
 | 
			
		||||
#define LOG_MGCP_PDATA(PDATA, LEVEL, FMT, ARGS...) \
 | 
			
		||||
	LOGP(DLMGCP, LEVEL, "endpoint:%s " FMT, (PDATA) ? ((PDATA)->epname ? : "null-epname") : "null-pdata", ##ARGS)
 | 
			
		||||
 | 
			
		||||
/*! Display an mgcp message on the log output.
 | 
			
		||||
 *  \param[in] message mgcp message string
 | 
			
		||||
@@ -80,41 +72,242 @@ void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Parse connection mode.
 | 
			
		||||
 *  \param[in] mode as string (recvonly, sendrecv, sendonly confecho or loopback)
 | 
			
		||||
 *  \returns MGCP_CONN_* on success, MGCP_CONN_NONE on error */
 | 
			
		||||
enum mgcp_connection_mode mgcp_parse_conn_mode(const char *mode)
 | 
			
		||||
 *  \param[in] mode as string (recvonly, sendrecv, sendonly or loopback)
 | 
			
		||||
 *  \param[in] endp pointer to endpoint (only used for log output)
 | 
			
		||||
 *  \param[out] associated connection to be modified accordingly
 | 
			
		||||
 *  \returns 0 on success, -1 on error */
 | 
			
		||||
int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
 | 
			
		||||
			 struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	if (!mode)
 | 
			
		||||
		return MGCP_CONN_NONE;
 | 
			
		||||
	if (!mode) {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "missing connection mode\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return -1;
 | 
			
		||||
	if (!endp)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (strcasecmp(mode, "recvonly") == 0)
 | 
			
		||||
		return MGCP_CONN_RECV_ONLY;
 | 
			
		||||
	if (strcasecmp(mode, "sendrecv") == 0)
 | 
			
		||||
		return MGCP_CONN_RECV_SEND;
 | 
			
		||||
	if (strcasecmp(mode, "sendonly") == 0)
 | 
			
		||||
		return MGCP_CONN_SEND_ONLY;
 | 
			
		||||
	if (strcasecmp(mode, "confecho") == 0)
 | 
			
		||||
		return MGCP_CONN_CONFECHO;
 | 
			
		||||
	if (strcasecmp(mode, "loopback") == 0)
 | 
			
		||||
		return MGCP_CONN_LOOPBACK;
 | 
			
		||||
	return MGCP_CONN_NONE;
 | 
			
		||||
		conn->mode = MGCP_CONN_RECV_ONLY;
 | 
			
		||||
	else if (strcasecmp(mode, "sendrecv") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_RECV_SEND;
 | 
			
		||||
	else if (strcasecmp(mode, "sendonly") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_SEND_ONLY;
 | 
			
		||||
	else if (strcasecmp(mode, "loopback") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_LOOPBACK;
 | 
			
		||||
	else {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "unknown connection mode: '%s'\n", mode);
 | 
			
		||||
		ret = -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Special handling für RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		conn->u.rtp.end.output_enabled =
 | 
			
		||||
		    conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
 | 
			
		||||
		 mode, conn->mode);
 | 
			
		||||
 | 
			
		||||
	/* Special handling für RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n",
 | 
			
		||||
			 conn->u.rtp.end.output_enabled);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* The VTY might change the connection mode at any time, so we have
 | 
			
		||||
	 * to hold a copy of the original connection mode */
 | 
			
		||||
	conn->mode_orig = conn->mode;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* We have a null terminated string with the endpoint name here. We only
 | 
			
		||||
 * support two kinds. Simple ones as seen on the BSC level and the ones
 | 
			
		||||
 * seen on the trunk side. (helper function for find_endpoint()) */
 | 
			
		||||
static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
 | 
			
		||||
					      const char *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	char *rest = NULL;
 | 
			
		||||
	struct mgcp_trunk_config *tcfg;
 | 
			
		||||
	int trunk, endp;
 | 
			
		||||
	struct mgcp_endpoint *endp_ptr;
 | 
			
		||||
 | 
			
		||||
	trunk = strtoul(mgcp + 6, &rest, 10);
 | 
			
		||||
	if (rest == NULL || rest[0] != '/' || trunk < 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endp = strtoul(rest + 1, &rest, 10);
 | 
			
		||||
	if (rest == NULL || rest[0] != '@') {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* signalling is on timeslot 1 */
 | 
			
		||||
	if (endp == 1)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	tcfg = mgcp_trunk_num(cfg, trunk);
 | 
			
		||||
	if (!tcfg) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
 | 
			
		||||
		     trunk);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!tcfg->endpoints) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Endpoints of trunk %d not allocated.\n", trunk);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (endp < 1 || endp >= tcfg->number_endpoints) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
 | 
			
		||||
		     mgcp);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endp_ptr = &tcfg->endpoints[endp];
 | 
			
		||||
	endp_ptr->wildcarded_req = false;
 | 
			
		||||
	return endp_ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find an endpoint that is not in use. Do this by going through the endpoint
 | 
			
		||||
 * array, check the callid. A callid nullpointer indicates that the endpoint
 | 
			
		||||
 * is free */
 | 
			
		||||
static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
 | 
			
		||||
						unsigned int number_endpoints)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < number_endpoints; i++) {
 | 
			
		||||
		if (endpoints[i].callid == NULL) {
 | 
			
		||||
			endp = &endpoints[i];
 | 
			
		||||
			LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
 | 
			
		||||
			     "found free endpoint\n");
 | 
			
		||||
			endp->wildcarded_req = true;
 | 
			
		||||
			return endp;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n");
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the domain name, which is supplied with the endpoint name
 | 
			
		||||
 * matches the configuration. */
 | 
			
		||||
static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	char *domain_to_check;
 | 
			
		||||
 | 
			
		||||
	domain_to_check = strstr(mgcp, "@");
 | 
			
		||||
	if (!domain_to_check)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Accept any domain if configured as "*" */
 | 
			
		||||
	if (!strcmp(cfg->domain, "*"))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(domain_to_check+1, cfg->domain) != 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Search the endpoint pool for the endpoint that had been selected via the
 | 
			
		||||
 * MGCP message (helper function for mgcp_analyze_header()) */
 | 
			
		||||
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
 | 
			
		||||
					   const char *mgcp,
 | 
			
		||||
					   int *cause)
 | 
			
		||||
{
 | 
			
		||||
	char *endptr = NULL;
 | 
			
		||||
	unsigned int gw = INT_MAX;
 | 
			
		||||
	const char *endpoint_number_str;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
 | 
			
		||||
	*cause = 0;
 | 
			
		||||
 | 
			
		||||
	/* Check if the domainname in the request is correct */
 | 
			
		||||
	if (check_domain_name(cfg, mgcp)) {
 | 
			
		||||
		*cause = -500;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if the E1 trunk is requested */
 | 
			
		||||
	if (strncmp(mgcp, "ds/e1", 5) == 0) {
 | 
			
		||||
		endp = find_e1_endpoint(cfg, mgcp);
 | 
			
		||||
		if (!endp)
 | 
			
		||||
			*cause = -500;
 | 
			
		||||
		return endp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if the virtual trunk is addressed (new, correct way with prefix) */
 | 
			
		||||
	if (strncmp
 | 
			
		||||
	    (mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
 | 
			
		||||
	     strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) {
 | 
			
		||||
		endpoint_number_str =
 | 
			
		||||
		    mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
 | 
			
		||||
		if (endpoint_number_str[0] == '*') {
 | 
			
		||||
			endp = find_free_endpoint(cfg->trunk.endpoints,
 | 
			
		||||
						  cfg->trunk.number_endpoints);
 | 
			
		||||
			if (!endp)
 | 
			
		||||
				*cause = -403;
 | 
			
		||||
			return endp;
 | 
			
		||||
		}
 | 
			
		||||
		gw = strtoul(endpoint_number_str, &endptr, 16);
 | 
			
		||||
		if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
 | 
			
		||||
			endp = &cfg->trunk.endpoints[gw];
 | 
			
		||||
			endp->wildcarded_req = false;
 | 
			
		||||
			return endp;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Deprecated method without prefix */
 | 
			
		||||
	LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
	     "Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n",
 | 
			
		||||
	     MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp);
 | 
			
		||||
	gw = strtoul(mgcp, &endptr, 16);
 | 
			
		||||
	if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
 | 
			
		||||
		endp = &cfg->trunk.endpoints[gw];
 | 
			
		||||
		endp->wildcarded_req = false;
 | 
			
		||||
		return endp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
 | 
			
		||||
	*cause = -500;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Analyze and parse the the hader of an MGCP messeage string.
 | 
			
		||||
 *  \param[out] pdata caller provided memory to store the parsing results.
 | 
			
		||||
 *  \param[in] data mgcp message string.
 | 
			
		||||
 *  \returns 0 when the status line was complete and parseable, negative (MGCP
 | 
			
		||||
 *  cause code) on error. */
 | 
			
		||||
 *  \param[out] pdata caller provided memory to store the parsing results
 | 
			
		||||
 *  \param[in] data mgcp message string
 | 
			
		||||
 *  \returns when the status line was complete and transaction_id and
 | 
			
		||||
 *  endp out parameters are set, -1 on error */
 | 
			
		||||
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
 | 
			
		||||
{
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	char *elem, *save = NULL;
 | 
			
		||||
	int cause;
 | 
			
		||||
 | 
			
		||||
	/*! This function will parse the header part of the received
 | 
			
		||||
	 *  MGCP message. The parsing results are stored in pdata. */
 | 
			
		||||
	 *  MGCP message. The parsing results are stored in pdata.
 | 
			
		||||
	 *  The function will also automatically search the pool with
 | 
			
		||||
	 *  available endpoints in order to find an endpoint that matches
 | 
			
		||||
	 *  the endpoint string in in the header */
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(data);
 | 
			
		||||
	pdata->trans = "000000";
 | 
			
		||||
 | 
			
		||||
	for (elem = strtok_r(data, " ", &save); elem;
 | 
			
		||||
	     elem = strtok_r(NULL, " ", &save)) {
 | 
			
		||||
@@ -123,104 +316,42 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
 | 
			
		||||
			pdata->trans = elem;
 | 
			
		||||
			break;
 | 
			
		||||
		case 1:
 | 
			
		||||
			pdata->epname = elem;
 | 
			
		||||
			pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
 | 
			
		||||
			if (!pdata->endp) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
				     "Unable to find Endpoint `%s'\n", elem);
 | 
			
		||||
				OSMO_ASSERT(cause < 0);
 | 
			
		||||
				return cause;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case 2:
 | 
			
		||||
			if (strcasecmp("MGCP", elem)) {
 | 
			
		||||
				LOG_MGCP_PDATA(pdata, LOGL_ERROR, "MGCP header parsing error\n");
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
				     "MGCP header parsing error\n");
 | 
			
		||||
				return -510;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case 3:
 | 
			
		||||
			if (strcmp("1.0", elem))
 | 
			
		||||
			if (strcmp("1.0", elem)) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
 | 
			
		||||
				     "not supported\n", elem);
 | 
			
		||||
				return -528;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (i != 4) {
 | 
			
		||||
		LOG_MGCP_PDATA(pdata, LOGL_ERROR, "MGCP status line too short.\n");
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
 | 
			
		||||
		pdata->trans = "000000";
 | 
			
		||||
		pdata->endp = NULL;
 | 
			
		||||
		return -510;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool parse_x_osmo_ign(struct mgcp_parse_data *pdata, char *line)
 | 
			
		||||
{
 | 
			
		||||
	char *saveptr = NULL;
 | 
			
		||||
 | 
			
		||||
	if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
 | 
			
		||||
		return false;
 | 
			
		||||
	line += strlen(MGCP_X_OSMO_IGN_HEADER);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		char *token = strtok_r(line, " ", &saveptr);
 | 
			
		||||
		line = NULL;
 | 
			
		||||
		if (!token)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (!strcasecmp(token, "C"))
 | 
			
		||||
			pdata->hpars.x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
 | 
			
		||||
		else
 | 
			
		||||
			LOG_MGCP_PDATA(pdata, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Analyze and parse the the header of an MGCP message string.
 | 
			
		||||
 *  \param[inout] pdata caller provided memory to store the parsing results.
 | 
			
		||||
 *  \returns 0 when parsing was successful, negative (MGCP cause code) on error. */
 | 
			
		||||
int mgcp_parse_hdr_pars(struct mgcp_parse_data *pdata)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_parse_hdr_pars *hp = &pdata->hpars;
 | 
			
		||||
	char *line;
 | 
			
		||||
 | 
			
		||||
	mgcp_parse_hdr_pars_init(hp);
 | 
			
		||||
 | 
			
		||||
	for_each_line(line, pdata->save) {
 | 
			
		||||
		if (!mgcp_check_param(line)) {
 | 
			
		||||
			LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "wrong MGCP option format: '%s'\n", line);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch (toupper(line[0])) {
 | 
			
		||||
		case 'L':
 | 
			
		||||
			hp->local_options = (const char *)line + 3;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'C':
 | 
			
		||||
			hp->callid = (const char *)line + 3;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'I':
 | 
			
		||||
			hp->connid = (const char *)line + 3;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'M':
 | 
			
		||||
			hp->mode = mgcp_parse_conn_mode((const char *)line + 3);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'X':
 | 
			
		||||
			if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
 | 
			
		||||
				hp->remote_osmux_cid = mgcp_parse_osmux_cid(line);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			if (parse_x_osmo_ign(pdata, line))
 | 
			
		||||
				break;
 | 
			
		||||
			/* Ignore unknown X-headers */
 | 
			
		||||
			break;
 | 
			
		||||
		case '\0':
 | 
			
		||||
			hp->have_sdp = true;
 | 
			
		||||
			goto mgcp_header_done;
 | 
			
		||||
		default:
 | 
			
		||||
			LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "CRCX: unhandled option: '%c'/%d\n", *line, *line);
 | 
			
		||||
			return -539;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
mgcp_header_done:
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Extract OSMUX CID from an MGCP parameter line (string).
 | 
			
		||||
 *  \param[in] line single parameter line from the MGCP message
 | 
			
		||||
 *  \returns OSMUX CID, -1 wildcard, -2 on error */
 | 
			
		||||
@@ -231,43 +362,48 @@ int mgcp_parse_osmux_cid(const char *line)
 | 
			
		||||
 | 
			
		||||
	if (strcasecmp(line + 2, "Osmux: *") == 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
 | 
			
		||||
		return MGCP_PARSE_HDR_PARS_OSMUX_CID_WILDCARD;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
 | 
			
		||||
		     line);
 | 
			
		||||
		return MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET;
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (osmux_cid > OSMUX_CID_MAX) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
 | 
			
		||||
		     osmux_cid, OSMUX_CID_MAX);
 | 
			
		||||
		return MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET;
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "MGCP client offered Osmux CID %u\n", osmux_cid);
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
 | 
			
		||||
 | 
			
		||||
	return osmux_cid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Check MGCP parameter line (string) for plausibility.
 | 
			
		||||
 *  \param[in] endp pointer to endpoint (only used for log output)
 | 
			
		||||
 *  \param[in] line single parameter line from the MGCP message
 | 
			
		||||
 *  \returns true when line seems plausible, false on error */
 | 
			
		||||
bool mgcp_check_param(const char *line)
 | 
			
		||||
 *  \returns 1 when line seems plausible, 0 on error */
 | 
			
		||||
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
 | 
			
		||||
{
 | 
			
		||||
	const size_t line_len = strlen(line);
 | 
			
		||||
	if (line[0] != '\0' && line_len < 2)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (line[0] != '\0' && line_len < 2) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Wrong MGCP option format: '%s' on 0x%x\n",
 | 
			
		||||
		     line, ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* FIXME: A couple more checks wouldn't hurt... */
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Check if the specified callid seems plausible.
 | 
			
		||||
  * \param[in] endp pointer to endpoint
 | 
			
		||||
  * \param{in] callid to verify
 | 
			
		||||
  * \returns 0 when callid seems plausible, -1 on error */
 | 
			
		||||
  * \returns 1 when callid seems plausible, 0 on error */
 | 
			
		||||
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
 | 
			
		||||
{
 | 
			
		||||
	/*! This function compares the supplied callid with the called that is
 | 
			
		||||
@@ -328,7 +464,7 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if connection exists */
 | 
			
		||||
	if (mgcp_endp_get_conn(endp, conn_id))
 | 
			
		||||
	if (mgcp_conn_get(endp, conn_id))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,347 +0,0 @@
 | 
			
		||||
/* 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." },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "mgcp",
 | 
			
		||||
	.group_description = "mgcp general statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.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." },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "crcx",
 | 
			
		||||
	.group_description = "crxc statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.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." },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "mdcx",
 | 
			
		||||
	.group_description = "mdcx statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.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." },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "dlcx",
 | 
			
		||||
	.group_description = "dlcx statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.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." }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "e1",
 | 
			
		||||
	.group_description = "e1 statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(e1_rate_ctr_desc),
 | 
			
		||||
	.ctr_desc = e1_rate_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "all_rtp_conn",
 | 
			
		||||
	.group_description = "aggregated statistics for all rtp connections",
 | 
			
		||||
	.class_id = 1,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc),
 | 
			
		||||
	.ctr_desc = all_rtp_conn_rate_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_group_desc all_osmux_conn_rate_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "all_osmux_conn",
 | 
			
		||||
	.group_description = "aggregated statistics for all osmux connections",
 | 
			
		||||
	.class_id = 1,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(all_osmux_conn_rate_ctr_desc),
 | 
			
		||||
	.ctr_desc = all_osmux_conn_rate_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! allocate global rate counters
 | 
			
		||||
 *  (called once at startup).
 | 
			
		||||
 *  \param[in] cfg mgw configuration for which the rate counters are allocated.
 | 
			
		||||
 *  \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;
 | 
			
		||||
	static atomic_uint all_osmux_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);
 | 
			
		||||
	}
 | 
			
		||||
	if (ratectr->all_osmux_conn_stats == NULL) {
 | 
			
		||||
		ratectr->all_osmux_conn_stats = rate_ctr_group_alloc(trunk, &all_osmux_conn_rate_ctr_group_desc,
 | 
			
		||||
								   all_osmux_conn_rate_ctr_index++);
 | 
			
		||||
		if (!ratectr->all_osmux_conn_stats)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		snprintf(ctr_name, sizeof(ctr_name), "%s-%u:osmux_conn", mgcp_trunk_type_strs_str(trunk->trunk_type),
 | 
			
		||||
			 trunk->trunk_nr);
 | 
			
		||||
		rate_ctr_group_set_name(ratectr->all_osmux_conn_stats, ctr_name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* E1 specific */
 | 
			
		||||
	if (trunk->trunk_type == MGCP_TRUNK_E1 && ratectr->e1_stats == NULL) {
 | 
			
		||||
		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;
 | 
			
		||||
	}
 | 
			
		||||
	if (ratectr->all_osmux_conn_stats) {
 | 
			
		||||
		rate_ctr_group_free(ratectr->all_osmux_conn_stats);
 | 
			
		||||
		ratectr->all_osmux_conn_stats = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* E1 specific */
 | 
			
		||||
	if (ratectr->e1_stats) {
 | 
			
		||||
		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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,104 +0,0 @@
 | 
			
		||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2012 by On-Waves
 | 
			
		||||
 * (C) 2013-2024 by sysmocom - s.f.m.c. GmbH
 | 
			
		||||
 * 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 <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/osmo_io.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_rtp_end.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
 | 
			
		||||
/***********************
 | 
			
		||||
 * mgcp_rtp_end
 | 
			
		||||
 **********************/
 | 
			
		||||
 | 
			
		||||
void mgcp_rtp_end_init(struct mgcp_rtp_end *end, struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk *trunk = conn_rtp->conn->endp->trunk;
 | 
			
		||||
	struct mgcp_config *cfg = trunk->cfg;
 | 
			
		||||
 | 
			
		||||
	end->conn_rtp = conn_rtp;
 | 
			
		||||
	end->rtp = NULL;
 | 
			
		||||
	end->rtcp = NULL;
 | 
			
		||||
	memset(&end->addr, 0, sizeof(end->addr));
 | 
			
		||||
	end->rtcp_port = 0;
 | 
			
		||||
 | 
			
		||||
	/* Set default values */
 | 
			
		||||
	end->frames_per_packet = 0;	/* unknown */
 | 
			
		||||
	end->output_enabled = false;
 | 
			
		||||
	end->maximum_packet_time = -1;
 | 
			
		||||
 | 
			
		||||
	end->force_aligned_timing = trunk->force_aligned_timing;
 | 
			
		||||
	end->force_constant_ssrc = trunk->force_constant_ssrc;
 | 
			
		||||
	end->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
 | 
			
		||||
 | 
			
		||||
	if (cfg->force_ptime) {
 | 
			
		||||
		end->packet_duration_ms = cfg->force_ptime;
 | 
			
		||||
		end->force_output_ptime = 1;
 | 
			
		||||
	} else {
 | 
			
		||||
		end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Make sure codec table is reset */
 | 
			
		||||
	mgcp_codecset_reset(&end->cset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_rtp_end_cleanup(struct mgcp_rtp_end *end)
 | 
			
		||||
{
 | 
			
		||||
	mgcp_rtp_end_free_port(end);
 | 
			
		||||
	mgcp_codecset_reset(&end->cset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_rtp_end_set_packet_duration_ms(struct mgcp_rtp_end *end, uint32_t packet_duration_ms)
 | 
			
		||||
{
 | 
			
		||||
	if (end->force_output_ptime)
 | 
			
		||||
		return;
 | 
			
		||||
	end->packet_duration_ms = packet_duration_ms;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
 | 
			
		||||
{
 | 
			
		||||
	return (osmo_sockaddr_port(&rtp_end->addr.u.sa) != 0) &&
 | 
			
		||||
	       (osmo_sockaddr_is_any(&rtp_end->addr) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! free allocated RTP and RTCP ports.
 | 
			
		||||
 *  \param[in] end RTP end */
 | 
			
		||||
void mgcp_rtp_end_free_port(struct mgcp_rtp_end *end)
 | 
			
		||||
{
 | 
			
		||||
	if (end->rtp) {
 | 
			
		||||
		osmo_iofd_free(end->rtp);
 | 
			
		||||
		end->rtp = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (end->rtcp) {
 | 
			
		||||
		osmo_iofd_free(end->rtcp);
 | 
			
		||||
		end->rtcp = NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -21,19 +21,12 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/sockaddr_str.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_internal.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>
 | 
			
		||||
@@ -98,7 +91,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_type, const char *audio_name)
 | 
			
		||||
			  int payload, const char *audio_name)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
@@ -110,7 +103,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_type)
 | 
			
		||||
		if (codecs[i].payload_type != payload)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (sscanf(audio_name, "%63[^/]/%d/%d",
 | 
			
		||||
@@ -127,7 +120,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_type,
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
 | 
			
		||||
	     audio_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -239,12 +232,13 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
 | 
			
		||||
		if (delimiter == ';' || delimiter == ',')
 | 
			
		||||
			str_ptr[strlen(str_ptr) - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
		/* AMR octet aligned parameter (see also RFC 3267, section 8.3) */
 | 
			
		||||
		/* AMR octet aligned parameter */
 | 
			
		||||
		if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
 | 
			
		||||
			fmtp_param->param.amr_octet_aligned_present = true;
 | 
			
		||||
			fmtp_param->param.amr_octet_aligned = false;
 | 
			
		||||
			if (amr_octet_aligned == 1)
 | 
			
		||||
				fmtp_param->param.amr_octet_aligned = true;
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		param_str = strtok(NULL, " ");
 | 
			
		||||
@@ -261,42 +255,6 @@ 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)
 | 
			
		||||
@@ -312,30 +270,38 @@ static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Analyze SDP input string.
 | 
			
		||||
 *  \param[inout] p provided memory to store the parsing results.
 | 
			
		||||
 *  \param[in] endp trunk endpoint.
 | 
			
		||||
 *  \param[out] conn associated rtp connection.
 | 
			
		||||
 *  \param[out] caller provided memory to store the parsing results.
 | 
			
		||||
 *
 | 
			
		||||
 *  Note: In conn (conn->end) the function returns the packet duration,
 | 
			
		||||
 *  rtp port, rtcp port and the codec information.
 | 
			
		||||
 *  \returns 0 on success, -1 on failure. */
 | 
			
		||||
int mgcp_parse_sdp_data(struct mgcp_parse_data *p)
 | 
			
		||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
			struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(p);
 | 
			
		||||
	struct mgcp_parse_sdp *sdp = &p->sdp;
 | 
			
		||||
	struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int codecs_used = 0;
 | 
			
		||||
	struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int fmtp_used = 0;
 | 
			
		||||
	struct mgcp_codec_param *codec_param;
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
	char *line;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	void *tmp_ctx = talloc_new(NULL);
 | 
			
		||||
	struct mgcp_rtp_end *rtp;
 | 
			
		||||
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	int payload;
 | 
			
		||||
	int ptime, ptime2 = 0;
 | 
			
		||||
	char audio_name[64];
 | 
			
		||||
	int port, rc;
 | 
			
		||||
	char ipv4[16];
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
	OSMO_ASSERT(p);
 | 
			
		||||
 | 
			
		||||
	rtp = &conn->end;
 | 
			
		||||
	memset(&codecs, 0, sizeof(codecs));
 | 
			
		||||
	mgcp_parse_sdp_init(sdp);
 | 
			
		||||
 | 
			
		||||
	for_each_line(line, p->save) {
 | 
			
		||||
		switch (line[0]) {
 | 
			
		||||
@@ -346,26 +312,26 @@ int mgcp_parse_sdp_data(struct mgcp_parse_data *p)
 | 
			
		||||
			/* skip these SDP attributes */
 | 
			
		||||
			break;
 | 
			
		||||
		case 'a':
 | 
			
		||||
			if (sscanf(line, "a=rtpmap:%d %63s", &payload_type, audio_name) == 2) {
 | 
			
		||||
				codecs_update(tmp_ctx, codecs, codecs_used, payload_type, audio_name);
 | 
			
		||||
			if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
 | 
			
		||||
				codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
 | 
			
		||||
				if (ptime2 > 0 && ptime2 != ptime)
 | 
			
		||||
					sdp->ptime = 0;
 | 
			
		||||
					rtp->packet_duration_ms = 0;
 | 
			
		||||
				else
 | 
			
		||||
					sdp->ptime = ptime;
 | 
			
		||||
					rtp->packet_duration_ms = ptime;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
 | 
			
		||||
				sdp->maxptime = ptime2;
 | 
			
		||||
				rtp->maximum_packet_time = ptime2;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (strncmp("a=fmtp:", line, 6) == 0) {
 | 
			
		||||
				rc = fmtp_from_sdp(tmp_ctx, &fmtp_params[fmtp_used], line);
 | 
			
		||||
				rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line);
 | 
			
		||||
				if (rc >= 0)
 | 
			
		||||
					fmtp_used++;
 | 
			
		||||
				break;
 | 
			
		||||
@@ -374,23 +340,32 @@ int mgcp_parse_sdp_data(struct mgcp_parse_data *p)
 | 
			
		||||
			break;
 | 
			
		||||
		case 'm':
 | 
			
		||||
			rc = sscanf(line, "m=audio %d RTP/AVP", &port);
 | 
			
		||||
			if (rc == 1)
 | 
			
		||||
				sdp->rtp_port = port;
 | 
			
		||||
			if (rc == 1) {
 | 
			
		||||
				rtp->rtp_port = htons(port);
 | 
			
		||||
				rtp->rtcp_port = htons(port + 1);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			rc = pt_from_sdp(tmp_ctx, codecs, ARRAY_SIZE(codecs), line);
 | 
			
		||||
			rc = pt_from_sdp(conn->conn, codecs,
 | 
			
		||||
					 ARRAY_SIZE(codecs), line);
 | 
			
		||||
			if (rc > 0)
 | 
			
		||||
				codecs_used = rc;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'c':
 | 
			
		||||
			if (audio_ip_from_sdp(&sdp->rem_addr, line) < 0) {
 | 
			
		||||
				talloc_free(tmp_ctx);
 | 
			
		||||
				return -1;
 | 
			
		||||
 | 
			
		||||
			if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
 | 
			
		||||
				inet_aton(ipv4, &rtp->addr);
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			if (p->endp)
 | 
			
		||||
				LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
			     "Unhandled SDP option: '%c'/%d on %s\n",
 | 
			
		||||
			     line[0], line[0], p->epname);
 | 
			
		||||
				     "Unhandled SDP option: '%c'/%d on 0x%x\n",
 | 
			
		||||
				     line[0], line[0],
 | 
			
		||||
				     ENDPOINT_NUMBER(p->endp));
 | 
			
		||||
			else
 | 
			
		||||
				LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
				     "Unhandled SDP option: '%c'/%d\n",
 | 
			
		||||
				     line[0], line[0]);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -404,22 +379,23 @@ int mgcp_parse_sdp_data(struct mgcp_parse_data *p)
 | 
			
		||||
	/* Store parsed codec information */
 | 
			
		||||
	for (i = 0; i < codecs_used; i++) {
 | 
			
		||||
		codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
 | 
			
		||||
		rc = mgcp_codecset_add_codec(&sdp->cset, codecs[i].payload_type, codecs[i].map_line, codec_param);
 | 
			
		||||
		rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE, "%s: failed to add codec\n", p->epname);
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(tmp_ctx);
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
	     "%s: Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
 | 
			
		||||
	     p->epname, sdp->rtp_port, osmo_sockaddr_ntop(&sdp->rem_addr.u.sa, ipbuf), sdp->ptime);
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
	     "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
 | 
			
		||||
	     ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
 | 
			
		||||
	     rtp->packet_duration_ms);
 | 
			
		||||
	if (codecs_used == 0)
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_NOTICE, "none");
 | 
			
		||||
	for (i = 0; i < codecs_used; i++) {
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
 | 
			
		||||
		      sdp->cset.codecs[i].payload_type,
 | 
			
		||||
		      strlen(sdp->cset.codecs[i].subtype_name) ? sdp->cset.codecs[i].subtype_name : "unknown");
 | 
			
		||||
		      rtp->codecs[i].payload_type,
 | 
			
		||||
		      rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_NOTICE, " ");
 | 
			
		||||
	}
 | 
			
		||||
	LOGPC(DLMGCP, LOGL_NOTICE, "\n");
 | 
			
		||||
@@ -469,15 +445,37 @@ static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add fmtp strings to sdp payload */
 | 
			
		||||
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
 | 
			
		||||
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
 | 
			
		||||
		    const char *fmtp_extra)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int fmtp_extra_pt = -1;
 | 
			
		||||
	char *fmtp_extra_pars = "";
 | 
			
		||||
 | 
			
		||||
	/* When no fmtp parameters ara available but an fmtp extra string
 | 
			
		||||
	 * is configured, just add the fmtp extra string */
 | 
			
		||||
	if (fmtp_params_len == 0 && fmtp_extra) {
 | 
			
		||||
		return msgb_printf(sdp, "%s\r\n", fmtp_extra);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* When there is fmtp extra configured we dissect it in order to drop
 | 
			
		||||
	 * in the configured extra parameters at the right place when
 | 
			
		||||
	 * generating the fmtp strings. */
 | 
			
		||||
	if (fmtp_extra) {
 | 
			
		||||
		if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
 | 
			
		||||
			fmtp_extra_pt = -1;
 | 
			
		||||
 | 
			
		||||
		fmtp_extra_pars = strstr(fmtp_extra, " ");
 | 
			
		||||
 | 
			
		||||
		if (!fmtp_extra_pars)
 | 
			
		||||
			fmtp_extra_pars = "";
 | 
			
		||||
		else
 | 
			
		||||
			fmtp_extra_pars++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < fmtp_params_len; i++) {
 | 
			
		||||
		rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		/* Add amr octet align parameter */
 | 
			
		||||
		if (fmtp_params[i].param.amr_octet_aligned_present) {
 | 
			
		||||
@@ -489,7 +487,14 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rc = msgb_printf(sdp, "\r\n");
 | 
			
		||||
		/* Append extra parameters from fmtp extra */
 | 
			
		||||
		if (fmtp_params[i].payload_type == fmtp_extra_pt) {
 | 
			
		||||
			rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rc = msgb_printf(sdp, "\r\n", fmtp_params[i].payload_type);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
@@ -508,6 +513,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
			    const char *addr)
 | 
			
		||||
{
 | 
			
		||||
	const struct mgcp_rtp_codec *codec;
 | 
			
		||||
	const char *fmtp_extra;
 | 
			
		||||
	const char *audio_name;
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	struct sdp_fmtp_param fmtp_param;
 | 
			
		||||
@@ -516,28 +522,26 @@ 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);
 | 
			
		||||
	OSMO_ASSERT(sdp);
 | 
			
		||||
	OSMO_ASSERT(addr);
 | 
			
		||||
 | 
			
		||||
	codec = conn->end.cset.codec;
 | 
			
		||||
	/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
 | 
			
		||||
	endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
 | 
			
		||||
					      &codec, &fmtp_extra,
 | 
			
		||||
					      (struct mgcp_conn_rtp *)conn);
 | 
			
		||||
 | 
			
		||||
	audio_name = codec->audio_name;
 | 
			
		||||
	payload_type = codec->payload_type;
 | 
			
		||||
 | 
			
		||||
	addr_is_v6 = osmo_ip_str_type(addr) == AF_INET6;
 | 
			
		||||
 | 
			
		||||
	rc = msgb_printf(sdp,
 | 
			
		||||
			 "v=0\r\n"
 | 
			
		||||
			 "o=- %s 23 IN IP%c %s\r\n"
 | 
			
		||||
			 "o=- %s 23 IN IP4 %s\r\n"
 | 
			
		||||
			 "s=-\r\n"
 | 
			
		||||
			 "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);
 | 
			
		||||
			 "c=IN IP4 %s\r\n"
 | 
			
		||||
			 "t=0 0\r\n", conn->conn->id, addr, addr);
 | 
			
		||||
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		goto buffer_too_small;
 | 
			
		||||
@@ -546,14 +550,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->trunk->cfg->osmux.local_port;
 | 
			
		||||
			local_port = endp->cfg->osmux_port;
 | 
			
		||||
		else
 | 
			
		||||
			local_port = conn->end.local_port;
 | 
			
		||||
		rc = add_audio(sdp, payload_types, 1, local_port);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto buffer_too_small;
 | 
			
		||||
 | 
			
		||||
		if (endp->trunk->audio_send_name) {
 | 
			
		||||
		if (endp->tcfg->audio_send_name) {
 | 
			
		||||
			rc = add_rtpmap(sdp, payload_type, audio_name);
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				goto buffer_too_small;
 | 
			
		||||
@@ -565,11 +569,11 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
			fmtp_params[0] = fmtp_param;
 | 
			
		||||
			fmtp_params_len = 1;
 | 
			
		||||
		}
 | 
			
		||||
		rc = add_fmtp(sdp, fmtp_params, fmtp_params_len);
 | 
			
		||||
		rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto buffer_too_small;
 | 
			
		||||
	}
 | 
			
		||||
	if (conn->end.packet_duration_ms > 0 && endp->trunk->audio_send_ptime) {
 | 
			
		||||
	if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
 | 
			
		||||
		rc = msgb_printf(sdp, "a=ptime:%u\r\n",
 | 
			
		||||
				 conn->end.packet_duration_ms);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
 
 | 
			
		||||
@@ -22,24 +22,16 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#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>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <inttypes.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 = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
 | 
			
		||||
	struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
 | 
			
		||||
 | 
			
		||||
	*expected = state->stats.cycles + state->stats.max_seq;
 | 
			
		||||
	*expected = *expected - state->stats.base_seq + 1;
 | 
			
		||||
@@ -80,10 +72,10 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
 | 
			
		||||
	int ploss;
 | 
			
		||||
	int nchars;
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
 | 
			
		||||
	struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
 | 
			
		||||
	struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
 | 
			
		||||
	struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
 | 
			
		||||
	struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
 | 
			
		||||
	struct rate_ctr *octets_rx = &conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR];
 | 
			
		||||
	struct rate_ctr *packets_tx = &conn->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR];
 | 
			
		||||
	struct rate_ctr *octets_tx = &conn->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR];
 | 
			
		||||
 | 
			
		||||
	calc_loss(conn, &expected, &ploss);
 | 
			
		||||
	jitter = calc_jitter(&conn->state);
 | 
			
		||||
@@ -99,7 +91,7 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
 | 
			
		||||
	str += nchars;
 | 
			
		||||
	str_len -= nchars;
 | 
			
		||||
 | 
			
		||||
	if (conn->conn->endp->trunk->cfg->osmux.usage != OSMUX_USAGE_OFF) {
 | 
			
		||||
	if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
 | 
			
		||||
		/* Error Counter */
 | 
			
		||||
		nchars = snprintf(str, str_len,
 | 
			
		||||
				  "\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
 | 
			
		||||
@@ -112,12 +104,9 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
 | 
			
		||||
		str_len -= nchars;
 | 
			
		||||
 | 
			
		||||
		if (conn->osmux.state == OSMUX_STATE_ENABLED) {
 | 
			
		||||
			struct rate_ctr *osmux_chunks_rx, *osmux_octets_rx;
 | 
			
		||||
			osmux_chunks_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_CHUNKS_RX_CTR);
 | 
			
		||||
			osmux_octets_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_OCTETS_RX_CTR);
 | 
			
		||||
			snprintf(str, str_len,
 | 
			
		||||
				 "\r\nX-Osmux-ST: CR=%" PRIu64 ", BR=%" PRIu64,
 | 
			
		||||
				 osmux_chunks_rx->current, osmux_octets_rx->current);
 | 
			
		||||
				 "\r\nX-Osmux-ST: CR=%u, BR=%u",
 | 
			
		||||
				 conn->osmux.stats.chunks, conn->osmux.stats.octets);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -141,7 +130,7 @@ void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn)
 | 
			
		||||
	 * keep this option open: */
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		mgcp_format_stats_rtp(str, str_len, mgcp_conn_get_conn_rtp(conn));
 | 
			
		||||
		mgcp_format_stats_rtp(str, str_len, &conn->u.rtp);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,207 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (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;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,348 +0,0 @@
 | 
			
		||||
/* 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Try to find a free port by attempting to bind on it. Also handle the
 | 
			
		||||
 * counter that points on the next free port. Since we have a pointer
 | 
			
		||||
 * to the next free port, binding should in work on the first attempt in
 | 
			
		||||
 * general. In case of failure the next port is tried until the whole port
 | 
			
		||||
 * range is tried once. */
 | 
			
		||||
int mgcp_trunk_allocate_conn_rtp_ports(struct mgcp_trunk *trunk, struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	struct mgcp_port_range *range;
 | 
			
		||||
	unsigned int tries;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(trunk);
 | 
			
		||||
	OSMO_ASSERT(conn_rtp);
 | 
			
		||||
 | 
			
		||||
	range = &trunk->cfg->net_ports;
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&range->lock);
 | 
			
		||||
	/* attempt to find a port */
 | 
			
		||||
	tries = (range->range_end - range->range_start) / 2;
 | 
			
		||||
	for (i = 0; i < tries; ++i) {
 | 
			
		||||
		int rc;
 | 
			
		||||
 | 
			
		||||
		if (range->last_port >= range->range_end)
 | 
			
		||||
			range->last_port = range->range_start;
 | 
			
		||||
 | 
			
		||||
		rc = mgcp_conn_rtp_bind_rtp_ports(conn_rtp, range->last_port);
 | 
			
		||||
 | 
			
		||||
		range->last_port += 2;
 | 
			
		||||
		if (rc == 0) {
 | 
			
		||||
			pthread_mutex_unlock(&range->lock);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	pthread_mutex_unlock(&range->lock);
 | 
			
		||||
	LOGPCONN(conn_rtp->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
		 "Allocating a RTP/RTCP port failed %u times.\n", tries);
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -9,10 +9,7 @@ AM_CFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOCTRL_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOABIS_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOTRAU_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
@@ -29,8 +26,5 @@ osmo_mgw_LDADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOCTRL_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(LIBOSMOTRAU_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -30,17 +30,14 @@
 | 
			
		||||
#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_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/vty.h>
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
@@ -50,8 +47,6 @@
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/ctrl/control_if.h>
 | 
			
		||||
#include <osmocom/ctrl/control_vty.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/telnet_interface.h>
 | 
			
		||||
#include <osmocom/vty/logging.h>
 | 
			
		||||
@@ -59,30 +54,22 @@
 | 
			
		||||
#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 *reset_trunk;
 | 
			
		||||
static struct mgcp_trunk_config *reset_trunk;
 | 
			
		||||
static int reset_endpoints = 0;
 | 
			
		||||
static int daemonize = 0;
 | 
			
		||||
 | 
			
		||||
const char *osmomgw_copyright =
 | 
			
		||||
	"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
 | 
			
		||||
	"Copyright (C) 2017-2022 by sysmocom s.f.m.c. GmbH\r\n"
 | 
			
		||||
	"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
 | 
			
		||||
	"Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n"
 | 
			
		||||
	"Philipp Maier\r\n\r\n"
 | 
			
		||||
	"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
 | 
			
		||||
@@ -92,60 +79,28 @@ const char *osmomgw_copyright =
 | 
			
		||||
static char *config_file = "osmo-mgw.cfg";
 | 
			
		||||
 | 
			
		||||
/* used by msgb and mgcp */
 | 
			
		||||
void *tall_mgw_ctx = NULL;
 | 
			
		||||
void *tall_bsc_ctx = NULL;
 | 
			
		||||
 | 
			
		||||
static void print_help(void)
 | 
			
		||||
static void print_help()
 | 
			
		||||
{
 | 
			
		||||
	printf("Some useful options:\n");
 | 
			
		||||
	printf("Some useful help...\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},
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
@@ -159,11 +114,8 @@ 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_mgw_ctx, optarg);
 | 
			
		||||
			config_file = talloc_strdup(tall_bsc_ctx, optarg);
 | 
			
		||||
			break;
 | 
			
		||||
		case 's':
 | 
			
		||||
			log_set_use_color(osmo_stderr_target, 0);
 | 
			
		||||
@@ -188,20 +140,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 *trunk)
 | 
			
		||||
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
 | 
			
		||||
{
 | 
			
		||||
	/* Set flag so that, when read_call_agent() is called next time
 | 
			
		||||
	 * the reset can progress */
 | 
			
		||||
	reset_endpoints = 1;
 | 
			
		||||
 | 
			
		||||
	reset_trunk = trunk;
 | 
			
		||||
	reset_trunk = tcfg;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int read_call_agent(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_sockaddr addr;
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	socklen_t slen = sizeof(addr);
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	struct msgb *resp;
 | 
			
		||||
@@ -227,14 +179,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, &addr.u.sa, sizeof(addr));
 | 
			
		||||
		sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
 | 
			
		||||
		msgb_free(resp);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* reset endpoints */
 | 
			
		||||
	if (reset_endpoints) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
		     "Asked to reset endpoints: %u/%d\n",
 | 
			
		||||
		     "Asked to reset endpoints: %d/%d\n",
 | 
			
		||||
		     reset_trunk->trunk_nr, reset_trunk->trunk_type);
 | 
			
		||||
 | 
			
		||||
		/* reset flag */
 | 
			
		||||
@@ -242,13 +194,24 @@ 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 = 0; i < reset_trunk->number_endpoints; ++i)
 | 
			
		||||
			mgcp_endp_release(reset_trunk->endpoints[i]);
 | 
			
		||||
		for (i = 1; i < reset_trunk->number_endpoints; ++i)
 | 
			
		||||
			mgcp_endp_release(&reset_trunk->endpoints[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_vty_is_config_node(struct vty *vty, int node)
 | 
			
		||||
{
 | 
			
		||||
	switch (node) {
 | 
			
		||||
	case CONFIG_NODE:
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_vty_go_parent(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	switch (vty->node) {
 | 
			
		||||
@@ -258,7 +221,7 @@ int mgcp_vty_go_parent(struct vty *vty)
 | 
			
		||||
		break;
 | 
			
		||||
	case MGCP_NODE:
 | 
			
		||||
	default:
 | 
			
		||||
		if (vty->node != CONFIG_NODE)
 | 
			
		||||
		if (mgcp_vty_is_config_node(vty, vty->node))
 | 
			
		||||
			vty->node = CONFIG_NODE;
 | 
			
		||||
		else
 | 
			
		||||
			vty->node = ENABLE_NODE;
 | 
			
		||||
@@ -269,36 +232,12 @@ int mgcp_vty_go_parent(struct vty *vty)
 | 
			
		||||
	return vty->node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void signal_handler(int signum)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stdout, "signal %u received\n", signum);
 | 
			
		||||
 | 
			
		||||
	switch (signum) {
 | 
			
		||||
	case SIGABRT:
 | 
			
		||||
		/* in case of abort, we want to obtain a talloc report and
 | 
			
		||||
		 * then run default SIGABRT handler, who will generate coredump
 | 
			
		||||
		 * and abort the process. abort() should do this for us after we
 | 
			
		||||
		 * return, but program wouldn't exit if an external SIGABRT is
 | 
			
		||||
		 * received.
 | 
			
		||||
		 */
 | 
			
		||||
		talloc_report(tall_vty_ctx, stderr);
 | 
			
		||||
		talloc_report_full(tall_mgw_ctx, stderr);
 | 
			
		||||
		signal(SIGABRT, SIG_DFL);
 | 
			
		||||
		raise(SIGABRT);
 | 
			
		||||
		break;
 | 
			
		||||
	case SIGUSR1:
 | 
			
		||||
		talloc_report(tall_vty_ctx, stderr);
 | 
			
		||||
		talloc_report_full(tall_mgw_ctx, stderr);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct vty_app_info vty_info = {
 | 
			
		||||
	.name 		= "OsmoMGW",
 | 
			
		||||
	.version	= PACKAGE_VERSION,
 | 
			
		||||
	.go_parent_cb	= mgcp_vty_go_parent,
 | 
			
		||||
	.is_config_node	= mgcp_vty_is_config_node,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct log_info_cat log_categories[] = {
 | 
			
		||||
@@ -307,22 +246,13 @@ static const struct log_info_cat log_categories[] = {
 | 
			
		||||
		  .name = "DRTP",
 | 
			
		||||
		  .description = "RTP stream handling",
 | 
			
		||||
		  .color = "\033[1;30m",
 | 
			
		||||
		  .enabled = 1,
 | 
			
		||||
		  .loglevel = LOGL_NOTICE,
 | 
			
		||||
		  .enabled = 1,.loglevel = LOGL_NOTICE,
 | 
			
		||||
		  },
 | 
			
		||||
	[DE1] = {
 | 
			
		||||
		  .name = "DE1",
 | 
			
		||||
		  .description = "E1 line handling",
 | 
			
		||||
	[DIUUP] = {
 | 
			
		||||
		  .name = "DIUUP",
 | 
			
		||||
		  .description = "IuUP within RTP stream handling",
 | 
			
		||||
		  .color = "\033[1;31m",
 | 
			
		||||
		  .enabled = 1,
 | 
			
		||||
		  .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DOSMUX] = {
 | 
			
		||||
		  .name = "DOSMUX",
 | 
			
		||||
		  .description = "Osmux (Osmocom RTP multiplexing)",
 | 
			
		||||
		  .color = "\033[1;32m",
 | 
			
		||||
		  .enabled = 1,
 | 
			
		||||
		  .loglevel = LOGL_NOTICE,
 | 
			
		||||
		  .enabled = 1,.loglevel = LOGL_NOTICE,
 | 
			
		||||
		  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -336,67 +266,52 @@ int main(int argc, char **argv)
 | 
			
		||||
	unsigned int flags;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	tall_mgw_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
 | 
			
		||||
	vty_info.tall_ctx = tall_mgw_ctx;
 | 
			
		||||
	tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
 | 
			
		||||
	vty_info.tall_ctx = tall_bsc_ctx;
 | 
			
		||||
 | 
			
		||||
	msgb_talloc_ctx_init(tall_mgw_ctx, 0);
 | 
			
		||||
	msgb_talloc_ctx_init(tall_bsc_ctx, 0);
 | 
			
		||||
 | 
			
		||||
	signal(SIGABRT, &signal_handler);
 | 
			
		||||
	signal(SIGUSR1, &signal_handler);
 | 
			
		||||
	osmo_init_ignore_signals();
 | 
			
		||||
	osmo_init_logging2(tall_mgw_ctx, &log_info);
 | 
			
		||||
	libosmo_abis_init(tall_mgw_ctx);
 | 
			
		||||
	osmo_init_logging2(tall_bsc_ctx, &log_info);
 | 
			
		||||
 | 
			
		||||
	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_mgw_ctx);
 | 
			
		||||
	osmo_stats_init(tall_mgw_ctx);
 | 
			
		||||
	rate_ctr_init(tall_bsc_ctx);
 | 
			
		||||
	osmo_stats_init(tall_bsc_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_default(tall_mgw_ctx, NULL, OSMO_VTY_PORT_MGW);
 | 
			
		||||
	rc = telnet_init_dynif(tall_bsc_ctx, NULL,
 | 
			
		||||
			       vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	cfg->ctrl = ctrl_interface_setup(cfg, OSMO_CTRL_PORT_MGW, NULL);
 | 
			
		||||
	if (!cfg->ctrl) {
 | 
			
		||||
		fprintf(stderr, "Failed to init the control interface on %s:%u. Exiting\n",
 | 
			
		||||
			ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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 (strlen(cfg->call_agent_addr))
 | 
			
		||||
	if (cfg->call_agent_addr)
 | 
			
		||||
		flags |= OSMO_SOCK_F_CONNECT;
 | 
			
		||||
 | 
			
		||||
	rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
 | 
			
		||||
	rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
 | 
			
		||||
				cfg->source_addr, cfg->source_port,
 | 
			
		||||
				cfg->call_agent_addr, strlen(cfg->call_agent_addr) ? 2727 : 0, flags);
 | 
			
		||||
				cfg->call_agent_addr, cfg->call_agent_addr ? 2727 : 0, flags);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		perror("Gateway failed to bind");
 | 
			
		||||
		return -1;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	mgcp_client \
 | 
			
		||||
	mgcp \
 | 
			
		||||
	iuup \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
 | 
			
		||||
@@ -34,11 +35,11 @@ DISTCLEANFILES = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
if ENABLE_EXT_TESTS
 | 
			
		||||
python-tests: $(top_builddir)/src/osmo-mgw/osmo-mgw
 | 
			
		||||
python-tests: $(BUILT_SOURCES)
 | 
			
		||||
	osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
 | 
			
		||||
	osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
 | 
			
		||||
else
 | 
			
		||||
python-tests:
 | 
			
		||||
python-tests: $(BUILT_SOURCES)
 | 
			
		||||
	echo "Not running python-based tests (determined at configure-time)"
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								tests/iuup/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								tests/iuup/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_srcdir) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	-ggdb3 \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	iuup_test.ok \
 | 
			
		||||
	iuup_test.err \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS = \
 | 
			
		||||
	iuup_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
iuup_test_SOURCES = \
 | 
			
		||||
	iuup_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
iuup_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBRARY_DL) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	-lm  \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
update_exp:
 | 
			
		||||
	$(builddir)/iuup_test >$(srcdir)/iuup_test.ok 2>$(srcdir)/iuup_test.err
 | 
			
		||||
							
								
								
									
										156
									
								
								tests/iuup/iuup_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								tests/iuup/iuup_test.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,156 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/iuup_cn_node.h>
 | 
			
		||||
#include <osmocom/mgcp/iuup_protocol.h>
 | 
			
		||||
 | 
			
		||||
void *ctx = NULL;
 | 
			
		||||
 | 
			
		||||
static const char *dump(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	return osmo_hexdump_nospc(msg->data, msg->len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct msgb *msgb_from_hex(const char *label, const char *hex)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = msgb_alloc_headroom(4096 + OSMO_IUUP_HEADROOM,
 | 
			
		||||
					       OSMO_IUUP_HEADROOM, label);
 | 
			
		||||
	unsigned char *rc;
 | 
			
		||||
	msg->l2h = msg->data;
 | 
			
		||||
	rc = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
 | 
			
		||||
	OSMO_ASSERT(rc == msg->l2h);
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *expect_rx_payload = NULL;
 | 
			
		||||
int rx_payload(struct msgb *msg, void *node_priv)
 | 
			
		||||
{
 | 
			
		||||
	printf("rx_payload() invoked by iuup_cn!\n");
 | 
			
		||||
	printf("        [IuUP] -RTP->\n");
 | 
			
		||||
	printf("%s\n", dump(msg));
 | 
			
		||||
	printf("node_priv=%p\n", node_priv);
 | 
			
		||||
	if (!expect_rx_payload) {
 | 
			
		||||
		printf("ERROR: did not expect rx_payload()\n");
 | 
			
		||||
		exit(-1);
 | 
			
		||||
	} else if (strcmp(expect_rx_payload, dump(msg))) {
 | 
			
		||||
		printf("ERROR: mismatches expected msg %s\n", expect_rx_payload);
 | 
			
		||||
		exit(-1);
 | 
			
		||||
	} else
 | 
			
		||||
		printf("ok: matches expected msg\n");
 | 
			
		||||
	expect_rx_payload = NULL;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *expect_tx_msg = NULL;
 | 
			
		||||
int tx_msg(struct msgb *msg, void *node_priv)
 | 
			
		||||
{
 | 
			
		||||
	printf("tx_msg() invoked by iuup_cn!\n");
 | 
			
		||||
	printf(" <-PDU- [IuUP]\n");
 | 
			
		||||
	printf("%s\n", dump(msg));
 | 
			
		||||
	printf("node_priv=%p\n", node_priv);
 | 
			
		||||
	if (!expect_tx_msg) {
 | 
			
		||||
		printf("ERROR: did not expect tx_msg()\n");
 | 
			
		||||
		exit(-1);
 | 
			
		||||
	} else if (strcmp(expect_tx_msg, dump(msg))) {
 | 
			
		||||
		printf("ERROR: mismatches expected msg %s\n", expect_tx_msg);
 | 
			
		||||
		exit(-1);
 | 
			
		||||
	} else
 | 
			
		||||
		printf("ok: matches expected msg\n");
 | 
			
		||||
	expect_tx_msg = NULL;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rx_pdu(struct osmo_iuup_cn *cn, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	printf(" -PDU-> [IuUP]\n");
 | 
			
		||||
	printf("%s\n", dump(msg));
 | 
			
		||||
	rc = osmo_iuup_cn_rx_pdu(cn, msg);
 | 
			
		||||
	printf("rc=%d\n", rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tx_payload(struct osmo_iuup_cn *cn, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	printf("        [IuUP] <-RTP-\n");
 | 
			
		||||
	printf("%s\n", dump(msg));
 | 
			
		||||
	rc = osmo_iuup_cn_tx_payload(cn, msg);
 | 
			
		||||
	printf("rc=%d\n", rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_cn_session()
 | 
			
		||||
{
 | 
			
		||||
	void *node_priv = (void*)0x2342;
 | 
			
		||||
 | 
			
		||||
	struct osmo_iuup_cn_cfg cfg = {
 | 
			
		||||
		.node_priv = node_priv,
 | 
			
		||||
		.rx_payload = rx_payload,
 | 
			
		||||
		.tx_msg = tx_msg,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct osmo_iuup_cn *cn = osmo_iuup_cn_init(ctx, &cfg, __func__);
 | 
			
		||||
	OSMO_ASSERT(cn);
 | 
			
		||||
 | 
			
		||||
	printf("\nSend IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack\n");
 | 
			
		||||
	expect_tx_msg = "8060dc5219495e3f00010111" /* RTP header */
 | 
			
		||||
			"e4002400"; /* IuUP Init Ack */
 | 
			
		||||
	rx_pdu(cn,
 | 
			
		||||
	       msgb_from_hex("IuUP-Init",
 | 
			
		||||
			     "8060dc5219495e3f00010111" /* <- RTP header */
 | 
			
		||||
			     "e000df99" /* <- IuUP header */
 | 
			
		||||
			     "160051673c01270000820000001710000100" /* IuUP params */));
 | 
			
		||||
 | 
			
		||||
#define RTP_HEADER "8060944c6256042c00010102"
 | 
			
		||||
#define IUUP_HEADER "0100e2b3"
 | 
			
		||||
#define RTP_PAYLOAD "6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0"
 | 
			
		||||
	printf("\nReceive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet\n");
 | 
			
		||||
	printf("i.e. should strip away " IUUP_HEADER "\n");
 | 
			
		||||
	expect_rx_payload = RTP_HEADER "f03c" RTP_PAYLOAD;
 | 
			
		||||
	rx_pdu(cn,
 | 
			
		||||
	       msgb_from_hex("IuUP-Data",
 | 
			
		||||
			     RTP_HEADER IUUP_HEADER RTP_PAYLOAD));
 | 
			
		||||
 | 
			
		||||
	printf("\nTransmit RTP. Expecting tx_msg() with inserted IuUP header\n");
 | 
			
		||||
	expect_tx_msg = RTP_HEADER "000002b3" RTP_PAYLOAD;
 | 
			
		||||
	tx_payload(cn,
 | 
			
		||||
		   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
 | 
			
		||||
 | 
			
		||||
	printf("\nMore RTP, each time the Frame Nr advances, causing a new header CRC.\n");
 | 
			
		||||
	expect_tx_msg = RTP_HEADER "0100e2b3" RTP_PAYLOAD;
 | 
			
		||||
	tx_payload(cn,
 | 
			
		||||
		   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
 | 
			
		||||
	expect_tx_msg = RTP_HEADER "02007eb3" RTP_PAYLOAD;
 | 
			
		||||
	tx_payload(cn,
 | 
			
		||||
		   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
 | 
			
		||||
	expect_tx_msg = RTP_HEADER "03009eb3" RTP_PAYLOAD;
 | 
			
		||||
	tx_payload(cn,
 | 
			
		||||
		   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
 | 
			
		||||
 | 
			
		||||
	printf("All done.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct log_info_cat log_categories[] = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct log_info log_info = {
 | 
			
		||||
	.cat = log_categories,
 | 
			
		||||
	.num_cat = ARRAY_SIZE(log_categories),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(void)
 | 
			
		||||
{
 | 
			
		||||
	ctx = talloc_named_const(NULL, 0, __FILE__);
 | 
			
		||||
	void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
 | 
			
		||||
	osmo_init_logging2(ctx, &log_info);
 | 
			
		||||
 | 
			
		||||
	test_cn_session();
 | 
			
		||||
 | 
			
		||||
	talloc_free(msgb_ctx);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								tests/iuup/iuup_test.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/iuup/iuup_test.err
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										58
									
								
								tests/iuup/iuup_test.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								tests/iuup/iuup_test.ok
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
 | 
			
		||||
Send IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack
 | 
			
		||||
 -PDU-> [IuUP]
 | 
			
		||||
8060dc5219495e3f00010111e000df99160051673c01270000820000001710000100
 | 
			
		||||
tx_msg() invoked by iuup_cn!
 | 
			
		||||
 <-PDU- [IuUP]
 | 
			
		||||
8060dc5219495e3f00010111e4002400
 | 
			
		||||
node_priv=0x2342
 | 
			
		||||
ok: matches expected msg
 | 
			
		||||
rc=0
 | 
			
		||||
 | 
			
		||||
Receive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet
 | 
			
		||||
i.e. should strip away 0100e2b3
 | 
			
		||||
 -PDU-> [IuUP]
 | 
			
		||||
8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
			
		||||
rx_payload() invoked by iuup_cn!
 | 
			
		||||
        [IuUP] -RTP->
 | 
			
		||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
			
		||||
node_priv=0x2342
 | 
			
		||||
ok: matches expected msg
 | 
			
		||||
rc=0
 | 
			
		||||
 | 
			
		||||
Transmit RTP. Expecting tx_msg() with inserted IuUP header
 | 
			
		||||
        [IuUP] <-RTP-
 | 
			
		||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
			
		||||
tx_msg() invoked by iuup_cn!
 | 
			
		||||
 <-PDU- [IuUP]
 | 
			
		||||
8060944c6256042c00010102000002b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
			
		||||
node_priv=0x2342
 | 
			
		||||
ok: matches expected msg
 | 
			
		||||
rc=0
 | 
			
		||||
 | 
			
		||||
More RTP, each time the Frame Nr advances, causing a new header CRC.
 | 
			
		||||
        [IuUP] <-RTP-
 | 
			
		||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
			
		||||
tx_msg() invoked by iuup_cn!
 | 
			
		||||
 <-PDU- [IuUP]
 | 
			
		||||
8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
			
		||||
node_priv=0x2342
 | 
			
		||||
ok: matches expected msg
 | 
			
		||||
rc=0
 | 
			
		||||
        [IuUP] <-RTP-
 | 
			
		||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
			
		||||
tx_msg() invoked by iuup_cn!
 | 
			
		||||
 <-PDU- [IuUP]
 | 
			
		||||
8060944c6256042c0001010202007eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
			
		||||
node_priv=0x2342
 | 
			
		||||
ok: matches expected msg
 | 
			
		||||
rc=0
 | 
			
		||||
        [IuUP] <-RTP-
 | 
			
		||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
			
		||||
tx_msg() invoked by iuup_cn!
 | 
			
		||||
 <-PDU- [IuUP]
 | 
			
		||||
8060944c6256042c0001010203009eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
			
		||||
node_priv=0x2342
 | 
			
		||||
ok: matches expected msg
 | 
			
		||||
rc=0
 | 
			
		||||
All done.
 | 
			
		||||
@@ -11,21 +11,18 @@ AM_CFLAGS = \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOABIS_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOTRAU_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	-no-install \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	mgcp_test.ok \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
check_PROGRAMS = \
 | 
			
		||||
noinst_PROGRAMS = \
 | 
			
		||||
	mgcp_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
@@ -38,13 +35,7 @@ mgcp_test_LDADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(LIBOSMOTRAU_LIBS) \
 | 
			
		||||
	$(LIBRARY_DL) \
 | 
			
		||||
	$(LIBRARY_DLSYM) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	-lm  \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
update_exp:
 | 
			
		||||
	$(builddir)/mgcp_test >$(srcdir)/mgcp_test.ok
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -73,7 +73,7 @@ v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -97,12 +97,11 @@ Response matches our expectations.
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_ADDR000
 | 
			
		||||
Testing MDCX4
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983216 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
@@ -112,31 +111,7 @@ c=IN IP4 0.0.0.0
 | 
			
		||||
c=IN IP4 0.0.0.0
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains 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:20
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
@@ -149,19 +124,18 @@ Dummy packets: 2
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_PT1
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
MDCX 18983218 1@mgw MGCP 1.0
 | 
			
		||||
M: SENDRECV
 | 
			
		||||
C: 2
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983217 1@mgw MGCP 1.0
 | 
			
		||||
M: SENDRECV
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20-40, a:AMR, nt:IN
 | 
			
		||||
o=- %s 23 IN IP4 5.6.7.8
 | 
			
		||||
c=IN IP4 5.6.7.8
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- %s 23 IN IP4 0.0.0.0
 | 
			
		||||
c=IN IP4 0.0.0.0
 | 
			
		||||
t=0 0
 | 
			
		||||
a=ptime:20
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
@@ -174,19 +148,18 @@ Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_PT2
 | 
			
		||||
MDCX 18983219 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983218 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
o=- %s 23 IN IP4 5.6.7.8
 | 
			
		||||
c=IN IP4 5.6.7.8
 | 
			
		||||
L: p:20-20, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- %s 23 IN IP4 0.0.0.0
 | 
			
		||||
c=IN IP4 0.0.0.0
 | 
			
		||||
a=ptime:20
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
@@ -199,19 +172,18 @@ Dummy packets: 2
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
MDCX 18983220 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
Testing MDCX4_PT3
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983219 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
o=- %s 23 IN IP4 5.6.7.8
 | 
			
		||||
c=IN IP4 5.6.7.8
 | 
			
		||||
I: %s
 | 
			
		||||
L: a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- %s 23 IN IP4 0.0.0.0
 | 
			
		||||
a=ptime:20
 | 
			
		||||
c=IN IP4 0.0.0.0
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
@@ -224,19 +196,18 @@ Dummy packets: 2
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
MDCX 18983221 1@mgw MGCP 1.0
 | 
			
		||||
m: sendrecv
 | 
			
		||||
c: 2
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_PT4
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983220 1@mgw MGCP 1.0
 | 
			
		||||
m: sendrecv
 | 
			
		||||
o=- %s 23 IN IP4 5.6.7.8
 | 
			
		||||
c=IN IP4 5.6.7.8
 | 
			
		||||
c: 2
 | 
			
		||||
i: %s
 | 
			
		||||
l: A:amr, NT:IN
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
a=ptime:20
 | 
			
		||||
o=- %s 23 IN IP4 0.0.0.0
 | 
			
		||||
c=IN IP4 0.0.0.0
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
@@ -249,19 +220,18 @@ Dummy packets: 2
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
MDCX 18983222 1@mgw MGCP 1.0
 | 
			
		||||
M: sendonly
 | 
			
		||||
C: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_SO
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983221 1@mgw MGCP 1.0
 | 
			
		||||
o=- %s 23 IN IP4 5.6.7.8
 | 
			
		||||
c=IN IP4 5.6.7.8
 | 
			
		||||
M: sendonly
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
a=ptime:20
 | 
			
		||||
v=0
 | 
			
		||||
o=- %s 23 IN IP4 0.0.0.0
 | 
			
		||||
c=IN IP4 0.0.0.0
 | 
			
		||||
t=0 0
 | 
			
		||||
@@ -273,9 +243,8 @@ Response matches our expectations.
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
MDCX 18983223 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_RO
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
@@ -304,15 +273,9 @@ Response matches our expectations.
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_ZYN
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
@@ -428,7 +391,7 @@ v=0
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
a=ptime:20
 | 
			
		||||
Testing CRCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
@@ -478,6 +441,7 @@ checking response:
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 6@mgw MGCP 1.0
 | 
			
		||||
@@ -493,7 +457,7 @@ v=0
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX
 | 
			
		||||
a=ptime:20
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
@@ -506,7 +470,7 @@ Dummy packets: 2
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
MDCX 18983224 1@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
@@ -539,184 +503,6 @@ Response matches our expectations.
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
================================================
 | 
			
		||||
Testing AUEP_NULL
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
AUEP 18983215 null@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_NULL
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 null@mgw MGCP 1.0
 | 
			
		||||
m: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX_NULL
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 9 null@mgw MGCP 1.0
 | 
			
		||||
I: %s
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing DLCX_NULL
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
DLCX 8 null@mgw MGCP 1.0
 | 
			
		||||
I: %s
 | 
			
		||||
C: 2
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing RQNT_NULL
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
RQNT 186908782 null@mgw MGCP 1.0
 | 
			
		||||
X: B244F267488
 | 
			
		||||
S: D/9
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_PORT_0
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 3 1@mgw MGCP 1.0
 | 
			
		||||
m: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 0 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_PORT_0_IUFP
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 4 1@mgw MGCP 1.0
 | 
			
		||||
m: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 0 RTP/AVP 96
 | 
			
		||||
a=rtpmap:96 VND.3GPP.IUFP/16000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_PORT_0_IUFP_SENDRECV
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 4 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 0 RTP/AVP 96
 | 
			
		||||
a=rtpmap:96 VND.3GPP.IUFP/16000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_PORT_0_IUFP_SENDRECV2
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 4 1@mgw MGCP 1.0
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
a=sendrecv
 | 
			
		||||
m=audio 0 RTP/AVP 96
 | 
			
		||||
a=rtpmap:96 VND.3GPP.IUFP/16000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_PORT_0_IUFP_SENDRECV3
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 4 1@mgw MGCP 1.0
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 0 RTP/AVP 96
 | 
			
		||||
a=rtpmap:96 VND.3GPP.IUFP/16000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
m=audio 5904 RTP/AVP 111
 | 
			
		||||
a=rtpmap:111 AMR/8000/1
 | 
			
		||||
a=ptime:20
 | 
			
		||||
@@ -730,7 +516,7 @@ v=0
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
a=ptime:20
 | 
			
		||||
Testing CRCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
@@ -748,7 +534,7 @@ v=0
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
a=ptime:20
 | 
			
		||||
Re-transmitting CRCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
@@ -860,7 +646,7 @@ v=0
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
a=ptime:20
 | 
			
		||||
Testing packet loss calculation.
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
@@ -984,15 +770,6 @@ 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: 180320, dTS: 0, Seq: 1003
 | 
			
		||||
Out TS change: 20000, dTS: 0, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -163840
 | 
			
		||||
In TS: 180480, dTS: 160, Seq: 1004
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -163840
 | 
			
		||||
In TS: 180640, dTS: 160, Seq: 1005
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 5, Transit = -163760
 | 
			
		||||
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
 | 
			
		||||
@@ -1087,15 +864,6 @@ Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
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
 | 
			
		||||
In TS: 180320, dTS: 0, Seq: 1003
 | 
			
		||||
Out TS change: 20000, dTS: 0, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -163840
 | 
			
		||||
In TS: 180480, dTS: 160, Seq: 1004
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -163840
 | 
			
		||||
In TS: 180640, dTS: 160, Seq: 1005
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 5, Transit = -163760
 | 
			
		||||
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
 | 
			
		||||
@@ -1190,15 +958,6 @@ Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
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
 | 
			
		||||
In TS: 180320, dTS: 0, Seq: 1003
 | 
			
		||||
Out TS change: 20000, dTS: 0, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -163840
 | 
			
		||||
In TS: 180480, dTS: 160, Seq: 1004
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -163840
 | 
			
		||||
In TS: 180640, dTS: 160, Seq: 1005
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 5, Transit = -163760
 | 
			
		||||
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
 | 
			
		||||
@@ -1291,15 +1050,6 @@ 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: 180320, dTS: 0, Seq: 1003
 | 
			
		||||
Out TS change: 20000, dTS: 0, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -163840
 | 
			
		||||
In TS: 180480, dTS: 160, Seq: 1004
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -163840
 | 
			
		||||
In TS: 180640, dTS: 160, Seq: 1005
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 5, Transit = -163760
 | 
			
		||||
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
 | 
			
		||||
@@ -1314,7 +1064,7 @@ c=IN IP4 123.12.12.123
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
Testing multiple payload types
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
a=ptime:20
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
@@ -1331,7 +1081,7 @@ m=audio 5904 RTP/AVP 18 97 101
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
a=ptime:20
 | 
			
		||||
CRCX 2 2@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
X
 | 
			
		||||
@@ -1348,7 +1098,7 @@ m=audio 5904 RTP/AVP
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
a=ptime:20
 | 
			
		||||
CRCX 2 3@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
X
 | 
			
		||||
@@ -1365,7 +1115,7 @@ m=audio 5904 RTP/AVP 18
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
a=ptime:20
 | 
			
		||||
CRCX 2 4@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
X
 | 
			
		||||
@@ -1447,7 +1197,7 @@ v=0
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
Testing no sequence flow on initial packet
 | 
			
		||||
a=ptime:20
 | 
			
		||||
Testing no rtpmap name
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
@@ -1468,7 +1218,7 @@ p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
Testing get_lco_identifier()
 | 
			
		||||
p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
Testing mgcp_codec_find_convertible()
 | 
			
		||||
'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
 | 
			
		||||
'XXXX,p:10,a:PCMU' -> 'p:10,a:PCMU'
 | 
			
		||||
'10,a:PCMU' -> 'a:PCMU'
 | 
			
		||||
'10, a:PCMU' -> 'a:PCMU'
 | 
			
		||||
@@ -1478,15 +1228,13 @@ Testing mgcp_codec_find_convertible()
 | 
			
		||||
' a:PCMU' -> 'a:PCMU'
 | 
			
		||||
'' -> '(null)'
 | 
			
		||||
p10, aPCMU -> (null)
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
    conn[1]: codec:AMR, pt:96
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:96
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
'10,a :PCMU' -> '(null)'
 | 
			
		||||
 | 
			
		||||
Testing mgcp_codec_pt_translate()
 | 
			
		||||
#0: same order, but differing payload type numbers
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0:  96 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
@@ -1496,15 +1244,13 @@ Testing mgcp_codec_find_convertible()
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:PCMU, pt:0
 | 
			
		||||
    conn[1]: codec:PCMU, pt:0
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:GSM-HR-08, pt:97
 | 
			
		||||
    conn[0]: codec:GSM-HR-08, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
 | 
			
		||||
#1: different order and different payload type numbers
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    1: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    2: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0:  97 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
@@ -1514,15 +1260,12 @@ Testing mgcp_codec_find_convertible()
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:PCMU, pt:0
 | 
			
		||||
    conn[1]: codec:PCMU, pt:0
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:GSM-HR-08, pt:97
 | 
			
		||||
    conn[0]: codec:GSM-HR-08, pt:96
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
 | 
			
		||||
#2: both sides have the same payload_type numbers assigned to differing codecs
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    1:  96 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    2:  97 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
@@ -1530,13 +1273,9 @@ Testing mgcp_codec_find_convertible()
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2:  96 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 96) -> 97
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    codec decision failed (expected)!
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:96
 | 
			
		||||
    conn[0]: codec:none, pt:none
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 96
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 97) -> 96
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
			
		||||
#3: conn0 has no codecs
 | 
			
		||||
@@ -1544,116 +1283,39 @@ Testing mgcp_codec_find_convertible()
 | 
			
		||||
    (none)
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0:  96 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
    conn[1]: codec:none, pt:none
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    codec decision failed (expected)!
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#5: test AMR with differing octet-aligned settings (both <-> both)
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2:  97 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
			
		||||
#4: conn1 has no codecs
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
    conn[1]: codec:AMR, pt:121
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#6: test AMR with differing octet-aligned settings (oa <-> both)
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    (none)
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
			
		||||
#5: test AMR with differing octet-aligned settings
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 111 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
    1: 112 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
    1: 121 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
    conn[1]: codec:AMR, pt:121
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:121
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#7: test AMR with differing octet-aligned settings (bwe <-> both)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 112 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
    1: 121 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#8: test AMR with missing octet-aligned settings (oa <-> unset)
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
    1: 121 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 121
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 121) -> 111
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=unset  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#9: test AMR with missing octet-aligned settings (bwe <-> unset)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 111 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=unset  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#10: test AMR with NULL param (oa <-> null)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 112 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
#6: test AMR with missing octet-aligned settings (defaults to 0)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 111 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#11: test AMR with NULL param (bwe <-> null)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 112 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#12: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit
 | 
			
		||||
    1: 112 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=unset  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
 | 
			
		||||
#7: test AMR with NULL param (defaults to 0)
 | 
			
		||||
@@ -1662,16 +1324,14 @@ Testing mgcp_codec_find_convertible()
 | 
			
		||||
    1: 112 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:PCMU, pt:0
 | 
			
		||||
    conn[1]: codec:PCMU, pt:0
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:GSM-HR-08, pt:97
 | 
			
		||||
    conn[0]: codec:GSM-HR-08, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#13: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
 | 
			
		||||
#8: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    1: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    2: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0:  97 GSM-HR-08/8000  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000  -> rc=0
 | 
			
		||||
@@ -1680,16 +1340,14 @@ Testing mgcp_codec_find_convertible()
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:PCMU, pt:0
 | 
			
		||||
    conn[1]: codec:PCMU, pt:0
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:GSM-HR-08, pt:97
 | 
			
		||||
    conn[0]: codec:GSM-HR-08, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#14: test whether channel number matching is waterproof
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
 | 
			
		||||
#9: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    1: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    2: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0:  97 GSM-HR-08  -> rc=0
 | 
			
		||||
    1:   0 PCMU  -> rc=0
 | 
			
		||||
@@ -1697,15 +1355,10 @@ Testing mgcp_codec_find_convertible()
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:GSM-HR-08, pt:111
 | 
			
		||||
    conn[1]: codec:GSM-HR-08, pt:121
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:GSM-HR-08, pt:121
 | 
			
		||||
    conn[0]: codec:GSM-HR-08, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
 | 
			
		||||
#10: test whether channel number matching is waterproof
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 111 GSM-HR-08/8000  -> rc=0
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,6 @@ AM_CFLAGS = \
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	-no-install \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
@@ -23,7 +22,7 @@ EXTRA_DIST = \
 | 
			
		||||
	mgcp_client_test.err \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
check_PROGRAMS = \
 | 
			
		||||
noinst_PROGRAMS = \
 | 
			
		||||
	mgcp_client_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
@@ -36,7 +35,6 @@ mgcp_client_test_LDADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBRARY_DL) \
 | 
			
		||||
	$(LIBRARY_DLSYM) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user