mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
Compare commits
141 Commits
laforge/li
...
lynxis/pre
Author | SHA1 | Date | |
---|---|---|---|
|
c56f68abd7 | ||
|
01fc7fb239 | ||
|
370f4e3867 | ||
|
3acaadd44d | ||
|
f6a9cd53f4 | ||
|
f81151bd3f | ||
|
9c0bab9b48 | ||
|
2fa9279702 | ||
|
a622828806 | ||
|
2ab3af5263 | ||
|
8571981205 | ||
|
2930c2d8ad | ||
|
6cd30366be | ||
|
ffb3e8d2ec | ||
|
b3f606a826 | ||
|
838a381b49 | ||
|
c5aed8c10e | ||
|
5b2283ba68 | ||
|
f42457ec98 | ||
|
09db3b69ed | ||
|
4ebab46847 | ||
|
19c4fd0562 | ||
|
e198087a15 | ||
|
50643e419d | ||
|
e21c93fc96 | ||
|
956a2eb418 | ||
|
90835e0083 | ||
|
8643faad59 | ||
|
03e1d5c7c5 | ||
|
5bc821566f | ||
|
b67fb4fc63 | ||
|
4b36c7a34e | ||
|
15b06b9d3c | ||
|
241f119d8e | ||
|
5938727956 | ||
|
ec49e842fe | ||
|
3ae6040afd | ||
|
9b0db3860c | ||
|
0317b106fe | ||
|
920420284c | ||
|
ae33266f6c | ||
|
ea452c93ca | ||
|
195cab61ab | ||
|
23880357f2 | ||
|
2fd3a60ed8 | ||
|
569335bc34 | ||
|
4c098cfb32 | ||
|
d3f2ae8d6a | ||
|
faa1b9dd0a | ||
|
f85f656b18 | ||
|
51442a7886 | ||
|
88824c7c7d | ||
|
6202b37a0b | ||
|
dd93694587 | ||
|
ead844e65e | ||
|
d6278889c4 | ||
|
2535d12377 | ||
|
ed21f2eecf | ||
|
31b05fe895 | ||
|
e4b33dd303 | ||
|
184892b371 | ||
|
8a3c5a0699 | ||
|
7d5e706964 | ||
|
04d1ca7895 | ||
|
e4dc412e7b | ||
|
1f94ba58ea | ||
|
104548372c | ||
|
79b2387c0b | ||
|
39bc234f9e | ||
|
3a401548ce | ||
|
cc12b708a7 | ||
|
114c4ff913 | ||
|
9fa9783dc6 | ||
|
3d09bbbbc4 | ||
|
34ed929877 | ||
|
fef5ee84f2 | ||
|
ad228e16cf | ||
|
869842e16c | ||
|
3bac10082a | ||
|
158d842969 | ||
|
a2ec888287 | ||
|
1a331b5405 | ||
|
83d75bb19a | ||
|
7a8e4a0215 | ||
|
d4d44cacbc | ||
|
554488dee8 | ||
|
d46c75dac9 | ||
|
2a6629b27e | ||
|
95e1a3b53c | ||
|
00a0862e9c | ||
|
d4195472f5 | ||
|
741b987923 | ||
|
3398964176 | ||
|
c5bb5816cd | ||
|
1c832b34b1 | ||
|
88e9ecbfb5 | ||
|
3386a8791e | ||
|
2529f046e8 | ||
|
ae819a1be4 | ||
|
2878423b95 | ||
|
714b2281b8 | ||
|
c7aa82b109 | ||
|
d69cdf75e0 | ||
|
6cad4c3e7f | ||
|
2238185739 | ||
|
456dbb8070 | ||
|
a3df1f8732 | ||
|
e66311b1bd | ||
|
7e77a293a5 | ||
|
2932bc4955 | ||
|
1168969378 | ||
|
3d5990fc65 | ||
|
21ca60807c | ||
|
aba422a6cc | ||
|
d0a25a05a2 | ||
|
bbc26f444a | ||
|
c49975de02 | ||
|
6a87169a31 | ||
|
3a0b3b2182 | ||
|
3f8fdf0e7a | ||
|
36e6e7f074 | ||
|
03f51df03c | ||
|
46f4f14024 | ||
|
ecd8c048fc | ||
|
ed739b9dac | ||
|
5322912630 | ||
|
772205aae1 | ||
|
853883d1f6 | ||
|
6a29d326e0 | ||
|
2483f1b050 | ||
|
b8b85a1b2e | ||
|
53edff3c70 | ||
|
625e05a6b2 | ||
|
3355fd674f | ||
|
f2ba81303e | ||
|
cbafa255cc | ||
|
14ce472225 | ||
|
c00d016e20 | ||
|
f29ff888a2 | ||
|
29b9206e80 | ||
|
9e3c66b181 |
@@ -1,3 +1,3 @@
|
||||
[gerrit]
|
||||
host=gerrit.osmocom.org
|
||||
project=osmo-mgw
|
||||
project=openbsc
|
||||
|
@@ -17,17 +17,10 @@ SUBDIRS = \
|
||||
$(NULL)
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = \
|
||||
libosmo-legacy-mgcp.pc \
|
||||
libosmo-mgcp-client.pc \
|
||||
libosmo-mgcp.pc \
|
||||
$(NULL)
|
||||
pkgconfig_DATA = libosmo-legacy-mgcp.pc
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = git-version-gen osmoappdesc.py .version
|
||||
|
||||
@RELMAKE@
|
||||
|
||||
$(top_srcdir)/.version:
|
||||
echo $(VERSION) > $@-t && mv $@-t $@
|
||||
dist-hook:
|
||||
|
53
README
53
README
@@ -1,28 +1,39 @@
|
||||
About OsmoMGW
|
||||
About OpenBSC
|
||||
=============
|
||||
|
||||
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.
|
||||
OpenBSC started as a minimalistic all-in-one implementation of the GSM Network,
|
||||
with particular emphasis on the functionality typically provided by the BSC,
|
||||
MSC, HLR, VLR and SMSC. Today it is a growing suite of libraries and programs,
|
||||
implementing protocol stacks and functional elements, including
|
||||
|
||||
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.
|
||||
* OsmoBSC - a pure GSM BSC, speaking Abis/IP to the BTS and A/IP to the MSC
|
||||
* OsmoBSC-MGCP - MGCP helper to the OsmoBSC software
|
||||
* OsmoNITB - a BSC+MSC+VLR+HLR+SMSC "Network in the box".
|
||||
* OsmoMSC - a voice CN with A/IP and IuCS/IP towards the BSC and/or HNB-GW
|
||||
* OsmoSGSN - a GPRS SGSN with Gb/IP and IuPS/IP towards the PCU and/or HNB-GW
|
||||
* Osmo-GbProxy - a Proxy to aggregate many Gb links as one Gb link to the SGSN
|
||||
* OsmoBSCNAT - a gateway aggregating many A links as one A link to the MSC
|
||||
* OsmoGTPHUB - a hub aggregating many GTP links (between SGSN and GGSN)
|
||||
* ipaccess-utils - some tools to discover + configure ip.access nanoBTS
|
||||
* bs11_config - a tool to configure the Siemens BS-11 microBTS
|
||||
|
||||
The OsmoMGW program exposes an MGCP interface towards clients like OsmoMSC and
|
||||
OsmoBSC, and receives and sends RTP streams as configured via MGCP.
|
||||
Various interfaces towards the BTS are supported, among which are:
|
||||
|
||||
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC (found
|
||||
in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
|
||||
* Classic A-bis over E1 using a mISDN based E1 interface. In other
|
||||
words, you can connect existing GSM Base Transceiver Station (BTS)
|
||||
through E1 to OpenBSC. So far, we have made it work with the Siemens BS-11,
|
||||
various Ericsson RBS2xxx BTS models and the Nokia MetroSite.
|
||||
|
||||
The libosmo-mgcp library exposes MGCP server utilities used by e.g. OsmoBSC-NAT
|
||||
(found in osmo-bsc.git) to navigate RTP streams through a NAT.
|
||||
(At time of writing, this is still called libosmo-legacy-mgcp.)
|
||||
* A-bis over IP as used by the ip.access nanoBTS product family as well as
|
||||
the Open Source OsmoBTS software (by the same authors as OpenBSC). OsmoBTS
|
||||
in turn supports various transceiver hardware, including the sysmoBTS
|
||||
product family, as well as SDR transceivers supported by OsmoTRX, such as
|
||||
the UmTRX or USRP boardss.
|
||||
|
||||
Find OsmoMGW issue tracker and wiki online at
|
||||
https://osmocom.org/projects/osmo-mgw
|
||||
https://osmocom.org/projects/osmo-mgw/wiki
|
||||
* IuCS and IuPS over IP towards an HNB-GW (see osmo-iuh) for UMTS (3G)
|
||||
voice and data links.
|
||||
|
||||
Find OpenBSC online at
|
||||
http://openbsc.osmocom.org/
|
||||
|
||||
Harald Welte <laforge@gnumonks.org>
|
||||
|
29
TODO-RELEASE
29
TODO-RELEASE
@@ -1,29 +0,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
|
||||
libosmo-mgcp API/ABI change parse and represent connection identifiers as hex strings
|
||||
libosmo-mgcp API/ABI change connection identifiers are assigned by the server, not CA
|
||||
libosmo-mgcp-client API/ABI change parse and store connection identifier in response
|
32
configure.ac
32
configure.ac
@@ -12,10 +12,6 @@ AC_CONFIG_TESTDIR(tests)
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl include release helper
|
||||
RELMAKE='-include osmo-release.mk'
|
||||
AC_SUBST([RELMAKE])
|
||||
|
||||
dnl checks for programs
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_CC
|
||||
@@ -39,22 +35,9 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DL)
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.1.0)
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-sanitize],
|
||||
[Compile with address sanitizer enabled],
|
||||
)],
|
||||
[sanitize=$enableval], [sanitize="no"])
|
||||
if test x"$sanitize" = x"yes"
|
||||
then
|
||||
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
fi
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.9.5)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.0.1)
|
||||
|
||||
# Enable/disable transcoding within osmo-bsc_mgcp?
|
||||
AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])],
|
||||
@@ -131,24 +114,15 @@ AM_CONFIG_HEADER(bscconfig.h)
|
||||
|
||||
AC_OUTPUT(
|
||||
libosmo-legacy-mgcp.pc
|
||||
libosmo-mgcp-client.pc
|
||||
libosmo-mgcp.pc
|
||||
include/Makefile
|
||||
include/osmocom/Makefile
|
||||
include/osmocom/legacy_mgcp/Makefile
|
||||
include/osmocom/mgcp_client/Makefile
|
||||
include/osmocom/mgcp/Makefile
|
||||
src/Makefile
|
||||
src/libosmo-legacy-mgcp/Makefile
|
||||
src/libosmo-mgcp-client/Makefile
|
||||
src/libosmo-mgcp/Makefile
|
||||
src/osmo-bsc_mgcp/Makefile
|
||||
src/osmo-mgw/Makefile
|
||||
tests/Makefile
|
||||
tests/atlocal
|
||||
tests/legacy_mgcp/Makefile
|
||||
tests/mgcp_client/Makefile
|
||||
tests/mgcp/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
contrib/Makefile
|
||||
|
278
contrib/ipa.py
278
contrib/ipa.py
@@ -1,278 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- mode: python-mode; py-indent-tabs-mode: nil -*-
|
||||
"""
|
||||
/*
|
||||
* Copyright (C) 2016 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 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, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
"""
|
||||
|
||||
import struct, random, sys
|
||||
|
||||
class IPA(object):
|
||||
"""
|
||||
Stateless IPA protocol multiplexer: add/remove/parse (extended) header
|
||||
"""
|
||||
version = "0.0.5"
|
||||
TCP_PORT_OML = 3002
|
||||
TCP_PORT_RSL = 3003
|
||||
# OpenBSC extensions: OSMO, MGCP_OLD
|
||||
PROTO = dict(RSL=0x00, CCM=0xFE, SCCP=0xFD, OML=0xFF, OSMO=0xEE, MGCP_OLD=0xFC)
|
||||
# ...OML Router Control, GSUP GPRS extension, Osmocom Authn Protocol
|
||||
EXT = dict(CTRL=0, MGCP=1, LAC=2, SMSC=3, ORC=4, GSUP=5, OAP=6)
|
||||
# OpenBSC extension: SCCP_OLD
|
||||
MSGT = dict(PING=0x00, PONG=0x01, ID_GET=0x04, ID_RESP=0x05, ID_ACK=0x06, SCCP_OLD=0xFF)
|
||||
_IDTAG = dict(SERNR=0, UNITNAME=1, LOCATION=2, TYPE=3, EQUIPVERS=4, SWVERSION=5, IPADDR=6, MACADDR=7, UNIT=8)
|
||||
CTRL_GET = 'GET'
|
||||
CTRL_SET = 'SET'
|
||||
CTRL_REP = 'REPLY'
|
||||
CTRL_ERR = 'ERR'
|
||||
CTRL_TRAP = 'TRAP'
|
||||
|
||||
def _l(self, d, p):
|
||||
"""
|
||||
Reverse dictionary lookup: return key for a given value
|
||||
"""
|
||||
if p is None:
|
||||
return 'UNKNOWN'
|
||||
return list(d.keys())[list(d.values()).index(p)]
|
||||
|
||||
def _tag(self, t, v):
|
||||
"""
|
||||
Create TAG as TLV data
|
||||
"""
|
||||
return struct.pack(">HB", len(v) + 1, t) + v
|
||||
|
||||
def proto(self, p):
|
||||
"""
|
||||
Lookup protocol name
|
||||
"""
|
||||
return self._l(self.PROTO, p)
|
||||
|
||||
def ext(self, p):
|
||||
"""
|
||||
Lookup protocol extension name
|
||||
"""
|
||||
return self._l(self.EXT, p)
|
||||
|
||||
def msgt(self, p):
|
||||
"""
|
||||
Lookup message type name
|
||||
"""
|
||||
return self._l(self.MSGT, p)
|
||||
|
||||
def idtag(self, p):
|
||||
"""
|
||||
Lookup ID tag name
|
||||
"""
|
||||
return self._l(self._IDTAG, p)
|
||||
|
||||
def ext_name(self, proto, exten):
|
||||
"""
|
||||
Return proper extension byte name depending on the protocol used
|
||||
"""
|
||||
if self.PROTO['CCM'] == proto:
|
||||
return self.msgt(exten)
|
||||
if self.PROTO['OSMO'] == proto:
|
||||
return self.ext(exten)
|
||||
return None
|
||||
|
||||
def add_header(self, data, proto, ext=None):
|
||||
"""
|
||||
Add IPA header (with extension if necessary), data must be represented as bytes
|
||||
"""
|
||||
if ext is None:
|
||||
return struct.pack(">HB", len(data) + 1, proto) + data
|
||||
return struct.pack(">HBB", len(data) + 1, proto, ext) + data
|
||||
|
||||
def del_header(self, data):
|
||||
"""
|
||||
Strip IPA protocol header correctly removing extension if present
|
||||
Returns data length, IPA protocol, extension (or None if not defined for a give protocol) and the data without header
|
||||
"""
|
||||
if not len(data):
|
||||
return None, None, None, None
|
||||
(dlen, proto) = struct.unpack('>HB', data[:3])
|
||||
if self.PROTO['OSMO'] == proto or self.PROTO['CCM'] == proto: # there's extension which we have to unpack
|
||||
return struct.unpack('>HBB', data[:4]) + (data[4:], ) # length, protocol, extension, data
|
||||
return dlen, proto, None, data[3:] # length, protocol, _, data
|
||||
|
||||
def split_combined(self, data):
|
||||
"""
|
||||
Split the data which contains multiple concatenated IPA messages into tuple (first, rest) where rest contains remaining messages, first is the single IPA message
|
||||
"""
|
||||
(length, _, _, _) = self.del_header(data)
|
||||
return data[:(length + 3)], data[(length + 3):]
|
||||
|
||||
def tag_serial(self, data):
|
||||
"""
|
||||
Make TAG for serial number
|
||||
"""
|
||||
return self._tag(self._IDTAG['SERNR'], data)
|
||||
|
||||
def tag_name(self, data):
|
||||
"""
|
||||
Make TAG for unit name
|
||||
"""
|
||||
return self._tag(self._IDTAG['UNITNAME'], data)
|
||||
|
||||
def tag_loc(self, data):
|
||||
"""
|
||||
Make TAG for location
|
||||
"""
|
||||
return self._tag(self._IDTAG['LOCATION'], data)
|
||||
|
||||
def tag_type(self, data):
|
||||
"""
|
||||
Make TAG for unit type
|
||||
"""
|
||||
return self._tag(self._IDTAG['TYPE'], data)
|
||||
|
||||
def tag_equip(self, data):
|
||||
"""
|
||||
Make TAG for equipment version
|
||||
"""
|
||||
return self._tag(self._IDTAG['EQUIPVERS'], data)
|
||||
|
||||
def tag_sw(self, data):
|
||||
"""
|
||||
Make TAG for software version
|
||||
"""
|
||||
return self._tag(self._IDTAG['SWVERSION'], data)
|
||||
|
||||
def tag_ip(self, data):
|
||||
"""
|
||||
Make TAG for IP address
|
||||
"""
|
||||
return self._tag(self._IDTAG['IPADDR'], data)
|
||||
|
||||
def tag_mac(self, data):
|
||||
"""
|
||||
Make TAG for MAC address
|
||||
"""
|
||||
return self._tag(self._IDTAG['MACADDR'], data)
|
||||
|
||||
def tag_unit(self, data):
|
||||
"""
|
||||
Make TAG for unit ID
|
||||
"""
|
||||
return self._tag(self._IDTAG['UNIT'], data)
|
||||
|
||||
def identity(self, unit=b'', mac=b'', location=b'', utype=b'', equip=b'', sw=b'', name=b'', serial=b''):
|
||||
"""
|
||||
Make IPA IDENTITY tag list, by default returns empty concatenated bytes of tag list
|
||||
"""
|
||||
return self.tag_unit(unit) + self.tag_mac(mac) + self.tag_loc(location) + self.tag_type(utype) + self.tag_equip(equip) + self.tag_sw(sw) + self.tag_name(name) + self.tag_serial(serial)
|
||||
|
||||
def ping(self):
|
||||
"""
|
||||
Make PING message
|
||||
"""
|
||||
return self.add_header(b'', self.PROTO['CCM'], self.MSGT['PING'])
|
||||
|
||||
def pong(self):
|
||||
"""
|
||||
Make PONG message
|
||||
"""
|
||||
return self.add_header(b'', self.PROTO['CCM'], self.MSGT['PONG'])
|
||||
|
||||
def id_ack(self):
|
||||
"""
|
||||
Make ID_ACK CCM message
|
||||
"""
|
||||
return self.add_header(b'', self.PROTO['CCM'], self.MSGT['ID_ACK'])
|
||||
|
||||
def id_get(self):
|
||||
"""
|
||||
Make ID_GET CCM message
|
||||
"""
|
||||
return self.add_header(self.identity(), self.PROTO['CCM'], self.MSGT['ID_GET'])
|
||||
|
||||
def id_resp(self, data):
|
||||
"""
|
||||
Make ID_RESP CCM message
|
||||
"""
|
||||
return self.add_header(data, self.PROTO['CCM'], self.MSGT['ID_RESP'])
|
||||
|
||||
class Ctrl(IPA):
|
||||
"""
|
||||
Osmocom CTRL protocol implemented on top of IPA multiplexer
|
||||
"""
|
||||
def __init__(self):
|
||||
random.seed()
|
||||
|
||||
def add_header(self, data):
|
||||
"""
|
||||
Add CTRL header
|
||||
"""
|
||||
return super(Ctrl, self).add_header(data.encode('utf-8'), IPA.PROTO['OSMO'], IPA.EXT['CTRL'])
|
||||
|
||||
def rem_header(self, data):
|
||||
"""
|
||||
Remove CTRL header, check for appropriate protocol and extension
|
||||
"""
|
||||
(_, proto, ext, d) = super(Ctrl, self).del_header(data)
|
||||
if self.PROTO['OSMO'] != proto or self.EXT['CTRL'] != ext:
|
||||
return None
|
||||
return d
|
||||
|
||||
def parse(self, data, op=None):
|
||||
"""
|
||||
Parse Ctrl string returning (var, value) pair
|
||||
var could be None in case of ERROR message
|
||||
value could be None in case of GET message
|
||||
"""
|
||||
(s, i, v) = data.split(' ', 2)
|
||||
if s == self.CTRL_ERR:
|
||||
return None, v
|
||||
if s == self.CTRL_GET:
|
||||
return v, None
|
||||
(s, i, var, val) = data.split(' ', 3)
|
||||
if s == self.CTRL_TRAP and i != '0':
|
||||
return None, '%s with non-zero id %s' % (s, i)
|
||||
if op is not None and i != op:
|
||||
if s == self.CTRL_GET + '_' + self.CTRL_REP or s == self.CTRL_SET + '_' + self.CTRL_REP:
|
||||
return None, '%s with unexpected id %s' % (s, i)
|
||||
return var, val
|
||||
|
||||
def trap(self, var, val):
|
||||
"""
|
||||
Make TRAP message with given (vak, val) pair
|
||||
"""
|
||||
return self.add_header("%s 0 %s %s" % (self.CTRL_TRAP, var, val))
|
||||
|
||||
def cmd(self, var, val=None):
|
||||
"""
|
||||
Make SET/GET command message: returns (r, m) tuple where r is random operation id and m is assembled message
|
||||
"""
|
||||
r = random.randint(1, sys.maxsize)
|
||||
if val is not None:
|
||||
return r, self.add_header("%s %s %s %s" % (self.CTRL_SET, r, var, val))
|
||||
return r, self.add_header("%s %s %s" % (self.CTRL_GET, r, var))
|
||||
|
||||
def verify(self, reply, r, var, val=None):
|
||||
"""
|
||||
Verify reply to SET/GET command: returns (b, v) tuple where v is True/False verification result and v is the variable value
|
||||
"""
|
||||
(k, v) = self.parse(reply)
|
||||
if k != var or (val is not None and v != val):
|
||||
return False, v
|
||||
return True, v
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("IPA multiplexer v%s loaded." % IPA.version)
|
@@ -1,11 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
# jenkins build helper script for openbsc. This is how we build on jenkins.osmocom.org
|
||||
|
||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
|
||||
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
|
||||
set -ex
|
||||
|
||||
@@ -14,18 +7,22 @@ deps="$base/deps"
|
||||
inst="$deps/install"
|
||||
export deps inst
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
mkdir "$deps" || true
|
||||
rm -rf "$inst"
|
||||
|
||||
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
|
||||
|
||||
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
||||
"$deps"/libosmocore/contrib/verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
||||
|
||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
|
||||
osmo-build-dep.sh libosmo-abis
|
||||
osmo-build-dep.sh libosmo-netif
|
||||
if [ "x$IU" = "x--enable-iu" ]; then
|
||||
sccp_branch="old_sua"
|
||||
osmo_iuh_branch="old_sua"
|
||||
fi
|
||||
|
||||
PARALLEL_MAKE="" osmo-build-dep.sh libsmpp34
|
||||
|
||||
set +x
|
||||
echo
|
||||
@@ -42,8 +39,6 @@ $MAKE $PARALLEL_MAKE
|
||||
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
LD_LIBRARY_PATH="$inst/lib" \
|
||||
DISTCHECK_CONFIGURE_FLAGS="$MGCP --enable-vty-tests --enable-external-tests" \
|
||||
DISTCHECK_CONFIGURE_FLAGS="--enable-osmo-bsc --enable-nat $SMPP $MGCP $IU --enable-vty-tests --enable-external-tests" \
|
||||
$MAKE distcheck \
|
||||
|| cat-testlogs.sh
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
@@ -1,11 +0,0 @@
|
||||
[Unit]
|
||||
Description=Osmocom Media Gateway (MGW)
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
43
debian/changelog
vendored
43
debian/changelog
vendored
@@ -1,46 +1,3 @@
|
||||
osmo-mgw (1.2.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* jenkins: use osmo-clean-workspace.sh before and after build
|
||||
* vty: skip installing cmds now always installed by default
|
||||
* mgcp-client vty: use name 'mgw' instead of 'mgcpgw'
|
||||
* mgcp client: vty: tweak doc strings
|
||||
|
||||
[ Philipp Maier ]
|
||||
* sdp: refactoring sdp parser/generator
|
||||
* cosmetic: rename bts_codec to codec_str
|
||||
* cosmetic: fix coding style for mgcp_parse_sdp_data()
|
||||
* cosmetic: fix commenting style
|
||||
* cosmetic: correct whitespaces
|
||||
* client: fix stderror logging in unit-test
|
||||
* client: add unified function to generate MGCP messages
|
||||
* client: add ip address parsing to the client
|
||||
* protocol: allow wildcarded DLCX
|
||||
* mgcp: remove port/timeslot calculator functions from mgcp.h
|
||||
* network: add separate log category
|
||||
* cosmetic: make dummy packet handling more explicit
|
||||
* network: autdetect rtp bind ip-address
|
||||
* network: fix rtp packet length
|
||||
* network: remove unused return code
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* mgcp_client_vty.c: Fix VTY compatibility with 'mgcpgw bts-base'
|
||||
|
||||
-- Harald Welte <laforge@gnumonks.org> Fri, 10 Nov 2017 11:10:23 +0900
|
||||
|
||||
osmo-mgw (1.1.0) unstable; urgency=medium
|
||||
|
||||
* New upstream release
|
||||
|
||||
-- Harald Welte <lafore@gnumonks.org> Sat, 28 Oct 2017 12:48:41 +0200
|
||||
|
||||
osmo-mgw (1.0.2) unstable; urgency=low
|
||||
|
||||
* First release after major rename.
|
||||
|
||||
-- Max Suraev <msuraev@sysmocom.de> Thu, 14 Sep 2017 18:41:05 +0200
|
||||
|
||||
|
||||
osmo-mgw (0.1.0) unstable; urgency=low
|
||||
|
||||
* Initial release.
|
||||
|
56
debian/control
vendored
56
debian/control
vendored
@@ -9,51 +9,22 @@ Build-Depends: debhelper (>=9),
|
||||
libosmocore-dev,
|
||||
libosmo-netif-dev
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
|
||||
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
|
||||
Homepage: https://osmocom.org/projects/osmo-mgw
|
||||
Vcs-Git: git://git.osmocom.org/osmo-iuh.git
|
||||
Vcs-Browser: https://git.osmocom.org/osmo-iuh/
|
||||
Homepage: https://projects.osmocom.org/projects/osmohnbgw
|
||||
|
||||
Package: osmo-mgw
|
||||
Architecture: any
|
||||
Multi-Arch: foreign
|
||||
Depends: libosmo-mgcp1, ${misc:Depends}, ${shlibs:Depends}
|
||||
Depends: libosmo-legacy-mgcp0, ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
|
||||
Package: libosmo-mgcp1
|
||||
Section: libs
|
||||
Package: osmo-mgw-dbg
|
||||
Section: debug
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: libosmo-mgcp: Osmocom's Media Gateway server library
|
||||
|
||||
Package: libosmo-mgcp-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-mgcp1 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-mgcp: Osmocom's Media Gateway server library
|
||||
|
||||
Package: libosmo-mgcp-client2
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
|
||||
|
||||
Package: libosmo-mgcp-client-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-mgcp-client2 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
|
||||
|
||||
Package: osmo-bsc-mgcp
|
||||
Architecture: any
|
||||
Multi-Arch: foreign
|
||||
Depends: libosmo-legacy-mgcp0, ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: OsmoBSC-MGCP: Osmocom's Legacy Media Gateway; use osmo-mgw instead.
|
||||
Depends: osmo-mgw (= ${binary:Version}), ${misc:Depends}
|
||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
|
||||
Package: libosmo-legacy-mgcp0
|
||||
Section: libs
|
||||
@@ -61,11 +32,18 @@ Architecture: any
|
||||
Multi-Arch: same
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: libosmo-legacy-mgcp: Osmocom's Legacy Media Gateway server library; use libosmo-mgcp instead.
|
||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
|
||||
Package: libosmo-legacy-mgcp-dbg
|
||||
Section: debug
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-legacy-mgcp0 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
|
||||
Package: libosmo-legacy-mgcp-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-legacy-mgcp0 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-legacy-mgcp: Osmocom's Legacy Media Gateway server library; use libosmo-mgcp instead.
|
||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
|
2
debian/copyright
vendored
2
debian/copyright
vendored
@@ -6,7 +6,7 @@ Files: *
|
||||
Copyright: 2009-2014 On-Waves
|
||||
2009-2015 Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
2013 Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
2016-2017 sysmocom s.m.f.c. GmbH <info@sysmocom.de>
|
||||
2016 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
|
||||
|
7
debian/libosmo-legacy-mgcp-dev.install
vendored
7
debian/libosmo-legacy-mgcp-dev.install
vendored
@@ -1,4 +1,3 @@
|
||||
usr/include/osmocom/legacy_mgcp
|
||||
usr/lib/*/libosmo-legacy-mgcp.so
|
||||
usr/lib/*/libosmo-legacy-mgcp.a
|
||||
usr/lib/*/pkgconfig/libosmo-legacy-mgcp.pc
|
||||
usr/include
|
||||
usr/lib/*/*.so
|
||||
usr/lib/*/pkgconfig/*.pc
|
||||
|
2
debian/libosmo-legacy-mgcp0.install
vendored
2
debian/libosmo-legacy-mgcp0.install
vendored
@@ -1 +1 @@
|
||||
usr/lib/*/libosmo-legacy-mgcp.so.*
|
||||
usr/lib/*/*.so.*
|
||||
|
4
debian/libosmo-mgcp-client-dev.install
vendored
4
debian/libosmo-mgcp-client-dev.install
vendored
@@ -1,4 +0,0 @@
|
||||
usr/include/osmocom/mgcp_client
|
||||
usr/lib/*/libosmo-mgcp-client.so
|
||||
usr/lib/*/libosmo-mgcp-client.a
|
||||
usr/lib/*/pkgconfig/libosmo-mgcp-client.pc
|
1
debian/libosmo-mgcp-client2.install
vendored
1
debian/libosmo-mgcp-client2.install
vendored
@@ -1 +0,0 @@
|
||||
usr/lib/*/libosmo-mgcp-client.so.*
|
4
debian/libosmo-mgcp-dev.install
vendored
4
debian/libosmo-mgcp-dev.install
vendored
@@ -1,4 +0,0 @@
|
||||
usr/include/osmocom/mgcp
|
||||
usr/lib/*/libosmo-mgcp.so
|
||||
usr/lib/*/libosmo-mgcp.a
|
||||
usr/lib/*/pkgconfig/libosmo-mgcp.pc
|
1
debian/libosmo-mgcp1.install
vendored
1
debian/libosmo-mgcp1.install
vendored
@@ -1 +0,0 @@
|
||||
usr/lib/*/libosmo-mgcp.so.*
|
1
debian/osmo-bsc-mgcp.install
vendored
1
debian/osmo-bsc-mgcp.install
vendored
@@ -1 +0,0 @@
|
||||
usr/bin/osmo-bsc_mgcp
|
1
debian/osmo-bsc-mgcp.service
vendored
1
debian/osmo-bsc-mgcp.service
vendored
@@ -1 +0,0 @@
|
||||
../contrib/systemd/osmo-bsc-mgcp.service
|
2
debian/osmo-mgw.install
vendored
2
debian/osmo-mgw.install
vendored
@@ -1 +1 @@
|
||||
usr/bin/osmo-mgw
|
||||
usr/bin
|
||||
|
1
debian/osmo-mgw.service
vendored
1
debian/osmo-mgw.service
vendored
@@ -1 +0,0 @@
|
||||
../contrib/systemd/osmo-mgw.service
|
8
debian/rules
vendored
8
debian/rules
vendored
@@ -10,8 +10,6 @@ DEBIAN := $(shell dpkg-parsechangelog | grep '^Version:' | cut -d' ' -f2)
|
||||
DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1)
|
||||
VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
|
||||
|
||||
CFLAGS += -g
|
||||
|
||||
# main packaging script based on dh7 syntax
|
||||
%:
|
||||
dh $@ --with autoreconf
|
||||
@@ -26,11 +24,11 @@ CFLAGS += -g
|
||||
#override_dh_install:
|
||||
# dh_install --list-missing -X.la -X.pyc -X.pyo
|
||||
|
||||
override_dh_auto_test:
|
||||
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
|
||||
|
||||
override_dh_autoreconf:
|
||||
echo $(VERSION) > .tarball-version
|
||||
dh_autoreconf
|
||||
|
||||
# See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg
|
||||
override_dh_strip:
|
||||
dh_strip --dbg-package=osmo-mgw-dbg
|
||||
dh_strip --dbg-package=libosmo-legacy-mgcp-dbg
|
||||
|
31
doc/BS11-OML.txt
Normal file
31
doc/BS11-OML.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
The Siemens BS-11 supports the following additional GSM 12.21 OML operations:
|
||||
|
||||
|
||||
CREATE OBJECT
|
||||
|
||||
abis_om_fom_hdr.obj_class can be
|
||||
A3:
|
||||
A5: ALCO, BBSIG, CCLK, GPSU, LI, PA
|
||||
A8: EnvaBTSE
|
||||
A9: BPORT
|
||||
|
||||
the abis_om_obj_inst.trx_nr field indicates the index of object, whereas the
|
||||
abis_om_fom_hdr.bts_nr indicates the type of the object.
|
||||
|
||||
enum abis_bs11_objtype {
|
||||
BS11_OBJ_ALCO = 0x01,
|
||||
BS11_OBJ_BBSIG = 0x02, /* obj_class: 0,1 */
|
||||
BS11_OBJ_TRX1 = 0x03, /* only DEACTIVATE TRX1 */
|
||||
BS11_OBJ_CCLK = 0x04,
|
||||
BS11_OBJ_GPSU = 0x06,
|
||||
BS11_OBJ_LI = 0x07,
|
||||
BS11_OBJ_PA = 0x09, /* obj_class: 0, 1*/
|
||||
};
|
||||
|
||||
In case of CREATE ENVABTSE, the abis_om_obj_inst.trx_nr indicates the EnvaBTSEx
|
||||
number.
|
||||
|
||||
In case of A9 (CREAETE BPORT), the abis_om_obj_inst.bts_nr indicates which BPORT
|
||||
shall be used.
|
||||
|
||||
|
25
doc/call-routing.txt
Normal file
25
doc/call-routing.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
Call routing in OpenBSC
|
||||
|
||||
Flow of events:
|
||||
|
||||
# MO call initiated by MS, CHANNEL RQD, IMMEDIATE ASSIGN
|
||||
# MS sends CC SETUP message, we assume already on TCH/H FACCH
|
||||
# OpenBSC does a subscriber lookup based on the target extension
|
||||
* If a subscriber is found:
|
||||
# send CALL PROCEEDING message to MO
|
||||
# page the MT subscriber and ask itI to ask for TCH/H
|
||||
# once paging completes, we have the TCH/H for the MT end
|
||||
# send SETUP to MT
|
||||
# receive CALL CONFIRMED from MT
|
||||
# set-up the TRAU mux mapping between the E1 subslots for both TCH/H
|
||||
# receive ALERTING from MT, route ALERTING to MO
|
||||
# receive CONNECT from MT, confirm to MT with CONNECT_ACK
|
||||
# send a CONNECT message to MO, receive CONNECT_ACK from MO
|
||||
* If subscriber is not found:
|
||||
# send RELEASE COMPLETE with apropriate cause to MO (1: unalloacated 3: no route)
|
||||
|
||||
|
||||
|
||||
Thoughts about RR/MM:
|
||||
|
||||
* we allocate RR/MM entities on demand, when we need them
|
95
doc/channel_release.txt
Normal file
95
doc/channel_release.txt
Normal file
@@ -0,0 +1,95 @@
|
||||
|
||||
GSM 04.08 7.1.7 / 9.1.7 RR CHANNEL RELESE
|
||||
|
||||
RSL 08.58 3.4 / ? RLL Link Release Request
|
||||
|
||||
RSL 08.58 4.6 / 8.4.5 DEACTivate SACCH
|
||||
* Deactivate SACCH according to Channel Release Proc 04.08
|
||||
* to be sent after RR CHANNEL RELEASE is sent to MS
|
||||
|
||||
RSL 08.58 4.7 / 8.4.14 RF CHANnel RELease
|
||||
* tells the BTS to release a radio channel
|
||||
* "when an activated radio channel is no longer needed"
|
||||
* BTS responds with RF CHANnel RELease ACKnowledge
|
||||
|
||||
|
||||
GSM 04.08 3.4.13: RR connection release procedure
|
||||
|
||||
* network sends RR CHANNEL RELEASE to MS on the DCCH
|
||||
* start T3109
|
||||
* deactivate SACCH
|
||||
* MS disconnects main signalling link (by sending DISC)
|
||||
* all other data links are disconnected by local end link release
|
||||
* network receives DISC (BTS sends RLL REL IND to BSC)
|
||||
* stop T3109
|
||||
* start T3111
|
||||
* when T3111 times out, the network can reuse the channls
|
||||
* if T3109 times out, the network deactivates the channels
|
||||
and can reuse them
|
||||
* this probably means simply RF CHANnel RELease
|
||||
|
||||
|
||||
== Implementation in OpenBSC ==
|
||||
|
||||
There are two possible reasons a gsm_subscriber_connection
|
||||
will be released. One is a network failure, the other is
|
||||
the completion of an operation/transaction.
|
||||
|
||||
=== Failure ===
|
||||
The BSC API will call the gsm_04_08.c:gsm0408_clear_request callback
|
||||
and the MSC part will release all transactions, operations and such
|
||||
and the channels will be released as error case.
|
||||
|
||||
=== Success ===
|
||||
Every time an 'operation' or 'transaction' is finished msc_release_connection
|
||||
will be called and it will determine if the gsm_subscriber_connection can
|
||||
be released.
|
||||
|
||||
In case it can be released bsc_api.c:gsm0808_clear will be called
|
||||
which will release all lchan's associated with the connection. For the
|
||||
primary channel a SACH Deactivate will be send with the release
|
||||
reason NORMAL RELEASE.
|
||||
|
||||
|
||||
bsc_api.c:gsm0808_clear
|
||||
* Release a channel used for handover
|
||||
* Release the primary lchan with normal release, SACH deactivate
|
||||
|
||||
chan_alloc.c:lchan_release(chan, sacch_deactivate, reason)
|
||||
* Start the release procedure. It is working in steps with callbacks
|
||||
coming from the abis_rsl.c code.
|
||||
* Release all SAPI's > 0 as local end (The BTS should send a
|
||||
REL_CONF a message)
|
||||
* Send SACH Deactivate on SAPI=0 if required.
|
||||
* Start T3109 (stop it when the main signalling link is disconnected)
|
||||
or when the channel released. On timeout start the error handling.
|
||||
* abis_rsl.c schedules the RSL_MT_RF_CHAN_REL once all SAPI's are
|
||||
released and after T3111 has timed out or there is an error.
|
||||
|
||||
RX of RELease INDication:
|
||||
* Calls internal rsl_handle_release which might release the RF.
|
||||
|
||||
RX of RELease CONFirmation:
|
||||
* Calls internal rsl_handle_release which might release the RF.
|
||||
|
||||
* RX of RF_CHAN_REL_ACK
|
||||
* call lchan_free()
|
||||
|
||||
|
||||
=== Integration with SMS ===
|
||||
|
||||
* RX of CP_ERROR or unimplemented MT
|
||||
* trigger trans_free() which will msc_release_connection()
|
||||
|
||||
* CP TC1* expired while waiting for CP-ACK
|
||||
* trigger trans_free() which will msc_release_connection()
|
||||
|
||||
* RX of RP_ERROR
|
||||
* trigger trans_free() which will msc_release_connection()
|
||||
|
||||
* TX of CP-ACK in MT DELIVER
|
||||
* trigger trans_free() which will msc_release_connection()
|
||||
|
||||
* RX of CP-ACK in MO SUBMIT
|
||||
* trigger trans_free() which will msc_release_connection()
|
||||
|
172
doc/e1-data-model.txt
Normal file
172
doc/e1-data-model.txt
Normal file
@@ -0,0 +1,172 @@
|
||||
E1 related data model
|
||||
|
||||
This data model describes the physical relationship of the individual
|
||||
parts in the network, it is not the logical/protocol side of the GSM
|
||||
network.
|
||||
|
||||
A BTS is connected to the BSC by some physical link. It could be an actual
|
||||
E1 link, but it could also be abis-over-IP with a mixture of TCP and RTP/UDP.
|
||||
|
||||
To further complicate the fact, multiple BTS can share one such pysical
|
||||
link. On a single E1 line, we can easily accomodate up to three BTS with
|
||||
two TRX each.
|
||||
|
||||
Thus, it is best for OpenBSC to have some kind of abstraction layer. The BSC's
|
||||
view of a BTS connected to it. We call this 'bts_link'. A bts_link can be
|
||||
* all the TCP and UDP streams of a Abis-over-IP BTS
|
||||
* a set of E1 timeslots for OML, RSL and TRAU connections on a E1 link
|
||||
* a serial line exclusively used for OML messages (T-Link)
|
||||
|
||||
A bts_link can be registered with the OpenBSC core at runtime.
|
||||
|
||||
struct trx_link {
|
||||
struct gsm_bts_trx *trx;
|
||||
};
|
||||
|
||||
struct bts_link {
|
||||
struct gsm_bts *bts;
|
||||
struct trx_link trx_links[NUM_TRX];
|
||||
};
|
||||
|
||||
Interface from stack to input core:
|
||||
======================================================================
|
||||
int abis_rsl_sendmsg(struct msgb *msg);
|
||||
send a message through a RSL link to the TRX specified by the caller in
|
||||
msg->trx.
|
||||
|
||||
int abis_rsl_rcvmsg(struct msgb *msg);
|
||||
receive a message from a RSL link from the TRX specified by the
|
||||
caller in msg->trx.
|
||||
|
||||
int abis_nm_sendmsg(struct msgb *msg);
|
||||
send a message through a OML link to the BTS specified by the caller in
|
||||
msg->trx->bts. The caller can just use bts->c0 to get the first TRX
|
||||
in a BTS. (OML messages are not really sent to a TRX but to the BTS)
|
||||
|
||||
int abis_nm_rcvmsg(struct msgb *msg);
|
||||
receive a message from a OML link from the BTS specified by the caller
|
||||
in msg->trx->bts. The caller can just use bts->c0 to get the first
|
||||
TRX in a BTS.
|
||||
|
||||
int abis_link_event(int event, void *data);
|
||||
signal some event (such as layer 1 connect/disconnect) from the
|
||||
input core to the stack.
|
||||
|
||||
int subch_demux_in(mx, const uint8_t *data, int len);
|
||||
receive 'len' bytes from a given E1 timeslot (TRAU frames)
|
||||
|
||||
int subchan_mux_out(mx, uint8_t *data, int len);
|
||||
obtain 'len' bytes of output data to be sent on E1 timeslot
|
||||
|
||||
Intrface by Input Core for Input Plugins
|
||||
======================================================================
|
||||
|
||||
int btslink_register_plugin();
|
||||
|
||||
|
||||
Configuration for the E1 input module
|
||||
======================================================================
|
||||
|
||||
BTS
|
||||
BTS number
|
||||
number of TRX
|
||||
OML link
|
||||
E1 line number
|
||||
timeslot number
|
||||
[subslot number]
|
||||
SAPI
|
||||
TEI
|
||||
for each TRX
|
||||
RSL link
|
||||
E1 line number
|
||||
timeslot number
|
||||
[subslot number]
|
||||
SAPI
|
||||
TEI
|
||||
for each TS
|
||||
E1 line number
|
||||
timeslot number
|
||||
subslot number
|
||||
|
||||
|
||||
E1 input module data model
|
||||
======================================================================
|
||||
|
||||
|
||||
enum e1inp_sign_type {
|
||||
E1INP_SIGN_NONE,
|
||||
E1INP_SIGN_OML,
|
||||
E1INP_SIGN_RSL,
|
||||
};
|
||||
|
||||
struct e1inp_sign_link {
|
||||
/* list of signalling links */
|
||||
struct llist_head list;
|
||||
|
||||
enum e1inp_sign_type type;
|
||||
|
||||
/* trx for msg->trx of received msgs */
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
/* msgb queue of to-be-transmitted msgs */
|
||||
struct llist_head tx_list;
|
||||
|
||||
/* SAPI and TEI on the E1 TS */
|
||||
uint8_t sapi;
|
||||
uint8_t tei;
|
||||
}
|
||||
|
||||
enum e1inp_ts_type {
|
||||
E1INP_TS_TYPE_NONE,
|
||||
E1INP_TS_TYPE_SIGN,
|
||||
E1INP_TS_TYPE_TRAU,
|
||||
};
|
||||
|
||||
/* A timeslot in the E1 interface */
|
||||
struct e1inp_ts {
|
||||
enum e1inp_ts_type type;
|
||||
struct e1inp_line *line;
|
||||
union {
|
||||
struct {
|
||||
struct llist_head sign_links;
|
||||
} sign;
|
||||
struct {
|
||||
/* subchannel demuxer for frames from E1 */
|
||||
struct subch_demux demux;
|
||||
/* subchannel muxer for frames to E1 */
|
||||
struct subch_mux mux;
|
||||
} trau;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
/* mISDN driver has one fd for each ts */
|
||||
struct osmo_fd;
|
||||
} misdn;
|
||||
} driver;
|
||||
};
|
||||
|
||||
struct e1inp_line {
|
||||
unsigned int num;
|
||||
char *name;
|
||||
|
||||
struct e1inp_ts ts[NR_E1_TS];
|
||||
|
||||
char *e1inp_driver;
|
||||
void *driver_data;
|
||||
};
|
||||
|
||||
/* Call from the Stack: configuration of this TS has changed */
|
||||
int e1inp_update_ts(struct e1inp_ts *ts);
|
||||
|
||||
/* Receive a packet from the E1 driver */
|
||||
int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
|
||||
uint8_t tei, uint8_t sapi);
|
||||
|
||||
/* Send a packet, callback function in the driver */
|
||||
int e1driver_tx_ts(struct e1inp_ts *ts, struct msgb *msg)
|
||||
|
||||
|
||||
struct e1inp_driver {
|
||||
const char *name;
|
||||
int (*want_write)(struct e1inp_ts *ts);
|
||||
};
|
105
doc/examples/osmo-bsc/osmo-bsc.cfg
Normal file
105
doc/examples/osmo-bsc/osmo-bsc.cfg
Normal file
@@ -0,0 +1,105 @@
|
||||
!
|
||||
! OsmoBSC (0.9.14+gitr1+3d331c0062bb0c9694dbd4d1eab7adc58138c3ae) configuration saved from vty
|
||||
!!
|
||||
password foo
|
||||
!
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
e1_input
|
||||
e1_line 0 driver ipa
|
||||
network
|
||||
network country code 1
|
||||
mobile network code 1
|
||||
short name OsmoBSC
|
||||
long name OsmoBSC
|
||||
auth policy closed
|
||||
location updating reject cause 13
|
||||
encryption a5 0
|
||||
neci 1
|
||||
paging any use tch 0
|
||||
rrlp mode none
|
||||
mm info 1
|
||||
handover 0
|
||||
handover window rxlev averaging 10
|
||||
handover window rxqual averaging 1
|
||||
handover window rxlev neighbor averaging 10
|
||||
handover power budget interval 6
|
||||
handover power budget hysteresis 3
|
||||
handover maximum distance 9999
|
||||
timer t3101 10
|
||||
timer t3103 0
|
||||
timer t3105 0
|
||||
timer t3107 0
|
||||
timer t3109 0
|
||||
timer t3111 0
|
||||
timer t3113 60
|
||||
timer t3115 0
|
||||
timer t3117 0
|
||||
timer t3119 0
|
||||
timer t3122 0
|
||||
timer t3141 0
|
||||
bts 0
|
||||
type nanobts
|
||||
band DCS1800
|
||||
cell_identity 0
|
||||
location_area_code 1
|
||||
training_sequence_code 7
|
||||
base_station_id_code 63
|
||||
ms max power 15
|
||||
cell reselection hysteresis 4
|
||||
rxlev access min 0
|
||||
channel allocator ascending
|
||||
rach tx integer 9
|
||||
rach max transmission 7
|
||||
dtx uplink force
|
||||
dtx downlink
|
||||
ip.access unit_id 0 0
|
||||
oml ip.access stream_id 255 line 0
|
||||
neighbor-list mode manual-si5
|
||||
neighbor-list add arfcn 100
|
||||
neighbor-list add arfcn 200
|
||||
si5 neighbor-list add arfcn 10
|
||||
si5 neighbor-list add arfcn 20
|
||||
gprs mode none
|
||||
trx 0
|
||||
rf_locked 0
|
||||
arfcn 871
|
||||
nominal power 23
|
||||
max_power_red 20
|
||||
rsl e1 tei 0
|
||||
timeslot 0
|
||||
phys_chan_config CCCH+SDCCH4
|
||||
hopping enabled 0
|
||||
timeslot 1
|
||||
phys_chan_config TCH/F
|
||||
hopping enabled 0
|
||||
timeslot 2
|
||||
phys_chan_config TCH/F
|
||||
hopping enabled 0
|
||||
timeslot 3
|
||||
phys_chan_config TCH/F
|
||||
hopping enabled 0
|
||||
timeslot 4
|
||||
phys_chan_config TCH/F
|
||||
hopping enabled 0
|
||||
timeslot 5
|
||||
phys_chan_config TCH/F
|
||||
hopping enabled 0
|
||||
timeslot 6
|
||||
phys_chan_config TCH/F
|
||||
hopping enabled 0
|
||||
timeslot 7
|
||||
phys_chan_config TCH/F
|
||||
hopping enabled 0
|
||||
msc
|
||||
ip.access rtp-base 4000
|
||||
timeout-ping 20
|
||||
timeout-pong 5
|
||||
dest 192.168.100.11 6666 0
|
||||
access-list-name msc-list
|
||||
no access-list-name
|
||||
bsc
|
||||
no access-list-name
|
||||
access-list-name bsc-list
|
@@ -1,14 +1,19 @@
|
||||
!
|
||||
! MGCP configuration example
|
||||
! MGCP configuration hand edited
|
||||
! !
|
||||
password foo
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
mgcp
|
||||
!local ip 10.23.24.2
|
||||
!bts ip 10.24.24.1
|
||||
!bind ip 10.23.24.1
|
||||
bind port 2427
|
||||
rtp base 4000
|
||||
rtp force-ptime 20
|
||||
sdp audio payload number 98
|
||||
sdp audio payload name AMR/8000
|
||||
number endpoints 31
|
||||
no rtcp-omit
|
||||
local ip 10.23.24.2
|
||||
bts ip 10.24.24.1
|
||||
bind ip 10.23.24.1
|
||||
bind port 2427
|
||||
rtp base 4000
|
||||
rtp force-ptime 20
|
||||
sdp audio payload number 98
|
||||
sdp audio payload name AMR/8000
|
||||
number endpoints 31
|
||||
no rtcp-omit
|
||||
|
1
doc/examples/osmo-bsc_nat/black-list.cfg
Normal file
1
doc/examples/osmo-bsc_nat/black-list.cfg
Normal file
@@ -0,0 +1 @@
|
||||
678012512671923:6:6:
|
13
doc/examples/osmo-bsc_nat/bscs.cfg
Normal file
13
doc/examples/osmo-bsc_nat/bscs.cfg
Normal file
@@ -0,0 +1,13 @@
|
||||
nat
|
||||
bsc 0
|
||||
token lol
|
||||
location_area_code 1234
|
||||
description bsc
|
||||
max-endpoints 32
|
||||
paging forbidden 0
|
||||
bsc 1
|
||||
token wat
|
||||
location_area_code 5678
|
||||
description bsc
|
||||
max-endpoints 32
|
||||
paging forbidden 0
|
66
doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg
Normal file
66
doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg
Normal file
@@ -0,0 +1,66 @@
|
||||
!
|
||||
! OsmoBSCNAT (0.12.0.266-2daa9) configuration saved from vty
|
||||
!!
|
||||
!
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging timestamp 0
|
||||
logging level all debug
|
||||
logging level rll notice
|
||||
logging level cc notice
|
||||
logging level mm notice
|
||||
logging level rr notice
|
||||
logging level rsl notice
|
||||
logging level nm info
|
||||
logging level mncc notice
|
||||
logging level pag notice
|
||||
logging level meas notice
|
||||
logging level sccp notice
|
||||
logging level msc notice
|
||||
logging level mgcp notice
|
||||
logging level ho notice
|
||||
logging level db notice
|
||||
logging level ref notice
|
||||
logging level gprs debug
|
||||
logging level ns info
|
||||
logging level bssgp debug
|
||||
logging level llc debug
|
||||
logging level sndcp debug
|
||||
logging level nat notice
|
||||
logging level ctrl notice
|
||||
logging level smpp debug
|
||||
logging level lglobal notice
|
||||
logging level llapd notice
|
||||
logging level linp notice
|
||||
logging level lmux notice
|
||||
logging level lmi notice
|
||||
logging level lmib notice
|
||||
logging level lsms notice
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
mgcp
|
||||
bind ip 0.0.0.0
|
||||
bind port 2427
|
||||
rtp bts-base 4000
|
||||
rtp net-base 16000
|
||||
rtp ip-dscp 0
|
||||
no rtcp-omit
|
||||
sdp audio-payload number 126
|
||||
sdp audio-payload name AMR/8000
|
||||
loop 0
|
||||
number endpoints 1
|
||||
call-agent ip 127.0.0.1
|
||||
rtp transcoder-base 0
|
||||
transcoder-remote-base 4000
|
||||
nat
|
||||
msc ip 127.0.0.1
|
||||
msc port 5000
|
||||
timeout auth 2
|
||||
timeout ping 20
|
||||
timeout pong 5
|
||||
ip-dscp 0
|
||||
bscs-config-file bscs.cfg
|
||||
access-list bla imsi-allow ^11$
|
44
doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg
Normal file
44
doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg
Normal file
@@ -0,0 +1,44 @@
|
||||
!
|
||||
! OsmoGbProxy (UNKNOWN) configuration saved from vty
|
||||
!!
|
||||
!
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging timestamp 0
|
||||
logging level all debug
|
||||
logging level gprs debug
|
||||
logging level ns info
|
||||
logging level bssgp debug
|
||||
logging level lglobal notice
|
||||
logging level llapd notice
|
||||
logging level linp notice
|
||||
logging level lmux notice
|
||||
logging level lmi notice
|
||||
logging level lmib notice
|
||||
logging level lsms notice
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
ns
|
||||
nse 666 nsvci 666
|
||||
nse 666 remote-role sgsn
|
||||
! nse 666 encapsulation framerelay-gre
|
||||
! nse 666 remote-ip 172.16.1.70
|
||||
! nse 666 fr-dlci 666
|
||||
timer tns-block 3
|
||||
timer tns-block-retries 3
|
||||
timer tns-reset 3
|
||||
timer tns-reset-retries 3
|
||||
timer tns-test 30
|
||||
timer tns-alive 3
|
||||
timer tns-alive-retries 10
|
||||
encapsulation udp local-port 23000
|
||||
! encapsulation framerelay-gre enabled 1
|
||||
gbproxy
|
||||
sgsn nsei 666
|
||||
core-mobile-country-code 666
|
||||
core-mobile-network-code 6
|
||||
core-access-point-name none match-imsi ^666066|^66607
|
||||
tlli-list max-length 200
|
25
doc/examples/osmo-gbproxy/osmo-gbproxy.cfg
Normal file
25
doc/examples/osmo-gbproxy/osmo-gbproxy.cfg
Normal file
@@ -0,0 +1,25 @@
|
||||
!
|
||||
! Osmocom Gb Proxy (0.9.0.404-6463) configuration saved from vty
|
||||
!!
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
gbproxy
|
||||
sgsn nsei 101
|
||||
ns
|
||||
nse 101 nsvci 101
|
||||
nse 101 remote-role sgsn
|
||||
nse 101 encapsulation udp
|
||||
nse 101 remote-ip 192.168.100.239
|
||||
nse 101 remote-port 7777
|
||||
timer tns-block 3
|
||||
timer tns-block-retries 3
|
||||
timer tns-reset 3
|
||||
timer tns-reset-retries 3
|
||||
timer tns-test 30
|
||||
timer tns-alive 3
|
||||
timer tns-alive-retries 10
|
||||
encapsulation framerelay-gre enabled 0
|
||||
encapsulation framerelay-gre local-ip 0.0.0.0
|
||||
encapsulation udp local-port 23000
|
90
doc/examples/osmo-gtphub/gtphub-example.txt
Normal file
90
doc/examples/osmo-gtphub/gtphub-example.txt
Normal file
@@ -0,0 +1,90 @@
|
||||
Here is a simple setup to test GTPHub operations. The IP addresses picked will
|
||||
work well only on a system that creates local addresses (127.0.0.123) on the
|
||||
fly (like linux) -- you may pick of course different IP addresses.
|
||||
|
||||
Overview of the example setup:
|
||||
|
||||
sgsnemu gtphub ggsn
|
||||
127.0.0.1 <--> 127.0.0.3 127.0.0.4 <--> 127.0.0.2
|
||||
|
||||
Prerequisites: openggsn.
|
||||
|
||||
Have a local directory where you store config files and from which you launch
|
||||
the GSNs and the hub (they will store restart counter files in that dir).
|
||||
In it, have these config files:
|
||||
|
||||
ggsn.conf:
|
||||
|
||||
# GGSN local address
|
||||
listen 127.0.0.2
|
||||
|
||||
# End User Addresses are picked from this range
|
||||
net 10.23.42.0/24
|
||||
|
||||
pcodns1 8.8.8.8
|
||||
|
||||
logfile /tmp/foo
|
||||
|
||||
gtphub.conf:
|
||||
|
||||
gtphub
|
||||
bind-to-sgsns 127.0.0.3
|
||||
bind-to-ggsns 127.0.0.4
|
||||
ggsn-proxy 127.0.0.2
|
||||
end
|
||||
|
||||
|
||||
(
|
||||
You may omit the ggsn-proxy if GRX ares is working, or if you add the GRX
|
||||
address and GGSN IP address to /etc/hosts something like:
|
||||
|
||||
127.0.0.2 internet.mnc070.mcc901.gprs
|
||||
|
||||
)
|
||||
|
||||
|
||||
Once the config files are in place, start the programs, in separate terminals.
|
||||
GGSN and SGSN need to be started with root priviliges to be able to create tun
|
||||
interfaces. GTPHub may run as unprivileged user.
|
||||
|
||||
The LD_LIBRARY_PATH below may be needed if OpenGGSN installed to /usr/local.
|
||||
|
||||
|
||||
1. GGSN:
|
||||
|
||||
sudo -s
|
||||
cd <your-test-dir>
|
||||
LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/ggsn -f -c ./ggsn.conf
|
||||
|
||||
2. GTPHub:
|
||||
|
||||
cd <your-test-dir>
|
||||
path/to/openbsc/openbsc/src/gprs/osmo-gtphub -c gtphub.conf #-e 1 #for DEBUG level
|
||||
|
||||
3. SGSN tests:
|
||||
|
||||
sudo -s
|
||||
cd <your-test-dir>
|
||||
/usr/local/bin/sgsnemu --createif -l 127.0.0.1 -r 127.0.0.3 --imsi 420001214365100 --contexts=3
|
||||
|
||||
Add more SGSNs using different IMSIs and local ports (if the same IMSI is used,
|
||||
the GGSN will reuse TEIs and tunnels will be discarded automatically):
|
||||
|
||||
/usr/local/bin/sgsnemu --createif -l 127.0.0.11 -r 127.0.0.3 --imsi 420001214365300 --contexts=3
|
||||
|
||||
This shows the basic setup of GTPHub. Testing internet traffic via sgsnemu
|
||||
still needs some effort to announce a mobile subscriber or the like (I have
|
||||
used a real BTS, osmo-sgsn and a testing SIM in a web phone, instead).
|
||||
|
||||
The core capability of GTPHub is to manage more than two GSNs, e.g. an SGSN
|
||||
contacting various GGSNs over the single GTPHub link. You would configure the
|
||||
SGSN to use one fixed GGSN (sending to gtphub) and gtphub will resolve the
|
||||
GGSNs once it has received the messages. So the SGSN may be behind NAT (add
|
||||
"sgsn-use-sender" to gtphub.conf) and communicate to various GGSNs over a
|
||||
single link to gtphub.
|
||||
|
||||
I hope this helps to get you going.
|
||||
Any suggestions/patches are welcome!
|
||||
|
||||
~Neels
|
||||
|
25
doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg
Normal file
25
doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg
Normal file
@@ -0,0 +1,25 @@
|
||||
!
|
||||
! Osmocom gtphub configuration
|
||||
!
|
||||
! This file is used for VTY tests, referenced by openbsc/osmoappdesc.py
|
||||
! For the test, try to use most config commands.
|
||||
!
|
||||
|
||||
line vty
|
||||
no login
|
||||
|
||||
gtphub
|
||||
! Local addresses to listen on and send from, both on one interface.
|
||||
! The side towards SGSN uses nonstandard ports.
|
||||
bind-to-sgsns ctrl 127.0.0.1 12123 user 127.0.0.1 12153
|
||||
! The GGSN side with standard ports.
|
||||
bind-to-ggsns 127.0.0.1
|
||||
|
||||
! Proxy: unconditionally direct all traffic to...
|
||||
sgsn-proxy 127.0.0.4
|
||||
|
||||
! Proxy with nonstandard ports or separate IPs:
|
||||
ggsn-proxy ctrl 127.0.0.3 2123 user 127.0.0.5 2152
|
||||
|
||||
! Add a name server for GGSN resolution
|
||||
grx-dns-add 192.168.0.1
|
25
doc/examples/osmo-gtphub/osmo-gtphub.cfg
Normal file
25
doc/examples/osmo-gtphub/osmo-gtphub.cfg
Normal file
@@ -0,0 +1,25 @@
|
||||
!
|
||||
! Osmocom gtphub configuration
|
||||
!
|
||||
|
||||
line vty
|
||||
no login
|
||||
|
||||
gtphub
|
||||
! Local addresses to listen on and send from, each on standard ports
|
||||
! 2123 and 2152. Setting these addresses is mandatory.
|
||||
bind-to-sgsns 127.0.0.1
|
||||
bind-to-ggsns 127.0.0.2
|
||||
|
||||
! Local nonstandard ports or separate IPs:
|
||||
!bind-to-sgsns ctrl 127.0.0.1 2342 user 127.0.0.1 4223
|
||||
|
||||
! Proxy: unconditionally direct all traffic to...
|
||||
!ggsn-proxy 127.0.0.3
|
||||
!sgsn-proxy 127.0.0.4
|
||||
|
||||
! Proxy with nonstandard ports or separate IPs:
|
||||
!ggsn-proxy ctrl 127.0.0.3 2123 user 127.0.0.5 2152
|
||||
|
||||
! Add a name server for GGSN resolution
|
||||
!grx-dns-add 192.168.0.1
|
@@ -1,18 +0,0 @@
|
||||
!
|
||||
! MGCP configuration example
|
||||
!
|
||||
mgcp
|
||||
bind ip 127.0.0.1
|
||||
rtp port-range 4002 16000
|
||||
rtp bind-ip 10.9.1.122
|
||||
rtp ip-probing
|
||||
rtp ip-tos 184
|
||||
bind port 2427
|
||||
sdp audio payload number 98
|
||||
sdp audio payload name GSM
|
||||
number endpoints 31
|
||||
loop 0
|
||||
force-realloc 1
|
||||
rtcp-omit
|
||||
rtp-patch ssrc
|
||||
rtp-patch timestamp
|
19
doc/examples/osmo-msc/osmo-msc.cfg
Normal file
19
doc/examples/osmo-msc/osmo-msc.cfg
Normal file
@@ -0,0 +1,19 @@
|
||||
!
|
||||
! OsmoMSC configuration saved from vty
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
network
|
||||
network country code 1
|
||||
mobile network code 1
|
||||
short name OsmoMSC
|
||||
long name OsmoMSC
|
||||
auth policy closed
|
||||
location updating reject cause 13
|
||||
encryption a5 0
|
||||
rrlp mode none
|
||||
mm info 1
|
||||
msc
|
||||
mgcpgw remote-ip 10.23.24.1
|
||||
assign-tmsi
|
29
doc/examples/osmo-sgsn/osmo-sgsn.cfg
Normal file
29
doc/examples/osmo-sgsn/osmo-sgsn.cfg
Normal file
@@ -0,0 +1,29 @@
|
||||
!
|
||||
! Osmocom SGSN configuration
|
||||
!
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
sgsn
|
||||
gtp local-ip 10.23.24.1
|
||||
ggsn 0 remote-ip 10.23.24.2
|
||||
ggsn 0 gtp-version 1
|
||||
auth-policy remote
|
||||
gsup remote-ip 127.0.0.1
|
||||
gsup remote-port 4222
|
||||
!
|
||||
ns
|
||||
timer tns-block 3
|
||||
timer tns-block-retries 3
|
||||
timer tns-reset 3
|
||||
timer tns-reset-retries 3
|
||||
timer tns-test 30
|
||||
timer tns-alive 3
|
||||
timer tns-alive-retries 10
|
||||
encapsulation udp local-ip 10.23.24.1
|
||||
encapsulation udp local-port 23000
|
||||
encapsulation framerelay-gre enabled 0
|
||||
!
|
||||
bssgp
|
||||
!
|
54
doc/gsm-hopping.txt
Normal file
54
doc/gsm-hopping.txt
Normal file
@@ -0,0 +1,54 @@
|
||||
according to GSM 05.02:
|
||||
|
||||
general parameters from CCCH:
|
||||
* CA cell allocation of ARFCN's (System Information / BCCH)
|
||||
* FN: TDMA frame number (t1,t2,t3') in SCH
|
||||
|
||||
specific parameters from channel assignment:
|
||||
* MA: mobile allocation, defines set of ARFCN's, up to 64
|
||||
* MAIO: index
|
||||
* HSN: hopping sequence generator number (0..64)
|
||||
|
||||
|
||||
hopping sequence generation (6.2.3):
|
||||
|
||||
uint8_t rntable[114] = {
|
||||
48, 98, 63, 1, 36, 95, 78, 102, 94, 73,
|
||||
0, 64, 25, 81, 76, 59, 124, 23, 104, 100,
|
||||
101, 47, 118, 85, 18, 56, 96, 86, 54, 2,
|
||||
80, 34, 127, 13, 6, 89, 57, 103, 12, 74,
|
||||
55, 111, 75, 38, 109, 71, 112, 29, 11, 88,
|
||||
87, 19, 3, 68, 110, 26, 33, 31, 8, 45,
|
||||
82, 58, 40, 107, 32, 5, 106, 92, 62, 67,
|
||||
77, 108, 122, 37, 60, 66, 121, 42, 51, 126,
|
||||
117, 114, 4, 90, 43, 52, 53, 113, 120, 72,
|
||||
16, 49, 7, 79, 119, 61, 22, 84, 9, 97,
|
||||
125, 99, 17, 123
|
||||
};
|
||||
|
||||
/* mai=0 represents lowest ARFCN in the MA */
|
||||
|
||||
|
||||
uint8_t hopping_mai(uint8_t hsn, uint32_t fn, uint8_t maio,
|
||||
uint8_t t1, uint8_t t2, uint8_t t3_)
|
||||
{
|
||||
uint8_t mai;
|
||||
|
||||
if (hsn == 0) /* cyclic hopping */
|
||||
mai = (fn + maio) % n;
|
||||
else {
|
||||
uint32_t m, m_, t_, s;
|
||||
|
||||
m = t2 + rntable[(hsn xor (t1 % 64)) + t3];
|
||||
m_ = m % (2^NBIN);
|
||||
t_ = t3 % (2^NBIN);
|
||||
if (m_ < n then)
|
||||
s = m_;
|
||||
else
|
||||
s = (m_ + t_) % n;
|
||||
mai = (s + maio) % n;
|
||||
}
|
||||
|
||||
return mai;
|
||||
}
|
||||
|
89
doc/handover.txt
Normal file
89
doc/handover.txt
Normal file
@@ -0,0 +1,89 @@
|
||||
Ideas about a handover algorithm
|
||||
======================================================================
|
||||
|
||||
This is mostly based on the results presented in Chapter 8 of "Performance
|
||||
Enhancements in a Frequency Hopping GSM Network" by Thomas Toftegaard Nielsen
|
||||
and Joeroen Wigard.
|
||||
|
||||
|
||||
=== Reasons for performing handover ===
|
||||
|
||||
Section 2.1.1: Handover used in their CAPACITY simulation:
|
||||
|
||||
1) Interference Handover
|
||||
|
||||
Average RXLEV is satisfactory high, but average RXQUAL too low indicates
|
||||
interference to the channel. Handover should be made.
|
||||
|
||||
2) Bad Quality
|
||||
|
||||
Averaged RXQUAL is lower than a threshold
|
||||
|
||||
3) Low Level / Signal Strength
|
||||
|
||||
Average RXLEV is lower than a threshold
|
||||
|
||||
4) Distance Handover
|
||||
|
||||
MS is too far away from a cell (measured by TA)
|
||||
|
||||
5) Power budget / Better Cell
|
||||
|
||||
RX Level of neighbor cell is at least "HO Margin dB" dB better than the
|
||||
current serving cell.
|
||||
|
||||
=== Ideal parameters for HO algorithm ===
|
||||
|
||||
Chapter 8, Section 2.2, Table 24:
|
||||
|
||||
Window RXLEV averaging: 10 SACCH frames (no weighting)
|
||||
Window RXQUAL averaging: 1 SACCH frame (no averaging)
|
||||
Level Threashold: 1 of the last 1 AV-RXLEV values < -110dBm
|
||||
Quality Threshold: 3 of the last 4 AV-RXQUAL values >= 5
|
||||
Interference Threshold: 1 of the last AV-RXLEV > -85 dBm &
|
||||
3 of the last 4 AV-RXQUAL values >= 5
|
||||
Power Budget: Level of neighbor cell > 3 dB better
|
||||
Power Budget Interval: Every 6 SACCH frames (6 seconds ?!?)
|
||||
Distance Handover: Disabled
|
||||
Evaluation rule 1: RXLEV of the candidate cell a tleast -104 dBm
|
||||
Evaluation rule 2: Level of candidate cell > 3dB better own cell
|
||||
Timer Successful HO: 5 SACCH frames
|
||||
Timer Unsuccessful HO: 1 SACCH frame
|
||||
|
||||
In a non-frequency hopping case, RXQUAL threshold can be decreased to
|
||||
RXLEV >= 4
|
||||
|
||||
When frequency hopping is enabled, the following additional parameters
|
||||
should be introduced:
|
||||
|
||||
* No intra-cell handover
|
||||
* Use a HO Margin of 2dB
|
||||
|
||||
=== Handover Channel Reservation ===
|
||||
|
||||
In loaded network, each cell should reserve some channels for handovers,
|
||||
rather than using all of them for new call establishment. This reduces the
|
||||
need to drop calls due to failing handovers, at the expense of failing new call
|
||||
attempts.
|
||||
|
||||
=== Dynamic HO Margin ===
|
||||
|
||||
The handover margin (hysteresis) should depend on the RXQUAL. Optimal results
|
||||
were achieved with the following settings:
|
||||
* RXQUAL <= 4: 9 dB
|
||||
* RXQUAL == 5: 6 dB
|
||||
* RXQUAL >= 6: 1 dB
|
||||
|
||||
|
||||
|
||||
== Actual Handover on a protocol level ==
|
||||
|
||||
After the BSC has decided a handover shall be done, it has to
|
||||
|
||||
# allocate a channel at the new BTS
|
||||
# allocate a handover reference
|
||||
# activate the channel on the BTS side using RSL CHANNEL ACTIVATION,
|
||||
indicating the HO reference
|
||||
# BTS responds with CHAN ACT ACK, including GSM frame number
|
||||
# BSC sends 04.08 HO CMD to MS using old BTS
|
||||
|
94
doc/ipa-sccp.txt
Normal file
94
doc/ipa-sccp.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
|
||||
IPA SCCP message flow in the BSC
|
||||
|
||||
February, 2013 Holger Hans Peter Freyther
|
||||
|
||||
CONTENTS
|
||||
|
||||
1. SCCP inside the IPA header
|
||||
2. Supported SCCP message types
|
||||
3. Receiving SCCP messages
|
||||
4. Sending SCCP messages
|
||||
|
||||
|
||||
1. SCCP inside the IPA header
|
||||
|
||||
Many Soft-MSCs implement something that is called SCCP/lite. This means
|
||||
that SCCP messages are transported inside a small multiplexing protocol
|
||||
over TCP/IP. This is an alternative to a full SIGTRAN implementation.
|
||||
|
||||
The multiplexing protocol is the same as used with the sysmoBTS and the
|
||||
ip.access nanoBTS. It is a three byte header with two bytes for the length
|
||||
in network byte order and one byte for the type. The type to be used for
|
||||
SCCP is 0xFD.
|
||||
|
||||
struct ipa_header {
|
||||
uint16_t length_in_network_order;
|
||||
uint8_t type;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
|
||||
2. Supported SCCP message types
|
||||
|
||||
To implement GSM 08.08 only a subset of SCCP messages need to be implemented.
|
||||
For transporting paging and reset messages SCCP UDT messages are used. For
|
||||
the connections with a Mobile Station (MS) a SCCP connection is opened. This
|
||||
means that the SCCP CR, SCCP CC, SCCP CREF, SCCP RLC, SCCP RLSD, SCCP DT1
|
||||
and SCCP IT messages are supported.
|
||||
|
||||
|
||||
3. Receiving SCCP UDT messages
|
||||
|
||||
This is an illustration of the flow of messages. The IPA multiplexing protocol
|
||||
is used for various protocols. This means there is a central place where the
|
||||
multiplexing stream terminates. The stream is terminated in the osmo_bsc_msc.c
|
||||
file and the ipaccess_a_fd_cb method. For SCCP messages the SCCP dispatching
|
||||
sccp_system_incoming method is called. This function is implemented in the
|
||||
libosmo-sccp library.
|
||||
|
||||
To receive UDT messages osmo_bsc_sccp.c:osmo_bsc_sccp_init is using the
|
||||
sccp_set_read function to register a callback for UDT messages. The callback
|
||||
is msc_sccp_read and it is calling bsc_handle_udt that is implemented in the
|
||||
osmo_bsc_bssap.c. This function will handle the GSM 08.08 BSSAP messages.
|
||||
Currently only the reset acknowledge and the paging messages are handled.
|
||||
|
||||
The BSC currently does not accept incoming SCCP messages and is only opening
|
||||
SCCP connections to the MSC. When opening a connection the callbacks for state
|
||||
changes (connection confirmed, released, release complete) are set and a routine
|
||||
for handling incoming data. This registration is done in the osmo_bsc_sccp.c
|
||||
file and the bsc_create_new_connection method. The name of the callback is
|
||||
msc_outgoing_sccp_data and this will call bsc_handle_dt1 that is implemented
|
||||
in the osmo_bsc_bssap.c file. This will forward the messages to the right
|
||||
Mobile Station (MS).
|
||||
|
||||
|
||||
4. Sending SCCP messages
|
||||
|
||||
There are three parts to sending that will be explained below. The first part
|
||||
is to send an entire SCCP frame (which includes the GSM 08.08 data) to the
|
||||
MSC. This is done by first registering the low level sending. sccp_system_init
|
||||
is called with the function that is responsible for sending a message. The
|
||||
msc_sccp_write_ipa will call the msc_queue_write function with the data and
|
||||
the right MSC connection. Below the msc_queue_write the IPA header will be
|
||||
prepended to the msg and then send to the MSC.
|
||||
|
||||
The BSC supports multiple different A-link connections, the decision to pick
|
||||
the right MSC is done in this method. It is either done via the SCCP connection
|
||||
or the ctx pointer.
|
||||
|
||||
When the BSC is starting a BSS RESET message will be sent to the MSC. The reset
|
||||
is created in osmo_bsc_msc.c:initialize_if_needed and sccp_write is called with
|
||||
the GSM 08.08 data and the connection to use. The libosmo-sccp library will
|
||||
embed it into a SCCP UDT message and call the msc_sccp_write_ipa method.
|
||||
|
||||
When a new SCCP connection is to be created the bsc_create_new_connection
|
||||
in the osmo_bsc_sccp.c file. The sccp_connection_socket method will create
|
||||
the context for a SCCP connection. The state and data callback will be used
|
||||
to be notified about data and changes. Once the connection is configured the
|
||||
bsc_open_connection will be called that will ask the libosmo-sccp library to
|
||||
create a SCCP CR message using the sccp_connection_connect method. For active
|
||||
connections the sccp_connection_write method will be called.
|
||||
|
||||
|
||||
|
22
doc/oml-interface.txt
Normal file
22
doc/oml-interface.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
oml interface design notes
|
||||
|
||||
problems:
|
||||
|
||||
* there is no way how to tag a command sent to the BTS, with the response
|
||||
having the same tag to identify the originator of the command
|
||||
* therefore, we can have e.g. both the BSC and the OML interface send a
|
||||
SET ATTRIBUTE message, where the responses would end up at the wrong
|
||||
query.
|
||||
* The BTS has 10s to ACK/NACK a command. We do not run any timers.
|
||||
|
||||
the only possible solutions i can imagine:
|
||||
* have some kind of exclusive locking, where the OML interface gets blocked
|
||||
from the BSC and is exclusively assigned to the OML console until all commands
|
||||
of the OML console have terminated. This can either be done explicitly
|
||||
dynamically or on demand
|
||||
|
||||
* use the OML interface synchronously, i.e. always wait for the response from
|
||||
the BTS before
|
||||
|
||||
* unilateral / unsolicited messages need to be broadcasted to both the BSC and
|
||||
the OML console
|
33
doc/osmo-nitb-data_structures.dot
Normal file
33
doc/osmo-nitb-data_structures.dot
Normal file
@@ -0,0 +1,33 @@
|
||||
digraph G {
|
||||
net [label="gsm_network"]
|
||||
bts [label="gsm_bts"]
|
||||
trx [label="gsm_bts_trx"]
|
||||
ts [label="gsm_bts_trx_ts"]
|
||||
lchan [label="gsm_lchan"]
|
||||
sub [label="gsm_subscriber"]
|
||||
subcon [label="gsm_subscriber_conn"]
|
||||
sccpcon [label="osmo_bsc_sccp_con"]
|
||||
subgrp [label="gsm_subscriber_group"]
|
||||
|
||||
net -> bts
|
||||
bts -> trx
|
||||
trx -> ts
|
||||
ts -> lchan
|
||||
|
||||
lchan -> ts
|
||||
ts -> trx
|
||||
trx -> bts
|
||||
bts -> net
|
||||
|
||||
lchan -> subcon
|
||||
|
||||
subcon -> sub
|
||||
subcon -> sccpcon
|
||||
subcon -> lchan
|
||||
subcon -> lchan [label="ho_lchan"]
|
||||
subcon -> bts
|
||||
subcon -> lchan [label="secondary_lchan"]
|
||||
|
||||
sub -> subgrp
|
||||
subgrp -> net
|
||||
}
|
48
doc/paging.txt
Normal file
48
doc/paging.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
GSM Paging implementation in OpenBSC
|
||||
|
||||
== Code structure ==
|
||||
|
||||
The code is implemented in the libbsc/paging.c file. The external
|
||||
interface is documented/specified in the include/openbsc/paging.h
|
||||
header file. The code is used by the NITB and BSC application.
|
||||
|
||||
|
||||
== Implementation ==
|
||||
|
||||
Paging can be initiated in two ways. The standard way is to page by
|
||||
LAC. Each BTS has its own list/queue of outstanding paging operation.
|
||||
When a subscriber is paged one "struct paging_request" per BTS will
|
||||
be allocated and added to the tail of the list. The BTS is supposed
|
||||
to be configured to not repeat the paging.
|
||||
|
||||
A paging_request will remain in the queue until a paging response or at
|
||||
the expiry of the T3113. Every 500 milliseconds a RSL paging command is
|
||||
send to the BTS. The 500 milliseconds is a throttling to not crash the
|
||||
ip.access nanoBTS. Once one paging_request has been handled it will be
|
||||
put at the end of the queue/list and the available slots for the BTS
|
||||
will be decreased.
|
||||
|
||||
The available slots will be updated based on the paging load information
|
||||
element of the CCCH Load indication. If no paging slots are considered
|
||||
to be available and no load indication is sent a timer is started. The
|
||||
current timeout is 500 milliseconds and at the expiry of the timer the
|
||||
available slots will be set to 20.
|
||||
|
||||
OpenBSC has the " paging free <-1-1024>" configuration option. In case
|
||||
there are less free channels than required no paging request will be
|
||||
sent to the BTS. Instead it will be attempted to send the paging request
|
||||
at the next timeout (500 milliseconds).
|
||||
|
||||
== Limitation ==
|
||||
|
||||
The paging throughput could be higher but this has lead to crashes on the
|
||||
ip.access nanoBTS in the past.
|
||||
|
||||
== Configuration ==
|
||||
|
||||
=== ip.access nanoBTS ===
|
||||
|
||||
The current CCCH Load indication threshold is 10% and the period is 1 second.
|
||||
The code can be found inside the src/libbsc/bts_ipaccess_nanobts.c inside the
|
||||
nanobts_attr_bts array.
|
@@ -92,8 +92,8 @@ fi
|
||||
if test -n "$v"
|
||||
then
|
||||
: # use $v
|
||||
elif test -d ./.git \
|
||||
&& v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|
||||
elif
|
||||
v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 HEAD 2>/dev/null` \
|
||||
&& case $v in
|
||||
[0-9]*) ;;
|
||||
|
@@ -1,15 +1,6 @@
|
||||
SUBDIRS = \
|
||||
osmocom \
|
||||
$(NULL)
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
osmocom/legacy_mgcp/mgcp.h \
|
||||
osmocom/legacy_mgcp/mgcp_internal.h \
|
||||
osmocom/legacy_mgcp/mgcpgw_client.h \
|
||||
osmocom/legacy_mgcp/osmux.h \
|
||||
osmocom/mgcp_client/mgcp_client.h \
|
||||
osmocom/mgcp_client/mgcp_common.h \
|
||||
osmocom/mgcp/mgcp.h \
|
||||
osmocom/mgcp/mgcp_common.h \
|
||||
osmocom/mgcp/mgcp_internal.h \
|
||||
osmocom/mgcp/osmux.h \
|
||||
$(NULL)
|
||||
|
@@ -1,5 +1,3 @@
|
||||
SUBDIRS = \
|
||||
legacy_mgcp \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
$(NULL)
|
||||
|
@@ -177,6 +177,13 @@ enum mgcp_connection_mode {
|
||||
MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
struct mgcp_config {
|
||||
int source_port;
|
||||
char *local_ip;
|
||||
|
@@ -64,7 +64,7 @@ struct mgcp_rtp_state {
|
||||
uint32_t stats_jitter;
|
||||
int32_t stats_transit;
|
||||
int stats_cycles;
|
||||
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
|
||||
bool patched_first_rtp_payload;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_codec {
|
||||
|
73
include/osmocom/legacy_mgcp/mgcpgw_client.h
Normal file
73
include/osmocom/legacy_mgcp/mgcpgw_client.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MGCPGW_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
|
||||
#define MGCPGW_CLIENT_LOCAL_PORT_DEFAULT 0
|
||||
#define MGCPGW_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
|
||||
#define MGCPGW_CLIENT_REMOTE_PORT_DEFAULT 2427
|
||||
|
||||
struct msgb;
|
||||
struct vty;
|
||||
struct mgcpgw_client;
|
||||
|
||||
struct mgcpgw_client_conf {
|
||||
const char *local_addr;
|
||||
int local_port;
|
||||
const char *remote_addr;
|
||||
int remote_port;
|
||||
uint16_t first_endpoint;
|
||||
uint16_t last_endpoint;
|
||||
uint16_t bts_base;
|
||||
};
|
||||
|
||||
typedef unsigned int mgcp_trans_id_t;
|
||||
|
||||
struct mgcp_response_head {
|
||||
int response_code;
|
||||
mgcp_trans_id_t trans_id;
|
||||
const char *comment;
|
||||
};
|
||||
|
||||
struct mgcp_response {
|
||||
char *body;
|
||||
struct mgcp_response_head head;
|
||||
uint16_t audio_port;
|
||||
};
|
||||
|
||||
void mgcpgw_client_conf_init(struct mgcpgw_client_conf *conf);
|
||||
void mgcpgw_client_vty_init(void *talloc_ctx, int node, struct mgcpgw_client_conf *conf);
|
||||
int mgcpgw_client_config_write(struct vty *vty, const char *indent);
|
||||
struct mgcpgw_client_conf *mgcpgw_client_conf_actual(struct mgcpgw_client *mgcp);
|
||||
|
||||
struct mgcpgw_client *mgcpgw_client_init(void *ctx,
|
||||
struct mgcpgw_client_conf *conf);
|
||||
int mgcpgw_client_connect(struct mgcpgw_client *mgcp);
|
||||
|
||||
const char *mgcpgw_client_remote_addr_str(struct mgcpgw_client *mgcp);
|
||||
uint16_t mgcpgw_client_remote_port(struct mgcpgw_client *mgcp);
|
||||
uint32_t mgcpgw_client_remote_addr_n(struct mgcpgw_client *mgcp);
|
||||
|
||||
int mgcpgw_client_next_endpoint(struct mgcpgw_client *client);
|
||||
void mgcpgw_client_release_endpoint(uint16_t id, struct mgcpgw_client *client);
|
||||
|
||||
/* 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 mgcpgw_client_tx(struct mgcpgw_client *mgcp, struct msgb *msg,
|
||||
mgcp_response_cb_t response_cb, void *priv);
|
||||
|
||||
enum mgcp_connection_mode;
|
||||
|
||||
struct msgb *mgcp_msg_crcx(struct mgcpgw_client *mgcp,
|
||||
uint16_t rtp_endpoint, unsigned int call_id,
|
||||
enum mgcp_connection_mode mode);
|
||||
|
||||
struct msgb *mgcp_msg_mdcx(struct mgcpgw_client *mgcp,
|
||||
uint16_t rtp_endpoint, const char *rtp_conn_addr,
|
||||
uint16_t rtp_port, enum mgcp_connection_mode mode);
|
||||
|
||||
struct msgb *mgcp_msg_dlcx(struct mgcpgw_client *mgcp, uint16_t rtp_endpoint,
|
||||
unsigned int call_id);
|
@@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/write_queue.h>
|
||||
|
||||
#define MSGB_CB_MGCP_TRANS_ID 0
|
||||
|
||||
struct mgcp_client {
|
||||
struct mgcp_client_conf actual;
|
||||
struct mgcpgw_client {
|
||||
struct mgcpgw_client_conf actual;
|
||||
uint32_t remote_addr;
|
||||
struct osmo_wqueue wq;
|
||||
mgcp_trans_id_t next_trans_id;
|
||||
@@ -26,10 +24,10 @@ struct mgcp_response_pending {
|
||||
void *priv;
|
||||
};
|
||||
|
||||
int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg);
|
||||
int mgcpgw_client_rx(struct mgcpgw_client *mgcp, struct msgb *msg);
|
||||
|
||||
struct mgcp_response_pending * mgcp_client_pending_add(
|
||||
struct mgcp_client *mgcp,
|
||||
struct mgcp_response_pending * mgcpgw_client_pending_add(
|
||||
struct mgcpgw_client *mgcp,
|
||||
mgcp_trans_id_t trans_id,
|
||||
mgcp_response_cb_t response_cb,
|
||||
void *priv);
|
@@ -11,7 +11,8 @@ enum {
|
||||
};
|
||||
|
||||
int osmux_init(int role, struct mgcp_config *cfg);
|
||||
int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port);
|
||||
int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role,
|
||||
struct in_addr *addr, uint16_t port);
|
||||
void osmux_disable_endpoint(struct mgcp_endpoint *endp);
|
||||
void osmux_allocate_cid(struct mgcp_endpoint *endp);
|
||||
void osmux_release_cid(struct mgcp_endpoint *endp);
|
||||
|
@@ -16,7 +16,7 @@ extern struct cmd_element cfg_description_cmd;
|
||||
extern struct cmd_element cfg_no_description_cmd;
|
||||
|
||||
enum mgcp_vty_node {
|
||||
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
MGCP_NODE,
|
||||
TRUNK_NODE,
|
||||
};
|
||||
|
||||
|
@@ -1,9 +0,0 @@
|
||||
noinst_HEADERS = \
|
||||
vty.h \
|
||||
mgcp_msg.h \
|
||||
mgcp_conn.h \
|
||||
mgcp_stat.h \
|
||||
mgcp_ep.h \
|
||||
mgcp_sdp.h \
|
||||
debug.h \
|
||||
$(NULL)
|
@@ -1,35 +0,0 @@
|
||||
/* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#define DEBUG
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
DRTP,
|
||||
Debug_LastEntry,
|
||||
};
|
||||
|
||||
extern const struct log_info log_info;
|
@@ -1,237 +0,0 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
|
||||
/*
|
||||
* (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 <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#define RTP_PORT_DEFAULT_RANGE_START 16002
|
||||
#define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
|
||||
|
||||
/*
|
||||
* Handling of MGCP Endpoints and the MGCP Config
|
||||
*/
|
||||
struct mgcp_endpoint;
|
||||
struct mgcp_config;
|
||||
struct mgcp_trunk_config;
|
||||
struct mgcp_rtp_end;
|
||||
|
||||
#define MGCP_ENDP_CRCX 1
|
||||
#define MGCP_ENDP_DLCX 2
|
||||
#define MGCP_ENDP_MDCX 3
|
||||
|
||||
/*
|
||||
* what to do with the msg?
|
||||
* - continue as usual?
|
||||
* - reject and send a failure code?
|
||||
* - defer? do not send anything
|
||||
*/
|
||||
#define MGCP_POLICY_CONT 4
|
||||
#define MGCP_POLICY_REJECT 5
|
||||
#define MGCP_POLICY_DEFER 6
|
||||
|
||||
typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
|
||||
typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
|
||||
typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
|
||||
typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
|
||||
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
|
||||
|
||||
/**
|
||||
* Return:
|
||||
* < 0 in case no 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,
|
||||
char *data, int *len, int buf_size);
|
||||
typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
struct mgcp_rtp_end *src_end);
|
||||
|
||||
struct mgcp_conn_rtp;
|
||||
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
const char**subtype_name,
|
||||
const char**fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/**
|
||||
* This holds information on how to allocate ports
|
||||
*/
|
||||
struct mgcp_port_range {
|
||||
/* addr or NULL to fall-back to default */
|
||||
char *bind_addr;
|
||||
|
||||
/* dynamically allocated */
|
||||
int range_start;
|
||||
int range_end;
|
||||
int last_port;
|
||||
|
||||
/* set to true to enable automatic probing
|
||||
* of the local bind IP-Address, bind_addr
|
||||
* (or its fall back) is used when automatic
|
||||
* probing fails */
|
||||
bool bind_addr_probe;
|
||||
};
|
||||
|
||||
/* There are up to three modes in which the keep-alive dummy packet can be
|
||||
* sent. The beviour is controlled viw the keepalive_interval member of the
|
||||
* trunk config. If that member is set to 0 (MGCP_KEEPALIVE_NEVER) no dummy-
|
||||
* packet is sent at all and the timer that sends regular dummy packets
|
||||
* is no longer scheduled. If the keepalive_interval is set to -1, only
|
||||
* one dummy packet is sent when an CRCX or an MDCX is performed. No timer
|
||||
* is scheduled. For all vales greater 0, the a timer is scheduled and the
|
||||
* value is used as interval. See also mgcp_keepalive_timer_cb(),
|
||||
* handle_modify_con(), and handle_create_con() */
|
||||
#define MGCP_KEEPALIVE_ONCE (-1)
|
||||
#define MGCP_KEEPALIVE_NEVER 0
|
||||
|
||||
struct mgcp_trunk_config {
|
||||
struct llist_head entry;
|
||||
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
int trunk_nr;
|
||||
int trunk_type;
|
||||
|
||||
char *audio_fmtp_extra;
|
||||
char *audio_name;
|
||||
int audio_payload;
|
||||
int audio_send_ptime;
|
||||
int audio_send_name;
|
||||
int audio_loop;
|
||||
|
||||
int no_audio_transcoding;
|
||||
|
||||
int omit_rtcp;
|
||||
int keepalive_interval;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* 0: don't, 1: once */
|
||||
int force_aligned_timing;
|
||||
|
||||
/* spec handling */
|
||||
int force_realloc;
|
||||
|
||||
/* timer */
|
||||
struct osmo_timer_list keepalive_timer;
|
||||
|
||||
/* When set, incoming RTP packets are not filtered
|
||||
* when ports and ip-address do not match (debug) */
|
||||
int rtp_accept_all;
|
||||
|
||||
unsigned int number_endpoints;
|
||||
int vty_number_endpoints;
|
||||
struct mgcp_endpoint *endpoints;
|
||||
};
|
||||
|
||||
enum mgcp_role {
|
||||
MGCP_BSC = 0,
|
||||
MGCP_BSC_NAT,
|
||||
};
|
||||
|
||||
struct mgcp_config {
|
||||
int source_port;
|
||||
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 force_ptime;
|
||||
|
||||
mgcp_change change_cb;
|
||||
mgcp_policy policy_cb;
|
||||
mgcp_reset reset_cb;
|
||||
mgcp_realloc realloc_cb;
|
||||
mgcp_rqnt rqnt_cb;
|
||||
void *data;
|
||||
|
||||
uint32_t last_call_id;
|
||||
|
||||
/* trunk handling */
|
||||
struct mgcp_trunk_config trunk;
|
||||
struct llist_head trunks;
|
||||
|
||||
enum mgcp_role role;
|
||||
|
||||
/* osmux translator: 0 means disabled, 1 means enabled */
|
||||
int osmux;
|
||||
/* addr to bind the server to */
|
||||
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 osmux_batch;
|
||||
/* osmux batch size (in bytes) */
|
||||
int osmux_batch_size;
|
||||
/* osmux port */
|
||||
uint16_t osmux_port;
|
||||
/* Pad circuit with dummy messages until we see the first voice
|
||||
* message.
|
||||
*/
|
||||
uint16_t osmux_dummy;
|
||||
};
|
||||
|
||||
/* config management */
|
||||
struct mgcp_config *mgcp_config_alloc(void);
|
||||
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
|
||||
enum mgcp_role role);
|
||||
int mgcp_vty_init(void);
|
||||
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
|
||||
void mgcp_release_endp(struct mgcp_endpoint *endp);
|
||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
|
||||
|
||||
/*
|
||||
* format helper functions
|
||||
*/
|
||||
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
|
||||
|
||||
|
||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
|
||||
int mgcp_send_reset_all(struct mgcp_config *cfg);
|
||||
|
||||
|
||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port);
|
||||
int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len);
|
@@ -1,75 +0,0 @@
|
||||
/* MGCP common implementations.
|
||||
* These are used in libosmo-mgcp as well as libosmo-mgcp-client.
|
||||
* To avoid interdependency, these are implemented in .h file only. */
|
||||
|
||||
/*
|
||||
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* (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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Two copies of this file are kept in osmocom/mgcp/ and osmocom/mgcp_client/.
|
||||
* Since both are by definition identical, use the old header exclusion ifdefs
|
||||
* instead of '#pragma once' to avoid including both of these files.
|
||||
* Though at the time of writing there are no such users, this allows including
|
||||
* both libosmo-mgcp and libosmo-mgcp-client headers in the same file. */
|
||||
#ifndef OSMO_MGCP_COMMON_H
|
||||
#define OSMO_MGCP_COMMON_H
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#define for_each_non_empty_line(line, save) \
|
||||
for (line = strtok_r(NULL, "\r\n", &save); line; \
|
||||
line = strtok_r(NULL, "\r\n", &save))
|
||||
|
||||
enum mgcp_connection_mode {
|
||||
MGCP_CONN_NONE = 0,
|
||||
MGCP_CONN_RECV_ONLY = 1,
|
||||
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,
|
||||
};
|
||||
|
||||
/* Ensure that the msg->l2h is NUL terminated. */
|
||||
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||
{
|
||||
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)
|
||||
tail[0] = '\0';
|
||||
else if (tail[-1] == '\r' || tail[-1] == '\n')
|
||||
tail[-1] = '\0';
|
||||
else {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: "
|
||||
"Length: %d, Buffer size: %d\n",
|
||||
msgb_l2len(msg), msg->data_len);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* String length of Connection Identifiers
|
||||
* (see also RFC3435 2.1.3.2 Names of Connections) */
|
||||
#define MGCP_CONN_ID_LENGTH 32+1
|
||||
|
||||
#endif
|
@@ -1,39 +0,0 @@
|
||||
/* Message connection list handling */
|
||||
|
||||
/*
|
||||
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
enum mgcp_conn_type type, char *name);
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
|
||||
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);
|
@@ -1,50 +0,0 @@
|
||||
/* Endpoint types */
|
||||
|
||||
/*
|
||||
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct sockaddr_in;
|
||||
struct mgcp_conn;
|
||||
|
||||
/* Callback type for RTP dispatcher functions
|
||||
(e.g mgcp_dispatch_rtp_bridge_cb, see below) */
|
||||
typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in * addr,
|
||||
char *buf, unsigned int buf_size,
|
||||
struct mgcp_conn * conn);
|
||||
|
||||
/*! MGCP endpoint properties */
|
||||
struct mgcp_endpoint_type {
|
||||
/*!< maximum number of connections */
|
||||
int max_conns;
|
||||
|
||||
/*!< callback that defines how to dispatch incoming RTP data */
|
||||
mgcp_dispatch_rtp_cb dispatch_rtp_cb;
|
||||
};
|
||||
|
||||
/*! MGCP endpoint typeset */
|
||||
struct mgcp_endpoint_typeset {
|
||||
struct mgcp_endpoint_type rtp;
|
||||
};
|
||||
|
||||
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
|
||||
extern const struct mgcp_endpoint_typeset ep_typeset;
|
@@ -1,323 +0,0 @@
|
||||
/* MGCP Private Data */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#define CI_UNUSED 0
|
||||
|
||||
/* FIXME: This this is only needed to compile the currently
|
||||
* broken OSMUX support. Remove when fixed */
|
||||
#define CONN_ID_BTS "0"
|
||||
#define CONN_ID_NET "1"
|
||||
|
||||
enum mgcp_trunk_type {
|
||||
MGCP_TRUNK_VIRTUAL,
|
||||
MGCP_TRUNK_E1,
|
||||
};
|
||||
|
||||
struct mgcp_rtp_stream_state {
|
||||
uint32_t ssrc;
|
||||
uint16_t last_seq;
|
||||
uint32_t last_timestamp;
|
||||
uint32_t err_ts_counter;
|
||||
int32_t last_tsdelta;
|
||||
uint32_t last_arrival_time;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_state {
|
||||
int initialized;
|
||||
int patch_ssrc;
|
||||
|
||||
uint32_t orig_ssrc;
|
||||
|
||||
int seq_offset;
|
||||
|
||||
int32_t timestamp_offset;
|
||||
uint32_t packet_duration;
|
||||
|
||||
struct mgcp_rtp_stream_state in_stream;
|
||||
struct mgcp_rtp_stream_state out_stream;
|
||||
|
||||
/* jitter and packet loss calculation */
|
||||
int stats_initialized;
|
||||
uint16_t stats_base_seq;
|
||||
uint16_t stats_max_seq;
|
||||
uint32_t stats_ssrc;
|
||||
uint32_t stats_jitter;
|
||||
int32_t stats_transit;
|
||||
int stats_cycles;
|
||||
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;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_end {
|
||||
/* statistics */
|
||||
unsigned int packets_rx;
|
||||
unsigned int octets_rx;
|
||||
unsigned int packets_tx;
|
||||
unsigned int octets_tx;
|
||||
unsigned int dropped_packets;
|
||||
struct in_addr addr;
|
||||
|
||||
/* in network byte order */
|
||||
int rtp_port, rtcp_port;
|
||||
|
||||
/* audio codec information */
|
||||
struct mgcp_rtp_codec codec;
|
||||
struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */
|
||||
|
||||
/* per endpoint data */
|
||||
int frames_per_packet;
|
||||
uint32_t packet_duration_ms;
|
||||
char *fmtp_extra;
|
||||
int output_enabled;
|
||||
int force_output_ptime;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
||||
int force_aligned_timing;
|
||||
void *rtp_process_data;
|
||||
|
||||
/* Each end has a separate socket for RTP and RTCP */
|
||||
struct osmo_fd rtp;
|
||||
struct osmo_fd rtcp;
|
||||
|
||||
int local_port;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_tap {
|
||||
int enabled;
|
||||
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 */
|
||||
struct mgcp_rtp_tap tap_in;
|
||||
struct mgcp_rtp_tap tap_out;
|
||||
|
||||
/* Osmux states (optional) */
|
||||
struct {
|
||||
/* Osmux state: disabled, activating, active */
|
||||
enum osmux_state state;
|
||||
/* Allocated Osmux circuit ID for this endpoint */
|
||||
int allocated_cid;
|
||||
/* Used Osmux circuit ID for this endpoint */
|
||||
uint8_t cid;
|
||||
/* handle to batch messages */
|
||||
struct osmux_in_handle *in;
|
||||
/* handle to unbatch messages */
|
||||
struct osmux_out_handle out;
|
||||
/* statistics */
|
||||
struct {
|
||||
uint32_t chunks;
|
||||
uint32_t octets;
|
||||
} stats;
|
||||
} osmux;
|
||||
};
|
||||
|
||||
/*! 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 conntion */
|
||||
char id[MGCP_CONN_ID_LENGTH];
|
||||
|
||||
/*!< human readable name (vty, logging) */
|
||||
char name[256];
|
||||
|
||||
/*!< union with connection description */
|
||||
union {
|
||||
struct mgcp_conn_rtp rtp;
|
||||
} u;
|
||||
|
||||
/*!< pointer to optional private data */
|
||||
void *priv;
|
||||
};
|
||||
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
|
||||
struct mgcp_endpoint_type;
|
||||
|
||||
struct mgcp_endpoint {
|
||||
char *callid;
|
||||
struct mgcp_lco local_options;
|
||||
|
||||
struct llist_head conns;
|
||||
|
||||
/* backpointer */
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
|
||||
const struct mgcp_endpoint_type *type;
|
||||
|
||||
/* fields for re-transmission */
|
||||
char *last_trans;
|
||||
char *last_response;
|
||||
};
|
||||
|
||||
|
||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
|
||||
|
||||
/**
|
||||
* Internal structure while parsing a request
|
||||
*/
|
||||
struct mgcp_parse_data {
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_endpoint *endp;
|
||||
char *trans;
|
||||
char *save;
|
||||
int found;
|
||||
};
|
||||
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
char *buf, int rc, struct mgcp_conn_rtp *conn_src,
|
||||
struct mgcp_conn_rtp *conn_dst);
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
unsigned int buf_size, struct mgcp_conn *conn);
|
||||
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);
|
||||
|
||||
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_rtp_end *dst_end,
|
||||
struct mgcp_rtp_end *src_end);
|
||||
|
||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
const char**audio_name,
|
||||
const char**fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* internal RTP Annex A counting */
|
||||
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
|
||||
const uint16_t seq, const int32_t transit,
|
||||
const uint32_t ssrc);
|
||||
|
||||
int mgcp_set_ip_tos(int fd, int tos);
|
||||
|
||||
enum {
|
||||
MGCP_DEST_NET = 0,
|
||||
MGCP_DEST_BTS,
|
||||
};
|
||||
|
||||
|
||||
#define MGCP_DUMMY_LOAD 0x23
|
||||
|
||||
|
||||
/**
|
||||
* SDP related information
|
||||
*/
|
||||
/* Assume audio frame length of 20ms */
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
|
||||
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
|
||||
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
|
||||
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
|
||||
|
||||
#define PTYPE_UNDEFINED (-1)
|
||||
|
||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
|
@@ -1,56 +0,0 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* Message parser/generator utilities */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* (C) 2017 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct mgcp_conn;
|
||||
struct mgcp_parse_data;
|
||||
struct mgcp_endpoint;
|
||||
|
||||
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble);
|
||||
|
||||
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_osmux_cid(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);
|
||||
|
||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id);
|
||||
|
||||
char *mgcp_strline(char *str, char **saveptr);
|
||||
|
||||
#define for_each_line(line, save)\
|
||||
for (line = mgcp_strline(NULL, &save); line;\
|
||||
line = mgcp_strline(NULL, &save))
|
||||
|
||||
#define for_each_non_empty_line(line, save)\
|
||||
for (line = strtok_r(NULL, "\r\n", &save); line;\
|
||||
line = strtok_r(NULL, "\r\n", &save))
|
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* SDP generation and parsing
|
||||
*
|
||||
* (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2014 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
|
||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn,
|
||||
struct mgcp_parse_data *p);
|
||||
|
||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
int payload_type, const char *audio_name);
|
||||
|
||||
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
||||
const char *addr);
|
@@ -1,37 +0,0 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* The statistics generator */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* (C) 2017 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
/* Exposed for test purposes only, do not use actively */
|
||||
void calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *,
|
||||
uint32_t *expected, int *loss);
|
||||
|
||||
/* Exposed for test purposes only, do not use actively */
|
||||
uint32_t calc_jitter(struct mgcp_rtp_state *);
|
@@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <osmocom/netif/osmux.h>
|
||||
struct mgcp_conn_rtp;
|
||||
|
||||
#define OSMUX_PORT 1984
|
||||
|
||||
enum {
|
||||
OSMUX_ROLE_BSC = 0,
|
||||
OSMUX_ROLE_BSC_NAT,
|
||||
};
|
||||
|
||||
int osmux_init(int role, struct mgcp_config *cfg);
|
||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
struct in_addr *addr, uint16_t port);
|
||||
void osmux_disable_conn(struct mgcp_conn_rtp *conn);
|
||||
void osmux_allocate_cid(struct mgcp_conn_rtp *conn);
|
||||
void osmux_release_cid(struct mgcp_conn_rtp *conn);
|
||||
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
|
||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||
int osmux_get_cid(void);
|
||||
void osmux_put_cid(uint8_t osmux_cid);
|
||||
int osmux_used_cid(void);
|
||||
|
||||
enum osmux_state {
|
||||
OSMUX_STATE_DISABLED = 0,
|
||||
OSMUX_STATE_NEGOTIATING,
|
||||
OSMUX_STATE_ACTIVATING,
|
||||
OSMUX_STATE_ENABLED,
|
||||
};
|
||||
|
||||
enum osmux_usage {
|
||||
OSMUX_USAGE_OFF = 0,
|
||||
OSMUX_USAGE_ON = 1,
|
||||
OSMUX_USAGE_ONLY = 2,
|
||||
};
|
||||
|
@@ -1,31 +0,0 @@
|
||||
#ifndef OPENBSC_VTY_H
|
||||
#define OPENBSC_VTY_H
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/buffer.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
struct gsm_network;
|
||||
struct vty;
|
||||
|
||||
void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *);
|
||||
|
||||
struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base);
|
||||
|
||||
extern struct cmd_element cfg_description_cmd;
|
||||
extern struct cmd_element cfg_no_description_cmd;
|
||||
|
||||
enum mgcp_vty_node {
|
||||
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
TRUNK_NODE,
|
||||
};
|
||||
|
||||
struct log_info;
|
||||
int bsc_vty_init(struct gsm_network *network);
|
||||
int bsc_vty_init_extra(void);
|
||||
|
||||
void msc_vty_init(struct gsm_network *msc_network);
|
||||
|
||||
struct gsm_network *gsmnet_from_vty(struct vty *vty);
|
||||
|
||||
#endif
|
@@ -1,13 +0,0 @@
|
||||
BUILT_SOURCES = \
|
||||
mgcp_common.h \
|
||||
$(NULL)
|
||||
|
||||
noinst_HEADERS = \
|
||||
mgcp_client_internal.h \
|
||||
$(NULL)
|
||||
|
||||
mgcp_common.h: $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h
|
||||
echo -e "/*\n\n DO NOT EDIT THIS FILE!\n THIS IS OVERWRITTEN DURING BUILD\n This is an automatic copy of <osmocom/mgcp/mgcp_common.h>\n\n */" > mgcp_common.h
|
||||
cat $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h >> mgcp_common.h
|
||||
|
||||
CLEANFILES = mgcp_common.h
|
@@ -1,121 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_common.h>
|
||||
|
||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
|
||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0
|
||||
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
|
||||
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
|
||||
|
||||
struct msgb;
|
||||
struct vty;
|
||||
struct mgcp_client;
|
||||
|
||||
struct mgcp_client_conf {
|
||||
const char *local_addr;
|
||||
int local_port;
|
||||
const char *remote_addr;
|
||||
int remote_port;
|
||||
uint16_t first_endpoint;
|
||||
uint16_t last_endpoint;
|
||||
uint16_t bts_base;
|
||||
};
|
||||
|
||||
typedef unsigned int mgcp_trans_id_t;
|
||||
|
||||
struct mgcp_response_head {
|
||||
int response_code;
|
||||
mgcp_trans_id_t trans_id;
|
||||
const char *comment;
|
||||
char conn_id[MGCP_CONN_ID_LENGTH];
|
||||
};
|
||||
|
||||
struct mgcp_response {
|
||||
char *body;
|
||||
struct mgcp_response_head head;
|
||||
uint16_t audio_port;
|
||||
char audio_ip[INET_ADDRSTRLEN];
|
||||
};
|
||||
|
||||
enum mgcp_verb {
|
||||
MGCP_VERB_CRCX,
|
||||
MGCP_VERB_MDCX,
|
||||
MGCP_VERB_DLCX,
|
||||
MGCP_VERB_AUEP,
|
||||
MGCP_VERB_RSIP,
|
||||
};
|
||||
|
||||
#define MGCP_MSG_PRESENCE_ENDPOINT 0x0001
|
||||
#define MGCP_MSG_PRESENCE_CALL_ID 0x0002
|
||||
#define MGCP_MSG_PRESENCE_CONN_ID 0x0004
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
|
||||
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
|
||||
|
||||
/* See also RFC3435 section 3.2.1.3 */
|
||||
#define MGCP_ENDPOINT_MAXLEN (255*2+1+1)
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
struct mgcp_client_conf *conf);
|
||||
int mgcp_client_connect(struct mgcp_client *mgcp);
|
||||
|
||||
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp);
|
||||
uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp);
|
||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp);
|
||||
|
||||
int mgcp_client_next_endpoint(struct mgcp_client *client);
|
||||
void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client);
|
||||
|
||||
/* 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_crcx(struct mgcp_client *mgcp,
|
||||
uint16_t rtp_endpoint, unsigned int call_id,
|
||||
enum mgcp_connection_mode mode)
|
||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
|
||||
|
||||
struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
|
||||
uint16_t rtp_endpoint, const char *rtp_conn_addr,
|
||||
uint16_t rtp_port, enum mgcp_connection_mode mode)
|
||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
|
||||
|
||||
struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
|
||||
unsigned int call_id)
|
||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
|
||||
|
||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
|
||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
|
||||
|
||||
extern const struct value_string mgcp_client_connection_mode_strs[];
|
||||
static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
|
||||
{
|
||||
return get_value_string(mgcp_client_connection_mode_strs, mode);
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Osmocom Media Gateway Control Protocol Client library
|
||||
Description: C Utility Library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -losmo-mgcp-client
|
||||
Cflags: -I${includedir}/
|
@@ -1,10 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Osmocom Media Gateway Control Protocol library
|
||||
Description: C Utility Library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -losmo-mgcp
|
||||
Cflags: -I${includedir}/
|
@@ -16,15 +16,13 @@
|
||||
|
||||
|
||||
app_configs = {
|
||||
"osmo-mgw": ["doc/examples/osmo-mgw/osmo-mgw.cfg"],
|
||||
"osmo-bsc_mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"],
|
||||
"mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"],
|
||||
}
|
||||
|
||||
apps = [(4243, "src/osmo-mgw/osmo-mgw", "OsmoMGW", "osmo-mgw"),
|
||||
(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "osmo-bsc_mgcp"),
|
||||
apps = [(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp"),
|
||||
]
|
||||
|
||||
vty_command = ["./src/osmo-mgw/osmo-mgw", "-c",
|
||||
"doc/examples/osmo-mgw/osmo-mgw.cfg"]
|
||||
vty_command = ["./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "-c",
|
||||
"doc/examples/osmo-bsc_mgcp/osmo-bsc_mgcp.cfg"]
|
||||
|
||||
vty_app = apps[0]
|
||||
|
@@ -22,12 +22,9 @@ AM_LDFLAGS = \
|
||||
# Libraries
|
||||
SUBDIRS = \
|
||||
libosmo-legacy-mgcp \
|
||||
libosmo-mgcp-client \
|
||||
libosmo-mgcp \
|
||||
$(NULL)
|
||||
|
||||
# Programs
|
||||
SUBDIRS += \
|
||||
osmo-bsc_mgcp \
|
||||
osmo-mgw \
|
||||
$(NULL)
|
||||
|
@@ -15,15 +15,15 @@ AM_CFLAGS = \
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(LIBBCG729_LIBS) \
|
||||
$(LIBRARY_GSM) \
|
||||
$(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!
|
||||
# This is _NOT_ the library release version, it's an API version.
|
||||
# Please read Chapter 6 "Library interface versions" of the libtool
|
||||
# documentation before making any modification
|
||||
LEGACY_MGCP_LIBVERSION=0:0:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
@@ -41,6 +41,8 @@ libosmo_legacy_mgcp_la_SOURCES = \
|
||||
mgcp_vty.c \
|
||||
mgcp_osmux.c \
|
||||
mgcp_sdp.c \
|
||||
mgcpgw_client.c \
|
||||
mgcpgw_client_vty.c \
|
||||
$(NULL)
|
||||
if BUILD_MGCP_TRANSCODING
|
||||
libosmo_legacy_mgcp_la_SOURCES += \
|
||||
|
@@ -667,16 +667,10 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
|
||||
forward_data(rtp_end->rtp.fd, &endp->taps[tap_idx],
|
||||
buf, len);
|
||||
|
||||
/* FIXME: HACK HACK HACK. See OS#2459.
|
||||
* The ip.access nano3G needs the first RTP payload's first two bytes to read hex
|
||||
* 'e400', or it will reject the RAB assignment. It seems to not harm other femto
|
||||
* cells (as long as we patch only the first RTP payload in each stream).
|
||||
*/
|
||||
if (tap_idx == MGCP_TAP_BTS_OUT
|
||||
&& !rtp_state->patched_first_rtp_payload) {
|
||||
uint8_t *data = (uint8_t*)&buf[12];
|
||||
data[0] = 0xe4;
|
||||
data[1] = 0x00;
|
||||
osmo_hexparse("e400", data, 2);
|
||||
rtp_state->patched_first_rtp_payload = true;
|
||||
}
|
||||
|
||||
|
@@ -340,7 +340,8 @@ static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
|
||||
if (endp->osmux.state == OSMUX_STATE_ENABLED)
|
||||
goto out;
|
||||
|
||||
if (osmux_enable_endpoint(endp, &addr->sin_addr, addr->sin_port) < 0 ) {
|
||||
if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC_NAT,
|
||||
&addr->sin_addr, addr->sin_port) < 0 ){
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Could not enable osmux in endpoint %d\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
@@ -432,7 +433,8 @@ int osmux_init(int role, struct mgcp_config *cfg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port)
|
||||
int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role,
|
||||
struct in_addr *addr, uint16_t port)
|
||||
{
|
||||
/* If osmux is enabled, initialize the output handler. This handler is
|
||||
* used to reconstruct the RTP flow from osmux. The RTP SSRC is
|
||||
@@ -520,7 +522,8 @@ int osmux_send_dummy(struct mgcp_endpoint *endp)
|
||||
return 0;
|
||||
|
||||
if (endp->osmux.state == OSMUX_STATE_ACTIVATING) {
|
||||
if (osmux_enable_endpoint(endp, &endp->net_end.addr,
|
||||
if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC,
|
||||
&endp->net_end.addr,
|
||||
htons(endp->cfg->osmux_port)) < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Could not activate osmux in endpoint %d\n",
|
||||
|
@@ -172,7 +172,6 @@ static struct msgb *do_retransmission(const struct mgcp_endpoint *endp)
|
||||
|
||||
msg->l2h = msgb_put(msg, strlen(endp->last_response));
|
||||
memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
|
||||
display_mgcp_message(msg->l2h, msgb_l2len(msg), "Retransmitted response");
|
||||
return msg;
|
||||
}
|
||||
|
||||
@@ -318,12 +317,10 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
|
||||
if (!addr)
|
||||
addr = mgcp_net_src_addr(endp);
|
||||
|
||||
if (endp->osmux.state == OSMUX_STATE_NEGOTIATING) {
|
||||
if (endp->osmux.state == OSMUX_STATE_NEGOTIATING)
|
||||
sprintf(osmux_extension, "\nX-Osmux: %u", endp->osmux.cid);
|
||||
endp->osmux.state = OSMUX_STATE_ACTIVATING;
|
||||
} else {
|
||||
else
|
||||
osmux_extension[0] = '\0';
|
||||
}
|
||||
|
||||
len = snprintf(sdp_record, sizeof(sdp_record),
|
||||
"I: %u%s\n\n", endp->ci, osmux_extension);
|
||||
@@ -372,8 +369,8 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
|
||||
display_mgcp_message(msg->l2h, msgb_l2len(msg), "Received message");
|
||||
|
||||
/* attempt to treat it as a response */
|
||||
if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
|
||||
/* attempt to treat it as a response */
|
||||
if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
|
||||
return NULL;
|
||||
}
|
||||
|
@@ -591,16 +591,6 @@ DEFUN(cfg_mgcp_loop,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_force_realloc,
|
||||
cfg_mgcp_force_realloc_cmd,
|
||||
"force-realloc (0|1)",
|
||||
"Force endpoint reallocation when the endpoint is still seized\n"
|
||||
"Don't force reallocation\n" "force reallocation\n")
|
||||
{
|
||||
g_cfg->trunk.force_realloc = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_number_endp,
|
||||
cfg_mgcp_number_endp_cmd,
|
||||
"number endpoints <0-65534>",
|
||||
@@ -813,10 +803,9 @@ static int config_write_trunk(struct vty *vty)
|
||||
trunk->keepalive_interval, VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " no rtp keep-alive%s", VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " loop %d%s",
|
||||
trunk->audio_loop, VTY_NEWLINE);
|
||||
vty_out(vty, " force-realloc %d%s",
|
||||
trunk->force_realloc, VTY_NEWLINE);
|
||||
if (trunk->omit_rtcp)
|
||||
vty_out(vty, " rtcp-omit%s", VTY_NEWLINE);
|
||||
else
|
||||
@@ -1356,6 +1345,7 @@ int mgcp_vty_init(void)
|
||||
install_element(CONFIG_NODE, &cfg_mgcp_cmd);
|
||||
install_node(&mgcp_node, config_write_mgcp);
|
||||
|
||||
vty_install_default(MGCP_NODE);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd);
|
||||
@@ -1389,7 +1379,6 @@ int mgcp_vty_init(void)
|
||||
install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd_old);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd_old);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_loop_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_force_realloc_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_omit_rtcp_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_no_omit_rtcp_cmd);
|
||||
@@ -1415,6 +1404,7 @@ int mgcp_vty_init(void)
|
||||
|
||||
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
|
||||
install_node(&trunk_node, config_write_trunk);
|
||||
vty_install_default(TRUNK_NODE);
|
||||
install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_cmd);
|
||||
install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_once_cmd);
|
||||
install_element(TRUNK_NODE, &cfg_trunk_no_rtp_keepalive_cmd);
|
||||
|
@@ -23,11 +23,11 @@
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/byteswap.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
|
||||
#include <osmocom/legacy_mgcp/mgcpgw_client.h>
|
||||
#include <osmocom/legacy_mgcp/mgcp.h>
|
||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
|
||||
#include <osmocom/legacy_mgcp/mgcpgw_client_internal.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
@@ -36,10 +36,10 @@
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
||||
void mgcpgw_client_conf_init(struct mgcpgw_client_conf *conf)
|
||||
{
|
||||
/* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */
|
||||
*conf = (struct mgcp_client_conf){
|
||||
/* NULL and -1 default to MGCPGW_CLIENT_*_DEFAULT values */
|
||||
*conf = (struct mgcpgw_client_conf){
|
||||
.local_addr = NULL,
|
||||
.local_port = -1,
|
||||
.remote_addr = NULL,
|
||||
@@ -51,7 +51,7 @@ void mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
||||
}
|
||||
|
||||
/* Test if a given endpoint id is currently in use */
|
||||
static bool endpoint_in_use(uint16_t id, struct mgcp_client *client)
|
||||
static bool endpoint_in_use(uint16_t id, struct mgcpgw_client *client)
|
||||
{
|
||||
struct mgcp_inuse_endpoint *endpoint;
|
||||
llist_for_each_entry(endpoint, &client->inuse_endpoints, entry) {
|
||||
@@ -63,7 +63,7 @@ static bool endpoint_in_use(uint16_t id, struct mgcp_client *client)
|
||||
}
|
||||
|
||||
/* Find and seize an unsused endpoint id */
|
||||
int mgcp_client_next_endpoint(struct mgcp_client *client)
|
||||
int mgcpgw_client_next_endpoint(struct mgcpgw_client *client)
|
||||
{
|
||||
int i;
|
||||
uint16_t first_endpoint = client->actual.first_endpoint;
|
||||
@@ -96,7 +96,7 @@ int mgcp_client_next_endpoint(struct mgcp_client *client)
|
||||
}
|
||||
|
||||
/* Release a seized endpoint id to make it available again for other calls */
|
||||
void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client)
|
||||
void mgcpgw_client_release_endpoint(uint16_t id, struct mgcpgw_client *client)
|
||||
{
|
||||
struct mgcp_inuse_endpoint *endpoint;
|
||||
struct mgcp_inuse_endpoint *endpoint_tmp;
|
||||
@@ -108,9 +108,9 @@ void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client)
|
||||
}
|
||||
}
|
||||
|
||||
static void mgcp_client_handle_response(struct mgcp_client *mgcp,
|
||||
struct mgcp_response_pending *pending,
|
||||
struct mgcp_response *response)
|
||||
static void mgcpgw_client_handle_response(struct mgcpgw_client *mgcp,
|
||||
struct mgcp_response_pending *pending,
|
||||
struct mgcp_response *response)
|
||||
{
|
||||
if (!pending) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
@@ -134,7 +134,7 @@ static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
|
||||
|
||||
r->body = (char *)msg->data;
|
||||
|
||||
if (sscanf(r->body, "%3d %u %n",
|
||||
if (sscanf(r->body, "%3d %u %n",
|
||||
&r->head.response_code, &r->head.trans_id,
|
||||
&comment_pos) != 2)
|
||||
goto response_parse_failure;
|
||||
@@ -175,9 +175,9 @@ static bool mgcp_line_is_valid(const char *line)
|
||||
}
|
||||
|
||||
/* Parse a line like "m=audio 16002 RTP/AVP 98" */
|
||||
static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line)
|
||||
static int mgcp_parse_audio(struct mgcp_response *r, const char *line)
|
||||
{
|
||||
if (sscanf(line, "m=audio %hu",
|
||||
if (sscanf(line, "m=audio %hu",
|
||||
&r->audio_port) != 1)
|
||||
goto response_parse_failure;
|
||||
|
||||
@@ -185,34 +185,7 @@ static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line)
|
||||
|
||||
response_parse_failure:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse MGCP response header (audio port)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse a line like "c=IN IP4 10.11.12.13" */
|
||||
static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line)
|
||||
{
|
||||
struct in_addr ip_test;
|
||||
|
||||
if (strlen(line) < 16)
|
||||
goto response_parse_failure;
|
||||
|
||||
/* The current implementation strictly supports IPV4 only ! */
|
||||
if (memcmp("c=IN IP4 ", line, 9) != 0)
|
||||
goto response_parse_failure;
|
||||
|
||||
/* Extract IP-Address */
|
||||
osmo_strlcpy(r->audio_ip, line + 9, sizeof(r->audio_ip));
|
||||
|
||||
/* Check IP-Address */
|
||||
if (inet_aton(r->audio_ip, &ip_test) == 0)
|
||||
goto response_parse_failure;
|
||||
|
||||
return 0;
|
||||
|
||||
response_parse_failure:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse MGCP response header (audio ip)\n");
|
||||
"Failed to parse MGCP response header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -235,18 +208,13 @@ int mgcp_response_parse_params(struct mgcp_response *r)
|
||||
*data = '\0';
|
||||
data ++;
|
||||
|
||||
for_each_non_empty_line(line, data) {
|
||||
for_each_line(line, data) {
|
||||
if (!mgcp_line_is_valid(line))
|
||||
return -EINVAL;
|
||||
|
||||
switch (line[0]) {
|
||||
case 'm':
|
||||
rc = mgcp_parse_audio_port(r, line);
|
||||
if (rc)
|
||||
return rc;
|
||||
break;
|
||||
case 'c':
|
||||
rc = mgcp_parse_audio_ip(r, line);
|
||||
rc = mgcp_parse_audio(r, line);
|
||||
if (rc)
|
||||
return rc;
|
||||
break;
|
||||
@@ -258,65 +226,15 @@ int mgcp_response_parse_params(struct mgcp_response *r)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse a line like "I: 0cedfd5a19542d197af9afe5231f1d61" */
|
||||
static int mgcp_parse_conn_id(struct mgcp_response *r, const char *line)
|
||||
{
|
||||
if (strlen(line) < 4)
|
||||
goto response_parse_failure;
|
||||
|
||||
if (memcmp("I: ", line, 3) != 0)
|
||||
goto response_parse_failure;
|
||||
|
||||
osmo_strlcpy(r->head.conn_id, line + 3, sizeof(r->head.conn_id));
|
||||
return 0;
|
||||
|
||||
response_parse_failure:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse MGCP response (connectionIdentifier)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse MGCP parameters of the response */
|
||||
static int parse_head_params(struct mgcp_response *r)
|
||||
{
|
||||
char *line;
|
||||
int rc = 0;
|
||||
OSMO_ASSERT(r->body);
|
||||
char *data = r->body;
|
||||
char *data_end = strstr(r->body, "\n\n");
|
||||
|
||||
/* Protect SDP body, for_each_non_empty_line() will
|
||||
* only parse until it hits \0 mark. */
|
||||
if (data_end)
|
||||
*data_end = '\0';
|
||||
|
||||
for_each_non_empty_line(line, data) {
|
||||
switch (line[0]) {
|
||||
case 'I':
|
||||
rc = mgcp_parse_conn_id(r, line);
|
||||
if (rc)
|
||||
goto exit;
|
||||
break;
|
||||
default:
|
||||
/* skip unhandled parameters */
|
||||
break;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
/* Restore original state */
|
||||
if (data_end)
|
||||
*data_end = '\n';
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct mgcp_response_pending *mgcp_client_response_pending_get(
|
||||
struct mgcp_client *mgcp,
|
||||
mgcp_trans_id_t trans_id)
|
||||
static struct mgcp_response_pending *mgcpgw_client_response_pending_get(
|
||||
struct mgcpgw_client *mgcp,
|
||||
struct mgcp_response *r)
|
||||
{
|
||||
struct mgcp_response_pending *pending;
|
||||
if (!r)
|
||||
return NULL;
|
||||
llist_for_each_entry(pending, &mgcp->responses_pending, entry) {
|
||||
if (pending->trans_id == trans_id) {
|
||||
if (pending->trans_id == r->head.trans_id) {
|
||||
llist_del(&pending->entry);
|
||||
return pending;
|
||||
}
|
||||
@@ -330,7 +248,7 @@ static struct mgcp_response_pending *mgcp_client_response_pending_get(
|
||||
* mgcp_do_read that reads from the socket connected to the MGCP gateway. This
|
||||
* function is published mainly to be able to feed data from the test suite.
|
||||
*/
|
||||
int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg)
|
||||
int mgcpgw_client_rx(struct mgcpgw_client *mgcp, struct msgb *msg)
|
||||
{
|
||||
struct mgcp_response r = { 0 };
|
||||
struct mgcp_response_pending *pending;
|
||||
@@ -338,31 +256,25 @@ int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg)
|
||||
|
||||
rc = mgcp_response_parse_head(&r, msg);
|
||||
if (rc) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head)\n");
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = parse_head_params(&r);
|
||||
if (rc) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pending = mgcp_client_response_pending_get(mgcp, r.head.trans_id);
|
||||
pending = mgcpgw_client_response_pending_get(mgcp, &r);
|
||||
if (!pending) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find matching MGCP transaction for trans_id %d\n",
|
||||
r.head.trans_id);
|
||||
return -ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
mgcp_client_handle_response(mgcp, pending, &r);
|
||||
mgcpgw_client_handle_response(mgcp, pending, &r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mgcp_do_read(struct osmo_fd *fd)
|
||||
{
|
||||
struct mgcp_client *mgcp = fd->data;
|
||||
struct mgcpgw_client *mgcp = fd->data;
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
@@ -381,10 +293,10 @@ static int mgcp_do_read(struct osmo_fd *fd)
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
msg->l2h = msgb_put(msg, ret);
|
||||
ret = mgcp_client_rx(mgcp, msg);
|
||||
ret = mgcpgw_client_rx(mgcp, msg);
|
||||
talloc_free(msg);
|
||||
return ret;
|
||||
}
|
||||
@@ -396,7 +308,7 @@ static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
|
||||
unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf);
|
||||
unsigned int i;
|
||||
|
||||
osmo_strlcpy(strbuf, (const char*)msg->data, l);
|
||||
strncpy(strbuf, (const char*)msg->data, l);
|
||||
for (i = 0; i < sizeof(strbuf); i++) {
|
||||
if (strbuf[i] == '\n' || strbuf[i] == '\r') {
|
||||
strbuf[i] = '\0';
|
||||
@@ -415,12 +327,12 @@ static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
struct mgcp_client_conf *conf)
|
||||
struct mgcpgw_client *mgcpgw_client_init(void *ctx,
|
||||
struct mgcpgw_client_conf *conf)
|
||||
{
|
||||
struct mgcp_client *mgcp;
|
||||
struct mgcpgw_client *mgcp;
|
||||
|
||||
mgcp = talloc_zero(ctx, struct mgcp_client);
|
||||
mgcp = talloc_zero(ctx, struct mgcpgw_client);
|
||||
|
||||
INIT_LLIST_HEAD(&mgcp->responses_pending);
|
||||
INIT_LLIST_HEAD(&mgcp->inuse_endpoints);
|
||||
@@ -428,14 +340,14 @@ struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
mgcp->next_trans_id = 1;
|
||||
|
||||
mgcp->actual.local_addr = conf->local_addr ? conf->local_addr :
|
||||
MGCP_CLIENT_LOCAL_ADDR_DEFAULT;
|
||||
MGCPGW_CLIENT_LOCAL_ADDR_DEFAULT;
|
||||
mgcp->actual.local_port = conf->local_port >= 0 ? (uint16_t)conf->local_port :
|
||||
MGCP_CLIENT_LOCAL_PORT_DEFAULT;
|
||||
MGCPGW_CLIENT_LOCAL_PORT_DEFAULT;
|
||||
|
||||
mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr :
|
||||
MGCP_CLIENT_REMOTE_ADDR_DEFAULT;
|
||||
MGCPGW_CLIENT_REMOTE_ADDR_DEFAULT;
|
||||
mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
|
||||
MGCP_CLIENT_REMOTE_PORT_DEFAULT;
|
||||
MGCPGW_CLIENT_REMOTE_PORT_DEFAULT;
|
||||
|
||||
mgcp->actual.first_endpoint = conf->first_endpoint > 0 ? (uint16_t)conf->first_endpoint : 0;
|
||||
mgcp->actual.last_endpoint = conf->last_endpoint > 0 ? (uint16_t)conf->last_endpoint : 0;
|
||||
@@ -444,8 +356,9 @@ struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
return mgcp;
|
||||
}
|
||||
|
||||
int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
int mgcpgw_client_connect(struct mgcpgw_client *mgcp)
|
||||
{
|
||||
int on;
|
||||
struct sockaddr_in addr;
|
||||
struct osmo_wqueue *wq;
|
||||
int rc;
|
||||
@@ -457,19 +370,46 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
|
||||
wq = &mgcp->wq;
|
||||
|
||||
rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||
mgcp->actual.local_addr, mgcp->actual.local_port,
|
||||
mgcp->actual.remote_addr, mgcp->actual.remote_port,
|
||||
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
||||
if (rc < 0) {
|
||||
wq->bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (wq->bfd.fd < 0) {
|
||||
LOGP(DLMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(wq->bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
|
||||
LOGP(DLMGCP, LOGL_FATAL,
|
||||
"Failed to initialize socket %s:%u -> %s:%u for MGCP GW: %s\n",
|
||||
mgcp->actual.local_addr, mgcp->actual.local_port,
|
||||
mgcp->actual.remote_addr, mgcp->actual.remote_port, strerror(errno));
|
||||
"Failed to initialize socket for MGCP GW: %s\n",
|
||||
strerror(errno));
|
||||
rc = -errno;
|
||||
goto error_close_fd;
|
||||
}
|
||||
|
||||
/* bind socket */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
inet_aton(mgcp->actual.local_addr, &addr.sin_addr);
|
||||
addr.sin_port = htons(mgcp->actual.local_port);
|
||||
if (bind(wq->bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
LOGP(DLMGCP, LOGL_FATAL,
|
||||
"Failed to bind for MGCP GW to %s %u\n",
|
||||
mgcp->actual.local_addr, mgcp->actual.local_port);
|
||||
rc = -errno;
|
||||
goto error_close_fd;
|
||||
}
|
||||
|
||||
/* connect to the remote */
|
||||
inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
|
||||
addr.sin_port = htons(mgcp->actual.remote_port);
|
||||
if (connect(wq->bfd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
LOGP(DLMGCP, LOGL_FATAL,
|
||||
"Failed to connect to MGCP GW at %s %u: %s\n",
|
||||
mgcp->actual.remote_addr, mgcp->actual.remote_port,
|
||||
strerror(errno));
|
||||
rc = -errno;
|
||||
goto error_close_fd;
|
||||
}
|
||||
|
||||
mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
|
||||
|
||||
osmo_wqueue_init(wq, 10);
|
||||
@@ -478,6 +418,11 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
wq->read_cb = mgcp_do_read;
|
||||
wq->write_cb = mgcp_do_write;
|
||||
|
||||
if (osmo_fd_register(&wq->bfd) != 0) {
|
||||
LOGP(DLMGCP, LOGL_FATAL, "Failed to register BFD\n");
|
||||
rc = -EIO;
|
||||
goto error_close_fd;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n",
|
||||
mgcp->actual.local_addr, mgcp->actual.local_port,
|
||||
mgcp->actual.remote_addr, mgcp->actual.remote_port);
|
||||
@@ -489,24 +434,24 @@ error_close_fd:
|
||||
return rc;
|
||||
}
|
||||
|
||||
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp)
|
||||
const char *mgcpgw_client_remote_addr_str(struct mgcpgw_client *mgcp)
|
||||
{
|
||||
return mgcp->actual.remote_addr;
|
||||
}
|
||||
|
||||
uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp)
|
||||
uint16_t mgcpgw_client_remote_port(struct mgcpgw_client *mgcp)
|
||||
{
|
||||
return mgcp->actual.remote_port;
|
||||
}
|
||||
|
||||
/* Return the MGCP GW binary IPv4 address in network byte order. */
|
||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
|
||||
uint32_t mgcpgw_client_remote_addr_n(struct mgcpgw_client *mgcp)
|
||||
{
|
||||
return mgcp->remote_addr;
|
||||
}
|
||||
|
||||
struct mgcp_response_pending * mgcp_client_pending_add(
|
||||
struct mgcp_client *mgcp,
|
||||
struct mgcp_response_pending * mgcpgw_client_pending_add(
|
||||
struct mgcpgw_client *mgcp,
|
||||
mgcp_trans_id_t trans_id,
|
||||
mgcp_response_cb_t response_cb,
|
||||
void *priv)
|
||||
@@ -526,12 +471,9 @@ struct mgcp_response_pending * mgcp_client_pending_add(
|
||||
* response_cb. NOTE: the response_cb still needs to call
|
||||
* mgcp_response_parse_params(response) to get the parsed parameters -- to
|
||||
* potentially save some CPU cycles, only the head line has been parsed when
|
||||
* the response_cb is invoked.
|
||||
* Before the priv pointer becomes invalid, e.g. due to transaction timeout,
|
||||
* mgcp_client_cancel() needs to be called for this transaction.
|
||||
*/
|
||||
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
mgcp_response_cb_t response_cb, void *priv)
|
||||
* the response_cb is invoked. */
|
||||
int mgcpgw_client_tx(struct mgcpgw_client *mgcp, struct msgb *msg,
|
||||
mgcp_response_cb_t response_cb, void *priv)
|
||||
{
|
||||
struct mgcp_response_pending *pending;
|
||||
mgcp_trans_id_t trans_id;
|
||||
@@ -545,7 +487,7 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
|
||||
pending = mgcpgw_client_pending_add(mgcp, trans_id, response_cb, priv);
|
||||
|
||||
if (msgb_l2len(msg) > 4096) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
@@ -568,36 +510,10 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
|
||||
mgcp_tx_error:
|
||||
/* Pass NULL to response cb to indicate an error */
|
||||
mgcp_client_handle_response(mgcp, pending, NULL);
|
||||
mgcpgw_client_handle_response(mgcp, pending, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Cancel a pending transaction.
|
||||
* Should a priv pointer passed to mgcp_client_tx() become invalid, this function must be called. In
|
||||
* practical terms, if the caller of mgcp_client_tx() wishes to tear down a transaction without having
|
||||
* received a response this function must be called. The trans_id can be obtained by calling
|
||||
* mgcp_msg_trans_id() on the msgb produced by mgcp_msg_gen().
|
||||
*/
|
||||
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
|
||||
{
|
||||
struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id);
|
||||
if (!pending) {
|
||||
/* INFO is sufficient, it is not harmful to cancel a transaction twice. */
|
||||
LOGP(DLMGCP, LOGL_INFO, "Cannot cancel, no such transaction: %u\n", trans_id);
|
||||
return -ENOENT;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_INFO, "Canceled transaction %u\n", trans_id);
|
||||
talloc_free(pending);
|
||||
return 0;
|
||||
/* We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent
|
||||
* out and is no longer in the wqueue. If it still is in the wqueue, then sending MGCP messages
|
||||
* per se is broken and the program should notice so by a full wqueue. Even if this was called
|
||||
* before we had a chance to send out the message and it is still going to be sent, we will just
|
||||
* ignore the reply to it later. Removing a msgb from the wqueue here would just introduce more
|
||||
* bug surface in terms of failing to update wqueue API's counters or some such.
|
||||
*/
|
||||
}
|
||||
|
||||
static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
|
||||
const char *buf, int len)
|
||||
{
|
||||
@@ -646,7 +562,7 @@ static struct msgb *mgcp_msg_from_str(mgcp_trans_id_t trans_id,
|
||||
return mgcp_msg_from_buf(trans_id, compose, len);
|
||||
}
|
||||
|
||||
static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
|
||||
static mgcp_trans_id_t mgcpgw_client_next_trans_id(struct mgcpgw_client *mgcp)
|
||||
{
|
||||
/* avoid zero trans_id to distinguish from unset trans_id */
|
||||
if (!mgcp->next_trans_id)
|
||||
@@ -654,11 +570,11 @@ static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
|
||||
return mgcp->next_trans_id ++;
|
||||
}
|
||||
|
||||
struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
|
||||
struct msgb *mgcp_msg_crcx(struct mgcpgw_client *mgcp,
|
||||
uint16_t rtp_endpoint, unsigned int call_id,
|
||||
enum mgcp_connection_mode mode)
|
||||
{
|
||||
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
|
||||
mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
|
||||
return mgcp_msg_from_str(trans_id,
|
||||
"CRCX %u %x@mgw MGCP 1.0\r\n"
|
||||
"C: %x\r\n"
|
||||
@@ -668,15 +584,15 @@ struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
|
||||
trans_id,
|
||||
rtp_endpoint,
|
||||
call_id,
|
||||
mgcp_client_cmode_name(mode));
|
||||
mgcp_cmode_name(mode));
|
||||
}
|
||||
|
||||
struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
|
||||
struct msgb *mgcp_msg_mdcx(struct mgcpgw_client *mgcp,
|
||||
uint16_t rtp_endpoint, const char *rtp_conn_addr,
|
||||
uint16_t rtp_port, enum mgcp_connection_mode mode)
|
||||
|
||||
{
|
||||
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
|
||||
mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
|
||||
return mgcp_msg_from_str(trans_id,
|
||||
"MDCX %u %x@mgw MGCP 1.0\r\n"
|
||||
"M: %s\r\n"
|
||||
@@ -686,163 +602,21 @@ struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
|
||||
,
|
||||
trans_id,
|
||||
rtp_endpoint,
|
||||
mgcp_client_cmode_name(mode),
|
||||
mgcp_cmode_name(mode),
|
||||
rtp_conn_addr,
|
||||
rtp_port);
|
||||
}
|
||||
|
||||
struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
|
||||
struct msgb *mgcp_msg_dlcx(struct mgcpgw_client *mgcp, uint16_t rtp_endpoint,
|
||||
unsigned int call_id)
|
||||
{
|
||||
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
|
||||
mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
|
||||
return mgcp_msg_from_str(trans_id,
|
||||
"DLCX %u %x@mgw MGCP 1.0\r\n"
|
||||
"C: %x\r\n", trans_id, rtp_endpoint, call_id);
|
||||
}
|
||||
|
||||
#define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
|
||||
MGCP_MSG_PRESENCE_CALL_ID | \
|
||||
MGCP_MSG_PRESENCE_CONN_MODE)
|
||||
#define MGCP_MDCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
|
||||
MGCP_MSG_PRESENCE_CONN_ID)
|
||||
#define MGCP_DLCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
|
||||
#define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
|
||||
#define MGCP_RSIP_MANDATORY 0 /* none */
|
||||
|
||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
{
|
||||
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
|
||||
uint32_t mandatory_mask;
|
||||
struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
|
||||
int rc = 0;
|
||||
|
||||
msg->l2h = msg->data;
|
||||
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
|
||||
|
||||
/* Add command verb */
|
||||
switch (mgcp_msg->verb) {
|
||||
case MGCP_VERB_CRCX:
|
||||
mandatory_mask = MGCP_CRCX_MANDATORY;
|
||||
rc += msgb_printf(msg, "CRCX %u", trans_id);
|
||||
break;
|
||||
case MGCP_VERB_MDCX:
|
||||
mandatory_mask = MGCP_MDCX_MANDATORY;
|
||||
rc += msgb_printf(msg, "MDCX %u", trans_id);
|
||||
break;
|
||||
case MGCP_VERB_DLCX:
|
||||
mandatory_mask = MGCP_DLCX_MANDATORY;
|
||||
rc += msgb_printf(msg, "DLCX %u", trans_id);
|
||||
break;
|
||||
case MGCP_VERB_AUEP:
|
||||
mandatory_mask = MGCP_AUEP_MANDATORY;
|
||||
rc += msgb_printf(msg, "AUEP %u", trans_id);
|
||||
break;
|
||||
case MGCP_VERB_RSIP:
|
||||
mandatory_mask = MGCP_RSIP_MANDATORY;
|
||||
rc += msgb_printf(msg, "RSIP %u", trans_id);
|
||||
break;
|
||||
default:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Invalid command verb, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if mandatory fields are missing */
|
||||
if (!((mgcp_msg->presence & mandatory_mask) == mandatory_mask)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"One or more missing mandatory fields, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add endpoint name */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_ENDPOINT) {
|
||||
if (strlen(mgcp_msg->endpoint) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Empty endpoint name, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
rc += msgb_printf(msg, " %s", mgcp_msg->endpoint);
|
||||
}
|
||||
|
||||
/* Add protocol version */
|
||||
rc += msgb_printf(msg, " MGCP 1.0\r\n");
|
||||
|
||||
/* Add call id */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CALL_ID)
|
||||
rc += msgb_printf(msg, "C: %x\r\n", mgcp_msg->call_id);
|
||||
|
||||
/* Add connection id */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID) {
|
||||
if (strlen(mgcp_msg->conn_id) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Empty connection id, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
|
||||
}
|
||||
|
||||
/* Add local connection options */
|
||||
if (mgcp_msg->verb == MGCP_VERB_CRCX)
|
||||
rc += msgb_printf(msg, "L: p:20, a:AMR, nt:IN\r\n");
|
||||
|
||||
/* Add mode */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
|
||||
rc +=
|
||||
msgb_printf(msg, "M: %s\r\n",
|
||||
mgcp_client_cmode_name(mgcp_msg->conn_mode));
|
||||
|
||||
/* Add RTP address and port (SDP) */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
|
||||
&& mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
|
||||
if (mgcp_msg->audio_port == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Invalid port number, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
if (strlen(mgcp_msg->audio_ip) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Empty ip address, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
rc += msgb_printf(msg, "\r\n");
|
||||
rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
|
||||
rc +=
|
||||
msgb_printf(msg, "m=audio %u RTP/AVP 255\r\n",
|
||||
mgcp_msg->audio_port);
|
||||
}
|
||||
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"message buffer to small, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
msg = NULL;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* Retrieve the MGCP transaction ID from a msgb generated by mgcp_msg_gen() */
|
||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg)
|
||||
{
|
||||
return (mgcp_trans_id_t)msg->cb[MSGB_CB_MGCP_TRANS_ID];
|
||||
}
|
||||
|
||||
struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp)
|
||||
struct mgcpgw_client_conf *mgcpgw_client_conf_actual(struct mgcpgw_client *mgcp)
|
||||
{
|
||||
return &mgcp->actual;
|
||||
}
|
||||
|
||||
const struct value_string mgcp_client_connection_mode_strs[] = {
|
||||
{ MGCP_CONN_NONE, "none" },
|
||||
{ MGCP_CONN_RECV_SEND, "sendrecv" },
|
||||
{ MGCP_CONN_SEND_ONLY, "sendonly" },
|
||||
{ MGCP_CONN_RECV_ONLY, "recvonly" },
|
||||
{ MGCP_CONN_LOOPBACK, "loopback" },
|
||||
{ 0, NULL }
|
||||
};
|
172
src/libosmo-legacy-mgcp/mgcpgw_client_vty.c
Normal file
172
src/libosmo-legacy-mgcp/mgcpgw_client_vty.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/* MGCPGW client interface to quagga VTY */
|
||||
/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
|
||||
* Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
|
||||
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2009-2011 by Holger Hans Peter Freyther
|
||||
* 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 <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <talloc.h>
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/legacy_mgcp/vty.h>
|
||||
#include <osmocom/legacy_mgcp/mgcpgw_client.h>
|
||||
|
||||
#define MGCPGW_STR "MGCP gateway configuration for RTP streams\n"
|
||||
|
||||
void *global_mgcpgw_client_ctx = NULL;
|
||||
struct mgcpgw_client_conf *global_mgcpgw_client_conf = NULL;
|
||||
|
||||
DEFUN(cfg_mgcpgw_local_ip, cfg_mgcpgw_local_ip_cmd,
|
||||
"mgcpgw local-ip A.B.C.D",
|
||||
MGCPGW_STR "local bind to connect to MGCP gateway with\n"
|
||||
"local bind IP address\n")
|
||||
{
|
||||
if (!global_mgcpgw_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
OSMO_ASSERT(global_mgcpgw_client_ctx);
|
||||
global_mgcpgw_client_conf->local_addr =
|
||||
talloc_strdup(global_mgcpgw_client_ctx, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcpgw_local_port, cfg_mgcpgw_local_port_cmd,
|
||||
"mgcpgw local-port <0-65535>",
|
||||
MGCPGW_STR "local bind to connect to MGCP gateway with\n"
|
||||
"local bind port\n")
|
||||
{
|
||||
if (!global_mgcpgw_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
global_mgcpgw_client_conf->local_port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcpgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
|
||||
"mgcpgw remote-ip A.B.C.D",
|
||||
MGCPGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind IP address\n")
|
||||
{
|
||||
if (!global_mgcpgw_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
OSMO_ASSERT(global_mgcpgw_client_ctx);
|
||||
global_mgcpgw_client_conf->remote_addr =
|
||||
talloc_strdup(global_mgcpgw_client_ctx, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcpgw_remote_port, cfg_mgcpgw_remote_port_cmd,
|
||||
"mgcpgw remote-port <0-65535>",
|
||||
MGCPGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind port\n")
|
||||
{
|
||||
if (!global_mgcpgw_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
global_mgcpgw_client_conf->remote_port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcpgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
||||
"mgcpgw endpoint-range <1-65534> <1-65534>",
|
||||
MGCPGW_STR "usable range of endpoint identifiers\n"
|
||||
"set first useable endpoint identifier\n"
|
||||
"set the last useable endpoint identifier\n")
|
||||
{
|
||||
uint16_t first_endpoint = atoi(argv[0]);
|
||||
uint16_t last_endpoint = atoi(argv[1]);
|
||||
|
||||
if (last_endpoint < first_endpoint) {
|
||||
vty_out(vty, "last endpoint must be greater than first endpoint!%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
global_mgcpgw_client_conf->first_endpoint = first_endpoint;
|
||||
global_mgcpgw_client_conf->last_endpoint = last_endpoint;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define BTS_START_STR "First UDP port allocated for the BTS side\n"
|
||||
#define UDP_PORT_STR "UDP Port number\n"
|
||||
DEFUN(cfg_mgcp_rtp_bts_base_port,
|
||||
cfg_mgcp_rtp_bts_base_port_cmd,
|
||||
"mgcpgw bts-base <0-65534>",
|
||||
MGCPGW_STR
|
||||
BTS_START_STR
|
||||
UDP_PORT_STR)
|
||||
{
|
||||
global_mgcpgw_client_conf->bts_base = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int mgcpgw_client_config_write(struct vty *vty, const char *indent)
|
||||
{
|
||||
const char *addr;
|
||||
int port;
|
||||
uint16_t first_endpoint;
|
||||
uint16_t last_endpoint;
|
||||
uint16_t bts_base;
|
||||
|
||||
addr = global_mgcpgw_client_conf->local_addr;
|
||||
if (addr)
|
||||
vty_out(vty, "%smgcpgw local-ip %s%s", indent, addr,
|
||||
VTY_NEWLINE);
|
||||
port = global_mgcpgw_client_conf->local_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgcpgw local-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
addr = global_mgcpgw_client_conf->remote_addr;
|
||||
if (addr)
|
||||
vty_out(vty, "%smgcpgw remote-ip %s%s", indent, addr,
|
||||
VTY_NEWLINE);
|
||||
port = global_mgcpgw_client_conf->remote_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgcpgw remote-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
first_endpoint = global_mgcpgw_client_conf->first_endpoint;
|
||||
last_endpoint = global_mgcpgw_client_conf->last_endpoint;
|
||||
if (last_endpoint != 0) {
|
||||
vty_out(vty, "%smgcpgw endpoint-range %u %u%s", indent,
|
||||
first_endpoint, last_endpoint, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
bts_base = global_mgcpgw_client_conf->bts_base;
|
||||
if (bts_base) {
|
||||
vty_out(vty, "%smgcpgw bts-base %u%s", indent,
|
||||
bts_base, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
void mgcpgw_client_vty_init(void *talloc_ctx, int node, struct mgcpgw_client_conf *conf)
|
||||
{
|
||||
global_mgcpgw_client_ctx = talloc_ctx;
|
||||
global_mgcpgw_client_conf = conf;
|
||||
|
||||
install_element(node, &cfg_mgcpgw_local_ip_cmd);
|
||||
install_element(node, &cfg_mgcpgw_local_port_cmd);
|
||||
install_element(node, &cfg_mgcpgw_remote_ip_cmd);
|
||||
install_element(node, &cfg_mgcpgw_remote_port_cmd);
|
||||
install_element(node, &cfg_mgcpgw_endpoint_range_cmd);
|
||||
install_element(node, &cfg_mgcp_rtp_bts_base_port_cmd);
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_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=2:0:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp-client.la \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_client_la_SOURCES = \
|
||||
mgcp_client.c \
|
||||
mgcp_client_vty.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)
|
@@ -1,206 +0,0 @@
|
||||
/* MGCP client interface to quagga VTY */
|
||||
/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
|
||||
* Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
|
||||
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2009-2011 by Holger Hans Peter Freyther
|
||||
* 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 <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <talloc.h>
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
|
||||
#define MGW_STR "Configure MGCP connection to Media Gateway\n"
|
||||
|
||||
void *global_mgcp_client_ctx = NULL;
|
||||
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
|
||||
|
||||
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
|
||||
"mgw local-ip A.B.C.D",
|
||||
MGW_STR "local bind to connect to MGW from\n"
|
||||
"local bind IP address\n")
|
||||
{
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
OSMO_ASSERT(global_mgcp_client_ctx);
|
||||
global_mgcp_client_conf->local_addr =
|
||||
talloc_strdup(global_mgcp_client_ctx, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
|
||||
"mgcpgw local-ip A.B.C.D",
|
||||
MGW_STR "local bind to connect to MGCP gateway with\n"
|
||||
"local bind IP address\n")
|
||||
|
||||
DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd,
|
||||
"mgw local-port <0-65535>",
|
||||
MGW_STR "local port to connect to MGW from\n"
|
||||
"local bind port\n")
|
||||
{
|
||||
if (!global_mgcp_client_conf)
|
||||
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")
|
||||
|
||||
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
|
||||
"mgw remote-ip A.B.C.D",
|
||||
MGW_STR "remote IP address to reach the MGW at\n"
|
||||
"remote IP address\n")
|
||||
{
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
OSMO_ASSERT(global_mgcp_client_ctx);
|
||||
global_mgcp_client_conf->remote_addr =
|
||||
talloc_strdup(global_mgcp_client_ctx, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
|
||||
"mgcpgw remote-ip A.B.C.D",
|
||||
MGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind IP address\n")
|
||||
|
||||
DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd,
|
||||
"mgw remote-port <0-65535>",
|
||||
MGW_STR "remote port to reach the MGW at\n"
|
||||
"remote port\n")
|
||||
{
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
global_mgcp_client_conf->remote_port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
|
||||
"mgcpgw remote-port <0-65535>",
|
||||
MGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind port\n")
|
||||
|
||||
DEFUN(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
|
||||
"mgw endpoint-range <1-65534> <1-65534>",
|
||||
MGW_STR "usable range of endpoint identifiers\n"
|
||||
"set first usable endpoint identifier\n"
|
||||
"set last usable endpoint identifier\n")
|
||||
{
|
||||
uint16_t first_endpoint = atoi(argv[0]);
|
||||
uint16_t last_endpoint = atoi(argv[1]);
|
||||
|
||||
if (last_endpoint < first_endpoint) {
|
||||
vty_out(vty, "last endpoint must be greater than first endpoint!%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
global_mgcp_client_conf->first_endpoint = first_endpoint;
|
||||
global_mgcp_client_conf->last_endpoint = last_endpoint;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
||||
"mgcpgw endpoint-range <1-65534> <1-65534>",
|
||||
MGW_STR "usable range of endpoint identifiers\n"
|
||||
"set first useable endpoint identifier\n"
|
||||
"set the last useable endpoint identifier\n")
|
||||
|
||||
#define BTS_START_STR "First UDP port allocated for the BTS side\n"
|
||||
#define UDP_PORT_STR "UDP Port number\n"
|
||||
DEFUN(cfg_mgw_rtp_bts_base_port,
|
||||
cfg_mgw_rtp_bts_base_port_cmd,
|
||||
"mgw bts-base <0-65534>",
|
||||
MGW_STR
|
||||
BTS_START_STR
|
||||
UDP_PORT_STR)
|
||||
{
|
||||
global_mgcp_client_conf->bts_base = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||
cfg_mgcpgw_rtp_bts_base_port_cmd,
|
||||
"mgcpgw bts-base <0-65534>",
|
||||
MGW_STR
|
||||
BTS_START_STR
|
||||
UDP_PORT_STR)
|
||||
|
||||
int mgcp_client_config_write(struct vty *vty, const char *indent)
|
||||
{
|
||||
const char *addr;
|
||||
int port;
|
||||
uint16_t first_endpoint;
|
||||
uint16_t last_endpoint;
|
||||
uint16_t bts_base;
|
||||
|
||||
addr = global_mgcp_client_conf->local_addr;
|
||||
if (addr)
|
||||
vty_out(vty, "%smgw local-ip %s%s", indent, addr,
|
||||
VTY_NEWLINE);
|
||||
port = global_mgcp_client_conf->local_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgw local-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
addr = global_mgcp_client_conf->remote_addr;
|
||||
if (addr)
|
||||
vty_out(vty, "%smgw remote-ip %s%s", indent, addr,
|
||||
VTY_NEWLINE);
|
||||
port = global_mgcp_client_conf->remote_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgw remote-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
first_endpoint = global_mgcp_client_conf->first_endpoint;
|
||||
last_endpoint = global_mgcp_client_conf->last_endpoint;
|
||||
if (last_endpoint != 0) {
|
||||
vty_out(vty, "%smgw endpoint-range %u %u%s", indent,
|
||||
first_endpoint, last_endpoint, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
bts_base = global_mgcp_client_conf->bts_base;
|
||||
if (bts_base) {
|
||||
vty_out(vty, "%smgw bts-base %u%s", indent,
|
||||
bts_base, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
install_element(node, &cfg_mgw_local_ip_cmd);
|
||||
install_element(node, &cfg_mgw_local_port_cmd);
|
||||
install_element(node, &cfg_mgw_remote_ip_cmd);
|
||||
install_element(node, &cfg_mgw_remote_port_cmd);
|
||||
install_element(node, &cfg_mgw_endpoint_range_cmd);
|
||||
install_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
|
||||
|
||||
/* deprecated 'mgcpgw' commands */
|
||||
install_element(node, &cfg_mgcpgw_local_ip_cmd);
|
||||
install_element(node, &cfg_mgcpgw_local_port_cmd);
|
||||
install_element(node, &cfg_mgcpgw_remote_ip_cmd);
|
||||
install_element(node, &cfg_mgcpgw_remote_port_cmd);
|
||||
install_element(node, &cfg_mgcpgw_endpoint_range_cmd);
|
||||
install_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
|
||||
}
|
@@ -1,48 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
# This is not at all related to the release version, but a range of supported
|
||||
# API versions. Read TODO_RELEASE in the source tree's root!
|
||||
MGCP_LIBVERSION=1:0:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp.la \
|
||||
$(NULL)
|
||||
|
||||
noinst_HEADERS = \
|
||||
g711common.h \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_la_SOURCES = \
|
||||
mgcp_protocol.c \
|
||||
mgcp_network.c \
|
||||
mgcp_vty.c \
|
||||
mgcp_osmux.c \
|
||||
mgcp_sdp.c \
|
||||
mgcp_msg.c \
|
||||
mgcp_conn.c \
|
||||
mgcp_stat.c \
|
||||
mgcp_ep.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_LIBVERSION)
|
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* PCM - A-Law conversion
|
||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
||||
*
|
||||
* Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
|
||||
*
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
static inline int val_seg(int val)
|
||||
{
|
||||
int r = 0;
|
||||
val >>= 7; /*7 = 4 + 3*/
|
||||
if (val & 0xf0) {
|
||||
val >>= 4;
|
||||
r += 4;
|
||||
}
|
||||
if (val & 0x0c) {
|
||||
val >>= 2;
|
||||
r += 2;
|
||||
}
|
||||
if (val & 0x02)
|
||||
r += 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
|
||||
*
|
||||
* s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
|
||||
*
|
||||
* Linear Input Code Compressed Code
|
||||
* ------------------------ ---------------
|
||||
* 0000000wxyza 000wxyz
|
||||
* 0000001wxyza 001wxyz
|
||||
* 000001wxyzab 010wxyz
|
||||
* 00001wxyzabc 011wxyz
|
||||
* 0001wxyzabcd 100wxyz
|
||||
* 001wxyzabcde 101wxyz
|
||||
* 01wxyzabcdef 110wxyz
|
||||
* 1wxyzabcdefg 111wxyz
|
||||
*
|
||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
||||
* G711 is designed for 13 bits input signal, this function add extra shifting to take this into account.
|
||||
*/
|
||||
|
||||
static inline unsigned char s16_to_alaw(int pcm_val)
|
||||
{
|
||||
int mask;
|
||||
int seg;
|
||||
unsigned char aval;
|
||||
|
||||
if (pcm_val >= 0) {
|
||||
mask = 0xD5;
|
||||
} else {
|
||||
mask = 0x55;
|
||||
pcm_val = -pcm_val;
|
||||
if (pcm_val > 0x7fff)
|
||||
pcm_val = 0x7fff;
|
||||
}
|
||||
|
||||
if (pcm_val < 256) /*256 = 32 << 3*/
|
||||
aval = pcm_val >> 4; /*4 = 1 + 3*/
|
||||
else {
|
||||
/* Convert the scaled magnitude to segment number. */
|
||||
seg = val_seg(pcm_val);
|
||||
aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
|
||||
}
|
||||
return aval ^ mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
|
||||
*
|
||||
*/
|
||||
static inline int alaw_to_s16(unsigned char a_val)
|
||||
{
|
||||
int t;
|
||||
int seg;
|
||||
|
||||
a_val ^= 0x55;
|
||||
t = a_val & 0x7f;
|
||||
if (t < 16)
|
||||
t = (t << 4) + 8;
|
||||
else {
|
||||
seg = (t >> 4) & 0x07;
|
||||
t = ((t & 0x0f) << 4) + 0x108;
|
||||
t <<= seg -1;
|
||||
}
|
||||
return ((a_val & 0x80) ? t : -t);
|
||||
}
|
||||
/*
|
||||
* s16_to_ulaw() - Convert a linear PCM value to u-law
|
||||
*
|
||||
* In order to simplify the encoding process, the original linear magnitude
|
||||
* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
|
||||
* (33 - 8191). The result can be seen in the following encoding table:
|
||||
*
|
||||
* Biased Linear Input Code Compressed Code
|
||||
* ------------------------ ---------------
|
||||
* 00000001wxyza 000wxyz
|
||||
* 0000001wxyzab 001wxyz
|
||||
* 000001wxyzabc 010wxyz
|
||||
* 00001wxyzabcd 011wxyz
|
||||
* 0001wxyzabcde 100wxyz
|
||||
* 001wxyzabcdef 101wxyz
|
||||
* 01wxyzabcdefg 110wxyz
|
||||
* 1wxyzabcdefgh 111wxyz
|
||||
*
|
||||
* Each biased linear code has a leading 1 which identifies the segment
|
||||
* number. The value of the segment number is equal to 7 minus the number
|
||||
* of leading 0's. The quantization interval is directly available as the
|
||||
* four bits wxyz. * The trailing bits (a - h) are ignored.
|
||||
*
|
||||
* Ordinarily the complement of the resulting code word is used for
|
||||
* transmission, and so the code word is complemented before it is returned.
|
||||
*
|
||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
||||
*/
|
||||
|
||||
static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */
|
||||
{
|
||||
int mask;
|
||||
int seg;
|
||||
unsigned char uval;
|
||||
|
||||
if (pcm_val < 0) {
|
||||
pcm_val = 0x84 - pcm_val;
|
||||
mask = 0x7f;
|
||||
} else {
|
||||
pcm_val += 0x84;
|
||||
mask = 0xff;
|
||||
}
|
||||
if (pcm_val > 0x7fff)
|
||||
pcm_val = 0x7fff;
|
||||
|
||||
/* Convert the scaled magnitude to segment number. */
|
||||
seg = val_seg(pcm_val);
|
||||
|
||||
/*
|
||||
* Combine the sign, segment, quantization bits;
|
||||
* and complement the code word.
|
||||
*/
|
||||
uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
|
||||
return uval ^ mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM
|
||||
*
|
||||
* First, a biased linear code is derived from the code word. An unbiased
|
||||
* output can then be obtained by subtracting 33 from the biased code.
|
||||
*
|
||||
* Note that this function expects to be passed the complement of the
|
||||
* original code word. This is in keeping with ISDN conventions.
|
||||
*/
|
||||
static inline int ulaw_to_s16(unsigned char u_val)
|
||||
{
|
||||
int t;
|
||||
|
||||
/* Complement to obtain normal u-law value. */
|
||||
u_val = ~u_val;
|
||||
|
||||
/*
|
||||
* Extract and bias the quantization bits. Then
|
||||
* shift up by the segment number and subtract out the bias.
|
||||
*/
|
||||
t = ((u_val & 0x0f) << 3) + 0x84;
|
||||
t <<= (u_val & 0x70) >> 4;
|
||||
|
||||
return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84));
|
||||
}
|
@@ -1,311 +0,0 @@
|
||||
/* Message connection list handling */
|
||||
|
||||
/*
|
||||
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_ep.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* Allocate a new connection identifier. According to RFC3435, they must
|
||||
* be unique only within the scope of the endpoint. (Caller must provide
|
||||
* memory for id) */
|
||||
static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
||||
{
|
||||
int i;
|
||||
int k;
|
||||
int rc;
|
||||
uint8_t id_bin[16];
|
||||
char *id_hex;
|
||||
|
||||
/* Generate a connection id that is unique for the current endpoint.
|
||||
* Technically a counter would be sufficient, but in order to
|
||||
* be able to find a specific connection in large logfiles and to
|
||||
* prevent unintentional connections we assign the connection
|
||||
* identifiers randomly from a reasonable large number space */
|
||||
for (i = 0; i < 32; i++) {
|
||||
rc = osmo_get_rand_id(id_bin, sizeof(id_bin));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
id_hex = osmo_hexdump_nospc(id_bin, sizeof(id_bin));
|
||||
for (k = 0; k < strlen(id_hex); k++)
|
||||
id_hex[k] = toupper(id_hex[k]);
|
||||
|
||||
/* ensure that the generated conn_id is unique
|
||||
* for this endpoint */
|
||||
if (!mgcp_conn_get_rtp(endp, id_hex)) {
|
||||
osmo_strlcpy(id, id_hex, MGCP_CONN_ID_LENGTH);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x, unable to generate a unique connectionIdentifier\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Reset codec state and free memory */
|
||||
static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
codec->payload_type = -1;
|
||||
codec->subtype_name = NULL;
|
||||
codec->audio_name = NULL;
|
||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
||||
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
||||
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||
|
||||
/* see also mgcp_sdp.c, mgcp_set_audio_info() */
|
||||
talloc_free(codec->subtype_name);
|
||||
talloc_free(codec->audio_name);
|
||||
}
|
||||
|
||||
/* Reset states, free memory, set defaults and reset codec state */
|
||||
static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
struct mgcp_rtp_end *end = &conn->end;
|
||||
|
||||
conn->type = MGCP_RTP_DEFAULT;
|
||||
conn->osmux.allocated_cid = -1;
|
||||
|
||||
end->rtp.fd = -1;
|
||||
end->rtcp.fd = -1;
|
||||
end->local_port = 0;
|
||||
end->packets_rx = 0;
|
||||
end->octets_rx = 0;
|
||||
end->packets_tx = 0;
|
||||
end->octets_tx = 0;
|
||||
end->dropped_packets = 0;
|
||||
end->rtp_port = end->rtcp_port = 0;
|
||||
talloc_free(end->fmtp_extra);
|
||||
end->fmtp_extra = NULL;
|
||||
|
||||
/* Set default values */
|
||||
end->frames_per_packet = 0; /* unknown */
|
||||
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
|
||||
end->output_enabled = 0;
|
||||
|
||||
mgcp_rtp_codec_reset(&end->codec);
|
||||
mgcp_rtp_codec_reset(&end->alt_codec);
|
||||
}
|
||||
|
||||
/*! allocate a new connection list entry.
|
||||
* \param[in] ctx talloc context
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \param[in] type connection type (e.g. MGCP_CONN_TYPE_RTP)
|
||||
* \returns pointer to allocated connection, NULL on error */
|
||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
enum mgcp_conn_type type, char *name)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
int rc;
|
||||
|
||||
/* 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 */
|
||||
conn = talloc_zero(ctx, struct mgcp_conn);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
conn->endp = endp;
|
||||
conn->type = type;
|
||||
conn->mode = MGCP_CONN_NONE;
|
||||
conn->mode_orig = MGCP_CONN_NONE;
|
||||
conn->u.rtp.conn = conn;
|
||||
osmo_strlcpy(conn->name, name, sizeof(conn->name));
|
||||
rc = mgcp_alloc_id(endp, conn->id);
|
||||
if (rc < 0) {
|
||||
talloc_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
mgcp_rtp_conn_reset(&conn->u.rtp);
|
||||
break;
|
||||
default:
|
||||
/* NOTE: This should never be called with an
|
||||
* invalid type, its up to the programmer
|
||||
* to ensure propery types */
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
llist_add(&conn->entry, &endp->conns);
|
||||
|
||||
return 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_conn_get(struct mgcp_endpoint *endp, const char *id)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
if (strncmp(conn->id, id, sizeof(conn->id)) == 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;
|
||||
}
|
||||
|
||||
/*! 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 *conn;
|
||||
|
||||
conn = mgcp_conn_get(endp, id);
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
osmux_disable_conn(&conn->u.rtp);
|
||||
osmux_release_cid(&conn->u.rtp);
|
||||
mgcp_free_rtp_port(&conn->u.rtp.end);
|
||||
break;
|
||||
default:
|
||||
/* NOTE: This should never be called with an
|
||||
* invalid type, its up to the programmer
|
||||
* to ensure propery types */
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
llist_del(&conn->entry);
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
||||
/*! free oldest connection in the list.
|
||||
* \param[in] endp associated endpoint */
|
||||
void mgcp_conn_free_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);
|
||||
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);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*! dump basic connection information to human readble string.
|
||||
* \param[in] conn to dump
|
||||
* \returns human readble string */
|
||||
char *mgcp_conn_dump(struct mgcp_conn *conn)
|
||||
{
|
||||
static char str[sizeof(conn->name)+sizeof(conn->id)+256];
|
||||
|
||||
if (!conn) {
|
||||
snprintf(str, sizeof(str), "(null connection)");
|
||||
return str;
|
||||
}
|
||||
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
/* Dump RTP connection */
|
||||
snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
|
||||
"rtp:%u rtcp:%u)",
|
||||
conn->name,
|
||||
conn->id,
|
||||
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. */
|
||||
snprintf(str, sizeof(str), "(unknown connection type)");
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*! find destination connection on a specific endpoint.
|
||||
* \param[in] conn to search a destination for
|
||||
* \returns destination connection, NULL on failure */
|
||||
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
struct mgcp_conn *partner_conn;
|
||||
endp = conn->endp;
|
||||
|
||||
/*! NOTE: This simply works by grabbing the first connection that is
|
||||
* not the supplied connection, which is suitable for endpoints that
|
||||
* do not serve more than two connections. */
|
||||
|
||||
llist_for_each_entry(partner_conn, &endp->conns, entry) {
|
||||
if (conn != partner_conn) {
|
||||
return partner_conn;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
/* Endpoint types */
|
||||
|
||||
/*
|
||||
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp/mgcp_ep.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
|
||||
/* Endpoint typeset definition */
|
||||
const struct mgcp_endpoint_typeset ep_typeset = {
|
||||
/* Specify endpoint properties for RTP endpoint */
|
||||
.rtp.max_conns = 2,
|
||||
.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb
|
||||
};
|
@@ -1,406 +0,0 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* Message parser/generator utilities */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* (C) 2017 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 <limits.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>
|
||||
|
||||
/*! Display an mgcp message on the log output.
|
||||
* \param[in] message mgcp message string
|
||||
* \param[in] len message mgcp message string length
|
||||
* \param[in] preamble string to display in logtext in front of each line */
|
||||
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
|
||||
{
|
||||
unsigned char line[80];
|
||||
unsigned char *ptr;
|
||||
unsigned int consumed = 0;
|
||||
unsigned int consumed_line = 0;
|
||||
unsigned int line_count = 0;
|
||||
|
||||
if (!log_check_level(DLMGCP, LOGL_DEBUG))
|
||||
return;
|
||||
|
||||
while (1) {
|
||||
memset(line, 0, sizeof(line));
|
||||
ptr = line;
|
||||
consumed_line = 0;
|
||||
do {
|
||||
if (*message != '\n' && *message != '\r') {
|
||||
*ptr = *message;
|
||||
ptr++;
|
||||
}
|
||||
message++;
|
||||
consumed++;
|
||||
consumed_line++;
|
||||
} while (*message != '\n' && consumed < len
|
||||
&& consumed_line < sizeof(line));
|
||||
|
||||
if (strlen((const char *)line)) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "%s: line #%02u: %s\n",
|
||||
preamble, line_count, line);
|
||||
line_count++;
|
||||
}
|
||||
|
||||
if (consumed >= len)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Parse connection 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) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x missing connection mode\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
if (!conn)
|
||||
return -1;
|
||||
if (!endp)
|
||||
return -1;
|
||||
|
||||
if (strcmp(mode, "recvonly") == 0)
|
||||
conn->mode = MGCP_CONN_RECV_ONLY;
|
||||
else if (strcmp(mode, "sendrecv") == 0)
|
||||
conn->mode = MGCP_CONN_RECV_SEND;
|
||||
else if (strcmp(mode, "sendonly") == 0)
|
||||
conn->mode = MGCP_CONN_SEND_ONLY;
|
||||
else if (strcmp(mode, "loopback") == 0)
|
||||
conn->mode = MGCP_CONN_LOOPBACK;
|
||||
else {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x unknown connection mode: '%s'\n",
|
||||
ENDPOINT_NUMBER(endp), mode);
|
||||
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;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:0x%x conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn));
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:0x%x connection mode '%s' %d\n",
|
||||
ENDPOINT_NUMBER(endp), mode, conn->mode);
|
||||
|
||||
/* Special handling für RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x output_enabled %d\n",
|
||||
ENDPOINT_NUMBER(endp), 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return &tcfg->endpoints[endp];
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
unsigned int gw = INT_MAX;
|
||||
|
||||
if (strncmp(mgcp, "ds/e1", 5) == 0)
|
||||
return find_e1_endpoint(cfg, mgcp);
|
||||
|
||||
gw = strtoul(mgcp, &endptr, 10);
|
||||
if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@')
|
||||
return &cfg->trunk.endpoints[gw];
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Analyze and parse the the hader of an MGCP messeage string.
|
||||
* \param[out] pdata caller provided memory to store the parsing results
|
||||
* \param[in] data mgcp message string
|
||||
* \returns when the status line was complete and transaction_id and
|
||||
* endp out parameters are set, -1 on error */
|
||||
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
{
|
||||
int i = 0;
|
||||
char *elem, *save = NULL;
|
||||
|
||||
/*! This function will parse the header part of the received
|
||||
* MGCP message. The parsing results are stored in pdata.
|
||||
* The function will also automatically search the pool with
|
||||
* available endpoints in order to find an endpoint that matches
|
||||
* the endpoint string in in the header */
|
||||
|
||||
OSMO_ASSERT(data);
|
||||
pdata->trans = "000000";
|
||||
|
||||
for (elem = strtok_r(data, " ", &save); elem;
|
||||
elem = strtok_r(NULL, " ", &save)) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
pdata->trans = elem;
|
||||
break;
|
||||
case 1:
|
||||
pdata->endp = find_endpoint(pdata->cfg, elem);
|
||||
if (!pdata->endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Unable to find Endpoint `%s'\n", elem);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (strcmp("MGCP", elem)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MGCP header parsing error\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (strcmp("1.0", elem)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
|
||||
"not supported\n", elem);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i != 4) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
|
||||
pdata->trans = "000000";
|
||||
pdata->endp = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 on error */
|
||||
int mgcp_parse_osmux_cid(const char *line)
|
||||
{
|
||||
int osmux_cid;
|
||||
|
||||
if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1)
|
||||
return -1;
|
||||
|
||||
if (osmux_cid > OSMUX_CID_MAX) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
|
||||
osmux_cid, OSMUX_CID_MAX);
|
||||
return -1;
|
||||
}
|
||||
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 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) {
|
||||
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 1;
|
||||
}
|
||||
|
||||
/*! Check if the specified callid seems plausible.
|
||||
* \param[in] endp pointer to endpoint
|
||||
* \param{in] callid to verify
|
||||
* \returns 1 when callid seems plausible, 0 on error */
|
||||
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
||||
{
|
||||
/*! This function compares the supplied callid with the called that is
|
||||
* stored in the endpoint structure. */
|
||||
|
||||
if (!endp)
|
||||
return -1;
|
||||
if (!callid)
|
||||
return -1;
|
||||
if (!endp->callid)
|
||||
return -1;
|
||||
|
||||
if (strcmp(endp->callid, callid) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x CallIDs does not match '%s' != '%s'\n",
|
||||
ENDPOINT_NUMBER(endp), endp->callid, callid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Check if the specified connection id seems plausible.
|
||||
* \param[in] endp pointer to endpoint
|
||||
* \param{in] connection id to verify
|
||||
* \returns 1 when connection id seems plausible, 0 on error */
|
||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
|
||||
{
|
||||
/* Check for null identifiers */
|
||||
if (!conn_id) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (missing)\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check for empty connection identifiers */
|
||||
if (strlen(conn_id) == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (empty)\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check for over long connection identifiers */
|
||||
if (strlen(conn_id) > MGCP_CONN_ID_LENGTH) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (too long) 0x%s\n",
|
||||
ENDPOINT_NUMBER(endp), conn_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if connection exists */
|
||||
if (mgcp_conn_get(endp, conn_id))
|
||||
return 0;
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n",
|
||||
ENDPOINT_NUMBER(endp), conn_id);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! Extract individual lines from MCGP message.
|
||||
* \param[in] str MGCP message string, consisting of multiple lines
|
||||
* \param{in] saveptr pointer to next line in str
|
||||
* \returns line, NULL when done */
|
||||
char *mgcp_strline(char *str, char **saveptr)
|
||||
{
|
||||
char *result;
|
||||
|
||||
/*! The function must be called with *str set to the input string
|
||||
* for the first line. After that saveptr will be initalized.
|
||||
* all consecutive lines are extracted by calling the function
|
||||
* with str set to NULL. When done, the function will return NULL
|
||||
* to indicate that all lines have been parsed. */
|
||||
|
||||
if (str)
|
||||
*saveptr = str;
|
||||
|
||||
result = *saveptr;
|
||||
|
||||
if (*saveptr != NULL) {
|
||||
*saveptr = strpbrk(*saveptr, "\r\n");
|
||||
|
||||
if (*saveptr != NULL) {
|
||||
char *eos = *saveptr;
|
||||
|
||||
if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n')
|
||||
(*saveptr)++;
|
||||
(*saveptr)++;
|
||||
if ((*saveptr)[0] == '\0')
|
||||
*saveptr = NULL;
|
||||
|
||||
*eos = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,699 +0,0 @@
|
||||
/*
|
||||
* (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org>
|
||||
* (C) 2012-2013 by On Waves ehf <http://www.on-waves.com>
|
||||
* All rights not specifically granted under this license are 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* for printf */
|
||||
#include <string.h> /* for memcpy */
|
||||
#include <stdlib.h> /* for abs */
|
||||
#include <inttypes.h> /* for PRIu64 */
|
||||
#include <netinet/in.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
#include <osmocom/netif/osmux.h>
|
||||
#include <osmocom/netif/rtp.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
|
||||
static struct osmo_fd osmux_fd;
|
||||
|
||||
static LLIST_HEAD(osmux_handle_list);
|
||||
|
||||
struct osmux_handle {
|
||||
struct llist_head head;
|
||||
struct osmux_in_handle *in;
|
||||
struct in_addr rem_addr;
|
||||
int rem_port;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
static void *osmux;
|
||||
|
||||
/* Deliver OSMUX batch to the remote end */
|
||||
static void osmux_deliver_cb(struct msgb *batch_msg, void *data)
|
||||
{
|
||||
struct osmux_handle *handle = data;
|
||||
struct sockaddr_in out = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = handle->rem_port,
|
||||
};
|
||||
|
||||
memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr));
|
||||
sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0,
|
||||
(struct sockaddr *)&out, sizeof(out));
|
||||
msgb_free(batch_msg);
|
||||
}
|
||||
|
||||
/* Lookup existing OSMUX handle for specified destination address. */
|
||||
static struct osmux_handle *
|
||||
osmux_handle_find_get(struct in_addr *addr, int rem_port)
|
||||
{
|
||||
struct osmux_handle *h;
|
||||
|
||||
llist_for_each_entry(h, &osmux_handle_list, head) {
|
||||
if (memcmp(&h->rem_addr, addr, sizeof(struct in_addr)) == 0 &&
|
||||
h->rem_port == rem_port) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "using existing OSMUX handle "
|
||||
"for addr=%s:%d\n",
|
||||
inet_ntoa(*addr), ntohs(rem_port));
|
||||
h->refcnt++;
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Put down no longer needed OSMUX handle */
|
||||
static void osmux_handle_put(struct osmux_in_handle *in)
|
||||
{
|
||||
struct osmux_handle *h;
|
||||
|
||||
llist_for_each_entry(h, &osmux_handle_list, head) {
|
||||
if (h->in == in) {
|
||||
if (--h->refcnt == 0) {
|
||||
LOGP(DLMGCP, LOGL_INFO,
|
||||
"Releasing unused osmux handle for %s:%d\n",
|
||||
inet_ntoa(h->rem_addr),
|
||||
ntohs(h->rem_port));
|
||||
LOGP(DLMGCP, LOGL_INFO, "Stats: "
|
||||
"input RTP msgs: %u bytes: %"PRIu64" "
|
||||
"output osmux msgs: %u bytes: %"PRIu64"\n",
|
||||
in->stats.input_rtp_msgs,
|
||||
in->stats.input_rtp_bytes,
|
||||
in->stats.output_osmux_msgs,
|
||||
in->stats.output_osmux_bytes);
|
||||
llist_del(&h->head);
|
||||
osmux_xfrm_input_fini(h->in);
|
||||
talloc_free(h);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in);
|
||||
}
|
||||
|
||||
/* Allocate free OSMUX handle */
|
||||
static struct osmux_handle *
|
||||
osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
|
||||
{
|
||||
struct osmux_handle *h;
|
||||
|
||||
h = talloc_zero(osmux, struct osmux_handle);
|
||||
if (!h)
|
||||
return NULL;
|
||||
h->rem_addr = *addr;
|
||||
h->rem_port = rem_port;
|
||||
h->refcnt++;
|
||||
|
||||
h->in = talloc_zero(h, struct osmux_in_handle);
|
||||
if (!h->in) {
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* sequence number to start OSMUX message from */
|
||||
h->in->osmux_seq = 0;
|
||||
|
||||
h->in->batch_factor = cfg->osmux_batch;
|
||||
|
||||
/* If batch size is zero, the library defaults to 1470 bytes. */
|
||||
h->in->batch_size = cfg->osmux_batch_size;
|
||||
h->in->deliver = osmux_deliver_cb;
|
||||
osmux_xfrm_input_init(h->in);
|
||||
h->in->data = h;
|
||||
|
||||
llist_add(&h->head, &osmux_handle_list);
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "created new OSMUX handle for addr=%s:%d\n",
|
||||
inet_ntoa(*addr), ntohs(rem_port));
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
/* Lookup existing handle for a specified address, if the handle can not be
|
||||
* foud a the function will automatically allocate one */
|
||||
static struct osmux_in_handle *
|
||||
osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
|
||||
{
|
||||
struct osmux_handle *h;
|
||||
|
||||
h = osmux_handle_find_get(addr, rem_port);
|
||||
if (h != NULL)
|
||||
return h->in;
|
||||
|
||||
h = osmux_handle_alloc(cfg, addr, rem_port);
|
||||
if (h == NULL)
|
||||
return NULL;
|
||||
|
||||
return h->in;
|
||||
}
|
||||
|
||||
/*! send RTP packet through OSMUX connection.
|
||||
* \param[in] buf rtp data
|
||||
* \param[in] buf_len length of rtp data
|
||||
* \param[in] conn associated RTP connection
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
int ret;
|
||||
struct msgb *msg;
|
||||
|
||||
msg = msgb_alloc(4096, "RTP");
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
memcpy(msg->data, buf, buf_len);
|
||||
msgb_put(msg, buf_len);
|
||||
|
||||
while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.cid)) > 0) {
|
||||
/* batch full, build and deliver it */
|
||||
osmux_xfrm_input_deliver(conn->osmux.in);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Lookup the endpoint that corresponds to the specified address (port) */
|
||||
static struct mgcp_endpoint *
|
||||
endpoint_lookup(struct mgcp_config *cfg, int cid,
|
||||
struct in_addr *from_addr, int type)
|
||||
{
|
||||
struct mgcp_endpoint *endp = NULL;
|
||||
int i;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
|
||||
for (i=0; i<cfg->trunk.number_endpoints; i++) {
|
||||
struct in_addr *this;
|
||||
|
||||
endp = &cfg->trunk.endpoints[i];
|
||||
|
||||
#if 0
|
||||
if (!tmp->allocated)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
switch(type) {
|
||||
case MGCP_DEST_NET:
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (conn_net)
|
||||
this = &conn_net->end.addr;
|
||||
else
|
||||
this = NULL;
|
||||
break;
|
||||
case MGCP_DEST_BTS:
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
if (conn_bts)
|
||||
this = &conn_bts->end.addr;
|
||||
else
|
||||
this = NULL;
|
||||
break;
|
||||
default:
|
||||
/* Should not ever happen */
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (conn_net && this && conn_net->osmux.cid == cid
|
||||
&& this->s_addr == from_addr->s_addr)
|
||||
return endp;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void scheduled_tx_net_cb(struct msgb *msg, void *data)
|
||||
{
|
||||
struct mgcp_endpoint *endp = data;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_bts || !conn_net)
|
||||
return;
|
||||
|
||||
struct sockaddr_in addr = {
|
||||
.sin_addr = conn_net->end.addr,
|
||||
.sin_port = conn_net->end.rtp_port,
|
||||
};
|
||||
|
||||
conn_bts->end.octets_tx += msg->len;
|
||||
conn_bts->end.packets_tx++;
|
||||
|
||||
/* Send RTP data to NET */
|
||||
/* FIXME: Get rid of conn_bts and conn_net! */
|
||||
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
|
||||
conn_bts, conn_net);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
|
||||
{
|
||||
struct mgcp_endpoint *endp = data;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_bts || !conn_net)
|
||||
return;
|
||||
|
||||
struct sockaddr_in addr = {
|
||||
.sin_addr = conn_bts->end.addr,
|
||||
.sin_port = conn_bts->end.rtp_port,
|
||||
};
|
||||
|
||||
conn_net->end.octets_tx += msg->len;
|
||||
conn_net->end.packets_tx++;
|
||||
|
||||
/* Send RTP data to BTS */
|
||||
/* FIXME: Get rid of conn_bts and conn_net! */
|
||||
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
|
||||
conn_net, conn_bts);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
socklen_t slen = sizeof(*addr);
|
||||
int ret;
|
||||
|
||||
msg = msgb_alloc(4096, "OSMUX");
|
||||
if (!msg) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot allocate message\n");
|
||||
return NULL;
|
||||
}
|
||||
ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0,
|
||||
(struct sockaddr *)addr, &slen);
|
||||
if (ret <= 0) {
|
||||
msgb_free(msg);
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot receive message\n");
|
||||
return NULL;
|
||||
}
|
||||
msgb_put(msg, ret);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
#define osmux_chunk_length(msg, rem) (rem - msg->len);
|
||||
|
||||
int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmux_hdr *osmuxh;
|
||||
struct llist_head list;
|
||||
struct sockaddr_in addr;
|
||||
struct mgcp_config *cfg = ofd->data;
|
||||
uint32_t rem;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
|
||||
msg = osmux_recv(ofd, &addr);
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
/* not any further processing dummy messages */
|
||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
||||
goto out;
|
||||
|
||||
rem = msg->len;
|
||||
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
/* Yes, we use MGCP_DEST_NET to locate the origin */
|
||||
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
|
||||
&addr.sin_addr, MGCP_DEST_NET);
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_net)
|
||||
goto out;
|
||||
|
||||
if (!endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find an endpoint for circuit_id=%d\n",
|
||||
osmuxh->circuit_id);
|
||||
goto out;
|
||||
}
|
||||
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
conn_net->osmux.stats.chunks++;
|
||||
rem = msg->len;
|
||||
|
||||
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
|
||||
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is called from the bsc-nat */
|
||||
static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
|
||||
struct msgb *msg)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
uint8_t osmux_cid;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
|
||||
if (msg->len < 1 + sizeof(osmux_cid)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Discarding truncated Osmux dummy load\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n",
|
||||
inet_ntoa(addr->sin_addr));
|
||||
|
||||
if (!cfg->osmux) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"bsc wants to use Osmux but bsc-nat did not request it\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* extract the osmux CID from the dummy message */
|
||||
memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid));
|
||||
|
||||
endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS);
|
||||
if (!endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find endpoint for Osmux CID %d\n", osmux_cid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_net)
|
||||
goto out;
|
||||
|
||||
if (conn_net->osmux.state == OSMUX_STATE_ENABLED)
|
||||
goto out;
|
||||
|
||||
if (osmux_enable_conn(endp, conn_net, &addr->sin_addr, addr->sin_port) < 0 ) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Could not enable osmux in endpoint %d\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n",
|
||||
ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr),
|
||||
ntohs(addr->sin_port));
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmux_hdr *osmuxh;
|
||||
struct llist_head list;
|
||||
struct sockaddr_in addr;
|
||||
struct mgcp_config *cfg = ofd->data;
|
||||
uint32_t rem;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
|
||||
msg = osmux_recv(ofd, &addr);
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
/* not any further processing dummy messages */
|
||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
||||
return osmux_handle_dummy(cfg, &addr, msg);
|
||||
|
||||
rem = msg->len;
|
||||
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
/* Yes, we use MGCP_DEST_BTS to locate the origin */
|
||||
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
|
||||
&addr.sin_addr, MGCP_DEST_BTS);
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_net)
|
||||
goto out;
|
||||
|
||||
if (!endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find an endpoint for circuit_id=%d\n",
|
||||
osmuxh->circuit_id);
|
||||
goto out;
|
||||
}
|
||||
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
conn_net->osmux.stats.chunks++;
|
||||
rem = msg->len;
|
||||
|
||||
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
|
||||
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmux_init(int role, struct mgcp_config *cfg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch(role) {
|
||||
case OSMUX_ROLE_BSC:
|
||||
osmux_fd.cb = osmux_read_from_bsc_nat_cb;
|
||||
break;
|
||||
case OSMUX_ROLE_BSC_NAT:
|
||||
osmux_fd.cb = osmux_read_from_bsc_cb;
|
||||
break;
|
||||
default:
|
||||
LOGP(DLMGCP, LOGL_ERROR, "wrong role for OSMUX\n");
|
||||
return -1;
|
||||
}
|
||||
osmux_fd.data = cfg;
|
||||
|
||||
ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port);
|
||||
if (ret < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n");
|
||||
return ret;
|
||||
}
|
||||
mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
|
||||
osmux_fd.when |= BSC_FD_READ;
|
||||
|
||||
ret = osmo_fd_register(&osmux_fd);
|
||||
if (ret < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket\n");
|
||||
return ret;
|
||||
}
|
||||
cfg->osmux_init = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! enable OSXMUX circuit for a specified connection.
|
||||
* \param[in] endp mgcp endpoint (configuration)
|
||||
* \param[in] conn connection to disable
|
||||
* \param[in] addr IP address of remote OSMUX endpoint
|
||||
* \param[in] port portnumber of the remote OSMUX endpoint
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
struct in_addr *addr, uint16_t port)
|
||||
{
|
||||
/*! If osmux is enabled, initialize the output handler. This handler is
|
||||
* used to reconstruct the RTP flow from osmux. The RTP SSRC is
|
||||
* allocated based on the circuit ID (conn_net->osmux.cid), which is unique
|
||||
* in the local scope to the BSC/BSC-NAT. We use it to divide the RTP
|
||||
* SSRC space (2^32) by the 256 possible circuit IDs, then randomly
|
||||
* select one value from that window. Thus, we have no chance to have
|
||||
* overlapping RTP SSRC traveling to the BTSes behind the BSC,
|
||||
* similarly, for flows traveling to the MSC.
|
||||
*/
|
||||
static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256;
|
||||
uint16_t osmux_dummy = endp->cfg->osmux_dummy;
|
||||
|
||||
/* Check if osmux is enabled for the specified connection */
|
||||
if (conn->osmux.state == OSMUX_STATE_DISABLED) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "OSMUX not enabled for conn:%s\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
return -1;
|
||||
}
|
||||
|
||||
osmux_xfrm_output_init(&conn->osmux.out,
|
||||
(conn->osmux.cid * rtp_ssrc_winlen) +
|
||||
(random() % rtp_ssrc_winlen));
|
||||
|
||||
conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
|
||||
if (!conn->osmux.in) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle for conn:%s\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
return -1;
|
||||
}
|
||||
if (!osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u for conn:%s\n",
|
||||
conn->osmux.cid, mgcp_conn_dump(conn->conn));
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (endp->cfg->role) {
|
||||
case MGCP_BSC_NAT:
|
||||
conn->type = MGCP_OSMUX_BSC_NAT;
|
||||
break;
|
||||
case MGCP_BSC:
|
||||
conn->type = MGCP_OSMUX_BSC;
|
||||
break;
|
||||
}
|
||||
|
||||
conn->osmux.state = OSMUX_STATE_ENABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! disable OSXMUX circuit for a specified connection.
|
||||
* \param[in] conn connection to disable */
|
||||
void osmux_disable_conn(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
if (conn->osmux.state != OSMUX_STATE_ENABLED)
|
||||
return;
|
||||
|
||||
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
|
||||
conn->conn->id, conn->osmux.cid);
|
||||
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
|
||||
conn->osmux.state = OSMUX_STATE_DISABLED;
|
||||
conn->osmux.cid = -1;
|
||||
osmux_handle_put(conn->osmux.in);
|
||||
}
|
||||
|
||||
/*! relase OSXMUX cid, that had been allocated to this connection.
|
||||
* \param[in] conn connection with OSMUX cid to release */
|
||||
void osmux_release_cid(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
if (conn->osmux.state != OSMUX_STATE_ENABLED)
|
||||
return;
|
||||
|
||||
if (conn->osmux.allocated_cid >= 0)
|
||||
osmux_put_cid(conn->osmux.allocated_cid);
|
||||
conn->osmux.allocated_cid = -1;
|
||||
}
|
||||
|
||||
/*! allocate OSXMUX cid to connection.
|
||||
* \param[in] conn connection for which we allocate the OSMUX cid*/
|
||||
void osmux_allocate_cid(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
osmux_release_cid(conn);
|
||||
conn->osmux.allocated_cid = osmux_get_cid();
|
||||
}
|
||||
|
||||
/*! send RTP dummy packet to OSMUX connection port.
|
||||
* \param[in] endp mcgp endpoint that holds the RTP connection
|
||||
* \param[in] conn associated RTP connection
|
||||
* \returns bytes sent, -1 on error */
|
||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
char buf[1 + sizeof(uint8_t)];
|
||||
struct in_addr addr_unset = {};
|
||||
|
||||
/*! The dummy packet will not be sent via the actual OSMUX connection,
|
||||
* instead it is sent out of band to port where the remote OSMUX
|
||||
* multplexer is listening. The goal is to ensure that the connection
|
||||
* is kept open */
|
||||
|
||||
/*! We don't need to send the dummy load for osmux so often as another
|
||||
* endpoint may have already punched the hole in the firewall. This
|
||||
* approach is simple though. */
|
||||
|
||||
buf[0] = MGCP_DUMMY_LOAD;
|
||||
memcpy(&buf[1], &conn->osmux.cid, sizeof(conn->osmux.cid));
|
||||
|
||||
/* Wait until we have the connection information from MDCX */
|
||||
if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0)
|
||||
return 0;
|
||||
|
||||
if (conn->osmux.state == OSMUX_STATE_ACTIVATING) {
|
||||
if (osmux_enable_conn(endp, conn, &conn->end.addr,
|
||||
htons(endp->cfg->osmux_port)) < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Could not activate osmux for conn:%s\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Osmux CID %u for %s:%u is now enabled\n",
|
||||
conn->osmux.cid, inet_ntoa(conn->end.addr),
|
||||
endp->cfg->osmux_port);
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"sending OSMUX dummy load to %s CID %u\n",
|
||||
inet_ntoa(conn->end.addr), conn->osmux.cid);
|
||||
|
||||
return mgcp_udp_send(osmux_fd.fd, &conn->end.addr,
|
||||
htons(endp->cfg->osmux_port), buf, sizeof(buf));
|
||||
}
|
||||
|
||||
/*! bsc-nat allocates/releases the OSMUX cids (Circuit IDs). */
|
||||
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
|
||||
|
||||
/*! count the number of taken OSMUX cids.
|
||||
* \returns number of OSMUX cids in use */
|
||||
int osmux_used_cid(void)
|
||||
{
|
||||
int i, j, used = 0;
|
||||
|
||||
for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (osmux_cid_bitmap[i] & (1 << j))
|
||||
used += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
/*! take a free OSMUX cid.
|
||||
* \returns OSMUX cid */
|
||||
int osmux_get_cid(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (osmux_cid_bitmap[i] & (1 << j))
|
||||
continue;
|
||||
|
||||
osmux_cid_bitmap[i] |= (1 << j);
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"Allocating Osmux CID %u from pool\n", (i * 8) + j);
|
||||
return (i * 8) + j;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! put back a no longer used OSMUX cid.
|
||||
* \param[in] osmux_cid OSMUX cid */
|
||||
void osmux_put_cid(uint8_t osmux_cid)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid);
|
||||
osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8));
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,409 +0,0 @@
|
||||
/*
|
||||
* Some SDP file parsing...
|
||||
*
|
||||
* (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2014 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
struct sdp_rtp_map {
|
||||
/* the type */
|
||||
int payload_type;
|
||||
/* null, static or later dynamic codec name */
|
||||
char *codec_name;
|
||||
/* A pointer to the original line for later parsing */
|
||||
char *map_line;
|
||||
|
||||
int rate;
|
||||
int channels;
|
||||
};
|
||||
|
||||
/*! Set codec configuration depending on payload type and codec name.
|
||||
* \param[in] ctx talloc context.
|
||||
* \param[out] codec configuration (caller provided memory).
|
||||
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
||||
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
|
||||
* \returns 0 on success, -1 on failure. */
|
||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
int payload_type, const char *audio_name)
|
||||
{
|
||||
int rate = codec->rate;
|
||||
int channels = codec->channels;
|
||||
char audio_codec[64];
|
||||
|
||||
talloc_free(codec->subtype_name);
|
||||
codec->subtype_name = NULL;
|
||||
talloc_free(codec->audio_name);
|
||||
codec->audio_name = NULL;
|
||||
|
||||
if (payload_type != PTYPE_UNDEFINED)
|
||||
codec->payload_type = payload_type;
|
||||
|
||||
if (!audio_name) {
|
||||
switch (payload_type) {
|
||||
case 0:
|
||||
audio_name = "PCMU/8000/1";
|
||||
break;
|
||||
case 3:
|
||||
audio_name = "GSM/8000/1";
|
||||
break;
|
||||
case 8:
|
||||
audio_name = "PCMA/8000/1";
|
||||
break;
|
||||
case 18:
|
||||
audio_name = "G729/8000/1";
|
||||
break;
|
||||
default:
|
||||
/* Payload type is unknown, don't change rate and
|
||||
* channels. */
|
||||
/* TODO: return value? */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
||||
audio_codec, &rate, &channels) < 1)
|
||||
return -EINVAL;
|
||||
|
||||
codec->rate = rate;
|
||||
codec->channels = channels;
|
||||
codec->subtype_name = talloc_strdup(ctx, audio_codec);
|
||||
codec->audio_name = talloc_strdup(ctx, audio_name);
|
||||
|
||||
if (!strcmp(audio_codec, "G729")) {
|
||||
codec->frame_duration_num = 10;
|
||||
codec->frame_duration_den = 1000;
|
||||
} else {
|
||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
||||
}
|
||||
|
||||
if (payload_type < 0) {
|
||||
payload_type = 96;
|
||||
if (rate == 8000 && channels == 1) {
|
||||
if (!strcmp(audio_codec, "GSM"))
|
||||
payload_type = 3;
|
||||
else if (!strcmp(audio_codec, "PCMA"))
|
||||
payload_type = 8;
|
||||
else if (!strcmp(audio_codec, "PCMU"))
|
||||
payload_type = 0;
|
||||
else if (!strcmp(audio_codec, "G729"))
|
||||
payload_type = 18;
|
||||
}
|
||||
|
||||
codec->payload_type = payload_type;
|
||||
}
|
||||
|
||||
if (channels != 1)
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Channels != 1 in SDP: '%s'\n", audio_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < used; ++i) {
|
||||
switch (codecs[i].payload_type) {
|
||||
case 0:
|
||||
codecs[i].codec_name = "PCMU";
|
||||
codecs[i].rate = 8000;
|
||||
codecs[i].channels = 1;
|
||||
break;
|
||||
case 3:
|
||||
codecs[i].codec_name = "GSM";
|
||||
codecs[i].rate = 8000;
|
||||
codecs[i].channels = 1;
|
||||
break;
|
||||
case 8:
|
||||
codecs[i].codec_name = "PCMA";
|
||||
codecs[i].rate = 8000;
|
||||
codecs[i].channels = 1;
|
||||
break;
|
||||
case 18:
|
||||
codecs[i].codec_name = "G729";
|
||||
codecs[i].rate = 8000;
|
||||
codecs[i].channels = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
int payload, const char *audio_name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < used; ++i) {
|
||||
char audio_codec[64];
|
||||
int rate = -1;
|
||||
int channels = -1;
|
||||
if (codecs[i].payload_type != payload)
|
||||
continue;
|
||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
||||
audio_codec, &rate, &channels) < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
|
||||
audio_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
codecs[i].map_line = talloc_strdup(ctx, audio_name);
|
||||
codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
|
||||
codecs[i].rate = rate;
|
||||
codecs[i].channels = channels;
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
|
||||
audio_name);
|
||||
}
|
||||
|
||||
/* Check if the codec matches what is set up in the trunk config */
|
||||
static int is_codec_compatible(const struct mgcp_endpoint *endp,
|
||||
const struct sdp_rtp_map *codec)
|
||||
{
|
||||
char *codec_str;
|
||||
char audio_codec[64];
|
||||
|
||||
if (!codec->codec_name)
|
||||
return 0;
|
||||
|
||||
/* GSM, GSM/8000 and GSM/8000/1 should all be compatible...
|
||||
* let's go by name first. */
|
||||
codec_str = endp->tcfg->audio_name;
|
||||
if (sscanf(codec_str, "%63[^/]/%*d/%*d", audio_codec) < 1)
|
||||
return 0;
|
||||
|
||||
return strcasecmp(audio_codec, codec->codec_name) == 0;
|
||||
}
|
||||
|
||||
/*! Analyze SDP input string.
|
||||
* \param[in] endp trunk endpoint.
|
||||
* \param[out] conn associated rtp connection.
|
||||
* \param[out] caller provided memory to store the parsing results.
|
||||
* \returns 0 on success, -1 on failure.
|
||||
*
|
||||
* Note: In conn (conn->end) the function returns the packet duration,
|
||||
* the rtp port and the rtcp port */
|
||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn,
|
||||
struct mgcp_parse_data *p)
|
||||
{
|
||||
struct sdp_rtp_map codecs[10];
|
||||
int codecs_used = 0;
|
||||
char *line;
|
||||
int maxptime = -1;
|
||||
int i;
|
||||
int codecs_assigned = 0;
|
||||
void *tmp_ctx = talloc_new(NULL);
|
||||
struct mgcp_rtp_end *rtp;
|
||||
|
||||
int payload;
|
||||
int ptime, ptime2 = 0;
|
||||
char audio_name[64];
|
||||
int port, rc;
|
||||
char ipv4[16];
|
||||
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(p);
|
||||
|
||||
rtp = &conn->end;
|
||||
memset(&codecs, 0, sizeof(codecs));
|
||||
|
||||
for_each_line(line, p->save) {
|
||||
switch (line[0]) {
|
||||
case 'o':
|
||||
case 's':
|
||||
case 't':
|
||||
case 'v':
|
||||
/* skip these SDP attributes */
|
||||
break;
|
||||
case 'a':
|
||||
if (sscanf(line, "a=rtpmap:%d %63s",
|
||||
&payload, audio_name) == 2) {
|
||||
codecs_update(tmp_ctx, codecs,
|
||||
codecs_used, payload, audio_name);
|
||||
} else
|
||||
if (sscanf
|
||||
(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
|
||||
if (ptime2 > 0 && ptime2 != ptime)
|
||||
rtp->packet_duration_ms = 0;
|
||||
else
|
||||
rtp->packet_duration_ms = ptime;
|
||||
} else if (sscanf(line, "a=maxptime:%d", &ptime2)
|
||||
== 1) {
|
||||
maxptime = ptime2;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
rc = sscanf(line,
|
||||
"m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
|
||||
&port, &codecs[0].payload_type,
|
||||
&codecs[1].payload_type,
|
||||
&codecs[2].payload_type,
|
||||
&codecs[3].payload_type,
|
||||
&codecs[4].payload_type,
|
||||
&codecs[5].payload_type,
|
||||
&codecs[6].payload_type,
|
||||
&codecs[7].payload_type,
|
||||
&codecs[8].payload_type,
|
||||
&codecs[9].payload_type);
|
||||
if (rc >= 2) {
|
||||
rtp->rtp_port = htons(port);
|
||||
rtp->rtcp_port = htons(port + 1);
|
||||
codecs_used = rc - 1;
|
||||
codecs_initialize(tmp_ctx, codecs, codecs_used);
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
|
||||
if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
|
||||
inet_aton(ipv4, &rtp->addr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (p->endp)
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Unhandled SDP option: '%c'/%d on 0x%x\n",
|
||||
line[0], line[0],
|
||||
ENDPOINT_NUMBER(p->endp));
|
||||
else
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Unhandled SDP option: '%c'/%d\n",
|
||||
line[0], line[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now select the primary and alt_codec */
|
||||
for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
|
||||
struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
|
||||
&rtp->codec : &rtp->alt_codec;
|
||||
|
||||
if (endp->tcfg->no_audio_transcoding &&
|
||||
!is_codec_compatible(endp, &codecs[i])) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
|
||||
codecs[i].codec_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
mgcp_set_audio_info(p->cfg, codec,
|
||||
codecs[i].payload_type, codecs[i].map_line);
|
||||
codecs_assigned += 1;
|
||||
}
|
||||
|
||||
if (codecs_assigned > 0) {
|
||||
/* TODO/XXX: Store this per codec and derive it on use */
|
||||
if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
|
||||
rtp->codec.frame_duration_num * 1500) {
|
||||
/* more than 1 frame */
|
||||
rtp->packet_duration_ms = 0;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Got media info via SDP: port %d, payload %d (%s), "
|
||||
"duration %d, addr %s\n",
|
||||
ntohs(rtp->rtp_port), rtp->codec.payload_type,
|
||||
rtp->codec.subtype_name ? rtp->
|
||||
codec.subtype_name : "unknown", rtp->packet_duration_ms,
|
||||
inet_ntoa(rtp->addr));
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
return codecs_assigned > 0;
|
||||
}
|
||||
|
||||
/*! Generate SDP response string.
|
||||
* \param[in] endp trunk endpoint.
|
||||
* \param[in] conn associated rtp connection.
|
||||
* \param[out] sdp msg buffer to append resulting SDP string data.
|
||||
* \param[in] addr IPV4 address string (e.g. 192.168.100.1).
|
||||
* \returns 0 on success, -1 on failure. */
|
||||
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
||||
const char *addr)
|
||||
{
|
||||
const char *fmtp_extra;
|
||||
const char *audio_name;
|
||||
int payload_type;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(sdp);
|
||||
OSMO_ASSERT(addr);
|
||||
|
||||
/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
|
||||
endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
|
||||
&payload_type, &audio_name,
|
||||
&fmtp_extra,
|
||||
(struct mgcp_conn_rtp *)conn);
|
||||
|
||||
rc = msgb_printf(sdp,
|
||||
"v=0\r\n"
|
||||
"o=- %s 23 IN IP4 %s\r\n"
|
||||
"s=-\r\n"
|
||||
"c=IN IP4 %s\r\n"
|
||||
"t=0 0\r\n", conn->conn->id, addr, addr);
|
||||
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
|
||||
if (payload_type >= 0) {
|
||||
rc = msgb_printf(sdp, "m=audio %d RTP/AVP %d\r\n",
|
||||
conn->end.local_port, payload_type);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
|
||||
if (audio_name && endp->tcfg->audio_send_name) {
|
||||
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
|
||||
payload_type, audio_name);
|
||||
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
|
||||
if (fmtp_extra) {
|
||||
rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
|
||||
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
}
|
||||
if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
|
||||
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
|
||||
conn->end.packet_duration_ms);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
buffer_too_small:
|
||||
LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
|
||||
return -1;
|
||||
}
|
@@ -1,128 +0,0 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* The statistics generator */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* (C) 2017 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_stat.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
|
||||
void calc_loss(struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *end, uint32_t *expected,
|
||||
int *loss)
|
||||
{
|
||||
*expected = state->stats_cycles + state->stats_max_seq;
|
||||
*expected = *expected - state->stats_base_seq + 1;
|
||||
|
||||
if (!state->stats_initialized) {
|
||||
*expected = 0;
|
||||
*loss = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the sign is correct and use the biggest
|
||||
* positive/negative number that fits.
|
||||
*/
|
||||
*loss = *expected - end->packets_rx;
|
||||
if (*expected < end->packets_rx) {
|
||||
if (*loss > 0)
|
||||
*loss = INT_MIN;
|
||||
} else {
|
||||
if (*loss < 0)
|
||||
*loss = INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function for mgcp_format_stats_rtp() to calculate jitter */
|
||||
uint32_t calc_jitter(struct mgcp_rtp_state *state)
|
||||
{
|
||||
if (!state->stats_initialized)
|
||||
return 0;
|
||||
return state->stats_jitter >> 4;
|
||||
}
|
||||
|
||||
/* Generate statistics for an RTP connection */
|
||||
static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
||||
struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
uint32_t expected, jitter;
|
||||
int ploss;
|
||||
int nchars;
|
||||
|
||||
calc_loss(&conn->state, &conn->end, &expected, &ploss);
|
||||
jitter = calc_jitter(&conn->state);
|
||||
|
||||
nchars = snprintf(str, str_len,
|
||||
"\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
|
||||
conn->end.packets_tx, conn->end.octets_tx,
|
||||
conn->end.packets_rx, conn->end.octets_rx,
|
||||
ploss, jitter);
|
||||
if (nchars < 0 || nchars >= str_len)
|
||||
goto truncate;
|
||||
|
||||
str += nchars;
|
||||
str_len -= nchars;
|
||||
|
||||
/* Error Counter */
|
||||
nchars = snprintf(str, str_len,
|
||||
"\r\nX-Osmo-CP: EC TI=%u, TO=%u",
|
||||
conn->state.in_stream.err_ts_counter,
|
||||
conn->state.out_stream.err_ts_counter);
|
||||
if (nchars < 0 || nchars >= str_len)
|
||||
goto truncate;
|
||||
|
||||
str += nchars;
|
||||
str_len -= nchars;
|
||||
|
||||
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
|
||||
snprintf(str, str_len,
|
||||
"\r\nX-Osmux-ST: CR=%u, BR=%u",
|
||||
conn->osmux.stats.chunks, conn->osmux.stats.octets);
|
||||
}
|
||||
|
||||
truncate:
|
||||
str[str_len - 1] = '\0';
|
||||
}
|
||||
|
||||
/*! format statistics into an mgcp parameter string.
|
||||
* \param[out] str resulting string
|
||||
* \param[in] str_len length of the string buffer
|
||||
* \param[in] conn connection to evaluate */
|
||||
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn)
|
||||
{
|
||||
memset(str, 0, str_len);
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
/* NOTE: At the moment we only support generating statistics for
|
||||
* RTP connections. However, in the future we may also want to
|
||||
* generate statistics for other connection types as well. Lets
|
||||
* keep this option open: */
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
mgcp_format_stats_rtp(str, str_len, &conn->u.rtp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@ AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(LIBBCG729_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
@@ -23,6 +24,7 @@ osmo_bsc_mgcp_SOURCES = \
|
||||
osmo_bsc_mgcp_LDADD = \
|
||||
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBBCG729_LIBS) \
|
||||
$(LIBRARY_GSM) \
|
||||
|
@@ -189,33 +189,33 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
|
||||
|
||||
int mgcp_vty_is_config_node(struct vty *vty, int node)
|
||||
{
|
||||
switch (node) {
|
||||
case CONFIG_NODE:
|
||||
return 0;
|
||||
switch (node) {
|
||||
case CONFIG_NODE:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int mgcp_vty_go_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
case TRUNK_NODE:
|
||||
vty->node = MGCP_NODE;
|
||||
vty->index = NULL;
|
||||
break;
|
||||
case MGCP_NODE:
|
||||
default:
|
||||
if (mgcp_vty_is_config_node(vty, vty->node))
|
||||
vty->node = CONFIG_NODE;
|
||||
else
|
||||
vty->node = ENABLE_NODE;
|
||||
switch (vty->node) {
|
||||
case TRUNK_NODE:
|
||||
vty->node = MGCP_NODE;
|
||||
vty->index = NULL;
|
||||
break;
|
||||
case MGCP_NODE:
|
||||
default:
|
||||
if (mgcp_vty_is_config_node(vty, vty->node))
|
||||
vty->node = CONFIG_NODE;
|
||||
else
|
||||
vty->node = ENABLE_NODE;
|
||||
|
||||
vty->index = NULL;
|
||||
}
|
||||
vty->index = NULL;
|
||||
}
|
||||
|
||||
return vty->node;
|
||||
return vty->node;
|
||||
}
|
||||
|
||||
|
||||
@@ -231,8 +231,8 @@ static const struct log_info_cat log_categories[] = {
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
.cat = log_categories,
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
.cat = log_categories,
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@@ -256,8 +256,6 @@ int main(int argc, char **argv)
|
||||
cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format;
|
||||
#endif
|
||||
|
||||
cfg->trunk.force_realloc = 1;
|
||||
|
||||
vty_info.copyright = openbsc_copyright;
|
||||
vty_init(&vty_info);
|
||||
logging_vty_add_cmds(NULL);
|
||||
@@ -282,8 +280,8 @@ int main(int argc, char **argv)
|
||||
/* set some callbacks */
|
||||
cfg->reset_cb = mgcp_rsip_cb;
|
||||
|
||||
/* we need to bind a socket */
|
||||
if (rc == 0) {
|
||||
/* we need to bind a socket */
|
||||
if (rc == 0) {
|
||||
cfg->gw_fd.bfd.when = BSC_FD_READ;
|
||||
cfg->gw_fd.bfd.cb = read_call_agent;
|
||||
cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
@@ -1,26 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
bin_PROGRAMS = \
|
||||
osmo-mgw \
|
||||
$(NULL)
|
||||
|
||||
osmo_mgw_SOURCES = \
|
||||
mgw_main.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_mgw_LDADD = \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(NULL)
|
@@ -1,332 +0,0 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* The main method to drive it as a standalone process */
|
||||
|
||||
/*
|
||||
* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2011 by On-Waves
|
||||
* (C) 2017 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* 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 <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/vty.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/stats.h>
|
||||
|
||||
#include "../../bscconfig.h"
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
||||
/* FIXME: Make use of the rtp proxy code */
|
||||
|
||||
static struct mgcp_config *cfg;
|
||||
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 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"
|
||||
"This is free software: you are free to change and redistribute it.\r\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\r\n";
|
||||
|
||||
static char *config_file = "osmo-mgw.cfg";
|
||||
|
||||
/* used by msgb and mgcp */
|
||||
void *tall_bsc_ctx = NULL;
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
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'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch(c) {
|
||||
case 'h':
|
||||
print_help();
|
||||
exit(0);
|
||||
break;
|
||||
case 'c':
|
||||
config_file = talloc_strdup(tall_bsc_ctx, optarg);
|
||||
break;
|
||||
case 's':
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
break;
|
||||
case 'V':
|
||||
print_version(1);
|
||||
exit(0);
|
||||
break;
|
||||
case 'D':
|
||||
daemonize = 1;
|
||||
break;
|
||||
default:
|
||||
/* ignore */
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/* Callback function to be called when the RSIP ("Reset in Progress") mgcp
|
||||
* command is received */
|
||||
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
|
||||
{
|
||||
/* Set flag so that, when read_call_agent() is called next time
|
||||
* the reset can progress */
|
||||
reset_endpoints = 1;
|
||||
|
||||
reset_trunk = tcfg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_call_agent(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t slen = sizeof(addr);
|
||||
struct msgb *msg;
|
||||
struct msgb *resp;
|
||||
int i;
|
||||
|
||||
msg = (struct msgb *) fd->data;
|
||||
|
||||
/* read one less so we can use it as a \0 */
|
||||
int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0,
|
||||
(struct sockaddr *) &addr, &slen);
|
||||
if (rc < 0) {
|
||||
perror("Gateway failed to read");
|
||||
return -1;
|
||||
} else if (slen > sizeof(addr)) {
|
||||
fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n",
|
||||
(size_t) slen, sizeof(addr));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* handle message now */
|
||||
msg->l2h = msgb_put(msg, rc);
|
||||
resp = mgcp_handle_message(cfg, msg);
|
||||
msgb_reset(msg);
|
||||
|
||||
if (resp) {
|
||||
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: %d/%d\n",
|
||||
reset_trunk->trunk_nr, reset_trunk->trunk_type);
|
||||
|
||||
/* reset flag */
|
||||
reset_endpoints = 0;
|
||||
|
||||
/* Walk over all endpoints and trigger a release, this will release all
|
||||
* endpoints, possible open connections are forcefully dropped */
|
||||
for (i = 1; i < reset_trunk->number_endpoints; ++i)
|
||||
mgcp_release_endp(&reset_trunk->endpoints[i]);
|
||||
}
|
||||
|
||||
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) {
|
||||
case TRUNK_NODE:
|
||||
vty->node = MGCP_NODE;
|
||||
vty->index = NULL;
|
||||
break;
|
||||
case MGCP_NODE:
|
||||
default:
|
||||
if (mgcp_vty_is_config_node(vty, vty->node))
|
||||
vty->node = CONFIG_NODE;
|
||||
else
|
||||
vty->node = ENABLE_NODE;
|
||||
|
||||
vty->index = NULL;
|
||||
}
|
||||
|
||||
return vty->node;
|
||||
}
|
||||
|
||||
|
||||
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[] = {
|
||||
/* DLMGCP is provided by the MGCP library */
|
||||
[DRTP] = {
|
||||
.name = "DRTP",
|
||||
.description = "RTP stream handling",
|
||||
.color = "\033[1;30m",
|
||||
.enabled = 1,.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
.cat = log_categories,
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned int flags;
|
||||
int rc;
|
||||
|
||||
tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
|
||||
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
|
||||
|
||||
osmo_init_ignore_signals();
|
||||
osmo_init_logging(&log_info);
|
||||
|
||||
cfg = mgcp_config_alloc();
|
||||
if (!cfg)
|
||||
return -1;
|
||||
|
||||
vty_info.copyright = osmomgw_copyright;
|
||||
vty_init(&vty_info);
|
||||
logging_vty_add_cmds(NULL);
|
||||
osmo_stats_vty_add_cmds(&log_info);
|
||||
mgcp_vty_init();
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
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_dynif(tall_bsc_ctx, NULL,
|
||||
vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Set the reset callback function. This functions is called when the
|
||||
* mgcp-command "RSIP" (Reset in Progress) is received */
|
||||
cfg->reset_cb = mgcp_rsip_cb;
|
||||
|
||||
/* we need to bind a socket */
|
||||
flags = OSMO_SOCK_F_BIND;
|
||||
if (cfg->call_agent_addr)
|
||||
flags |= OSMO_SOCK_F_CONNECT;
|
||||
|
||||
rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||
cfg->source_addr, cfg->source_port,
|
||||
cfg->call_agent_addr, cfg->call_agent_addr ? 2727 : 0, flags);
|
||||
if (rc < 0) {
|
||||
perror("Gateway failed to bind");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfg->gw_fd.bfd.cb = read_call_agent;
|
||||
cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
|
||||
if (!cfg->gw_fd.bfd.data) {
|
||||
fprintf(stderr, "Gateway memory error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
|
||||
|
||||
/* initialisation */
|
||||
srand(time(NULL));
|
||||
|
||||
if (daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
if (rc < 0) {
|
||||
perror("Error during daemonize");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* main loop */
|
||||
while (1) {
|
||||
osmo_select_main(0);
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
@@ -1,7 +1,5 @@
|
||||
SUBDIRS = \
|
||||
legacy_mgcp \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
$(NULL)
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
@@ -27,6 +25,7 @@ EXTRA_DIST = \
|
||||
$(srcdir)/package.m4 \
|
||||
$(TESTSUITE) \
|
||||
vty_test_runner.py \
|
||||
ctrl_test_runner.py \
|
||||
$(NULL)
|
||||
|
||||
TESTSUITE = $(srcdir)/testsuite
|
||||
@@ -40,6 +39,7 @@ 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
|
||||
$(PYTHON) $(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v
|
||||
$(PYTHON) $(srcdir)/ctrl_test_runner.py -w $(abs_top_builddir) -v
|
||||
else
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
echo "Not running python-based tests (determined at configure-time)"
|
||||
|
575
tests/ctrl_test_runner.py
Normal file
575
tests/ctrl_test_runner.py
Normal file
@@ -0,0 +1,575 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# (C) 2013 by Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
# (C) 2014 by Holger Hans Peter Freyther
|
||||
# based on vty_test_runner.py:
|
||||
# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
|
||||
# (C) 2013 by Holger Hans Peter Freyther
|
||||
# based on bsc_control.py.
|
||||
|
||||
# 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/>.
|
||||
|
||||
import os
|
||||
import time
|
||||
import unittest
|
||||
import socket
|
||||
import sys
|
||||
import struct
|
||||
|
||||
import osmopy.obscvty as obscvty
|
||||
import osmopy.osmoutil as osmoutil
|
||||
|
||||
# add $top_srcdir/contrib to find ipa.py
|
||||
sys.path.append(os.path.join(sys.path[0], '..', 'contrib'))
|
||||
|
||||
from ipa import Ctrl, IPA
|
||||
|
||||
# to be able to find $top_srcdir/doc/...
|
||||
confpath = os.path.join(sys.path[0], '..')
|
||||
verbose = False
|
||||
|
||||
class TestCtrlBase(unittest.TestCase):
|
||||
|
||||
def ctrl_command(self):
|
||||
raise Exception("Needs to be implemented by a subclass")
|
||||
|
||||
def ctrl_app(self):
|
||||
raise Exception("Needs to be implemented by a subclass")
|
||||
|
||||
def setUp(self):
|
||||
osmo_ctrl_cmd = self.ctrl_command()[:]
|
||||
config_index = osmo_ctrl_cmd.index('-c')
|
||||
if config_index:
|
||||
cfi = config_index + 1
|
||||
osmo_ctrl_cmd[cfi] = os.path.join(confpath, osmo_ctrl_cmd[cfi])
|
||||
|
||||
try:
|
||||
self.proc = osmoutil.popen_devnull(osmo_ctrl_cmd)
|
||||
except OSError:
|
||||
print >> sys.stderr, "Current directory: %s" % os.getcwd()
|
||||
print >> sys.stderr, "Consider setting -b"
|
||||
time.sleep(2)
|
||||
|
||||
appstring = self.ctrl_app()[2]
|
||||
appport = self.ctrl_app()[0]
|
||||
self.connect("127.0.0.1", appport)
|
||||
self.next_id = 1000
|
||||
|
||||
def tearDown(self):
|
||||
self.disconnect()
|
||||
osmoutil.end_proc(self.proc)
|
||||
|
||||
def disconnect(self):
|
||||
if not (self.sock is None):
|
||||
self.sock.close()
|
||||
|
||||
def connect(self, host, port):
|
||||
if verbose:
|
||||
print "Connecting to host %s:%i" % (host, port)
|
||||
|
||||
retries = 30
|
||||
while True:
|
||||
try:
|
||||
sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sck.setblocking(1)
|
||||
sck.connect((host, port))
|
||||
except IOError:
|
||||
retries -= 1
|
||||
if retries <= 0:
|
||||
raise
|
||||
time.sleep(.1)
|
||||
continue
|
||||
break
|
||||
self.sock = sck
|
||||
return sck
|
||||
|
||||
def send(self, data):
|
||||
if verbose:
|
||||
print "Sending \"%s\"" %(data)
|
||||
data = Ctrl().add_header(data)
|
||||
return self.sock.send(data) == len(data)
|
||||
|
||||
def send_set(self, var, value, id):
|
||||
setmsg = "SET %s %s %s" %(id, var, value)
|
||||
return self.send(setmsg)
|
||||
|
||||
def send_get(self, var, id):
|
||||
getmsg = "GET %s %s" %(id, var)
|
||||
return self.send(getmsg)
|
||||
|
||||
def do_set(self, var, value):
|
||||
id = self.next_id
|
||||
self.next_id += 1
|
||||
self.send_set(var, value, id)
|
||||
return self.recv_msgs()[id]
|
||||
|
||||
def do_get(self, var):
|
||||
id = self.next_id
|
||||
self.next_id += 1
|
||||
self.send_get(var, id)
|
||||
return self.recv_msgs()[id]
|
||||
|
||||
def recv_msgs(self):
|
||||
responses = {}
|
||||
data = self.sock.recv(4096)
|
||||
while (len(data)>0):
|
||||
(head, data) = IPA().split_combined(data)
|
||||
answer = Ctrl().rem_header(head)
|
||||
if verbose:
|
||||
print "Got message:", answer
|
||||
(mtype, id, msg) = answer.split(None, 2)
|
||||
id = int(id)
|
||||
rsp = {'mtype': mtype, 'id': id}
|
||||
if mtype == "ERROR":
|
||||
rsp['error'] = msg
|
||||
else:
|
||||
split = msg.split(None, 1)
|
||||
rsp['var'] = split[0]
|
||||
if len(split) > 1:
|
||||
rsp['value'] = split[1]
|
||||
else:
|
||||
rsp['value'] = None
|
||||
responses[id] = rsp
|
||||
|
||||
if verbose:
|
||||
print "Decoded replies: ", responses
|
||||
|
||||
return responses
|
||||
|
||||
|
||||
class TestCtrlBSC(TestCtrlBase):
|
||||
|
||||
def tearDown(self):
|
||||
TestCtrlBase.tearDown(self)
|
||||
os.unlink("tmp_dummy_sock")
|
||||
|
||||
def ctrl_command(self):
|
||||
return ["./src/osmo-bsc/osmo-bsc", "-r", "tmp_dummy_sock", "-c",
|
||||
"doc/examples/osmo-bsc/osmo-bsc.cfg"]
|
||||
|
||||
def ctrl_app(self):
|
||||
return (4249, "./src/osmo-bsc/osmo-bsc", "OsmoBSC", "bsc")
|
||||
|
||||
def testCtrlErrs(self):
|
||||
r = self.do_get('invalid')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
self.assertEquals(r['error'], 'Command not found')
|
||||
|
||||
r = self.do_set('rf_locked', '999')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
self.assertEquals(r['error'], 'Value failed verification.')
|
||||
|
||||
r = self.do_get('bts')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
self.assertEquals(r['error'], 'Error while parsing the index.')
|
||||
|
||||
r = self.do_get('bts.999')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
self.assertEquals(r['error'], 'Error while resolving object')
|
||||
|
||||
def testBtsLac(self):
|
||||
r = self.do_get('bts.0.location-area-code')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.location-area-code')
|
||||
self.assertEquals(r['value'], '1')
|
||||
|
||||
r = self.do_set('bts.0.location-area-code', '23')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.location-area-code')
|
||||
self.assertEquals(r['value'], '23')
|
||||
|
||||
r = self.do_get('bts.0.location-area-code')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.location-area-code')
|
||||
self.assertEquals(r['value'], '23')
|
||||
|
||||
r = self.do_set('bts.0.location-area-code', '-1')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
self.assertEquals(r['error'], 'Input not within the range')
|
||||
|
||||
def testBtsCi(self):
|
||||
r = self.do_get('bts.0.cell-identity')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.cell-identity')
|
||||
self.assertEquals(r['value'], '0')
|
||||
|
||||
r = self.do_set('bts.0.cell-identity', '23')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.cell-identity')
|
||||
self.assertEquals(r['value'], '23')
|
||||
|
||||
r = self.do_get('bts.0.cell-identity')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.cell-identity')
|
||||
self.assertEquals(r['value'], '23')
|
||||
|
||||
r = self.do_set('bts.0.cell-identity', '-1')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
self.assertEquals(r['error'], 'Input not within the range')
|
||||
|
||||
def testBtsGenerateSystemInformation(self):
|
||||
r = self.do_get('bts.0.send-new-system-informations')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
self.assertEquals(r['error'], 'Write Only attribute')
|
||||
|
||||
# No RSL links so it will fail
|
||||
r = self.do_set('bts.0.send-new-system-informations', '1')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
self.assertEquals(r['error'], 'Failed to generate SI')
|
||||
|
||||
def testBtsChannelLoad(self):
|
||||
r = self.do_set('bts.0.channel-load', '1')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
self.assertEquals(r['error'], 'Read Only attribute')
|
||||
|
||||
# No RSL link so everything is 0
|
||||
r = self.do_get('bts.0.channel-load')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['value'],
|
||||
'CCCH+SDCCH4,0,0 TCH/F,0,0 TCH/H,0,0 SDCCH8,0,0'
|
||||
+ ' TCH/F_PDCH,0,0 CCCH+SDCCH4+CBCH,0,0'
|
||||
+ ' SDCCH8+CBCH,0,0 TCH/F_TCH/H_PDCH,0,0')
|
||||
|
||||
def testBtsOmlConnectionState(self):
|
||||
"""Check OML state. It will not be connected"""
|
||||
r = self.do_set('bts.0.oml-connection-state', '1')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
self.assertEquals(r['error'], 'Read Only attribute')
|
||||
|
||||
# No RSL link so everything is 0
|
||||
r = self.do_get('bts.0.oml-connection-state')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['value'], 'disconnected')
|
||||
|
||||
def testTrxPowerRed(self):
|
||||
r = self.do_get('bts.0.trx.0.max-power-reduction')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.trx.0.max-power-reduction')
|
||||
self.assertEquals(r['value'], '20')
|
||||
|
||||
r = self.do_set('bts.0.trx.0.max-power-reduction', '22')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.trx.0.max-power-reduction')
|
||||
self.assertEquals(r['value'], '22')
|
||||
|
||||
r = self.do_get('bts.0.trx.0.max-power-reduction')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.trx.0.max-power-reduction')
|
||||
self.assertEquals(r['value'], '22')
|
||||
|
||||
r = self.do_set('bts.0.trx.0.max-power-reduction', '1')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
self.assertEquals(r['error'], 'Value must be even')
|
||||
|
||||
def testTrxArfcn(self):
|
||||
r = self.do_get('bts.0.trx.0.arfcn')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.trx.0.arfcn')
|
||||
self.assertEquals(r['value'], '871')
|
||||
|
||||
r = self.do_set('bts.0.trx.0.arfcn', '873')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.trx.0.arfcn')
|
||||
self.assertEquals(r['value'], '873')
|
||||
|
||||
r = self.do_get('bts.0.trx.0.arfcn')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.trx.0.arfcn')
|
||||
self.assertEquals(r['value'], '873')
|
||||
|
||||
r = self.do_set('bts.0.trx.0.arfcn', '2000')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
self.assertEquals(r['error'], 'Input not within the range')
|
||||
|
||||
def testRfLock(self):
|
||||
r = self.do_get('bts.0.rf_state')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.rf_state')
|
||||
self.assertEquals(r['value'], 'inoperational,unlocked,on')
|
||||
|
||||
r = self.do_set('rf_locked', '1')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'rf_locked')
|
||||
self.assertEquals(r['value'], '1')
|
||||
|
||||
time.sleep(1.5)
|
||||
|
||||
r = self.do_get('bts.0.rf_state')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.rf_state')
|
||||
self.assertEquals(r['value'], 'inoperational,locked,off')
|
||||
|
||||
r = self.do_get('rf_locked')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'rf_locked')
|
||||
self.assertEquals(r['value'], 'state=off,policy=off')
|
||||
|
||||
r = self.do_set('rf_locked', '0')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'rf_locked')
|
||||
self.assertEquals(r['value'], '0')
|
||||
|
||||
time.sleep(1.5)
|
||||
|
||||
r = self.do_get('bts.0.rf_state')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'bts.0.rf_state')
|
||||
self.assertEquals(r['value'], 'inoperational,unlocked,on')
|
||||
|
||||
r = self.do_get('rf_locked')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'rf_locked')
|
||||
self.assertEquals(r['value'], 'state=off,policy=on')
|
||||
|
||||
def testTimezone(self):
|
||||
r = self.do_get('timezone')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'timezone')
|
||||
self.assertEquals(r['value'], 'off')
|
||||
|
||||
r = self.do_set('timezone', '-2,15,2')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'timezone')
|
||||
self.assertEquals(r['value'], '-2,15,2')
|
||||
|
||||
r = self.do_get('timezone')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'timezone')
|
||||
self.assertEquals(r['value'], '-2,15,2')
|
||||
|
||||
# Test invalid input
|
||||
r = self.do_set('timezone', '-2,15,2,5,6,7')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'timezone')
|
||||
self.assertEquals(r['value'], '-2,15,2')
|
||||
|
||||
r = self.do_set('timezone', '-2,15')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
r = self.do_set('timezone', '-2')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
r = self.do_set('timezone', '1')
|
||||
|
||||
r = self.do_set('timezone', 'off')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'timezone')
|
||||
self.assertEquals(r['value'], 'off')
|
||||
|
||||
r = self.do_get('timezone')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'timezone')
|
||||
self.assertEquals(r['value'], 'off')
|
||||
|
||||
def testMcc(self):
|
||||
r = self.do_set('mcc', '23')
|
||||
r = self.do_get('mcc')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'mcc')
|
||||
self.assertEquals(r['value'], '23')
|
||||
|
||||
r = self.do_set('mcc', '023')
|
||||
r = self.do_get('mcc')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'mcc')
|
||||
self.assertEquals(r['value'], '23')
|
||||
|
||||
def testMnc(self):
|
||||
r = self.do_set('mnc', '9')
|
||||
r = self.do_get('mnc')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'mnc')
|
||||
self.assertEquals(r['value'], '9')
|
||||
|
||||
r = self.do_set('mnc', '09')
|
||||
r = self.do_get('mnc')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'mnc')
|
||||
self.assertEquals(r['value'], '9')
|
||||
|
||||
|
||||
def testMccMncApply(self):
|
||||
# Test some invalid input
|
||||
r = self.do_set('mcc-mnc-apply', 'WRONG')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
|
||||
r = self.do_set('mcc-mnc-apply', '1,')
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
|
||||
r = self.do_set('mcc-mnc-apply', '200,3')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'mcc-mnc-apply')
|
||||
self.assertEquals(r['value'], 'Tried to drop the BTS')
|
||||
|
||||
# Set it again
|
||||
r = self.do_set('mcc-mnc-apply', '200,3')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'mcc-mnc-apply')
|
||||
self.assertEquals(r['value'], 'Nothing changed')
|
||||
|
||||
# Change it
|
||||
r = self.do_set('mcc-mnc-apply', '200,4')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'mcc-mnc-apply')
|
||||
self.assertEquals(r['value'], 'Tried to drop the BTS')
|
||||
|
||||
# Change it
|
||||
r = self.do_set('mcc-mnc-apply', '201,4')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'mcc-mnc-apply')
|
||||
self.assertEquals(r['value'], 'Tried to drop the BTS')
|
||||
|
||||
# Verify
|
||||
r = self.do_get('mnc')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'mnc')
|
||||
self.assertEquals(r['value'], '4')
|
||||
|
||||
r = self.do_get('mcc')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'mcc')
|
||||
self.assertEquals(r['value'], '201')
|
||||
|
||||
# Change it
|
||||
r = self.do_set('mcc-mnc-apply', '202,03')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'mcc-mnc-apply')
|
||||
self.assertEquals(r['value'], 'Tried to drop the BTS')
|
||||
|
||||
r = self.do_get('mnc')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'mnc')
|
||||
self.assertEquals(r['value'], '3')
|
||||
|
||||
r = self.do_get('mcc')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'mcc')
|
||||
self.assertEquals(r['value'], '202')
|
||||
|
||||
class TestCtrlNAT(TestCtrlBase):
|
||||
|
||||
def ctrl_command(self):
|
||||
return ["./src/osmo-bsc_nat/osmo-bsc_nat", "-c",
|
||||
"doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"]
|
||||
|
||||
def ctrl_app(self):
|
||||
return (4250, "./src/osmo-bsc_nat/osmo-bsc_nat", "OsmoNAT", "nat")
|
||||
|
||||
def testAccessList(self):
|
||||
r = self.do_get('net.0.bsc_cfg.0.access-list-name')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'net')
|
||||
self.assertEquals(r['value'], None)
|
||||
|
||||
r = self.do_set('net.0.bsc_cfg.0.access-list-name', 'bla')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'net')
|
||||
self.assertEquals(r['value'], 'bla')
|
||||
|
||||
r = self.do_get('net.0.bsc_cfg.0.access-list-name')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'net')
|
||||
self.assertEquals(r['value'], 'bla')
|
||||
|
||||
r = self.do_set('net.0.bsc_cfg.0.no-access-list-name', '1')
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'net')
|
||||
self.assertEquals(r['value'], None)
|
||||
|
||||
r = self.do_get('net.0.bsc_cfg.0.access-list-name')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'net')
|
||||
self.assertEquals(r['value'], None)
|
||||
|
||||
def testAccessListManagement(self):
|
||||
r = self.do_set("net.0.add.allow.access-list.404", "abc")
|
||||
self.assertEquals(r['mtype'], 'ERROR')
|
||||
|
||||
r = self.do_set("net.0.add.allow.access-list.bla", "^234$")
|
||||
self.assertEquals(r['mtype'], 'SET_REPLY')
|
||||
self.assertEquals(r['var'], 'net.0.add.allow.access-list.bla')
|
||||
self.assertEquals(r['value'], 'IMSI allow added to access list')
|
||||
|
||||
# TODO.. find a way to actually see if this rule has been
|
||||
# added. e.g. by implementing a get for the list.
|
||||
|
||||
class TestCtrlSGSN(TestCtrlBase):
|
||||
def ctrl_command(self):
|
||||
return ["./src/gprs/osmo-sgsn", "-c",
|
||||
"doc/examples/osmo-sgsn/osmo-sgsn.cfg"]
|
||||
|
||||
def ctrl_app(self):
|
||||
return (4251, "./src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn")
|
||||
|
||||
def testListSubscribers(self):
|
||||
# TODO. Add command to mark a subscriber as active
|
||||
r = self.do_get('subscriber-list-active-v1')
|
||||
self.assertEquals(r['mtype'], 'GET_REPLY')
|
||||
self.assertEquals(r['var'], 'subscriber-list-active-v1')
|
||||
self.assertEquals(r['value'], None)
|
||||
|
||||
def add_bsc_test(suite, workdir):
|
||||
if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")):
|
||||
print("Skipping the BSC test")
|
||||
return
|
||||
test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlBSC)
|
||||
suite.addTest(test)
|
||||
|
||||
def add_nat_test(suite, workdir):
|
||||
if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc_nat/osmo-bsc_nat")):
|
||||
print("Skipping the NAT test")
|
||||
return
|
||||
test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlNAT)
|
||||
suite.addTest(test)
|
||||
|
||||
def add_sgsn_test(suite, workdir):
|
||||
if not os.path.isfile(os.path.join(workdir, "src/gprs/osmo-sgsn")):
|
||||
print("Skipping the SGSN test")
|
||||
return
|
||||
test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlSGSN)
|
||||
suite.addTest(test)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
workdir = '.'
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-v", "--verbose", dest="verbose",
|
||||
action="store_true", help="verbose mode")
|
||||
parser.add_argument("-p", "--pythonconfpath", dest="p",
|
||||
help="searchpath for config")
|
||||
parser.add_argument("-w", "--workdir", dest="w",
|
||||
help="Working directory")
|
||||
args = parser.parse_args()
|
||||
|
||||
verbose_level = 1
|
||||
if args.verbose:
|
||||
verbose_level = 2
|
||||
verbose = True
|
||||
|
||||
if args.w:
|
||||
workdir = args.w
|
||||
|
||||
if args.p:
|
||||
confpath = args.p
|
||||
|
||||
print "confpath %s, workdir %s" % (confpath, workdir)
|
||||
os.chdir(workdir)
|
||||
print "Running tests for specific control commands"
|
||||
suite = unittest.TestSuite()
|
||||
add_bsc_test(suite, workdir)
|
||||
add_nat_test(suite, workdir)
|
||||
add_sgsn_test(suite, workdir)
|
||||
res = unittest.TextTestRunner(verbosity=verbose_level).run(suite)
|
||||
sys.exit(len(res.errors) + len(res.failures))
|
@@ -20,10 +20,12 @@ AM_LDFLAGS = \
|
||||
EXTRA_DIST = \
|
||||
mgcp_test.ok \
|
||||
mgcp_transcoding_test.ok \
|
||||
mgcpgw_client_test.ok \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
mgcp_test \
|
||||
mgcpgw_client_test \
|
||||
$(NULL)
|
||||
if BUILD_MGCP_TRANSCODING
|
||||
noinst_PROGRAMS += \
|
||||
@@ -59,3 +61,16 @@ mgcp_transcoding_test_LDADD = \
|
||||
$(LIBRARY_GSM) \
|
||||
-lm \
|
||||
$(NULL)
|
||||
|
||||
mgcpgw_client_test_SOURCES = \
|
||||
mgcpgw_client_test.c \
|
||||
$(NULL)
|
||||
|
||||
mgcpgw_client_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBRARY_DL) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBRARY_GSM) \
|
||||
$(NULL)
|
||||
|
@@ -489,11 +489,11 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
static void test_values(void)
|
||||
{
|
||||
/* Check that NONE disables all output */
|
||||
OSMO_ASSERT((MGCP_CONN_NONE & MGCP_CONN_RECV_SEND) == 0);
|
||||
OSMO_ASSERT((MGCP_CONN_NONE & MGCP_CONN_RECV_SEND) == 0)
|
||||
|
||||
/* Check that LOOPBACK enables all output */
|
||||
OSMO_ASSERT((MGCP_CONN_LOOPBACK & MGCP_CONN_RECV_SEND) ==
|
||||
MGCP_CONN_RECV_SEND);
|
||||
MGCP_CONN_RECV_SEND)
|
||||
}
|
||||
|
||||
|
||||
@@ -541,8 +541,7 @@ static void test_messages(void)
|
||||
if (msg)
|
||||
printf("%s failed '%s'\n", t->name, (char *) msg->data);
|
||||
} else if (strcmp((char *) msg->data, t->exp_resp) != 0)
|
||||
printf("%s failed.\nExpected:\n%s\nGot:\n%s\n",
|
||||
t->name, t->exp_resp, (char *) msg->data);
|
||||
printf("%s failed '%s'\n", t->name, (char *) msg->data);
|
||||
msgb_free(msg);
|
||||
|
||||
if (dummy_packets)
|
||||
@@ -1212,7 +1211,7 @@ const struct log_info log_info = {
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *msgb_ctx = msgb_talloc_ctx_init(NULL, 0);
|
||||
msgb_talloc_ctx_init(NULL, 0);
|
||||
osmo_init_logging(&log_info);
|
||||
|
||||
test_strline();
|
||||
@@ -1231,9 +1230,6 @@ int main(int argc, char **argv)
|
||||
test_no_name();
|
||||
test_osmux_cid();
|
||||
|
||||
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
|
||||
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
|
||||
talloc_free(msgb_ctx);
|
||||
printf("Done\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user