Compare commits
64 Commits
Author | SHA1 | Date |
---|---|---|
Vadim Yanitskiy | aafbfd3144 | |
Oliver Smith | d639836123 | |
Oliver Smith | 22091130e2 | |
Oliver Smith | 58f3e032d0 | |
Max | e3dd323579 | |
Vadim Yanitskiy | 6122400451 | |
Neels Hofmeyr | a8a5bcf2ed | |
Andreas Eversberg | ef0b0fd368 | |
Neels Hofmeyr | d50637eec6 | |
Neels Hofmeyr | e148da87ed | |
Neels Hofmeyr | da2c2207b2 | |
Pau Espin | a6fedff8a6 | |
Oliver Smith | 47e6130cae | |
Vadim Yanitskiy | a0a5f7de75 | |
Oliver Smith | 89180de32e | |
Harald Welte | 95312c53c2 | |
Harald Welte | a935cbec7c | |
arehbein | c65f13af46 | |
Pau Espin | ec505be5ee | |
Keith Whyte | 44b4a05032 | |
Max | 8120b11eaa | |
Pau Espin | 3f4625586f | |
Oliver Smith | c0af8f90f9 | |
Harald Welte | 2267178db3 | |
Harald Welte | bf3b705a2b | |
Pau Espin | b3d5007a53 | |
Pau Espin | 03bc63a1da | |
Vadim Yanitskiy | bd66804082 | |
Vadim Yanitskiy | f3eb44f54b | |
Vadim Yanitskiy | 5e178d2c55 | |
Keith Whyte | 364f237b42 | |
Oliver Smith | 1c76aadeed | |
Neels Hofmeyr | e7d05f40ba | |
Neels Hofmeyr | 0294e38243 | |
Neels Hofmeyr | 97c7916892 | |
Pau Espin Pedrol | d9e57e5f08 | |
Pau Espin Pedrol | 9484ad9a87 | |
Pau Espin Pedrol | d2a85888aa | |
Pau Espin Pedrol | 462ec86ff3 | |
Oliver Smith | ad353b885e | |
Alexander Couzens | 1bd26902d2 | |
Alexander Couzens | 0f27b1bd39 | |
Alexander Couzens | ef646b07e2 | |
Alexander Couzens | 3b95463d09 | |
Oliver Smith | e590dc15f4 | |
Pau Espin | 05eea1ef0a | |
Pau Espin | cebab664a8 | |
Pau Espin | b6cb607d12 | |
Alexander Couzens | f678caa819 | |
Matt Johnson | 52b2afce2c | |
Vadim Yanitskiy | 39a2e19309 | |
Harald Welte | 79d76480ac | |
Oliver Smith | e02b4dc84c | |
Oliver Smith | d02c8f72b1 | |
Oliver Smith | afb705309f | |
Pau Espin | e58ead7ac4 | |
Neels Hofmeyr | 037c6c1ba8 | |
Oliver Smith | 18cf9f39c5 | |
Neels Hofmeyr | 2e2db3e0ad | |
Oliver Smith | b544ea396a | |
Alexander Couzens | 632156a4a8 | |
Alexander Couzens | 5e3c9d7224 | |
Philipp Maier | e49a3d714c | |
Keith Whyte | 5319d4d979 |
|
@ -5,6 +5,7 @@ Makefile
|
|||
.deps
|
||||
*.sw?
|
||||
.version
|
||||
*~
|
||||
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
|
@ -33,3 +34,5 @@ doc/manuals/generated/
|
|||
doc/manuals/osmomsc-usermanual.xml
|
||||
doc/manuals/common
|
||||
doc/manuals/build
|
||||
|
||||
contrib/osmo-sip-connector.spec
|
||||
|
|
|
@ -3,7 +3,13 @@ AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
|||
SUBDIRS = src tests contrib doc
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = git-version-gen osmoappdesc.py .version
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
README.md \
|
||||
debian \
|
||||
git-version-gen \
|
||||
osmoappdesc.py \
|
||||
$(NULL)
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
Osmo SIP Connector
|
||||
==================
|
||||
|
||||
Simple utility to map MNCC to SIP and SIP to MNCC. The VTY interface
|
||||
can be used to make configurations. The code doesn't have any RTP or
|
||||
transcoding support.
|
||||
|
||||
Call identities can be either the MSISDN or the IMSI of the subscriber.
|
||||
|
||||
|
||||
Requirements of Equipment
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* DTMF need to be sent using SIP INFO messages. DTMF in RTP is not
|
||||
supported.
|
||||
|
||||
* BTS+PBX and SIP connector+PBX must be in the same network (UDP must be
|
||||
able to flow directly between these elements)
|
||||
|
||||
* No handover support.
|
||||
|
||||
* IP based BTS (e.g. Sysmocom sysmoBTS but no Siemens BS11)
|
||||
|
||||
* No emergency calls
|
||||
|
||||
Limitations
|
||||
^^^^^^^^^^^
|
||||
|
||||
* PT of RTP needs to match the one used by the BTS. E.g. AMR needs to use
|
||||
the same PT as the BTS. This is because rtp_payload2 is not yet supported
|
||||
by the osmo-bts software.
|
||||
|
||||
* AMR SDP file doesn't include the mode-set params and allowed codec modes.
|
||||
This needs to be configured in some way.
|
|
@ -0,0 +1,82 @@
|
|||
osmo-sip-connector - Osmocom SIP connector
|
||||
==========================================
|
||||
|
||||
This implements an interface between the MNCC (Mobile Network Call Control)
|
||||
interface of OsmoMSC (and also previously OsmoNITB) and SIP.
|
||||
|
||||
Call identities can be either the MSISDN or the IMSI of the subscriber.
|
||||
|
||||
Requirements of Equipment
|
||||
-------------------------
|
||||
|
||||
* DTMF need to be sent using SIP INFO messages. DTMF in RTP is not
|
||||
supported.
|
||||
* BTS+PBX and SIP connector+PBX must be in the same network (UDP must be
|
||||
able to flow directly between these elements)
|
||||
* No handover support.
|
||||
* IP based BTS (e.g. Sysmocom sysmoBTS but no Siemens BS11)
|
||||
* No emergency calls
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
* PT of RTP needs to match the one used by the BTS. E.g. AMR needs to use
|
||||
the same PT as the BTS. This is because rtp_payload2 is not yet supported
|
||||
by the osmo-bts software.
|
||||
|
||||
* AMR SDP file doesn't include the mode-set params and allowed codec modes.
|
||||
This needs to be configured in some way.
|
||||
|
||||
Homepage
|
||||
--------
|
||||
|
||||
You can find the osmo-sip-connector issue tracker and wiki online at
|
||||
<https://osmocom.org/projects/osmo-sip-conector> and <https://osmocom.org/projects/osmo-sip-conector/wiki>
|
||||
|
||||
|
||||
GIT Repository
|
||||
--------------
|
||||
|
||||
You can clone from the official osmo-msc.git repository using
|
||||
|
||||
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-sip-connector
|
||||
|
||||
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-sip-connector>
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
User Manuals and VTY reference manuals are [optionally] built in PDF form
|
||||
as part of the build process.
|
||||
|
||||
Pre-rendered PDF version of the current "master" can be found at
|
||||
[User Manual](https://downloads.osmocom.org/docs/latest/osmosipconnector-usermanual.pdf)
|
||||
as well as the [VTY Reference Manual](https://downloads.osmocom.org/docs/latest/osmosipconnector-vty-reference.pdf)
|
||||
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
Discussions related to osmo-sip-connector are happening on the
|
||||
openbsc@lists.osmocom.org mailing list, please see
|
||||
<https://lists.osmocom.org/mailman/listinfo/openbsc> for subscription
|
||||
options and the list archive.
|
||||
|
||||
Please observe the [Osmocom Mailing List
|
||||
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
|
||||
when posting.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Our coding standards are described at
|
||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
|
||||
|
||||
We use a Gerrit based patch submission/review process for managing
|
||||
contributions. Please see
|
||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for
|
||||
more details
|
||||
|
||||
The current patch queue for osmo-sip-connector can be seen at
|
||||
<https://gerrit.osmocom.org/#/q/project:osmo-sip-connector+status:open>
|
|
@ -0,0 +1,9 @@
|
|||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
|
||||
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
|
||||
# In short:
|
||||
# LIBVERSION=c:r:a
|
||||
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
|
||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
|
||||
# If any interfaces have been added since the last public release: c:r:a + 1.
|
||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
|
||||
#library what description / commit summary line
|
|
@ -18,13 +18,15 @@ dnl include release helper
|
|||
RELMAKE='-include osmo-release.mk'
|
||||
AC_SUBST([RELMAKE])
|
||||
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
AC_PROG_CC
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
|
||||
PKG_CHECK_MODULES(SOFIASIP, sofia-sip-ua-glib >= 1.12.0)
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
|
|
|
@ -28,7 +28,6 @@ export PATH="$inst/bin:$PATH"
|
|||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
if [ "$WITH_MANUALS" = "1" ]; then
|
||||
osmo-build-dep.sh osmo-gsm-manuals
|
||||
CONFIG="--enable-manuals"
|
||||
fi
|
||||
|
||||
|
@ -45,12 +44,12 @@ autoreconf --install --force
|
|||
$MAKE $PARALLEL_MAKE
|
||||
$MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck \
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck \
|
||||
|| cat-testlogs.sh
|
||||
|
||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||
make -C "$base/doc/manuals" publish
|
||||
fi
|
||||
|
||||
$MAKE maintainer-clean
|
||||
$MAKE $PARALLEL_MAKE maintainer-clean
|
||||
osmo-clean-workspace.sh
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
[Unit]
|
||||
Description=Osmo SIP Connector
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
StateDirectory=osmocom
|
||||
WorkingDirectory=%S/osmocom
|
||||
User=osmocom
|
||||
Group=osmocom
|
||||
ExecStart=/usr/bin/osmo-sip-connector -c /etc/osmocom/osmo-sip-connector.cfg
|
||||
RestartSec=2
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@ container:
|
|||
docker build -t osmo-freeswitch-pbx:latest .
|
||||
|
||||
run:
|
||||
docker run -it --name=osmo-freeswitch-pbx \
|
||||
docker run --rm -it --name=osmo-freeswitch-pbx \
|
||||
-p 5060:5060/udp -p 6000-6020:6000-6020/udp \
|
||||
--rm=true osmo-freeswitch-pbx:latest
|
||||
osmo-freeswitch-pbx:latest
|
||||
|
||||
stop:
|
||||
docker rm -f osmo-freeswitch-pbx
|
||||
|
|
|
@ -24,6 +24,6 @@ Build:
|
|||
|
||||
Run:
|
||||
|
||||
docker run yourimagename:tag
|
||||
docker run --rm yourimagename:tag
|
||||
|
||||
SIP is exposed on 5060 of your port and audio on 6000-6020
|
||||
|
|
|
@ -1,3 +1,125 @@
|
|||
osmo-sip-connector (1.6.3) unstable; urgency=medium
|
||||
|
||||
[ arehbein ]
|
||||
* Transition to use of 'telnet_init_default'
|
||||
|
||||
[ Harald Welte ]
|
||||
* doc: fix typo
|
||||
* doc: Expand the virtually empty user manual with some basics
|
||||
|
||||
[ Oliver Smith ]
|
||||
* sip: tweak log for release caused by status >= 300
|
||||
* debian: set compat level to 10
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* tests: $(BUILT_SOURCES) is not defined, depend on osmo-sip-connector
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 17:24:26 +0200
|
||||
|
||||
osmo-sip-connector (1.6.2) unstable; urgency=medium
|
||||
|
||||
[ Max ]
|
||||
* Set working directory in systemd service file
|
||||
|
||||
[ Keith Whyte ]
|
||||
* Set MNCC_F_GCR field when the gcr is present
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 17:47:28 +0100
|
||||
|
||||
osmo-sip-connector (1.6.1) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Add TODO-RELEASE
|
||||
|
||||
[ Harald Welte ]
|
||||
* update git URLs (git -> https; gitea)
|
||||
* Convert + Expand README file
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib/testpbx: fix docker run --rm linter error
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 18:29:48 +0200
|
||||
|
||||
osmo-sip-connector (1.6.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* fix use-after-free in SIP re-INVITE
|
||||
* SIP re-INVITE: fix media connection change detection
|
||||
* SIP re-INVITE: log media connection
|
||||
|
||||
[ Oliver Smith ]
|
||||
* debian/control: remove dh-systemd build-depend
|
||||
|
||||
[ Keith ]
|
||||
* MNCC v8: Implement Basic Support for Global Call Reference.
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* sip: fix memory leak (x_gcr) in send_invite()
|
||||
* sip: use osmo_{enc,dec}_gcr() from libosmocore
|
||||
* mncc: rework passing GCR over the MNCC interface
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 14:52:09 +0100
|
||||
|
||||
osmo-sip-connector (1.5.0) unstable; urgency=medium
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* debian/control: change maintainer to the Osmocom team / mailing list
|
||||
|
||||
[ Matt Johnson ]
|
||||
* sip: Specify invite contact tag
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* Revert "sip: Specify invite contact tag"
|
||||
* mncc: remove callref argument from mncc_write()
|
||||
* mncc: add return code for write/send function
|
||||
* mncc: Introduce mncc_rtp_write similiar to mncc_write
|
||||
* mncc: mncc_create_remote_leg: use mncc_write instead of write direct to the fd
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* mncc: Support IPv6 addresses (new version mncc 7)
|
||||
* sdp: Don't select addresses failing to parse
|
||||
* contrib/jenkins: Enable parallel make in make distcheck
|
||||
* mncc: Log write error code
|
||||
* .gitignore: Get rid of new autofoo tmp files
|
||||
* mncc: Fix write len
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib/jenkins: don't build osmo-gsm-manuals
|
||||
* configure.ac: set -std=gnu11
|
||||
|
||||
-- Pau Espin Pedrol <pespin@espeweb.net> Tue, 23 Feb 2021 13:42:08 +0100
|
||||
|
||||
osmo-sip-connector (1.4.1) unstable; urgency=medium
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib: import RPM spec
|
||||
* contrib: integrate RPM spec
|
||||
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
|
||||
|
||||
-- Harald Welte <laforge@osmocom.org> Thu, 13 Aug 2020 12:38:16 +0200
|
||||
|
||||
osmo-sip-connector (1.4.0) unstable; urgency=medium
|
||||
|
||||
[ Keith ]
|
||||
* coverity: Address issue found by coverity
|
||||
|
||||
[ Philipp Maier ]
|
||||
* mncc: check fd before closing a connection
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* .service: require to ran after network-online.target
|
||||
* exit if the sip agent failed to start
|
||||
|
||||
[ Oliver Smith ]
|
||||
* mncc.c: fix gsm_mncc_rtp size checks
|
||||
* osmoappdesc.py: switch to python 3
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* MNCC v6: add optional SDP to the socket protocol
|
||||
* logging from sofia: add missing newline
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 02 Jan 2020 21:35:01 +0100
|
||||
|
||||
osmo-sip-connector (1.3.0) unstable; urgency=medium
|
||||
|
||||
[ Keith ]
|
||||
|
|
|
@ -1 +1 @@
|
|||
7
|
||||
10
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
Source: osmo-sip-connector
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Holger Hans Peter Freyther <holger@moiji-mobile.com>
|
||||
Build-Depends: debhelper (>= 7.0.0~),
|
||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||
Build-Depends: debhelper (>= 10),
|
||||
autotools-dev,
|
||||
pkg-config,
|
||||
libsofia-sip-ua-glib-dev,
|
||||
libsofia-sip-ua-dev,
|
||||
dh-systemd (>= 1.5),
|
||||
dh-autoreconf,
|
||||
libosmocore-dev (>= 1.0.0),
|
||||
osmo-gsm-manuals-dev
|
||||
libosmocore-dev (>= 1.9.0),
|
||||
osmo-gsm-manuals-dev (>= 1.5.0)
|
||||
Standards-Version: 3.8.4
|
||||
Homepage: https://osmocom.org/projects/osmo-sip-conector/wiki
|
||||
Vcs-Git: git://git.osmocom.org/osmo-sip-connector.git
|
||||
Vcs-Browser: http://git.osmocom.org/osmo-sip-connector
|
||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-sip-connector
|
||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-sip-connector
|
||||
|
||||
Package: osmo-sip-connector
|
||||
Architecture: any
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
# Create the osmocom group and user (if it doesn't exist yet)
|
||||
if ! getent group osmocom >/dev/null; then
|
||||
groupadd --system osmocom
|
||||
fi
|
||||
if ! getent passwd osmocom >/dev/null; then
|
||||
useradd \
|
||||
--system \
|
||||
--gid osmocom \
|
||||
--home-dir /var/lib/osmocom \
|
||||
--shell /sbin/nologin \
|
||||
--comment "Open Source Mobile Communications" \
|
||||
osmocom
|
||||
fi
|
||||
|
||||
# Fix permissions of previous (root-owned) install (OS#4107)
|
||||
if dpkg --compare-versions "$2" le "1.7.0"; then
|
||||
if [ -e /etc/osmocom/osmo-sip-connector.cfg ]; then
|
||||
chown -v osmocom:osmocom /etc/osmocom/osmo-sip-connector.cfg
|
||||
chmod -v 0660 /etc/osmocom/osmo-sip-connector.cfg
|
||||
fi
|
||||
|
||||
if [ -d /etc/osmocom ]; then
|
||||
chown -v root:osmocom /etc/osmocom
|
||||
chmod -v 2775 /etc/osmocom
|
||||
fi
|
||||
|
||||
mkdir -p /var/lib/osmocom
|
||||
chown -R -v osmocom:osmocom /var/lib/osmocom
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# dh_installdeb(1) will replace this with shell code automatically
|
||||
# generated by other debhelper scripts.
|
||||
#DEBHELPER#
|
|
@ -1,3 +1,11 @@
|
|||
log stderr
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
|
||||
app
|
||||
mncc
|
||||
socket-path /tmp/msc_mncc
|
||||
|
|
|
@ -8,7 +8,7 @@ cover aspects of configuring and running OsmoSIPConnector.
|
|||
=== About OsmoSIPConnector
|
||||
|
||||
OsmoSIPConnector translates between Mobile Network Call Control (MNCC)
|
||||
used in the GSM newtwork and Voice over IP SIP call control messages so that
|
||||
used in the GSM network and Voice over IP SIP call control messages so that
|
||||
speech calls can traverse through the mobile network to SIP and vice versa. It
|
||||
has the following interfaces:
|
||||
|
||||
|
@ -16,6 +16,27 @@ has the following interfaces:
|
|||
- SIP towards the PBX
|
||||
- The Osmocom typical telnet VTY interface.
|
||||
|
||||
The SIP implemented by osmo-sip-connector can be characterized as follows:
|
||||
|
||||
Only a SIP trunk is supported; it will appear to the remote SIP server (PBX) like
|
||||
another PBX (or a public network) interfaced via a trunk. Specifically, this means
|
||||
there is no SIP REGISTER or any form of authentication supported. You
|
||||
will need to configure the SIP peer to implicitly authorize the trunk by
|
||||
its IP address / port.
|
||||
|
||||
osmo-sip-connector handles only the signaling translation between GSM CC
|
||||
and SIP, but does not handle RTP. The RTP user plane is passed
|
||||
transparently from the MSC-colocated osmo-mgw to the SIP side. This also
|
||||
means that no transcoding is performed. The RTP streams contain whatever
|
||||
cellular specific codec you have configured your network to use for this
|
||||
call (FR, EFR, HR, AMR). Hence, **the SIP peer must support the
|
||||
codec[s] you have configured on your MSC/BSC**
|
||||
|
||||
As the osmo-sip-connector attaches to the external MNCC socket of
|
||||
OsmoMSC, running osmo-sip-connector will disable the internal call
|
||||
routing of OsmoMSC, see the related OsmoMSC documentation. All mobile
|
||||
originated calls originating in GSM will be passed to the SIP connector.
|
||||
|
||||
Find the OsmoSIPConnector issue tracker and wiki online at
|
||||
|
||||
- https://osmocom.org/projects/osmo-sip-connector
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# (C) 2016 by Holger Hans Peter Freyther
|
||||
|
||||
|
|
17
src/call.c
17
src/call.c
|
@ -172,3 +172,20 @@ const char *call_leg_state(struct call_leg *leg)
|
|||
return "Unknown call type";
|
||||
}
|
||||
}
|
||||
|
||||
void call_leg_rx_sdp(struct call_leg *leg, const char *rx_sdp)
|
||||
{
|
||||
/* If no SDP was received, keep whatever SDP was previously seen. */
|
||||
if (!rx_sdp || !*rx_sdp || !strncmp(leg->rx_sdp, rx_sdp, sizeof(leg->rx_sdp))) {
|
||||
LOGP(DAPP, LOGL_DEBUG, "call(%u) leg(0x%p) no new SDP in %s\n", leg->call->id, leg,
|
||||
osmo_quote_str(rx_sdp, -1));
|
||||
LOGP(DAPP, LOGL_DEBUG, "call(%u) leg(0x%p) keep stored SDP=%s\n", leg->call->id, leg,
|
||||
osmo_quote_str(leg->rx_sdp, -1));
|
||||
return;
|
||||
}
|
||||
LOGP(DAPP, LOGL_DEBUG, "call(%u) leg(0x%p) received new SDP=%s\n", leg->call->id, leg, osmo_quote_str(rx_sdp, -1));
|
||||
LOGP(DAPP, LOGL_DEBUG, "call(%u) leg(0x%p) replaced old SDP=%s\n", leg->call->id, leg,
|
||||
osmo_quote_str(leg->rx_sdp, -1));
|
||||
OSMO_STRLCPY_ARRAY(leg->rx_sdp, rx_sdp);
|
||||
leg->rx_sdp_changed = true;
|
||||
}
|
||||
|
|
20
src/call.h
20
src/call.h
|
@ -5,8 +5,10 @@
|
|||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/gsm29205.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
struct sip_agent;
|
||||
struct mncc_connection;
|
||||
|
@ -30,6 +32,10 @@ struct call {
|
|||
|
||||
const char *source;
|
||||
const char *dest;
|
||||
|
||||
/* Global Call Reference */
|
||||
struct osmo_gcr_parsed gcr;
|
||||
bool gcr_present;
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -49,11 +55,19 @@ struct call_leg {
|
|||
/**
|
||||
* RTP data
|
||||
*/
|
||||
uint32_t ip;
|
||||
uint16_t port;
|
||||
struct sockaddr_storage addr;
|
||||
uint32_t payload_type;
|
||||
uint32_t payload_msg_type;
|
||||
|
||||
/* SDP as received for this call leg. If this is an MNCC call leg, contains the SDP most recently received in an
|
||||
* MNCC message; if this is a SIP call leg, contains the SDP most recently received in a SIP message. If no SDP
|
||||
* was received yet, this string is empty. Otherwise a nul terminated string. */
|
||||
char rx_sdp[1024];
|
||||
/* If the contents of rx_sdp[] changes, set rx_sdp_changed = true. When the other call leg transmits the next
|
||||
* message, it can decide whether to include SDP because there is new information, or whether to omit SDP
|
||||
* because it was already sent identically earlier. */
|
||||
bool rx_sdp_changed;
|
||||
|
||||
/**
|
||||
* Remote started to ring/alert
|
||||
*/
|
||||
|
@ -157,6 +171,8 @@ void calls_init(void);
|
|||
|
||||
struct call_leg *call_leg_other(struct call_leg *leg);
|
||||
|
||||
void call_leg_rx_sdp(struct call_leg *cl, const char *rx_sdp);
|
||||
|
||||
void call_leg_release(struct call_leg *leg);
|
||||
|
||||
|
||||
|
|
|
@ -147,8 +147,7 @@ int main(int argc, char **argv)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
rc = telnet_init_dynif(tall_mncc_ctx, NULL,
|
||||
vty_get_bind_addr(), OSMO_VTY_PORT_MNCC_SIP);
|
||||
rc = telnet_init_default(tall_mncc_ctx, NULL, OSMO_VTY_PORT_MNCC_SIP);
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
|
||||
|
@ -158,9 +157,11 @@ int main(int argc, char **argv)
|
|||
/* sofia sip */
|
||||
sip_agent_init(&g_app.sip.agent, &g_app);
|
||||
rc = sip_agent_start(&g_app.sip.agent);
|
||||
if (rc < 0)
|
||||
if (rc < 0) {
|
||||
LOGP(DSIP, LOGL_ERROR,
|
||||
"Failed to initialize SIP. Running broken\n");
|
||||
"Failed to initialize SIP\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
calls_init();
|
||||
app_setup(&g_app);
|
||||
|
|
252
src/mncc.c
252
src/mncc.c
|
@ -130,43 +130,60 @@ static void mncc_fill_header(struct gsm_mncc *mncc, uint32_t msg_type, uint32_t
|
|||
}
|
||||
}
|
||||
|
||||
static void mncc_write(struct mncc_connection *conn, struct gsm_mncc *mncc, uint32_t callref)
|
||||
static int mncc_write(struct mncc_connection *conn, struct gsm_mncc *mncc)
|
||||
{
|
||||
int rc;
|
||||
|
||||
LOGP(DMNCC, LOGL_DEBUG, "tx MNCC %s with SDP=%s\n", osmo_mncc_name(mncc->msg_type),
|
||||
osmo_quote_str(mncc->sdp, -1));
|
||||
|
||||
/*
|
||||
* TODO: we need to put cause in here for release or such? shall we return a
|
||||
* static struct?
|
||||
*/
|
||||
rc = write(conn->fd.fd, mncc, sizeof(*mncc));
|
||||
LOGP(DMNCC, LOGL_DEBUG, "MNCC sent message type: %s\n", osmo_mncc_name(mncc->msg_type));
|
||||
if (rc != sizeof(*mncc)) {
|
||||
LOGP(DMNCC, LOGL_ERROR, "Failed to send message for call(%u)\n", callref);
|
||||
LOGP(DMNCC, LOGL_ERROR, "Failed to send message for call(%u)\n", mncc->callref);
|
||||
close_connection(conn);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void mncc_send(struct mncc_connection *conn, uint32_t msg_type, uint32_t callref)
|
||||
static int mncc_send(struct mncc_connection *conn, uint32_t msg_type, uint32_t callref)
|
||||
{
|
||||
struct gsm_mncc mncc = { 0, };
|
||||
|
||||
mncc_fill_header(&mncc, msg_type, callref);
|
||||
mncc_write(conn, &mncc, callref);
|
||||
return mncc_write(conn, &mncc);
|
||||
}
|
||||
|
||||
static void mncc_rtp_send(struct mncc_connection *conn, uint32_t msg_type, uint32_t callref)
|
||||
static int mncc_rtp_write(struct mncc_connection *conn, struct gsm_mncc_rtp *rtp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
LOGP(DMNCC, LOGL_DEBUG, "tx MNCC %s with SDP=%s\n", osmo_mncc_name(rtp->msg_type),
|
||||
osmo_quote_str(rtp->sdp, -1));
|
||||
|
||||
rc = write(conn->fd.fd, rtp, sizeof(*rtp));
|
||||
if (rc != sizeof(*rtp)) {
|
||||
LOGP(DMNCC, LOGL_ERROR, "Failed to send message for call(%u): %d\n", rtp->callref, rc);
|
||||
close_connection(conn);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mncc_rtp_send(struct mncc_connection *conn, uint32_t msg_type, uint32_t callref, const char *sdp)
|
||||
{
|
||||
struct gsm_mncc_rtp mncc = { 0, };
|
||||
|
||||
mncc.msg_type = msg_type;
|
||||
mncc.callref = callref;
|
||||
if (sdp)
|
||||
OSMO_STRLCPY_ARRAY(mncc.sdp, sdp);
|
||||
|
||||
rc = write(conn->fd.fd, &mncc, sizeof(mncc));
|
||||
if (rc != sizeof(mncc)) {
|
||||
LOGP(DMNCC, LOGL_ERROR, "Failed to send message for call(%u)\n", callref);
|
||||
close_connection(conn);
|
||||
}
|
||||
return mncc_rtp_write(conn, &mncc);
|
||||
}
|
||||
|
||||
/* Send a MNCC_RTP_CONNECT to the MSC for the given call legs */
|
||||
|
@ -174,7 +191,7 @@ static bool send_rtp_connect(struct mncc_call_leg *leg, struct call_leg *other)
|
|||
{
|
||||
struct gsm_mncc_rtp mncc = { 0, };
|
||||
int rc;
|
||||
char ip_addr[INET_ADDRSTRLEN];
|
||||
char ip_addr[INET6_ADDRSTRLEN];
|
||||
|
||||
/*
|
||||
* Send RTP CONNECT and we handle the general failure of it by
|
||||
|
@ -182,17 +199,20 @@ static bool send_rtp_connect(struct mncc_call_leg *leg, struct call_leg *other)
|
|||
*/
|
||||
mncc.msg_type = MNCC_RTP_CONNECT;
|
||||
mncc.callref = leg->callref;
|
||||
mncc.ip = ntohl(other->ip);
|
||||
mncc.port = other->port;
|
||||
mncc.addr = other->addr;
|
||||
mncc.payload_type = other->payload_type;
|
||||
|
||||
/* Forward whichever SDP was last received on the other call leg */
|
||||
OSMO_STRLCPY_ARRAY(mncc.sdp, other->rx_sdp);
|
||||
|
||||
/*
|
||||
* FIXME: mncc.payload_msg_type should already be compatible.. but
|
||||
* payload_type should be different..
|
||||
*/
|
||||
struct in_addr net = { .s_addr = other->ip };
|
||||
inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr));
|
||||
LOGP(DMNCC, LOGL_DEBUG, "SEND rtp_connect: IP=(%s) PORT=(%u)\n", ip_addr, mncc.port);
|
||||
rc = write(leg->conn->fd.fd, &mncc, sizeof(mncc));
|
||||
LOGP(DMNCC, LOGL_DEBUG, "SEND rtp_connect: IP=(%s) PORT=(%u)\n",
|
||||
osmo_sockaddr_ntop((const struct sockaddr*)&other->addr, ip_addr),
|
||||
osmo_sockaddr_port((const struct sockaddr*)&other->addr));
|
||||
rc = mncc_rtp_write(leg->conn, &mncc);
|
||||
if (rc != sizeof(mncc)) {
|
||||
LOGP(DMNCC, LOGL_ERROR, "Failed to send message for call(%u)\n",
|
||||
leg->callref);
|
||||
|
@ -211,11 +231,21 @@ static void update_rtp(struct call_leg *_leg) {
|
|||
if (_leg->type == CALL_TYPE_MNCC) {
|
||||
leg = (struct mncc_call_leg *) _leg;
|
||||
struct call_leg *other = call_leg_other(&leg->base);
|
||||
if (!other)
|
||||
goto ret_release;
|
||||
send_rtp_connect(leg, other);
|
||||
} else {
|
||||
} else if (_leg->type == CALL_TYPE_SIP) {
|
||||
leg = (struct mncc_call_leg *) call_leg_other(_leg);
|
||||
if (!leg)
|
||||
goto ret_release;
|
||||
send_rtp_connect(leg, _leg);
|
||||
} else {
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
return;
|
||||
ret_release:
|
||||
LOGP(DMNCC, LOGL_ERROR, "Failed to find other leg.\n");
|
||||
_leg->release_call(_leg);
|
||||
}
|
||||
|
||||
|
||||
|
@ -255,14 +285,15 @@ static void mncc_call_leg_ring(struct call_leg *_leg)
|
|||
out_mncc.progress.location = GSM48_CAUSE_LOC_PRN_S_LU; /* Private network serving the local user */
|
||||
out_mncc.progress.descr = GSM48_PROGR_IN_BAND_AVAIL; /* In-band information or appropriate pattern now available */
|
||||
|
||||
mncc_write(leg->conn, &out_mncc, leg->callref);
|
||||
mncc_write(leg->conn, &out_mncc);
|
||||
|
||||
/*
|
||||
* If we have remote IP/port let's connect it already.
|
||||
* FIXME: We would like to keep this as recvonly...
|
||||
*/
|
||||
other_leg = call_leg_other(&leg->base);
|
||||
if (other_leg && other_leg->port != 0 && other_leg->ip != 0)
|
||||
if (other_leg && other_leg->addr.ss_family != AF_UNSPEC &&
|
||||
osmo_sockaddr_port((const struct sockaddr *)&other_leg->addr) != 0)
|
||||
send_rtp_connect(leg, other_leg);
|
||||
}
|
||||
|
||||
|
@ -314,8 +345,12 @@ static void mncc_call_leg_release(struct call_leg *_leg)
|
|||
/* Close the MNCC connection/socket */
|
||||
static void close_connection(struct mncc_connection *conn)
|
||||
{
|
||||
if (conn->fd.fd < 0)
|
||||
return;
|
||||
|
||||
osmo_fd_unregister(&conn->fd);
|
||||
close(conn->fd.fd);
|
||||
conn->fd.fd = -1;
|
||||
osmo_timer_schedule(&conn->reconnect, 5, 0);
|
||||
conn->state = MNCC_DISCONNECTED;
|
||||
if (conn->on_disconnect)
|
||||
|
@ -379,11 +414,16 @@ static void check_rtp_connect(struct mncc_connection *conn, const char *buf, int
|
|||
leg = mncc_find_leg_not_released(rtp->callref);
|
||||
if (!leg) {
|
||||
LOGP(DMNCC, LOGL_ERROR, "leg(%u) can not be found\n", rtp->callref);
|
||||
return mncc_send(conn, MNCC_REJ_REQ, rtp->callref);
|
||||
mncc_send(conn, MNCC_REJ_REQ, rtp->callref);
|
||||
return;
|
||||
}
|
||||
|
||||
call_leg_rx_sdp(&leg->base, rtp->sdp);
|
||||
|
||||
/* extract information about where the RTP is */
|
||||
if (rtp->ip != 0 || rtp->port != 0 || rtp->payload_type != 0)
|
||||
if (rtp->addr.ss_family != AF_UNSPEC ||
|
||||
osmo_sockaddr_port((const struct sockaddr *)&rtp->addr) != 0 ||
|
||||
rtp->payload_type != 0)
|
||||
return;
|
||||
|
||||
LOGP(DMNCC, LOGL_ERROR, "leg(%u) rtp connect failed\n", rtp->callref);
|
||||
|
@ -398,7 +438,7 @@ static void check_rtp_create(struct mncc_connection *conn, const char *buf, int
|
|||
{
|
||||
const struct gsm_mncc_rtp *rtp;
|
||||
struct mncc_call_leg *leg;
|
||||
char ip_addr[INET_ADDRSTRLEN];
|
||||
char ip_addr[INET6_ADDRSTRLEN];
|
||||
|
||||
if (rc < sizeof(*rtp)) {
|
||||
LOGP(DMNCC, LOGL_ERROR, "gsm_mncc_rtp of wrong size %d < %zu\n",
|
||||
|
@ -410,21 +450,22 @@ static void check_rtp_create(struct mncc_connection *conn, const char *buf, int
|
|||
leg = mncc_find_leg_not_released(rtp->callref);
|
||||
if (!leg) {
|
||||
LOGP(DMNCC, LOGL_ERROR, "call(%u) can not be found\n", rtp->callref);
|
||||
return mncc_send(conn, MNCC_REJ_REQ, rtp->callref);
|
||||
mncc_send(conn, MNCC_REJ_REQ, rtp->callref);
|
||||
return;
|
||||
}
|
||||
|
||||
/* extract information about where the RTP is */
|
||||
leg->base.ip = htonl(rtp->ip);
|
||||
leg->base.port = rtp->port;
|
||||
leg->base.addr = rtp->addr;
|
||||
leg->base.payload_type = rtp->payload_type;
|
||||
leg->base.payload_msg_type = rtp->payload_msg_type;
|
||||
call_leg_rx_sdp(&leg->base, rtp->sdp);
|
||||
|
||||
/* TODO.. now we can continue with the call */
|
||||
struct in_addr net = { .s_addr = leg->base.ip };
|
||||
inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr));
|
||||
LOGP(DMNCC, LOGL_INFO,
|
||||
"RTP continue leg(%u) ip(%s), port(%u) pt(%u) ptm(%u)\n",
|
||||
leg->callref, ip_addr, leg->base.port,
|
||||
leg->callref,
|
||||
osmo_sockaddr_ntop((const struct sockaddr*)&leg->base.addr, ip_addr),
|
||||
osmo_sockaddr_port((const struct sockaddr*)&leg->base.addr),
|
||||
leg->base.payload_type, leg->base.payload_msg_type);
|
||||
stop_cmd_timer(leg, MNCC_RTP_CREATE);
|
||||
continue_call(leg);
|
||||
|
@ -457,8 +498,11 @@ static void check_setup(struct mncc_connection *conn, const char *buf, int rc)
|
|||
const struct gsm_mncc_number *called;
|
||||
struct call *call;
|
||||
struct mncc_call_leg *leg;
|
||||
struct call_leg *other_leg;
|
||||
const char *sdp = NULL;
|
||||
struct osmo_gcr_parsed gcr;
|
||||
|
||||
if (rc != sizeof(*data)) {
|
||||
if (rc < sizeof(*data)) {
|
||||
LOGP(DMNCC, LOGL_ERROR, "gsm_mncc of wrong size %d vs. %zu\n",
|
||||
rc, sizeof(*data));
|
||||
return close_connection(conn);
|
||||
|
@ -473,7 +517,8 @@ static void check_setup(struct mncc_connection *conn, const char *buf, int rc)
|
|||
LOGP(DMNCC, LOGL_ERROR,
|
||||
"MNCC leg(%u) without called addr fields(%u)\n",
|
||||
data->callref, data->fields);
|
||||
return mncc_send(conn, MNCC_REJ_REQ, data->callref);
|
||||
mncc_send(conn, MNCC_REJ_REQ, data->callref);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Emergency without a called number present. Use the standard "emergency" number. */
|
||||
|
@ -484,14 +529,26 @@ static void check_setup(struct mncc_connection *conn, const char *buf, int rc)
|
|||
LOGP(DMNCC, LOGL_ERROR,
|
||||
"MNCC leg(%u) without calling addr fields(%u)\n",
|
||||
data->callref, data->fields);
|
||||
return mncc_send(conn, MNCC_REJ_REQ, data->callref);
|
||||
mncc_send(conn, MNCC_REJ_REQ, data->callref);
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO.. bearer caps and better audio handling */
|
||||
if (!continue_setup(conn, data)) {
|
||||
LOGP(DMNCC, LOGL_ERROR,
|
||||
"MNCC screening parameters failed leg(%u)\n", data->callref);
|
||||
return mncc_send(conn, MNCC_REJ_REQ, data->callref);
|
||||
mncc_send(conn, MNCC_REJ_REQ, data->callref);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Decode the Global Call Reference (if present) */
|
||||
if (data->fields & MNCC_F_GCR) {
|
||||
if (osmo_dec_gcr(&gcr, data->gcr, sizeof(data->gcr)) < 0) {
|
||||
LOGP(DMNCC, LOGL_ERROR,
|
||||
"MNCC leg(%u) failed to parse GCR\n", data->callref);
|
||||
mncc_send(conn, MNCC_REJ_REQ, data->callref);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create an RTP port and then allocate a call */
|
||||
|
@ -499,7 +556,8 @@ static void check_setup(struct mncc_connection *conn, const char *buf, int rc)
|
|||
if (!call) {
|
||||
LOGP(DMNCC, LOGL_ERROR,
|
||||
"MNCC leg(%u) failed to allocate call\n", data->callref);
|
||||
return mncc_send(conn, MNCC_REJ_REQ, data->callref);
|
||||
mncc_send(conn, MNCC_REJ_REQ, data->callref);
|
||||
return;
|
||||
}
|
||||
|
||||
leg = (struct mncc_call_leg *) call->initial;
|
||||
|
@ -514,13 +572,25 @@ static void check_setup(struct mncc_connection *conn, const char *buf, int rc)
|
|||
memcpy(&leg->called, called, sizeof(leg->called));
|
||||
memcpy(&leg->calling, &data->calling, sizeof(leg->calling));
|
||||
memcpy(&leg->imsi, data->imsi, sizeof(leg->imsi));
|
||||
call_leg_rx_sdp(&leg->base, data->sdp);
|
||||
|
||||
if (data->fields & MNCC_F_GCR) {
|
||||
leg->base.call->gcr_present = true;
|
||||
leg->base.call->gcr = gcr;
|
||||
}
|
||||
|
||||
LOGP(DMNCC, LOGL_INFO,
|
||||
"Created call(%u) with MNCC leg(%u) IMSI(%.16s)\n",
|
||||
call->id, leg->callref, data->imsi);
|
||||
|
||||
other_leg = call_leg_other(&leg->base);
|
||||
if (other_leg && *other_leg->rx_sdp && other_leg->rx_sdp_changed) {
|
||||
sdp = other_leg->rx_sdp;
|
||||
other_leg->rx_sdp_changed = false;
|
||||
}
|
||||
|
||||
start_cmd_timer(leg, MNCC_RTP_CREATE);
|
||||
mncc_rtp_send(conn, MNCC_RTP_CREATE, data->callref);
|
||||
mncc_rtp_send(conn, MNCC_RTP_CREATE, data->callref, sdp);
|
||||
}
|
||||
|
||||
/*! Find MNCC Call leg by given MNCC message
|
||||
|
@ -534,7 +604,7 @@ static struct mncc_call_leg *find_leg(struct mncc_connection *conn,
|
|||
{
|
||||
struct mncc_call_leg *leg;
|
||||
|
||||
if (rc != sizeof(**mncc)) {
|
||||
if (rc < sizeof(**mncc)) {
|
||||
LOGP(DMNCC, LOGL_ERROR, "gsm_mncc of wrong size %d vs. %zu\n",
|
||||
rc, sizeof(**mncc));
|
||||
close_connection(conn);
|
||||
|
@ -624,6 +694,8 @@ static void check_stp_cmpl_ind(struct mncc_connection *conn, const char *buf, in
|
|||
if (!leg)
|
||||
return;
|
||||
|
||||
call_leg_rx_sdp(&leg->base, data->sdp);
|
||||
|
||||
LOGP(DMNCC, LOGL_INFO, "leg(%u) is now connected.\n", leg->callref);
|
||||
stop_cmd_timer(leg, MNCC_SETUP_COMPL_IND);
|
||||
leg->state = MNCC_CC_CONNECTED;
|
||||
|
@ -653,17 +725,28 @@ static void check_cnf_ind(struct mncc_connection *conn, const char *buf, int rc)
|
|||
{
|
||||
const struct gsm_mncc *data;
|
||||
struct mncc_call_leg *leg;
|
||||
struct call_leg *other_leg;
|
||||
const char *sdp = NULL;
|
||||
|
||||
leg = find_leg(conn, buf, rc, &data);
|
||||
if (!leg)
|
||||
return;
|
||||
|
||||
call_leg_rx_sdp(&leg->base, data->sdp);
|
||||
|
||||
/* Forward SDP received from the other side */
|
||||
other_leg = call_leg_other(&leg->base);
|
||||
if (other_leg && *other_leg->rx_sdp && other_leg->rx_sdp_changed) {
|
||||
sdp = other_leg->rx_sdp;
|
||||
other_leg->rx_sdp_changed = false;
|
||||
}
|
||||
|
||||
LOGP(DMNCC, LOGL_DEBUG,
|
||||
"leg(%u) confirmed. creating RTP socket.\n",
|
||||
leg->callref);
|
||||
|
||||
start_cmd_timer(leg, MNCC_RTP_CREATE);
|
||||
mncc_rtp_send(conn, MNCC_RTP_CREATE, data->callref);
|
||||
mncc_rtp_send(conn, MNCC_RTP_CREATE, data->callref, sdp);
|
||||
}
|
||||
|
||||
static void check_alrt_ind(struct mncc_connection *conn, const char *buf, int rc)
|
||||
|
@ -676,6 +759,8 @@ static void check_alrt_ind(struct mncc_connection *conn, const char *buf, int rc
|
|||
if (!leg)
|
||||
return;
|
||||
|
||||
call_leg_rx_sdp(&leg->base, data->sdp);
|
||||
|
||||
LOGP(DMNCC, LOGL_DEBUG,
|
||||
"leg(%u) is alerting.\n", leg->callref);
|
||||
|
||||
|
@ -754,6 +839,8 @@ static void check_stp_cnf(struct mncc_connection *conn, const char *buf, int rc)
|
|||
if (!leg)
|
||||
return;
|
||||
|
||||
call_leg_rx_sdp(&leg->base, data->sdp);
|
||||
|
||||
LOGP(DMNCC, LOGL_DEBUG, "leg(%u) setup completed\n", leg->callref);
|
||||
|
||||
other_leg = call_leg_other(&leg->base);
|
||||
|
@ -783,6 +870,8 @@ static void check_dtmf_start(struct mncc_connection *conn, const char *buf, int
|
|||
if (!leg)
|
||||
return;
|
||||
|
||||
call_leg_rx_sdp(&leg->base, data->sdp);
|
||||
|
||||
LOGP(DMNCC, LOGL_DEBUG, "leg(%u) DTMF key=%c\n", leg->callref, data->keypad);
|
||||
|
||||
other_leg = call_leg_other(&leg->base);
|
||||
|
@ -792,7 +881,7 @@ static void check_dtmf_start(struct mncc_connection *conn, const char *buf, int
|
|||
mncc_fill_header(&out_mncc, MNCC_START_DTMF_RSP, leg->callref);
|
||||
out_mncc.fields |= MNCC_F_KEYPAD;
|
||||
out_mncc.keypad = data->keypad;
|
||||
mncc_write(conn, &out_mncc, leg->callref);
|
||||
mncc_write(conn, &out_mncc);
|
||||
}
|
||||
|
||||
static void check_dtmf_stop(struct mncc_connection *conn, const char *buf, int rc)
|
||||
|
@ -805,12 +894,14 @@ static void check_dtmf_stop(struct mncc_connection *conn, const char *buf, int r
|
|||
if (!leg)
|
||||
return;
|
||||
|
||||
call_leg_rx_sdp(&leg->base, data->sdp);
|
||||
|
||||
LOGP(DMNCC, LOGL_DEBUG, "leg(%u) DTMF key=%c\n", leg->callref, data->keypad);
|
||||
|
||||
mncc_fill_header(&out_mncc, MNCC_STOP_DTMF_RSP, leg->callref);
|
||||
out_mncc.fields |= MNCC_F_KEYPAD;
|
||||
out_mncc.keypad = data->keypad;
|
||||
mncc_write(conn, &out_mncc, leg->callref);
|
||||
mncc_write(conn, &out_mncc);
|
||||
}
|
||||
|
||||
static void check_hello(struct mncc_connection *conn, const char *buf, int rc)
|
||||
|
@ -839,6 +930,7 @@ int mncc_create_remote_leg(struct mncc_connection *conn, struct call *call)
|
|||
{
|
||||
struct mncc_call_leg *leg;
|
||||
struct gsm_mncc mncc = { 0, };
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
leg = talloc_zero(call, struct mncc_call_leg);
|
||||
|
@ -884,17 +976,34 @@ int mncc_create_remote_leg(struct mncc_connection *conn, struct call *call)
|
|||
OSMO_STRLCPY_ARRAY(mncc.called.number, call->dest);
|
||||
}
|
||||
|
||||
/* Encode the Global Call Reference (if present) */
|
||||
if (call->gcr_present) {
|
||||
msg = msgb_alloc(sizeof(mncc.gcr), "MNCC GCR");
|
||||
if (msg == NULL || (rc = osmo_enc_gcr(msg, &call->gcr)) == 0) {
|
||||
LOGP(DMNCC, LOGL_ERROR, "MNCC leg(%u) failed to encode GCR\n", call->id);
|
||||
} else {
|
||||
memcpy(&mncc.gcr[0], msg->data, rc);
|
||||
mncc.fields |= MNCC_F_GCR;
|
||||
}
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
/* The call->initial leg is a SIP call leg that starts an MT call. There was SDP received in the SIP INVITE that
|
||||
* started this call. This here will be the call->remote, always forwarding the SDP that came in on
|
||||
* call->initial. */
|
||||
if (call->initial && call->initial->rx_sdp_changed) {
|
||||
OSMO_STRLCPY_ARRAY(mncc.sdp, call->initial->rx_sdp);
|
||||
call->initial->rx_sdp_changed = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO/FIXME:
|
||||
* - Determine/request channel based on offered audio codecs
|
||||
* - Screening, redirect?
|
||||
* - Synth. the bearer caps based on codecs?
|
||||
*/
|
||||
rc = write(conn->fd.fd, &mncc, sizeof(mncc));
|
||||
rc = mncc_write(conn, &mncc);
|
||||
if (rc != sizeof(mncc)) {
|
||||
LOGP(DMNCC, LOGL_ERROR, "Failed to send message leg(%u)\n",
|
||||
leg->callref);
|
||||
close_connection(conn);
|
||||
talloc_free(leg);
|
||||
return -1;
|
||||
}
|
||||
|
@ -914,6 +1023,7 @@ static void mncc_reconnect(void *data)
|
|||
LOGP(DMNCC, LOGL_ERROR, "Failed to connect(%s). Retrying\n",
|
||||
conn->app->mncc.path);
|
||||
conn->state = MNCC_DISCONNECTED;
|
||||
conn->fd.fd = -1;
|
||||
osmo_timer_schedule(&conn->reconnect, 5, 0);
|
||||
return;
|
||||
}
|
||||
|
@ -922,6 +1032,53 @@ static void mncc_reconnect(void *data)
|
|||
conn->state = MNCC_WAIT_VERSION;
|
||||
}
|
||||
|
||||
static inline void log_mncc(const char *label, const uint8_t *buf, size_t buflen)
|
||||
{
|
||||
uint32_t msg_type;
|
||||
const struct gsm_mncc *gsm_mncc;
|
||||
const struct gsm_mncc_rtp *gsm_mncc_rtp;
|
||||
const char *sdp = NULL;
|
||||
|
||||
/* Any size errors will be logged elsewhere already, so just exit here if the buffer is too small. */
|
||||
if (buflen < 4)
|
||||
return;
|
||||
memcpy(&msg_type, buf, 4);
|
||||
|
||||
switch (msg_type) {
|
||||
case MNCC_SETUP_IND:
|
||||
case MNCC_DISC_IND:
|
||||
case MNCC_REL_IND:
|
||||
case MNCC_REJ_IND:
|
||||
case MNCC_REL_CNF:
|
||||
case MNCC_SETUP_COMPL_IND:
|
||||
case MNCC_SETUP_CNF:
|
||||
case MNCC_CALL_CONF_IND:
|
||||
case MNCC_ALERT_IND:
|
||||
case MNCC_HOLD_IND:
|
||||
case MNCC_RETRIEVE_IND:
|
||||
if (buflen < sizeof(gsm_mncc))
|
||||
return;
|
||||
gsm_mncc = (void *)buf;
|
||||
sdp = gsm_mncc->sdp;
|
||||
break;
|
||||
case MNCC_RTP_CREATE:
|
||||
case MNCC_RTP_CONNECT:
|
||||
if (buflen < sizeof(gsm_mncc_rtp))
|
||||
return;
|
||||
gsm_mncc_rtp = (void *)buf;
|
||||
sdp = gsm_mncc_rtp->sdp;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (sdp)
|
||||
LOGP(DMNCC, LOGL_DEBUG, "%sMNCC %s with SDP=%s\n", label, osmo_mncc_name(msg_type),
|
||||
osmo_quote_str(sdp, -1));
|
||||
else
|
||||
LOGP(DMNCC, LOGL_DEBUG, "%sMNCC %s\n", label, osmo_mncc_name(msg_type));
|
||||
}
|
||||
|
||||
/* osmo-fd read call-back for MNCC socket: read MNCC message + dispatch it */
|
||||
static int mncc_data(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
|
@ -941,10 +1098,10 @@ static int mncc_data(struct osmo_fd *fd, unsigned int what)
|
|||
goto bad_data;
|
||||
}
|
||||
|
||||
log_mncc("rx ", (void *)buf, rc);
|
||||
|
||||
/* Handle the received MNCC message */
|
||||
memcpy(&msg_type, buf, 4);
|
||||
|
||||
LOGP(DMNCC, LOGL_DEBUG, "MNCC rcvd message type: %s\n", osmo_mncc_name(msg_type));
|
||||
|
||||
switch (msg_type) {
|
||||
case MNCC_SOCKET_HELLO:
|
||||
check_hello(conn, buf, rc);
|
||||
|
@ -1012,6 +1169,7 @@ void mncc_connection_init(struct mncc_connection *conn, struct app_config *cfg)
|
|||
conn->reconnect.data = conn;
|
||||
conn->fd.cb = mncc_data;
|
||||
conn->fd.data = conn;
|
||||
conn->fd.fd = -1;
|
||||
conn->app = cfg;
|
||||
conn->state = MNCC_DISCONNECTED;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <osmocom/gsm/mncc.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
struct gsm_network;
|
||||
struct msgb;
|
||||
|
@ -122,6 +123,7 @@ struct gsm_call {
|
|||
#define MNCC_F_CCCAP 0x0800
|
||||
#define MNCC_F_KEYPAD 0x1000
|
||||
#define MNCC_F_SIGNAL 0x2000
|
||||
#define MNCC_F_GCR 0x4000
|
||||
|
||||
struct gsm_mncc {
|
||||
/* context based information */
|
||||
|
@ -158,6 +160,12 @@ struct gsm_mncc {
|
|||
|
||||
unsigned char lchan_type;
|
||||
unsigned char lchan_mode;
|
||||
|
||||
/* Global Call Reference (encoded as per 3GPP TS 29.205) */
|
||||
uint8_t gcr[16];
|
||||
|
||||
/* A buffer to contain SDP ('\0' terminated) */
|
||||
char sdp[1024];
|
||||
};
|
||||
|
||||
struct gsm_data_frame {
|
||||
|
@ -166,7 +174,7 @@ struct gsm_data_frame {
|
|||
unsigned char data[0];
|
||||
};
|
||||
|
||||
#define MNCC_SOCK_VERSION 5
|
||||
#define MNCC_SOCK_VERSION 8
|
||||
struct gsm_mncc_hello {
|
||||
uint32_t msg_type;
|
||||
uint32_t version;
|
||||
|
@ -185,10 +193,10 @@ struct gsm_mncc_hello {
|
|||
struct gsm_mncc_rtp {
|
||||
uint32_t msg_type;
|
||||
uint32_t callref;
|
||||
uint32_t ip;
|
||||
uint16_t port;
|
||||
struct sockaddr_storage addr;
|
||||
uint32_t payload_type;
|
||||
uint32_t payload_msg_type;
|
||||
char sdp[1024];
|
||||
};
|
||||
|
||||
struct gsm_mncc_bridge {
|
||||
|
|
139
src/sdp.c
139
src/sdp.c
|
@ -32,6 +32,8 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
/*
|
||||
* Check if the media mode attribute exists in SDP, in this
|
||||
* case update the passed pointer with the media mode
|
||||
|
@ -63,11 +65,11 @@ bool sdp_get_sdp_mode(const sip_t *sip, sdp_mode_t *mode) {
|
|||
|
||||
if (!sdp->sdp_media || !sdp->sdp_media->m_mode) {
|
||||
sdp_parser_free(parser);
|
||||
return sdp_sendrecv;
|
||||
return false;
|
||||
}
|
||||
|
||||
sdp_parser_free(parser);
|
||||
*mode = sdp->sdp_media->m_mode;
|
||||
sdp_parser_free(parser);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -122,6 +124,7 @@ bool sdp_screen_sdp(const sip_t *sip)
|
|||
}
|
||||
|
||||
sdp_parser_free(parser);
|
||||
/* FIXME: osmo-sip-connector should not interfere in codecs at all */
|
||||
return false;
|
||||
|
||||
success:
|
||||
|
@ -129,6 +132,9 @@ success:
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Extract RTP address, port and payload type from SDP received in SIP message, in order to populate the legacy MNCC
|
||||
* fields, for backwards compatibility. osmo-sip-connector now always sends the entire SDP info unchanged via MNCC,
|
||||
* which obsoletes the legacy fields. But for backwards compatibility, still populate the legacy fields. */
|
||||
bool sdp_extract_sdp(struct sip_call_leg *leg, const sip_t *sip, bool any_codec)
|
||||
{
|
||||
sdp_connection_t *conn;
|
||||
|
@ -136,6 +142,7 @@ bool sdp_extract_sdp(struct sip_call_leg *leg, const sip_t *sip, bool any_codec)
|
|||
sdp_parser_t *parser;
|
||||
sdp_media_t *media;
|
||||
const char *sdp_data;
|
||||
uint16_t port;
|
||||
bool found_conn = false, found_map = false;
|
||||
|
||||
if (!sip->sip_payload || !sip->sip_payload->pl_data) {
|
||||
|
@ -159,12 +166,22 @@ bool sdp_extract_sdp(struct sip_call_leg *leg, const sip_t *sip, bool any_codec)
|
|||
}
|
||||
|
||||
for (conn = sdp->sdp_connection; conn; conn = conn->c_next) {
|
||||
struct in_addr addr;
|
||||
|
||||
if (conn->c_addrtype != sdp_addr_ip4)
|
||||
switch (conn->c_addrtype) {
|
||||
case sdp_addr_ip4:
|
||||
if (inet_pton(AF_INET, conn->c_address,
|
||||
&((struct sockaddr_in*)&leg->base.addr)->sin_addr) != 1)
|
||||
continue;
|
||||
inet_aton(conn->c_address, &addr);
|
||||
leg->base.ip = addr.s_addr;
|
||||
leg->base.addr.ss_family = AF_INET;
|
||||
break;
|
||||
case sdp_addr_ip6:
|
||||
if (inet_pton(AF_INET6, conn->c_address,
|
||||
&((struct sockaddr_in6*)&leg->base.addr)->sin6_addr) != 1)
|
||||
continue;
|
||||
leg->base.addr.ss_family = AF_INET6;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
found_conn = true;
|
||||
break;
|
||||
}
|
||||
|
@ -178,10 +195,12 @@ bool sdp_extract_sdp(struct sip_call_leg *leg, const sip_t *sip, bool any_codec)
|
|||
continue;
|
||||
|
||||
for (map = media->m_rtpmaps; map; map = map->rm_next) {
|
||||
if (!any_codec && strcasecmp(map->rm_encoding, leg->wanted_codec) != 0)
|
||||
if (!any_codec
|
||||
&& leg->wanted_codec
|
||||
&& strcasecmp(map->rm_encoding, leg->wanted_codec) != 0)
|
||||
continue;
|
||||
|
||||
leg->base.port = media->m_port;
|
||||
port = media->m_port;
|
||||
leg->base.payload_type = map->rm_pt;
|
||||
found_map = true;
|
||||
break;
|
||||
|
@ -195,23 +214,58 @@ bool sdp_extract_sdp(struct sip_call_leg *leg, const sip_t *sip, bool any_codec)
|
|||
LOGP(DSIP, LOGL_ERROR, "leg(%p) did not find %d/%d\n",
|
||||
leg, found_conn, found_map);
|
||||
sdp_parser_free(parser);
|
||||
/* FIXME: osmo-sip-connector should not interfere in codecs at all */
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (leg->base.addr.ss_family) {
|
||||
case AF_INET:
|
||||
((struct sockaddr_in*)&leg->base.addr)->sin_port = htons(port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
((struct sockaddr_in6*)&leg->base.addr)->sin6_port = htons(port);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
sdp_parser_free(parser);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* One leg has sent a SIP or MNCC message, which is now translated/forwarded to the counterpart MNCC or SIP.
|
||||
* Take as much from the source's SDP as possible, but make sure the connection mode reflects the 'mode' arg (sendrecv,
|
||||
* recvonly, sendonly, inactive).
|
||||
* For example, if the MSC sent an MNCC_SETUP_IND, the SDP from the MNCC is found in 'other', while 'leg' reflects the
|
||||
* SIP side that should receive this SDP in the SIP Invite that is being composed by the caller of this function.
|
||||
* \param leg The target for which the returned SDP is intended.
|
||||
* \param other The source of which we are to reflect the SDP.
|
||||
* \return SDP string, using 'leg' as talloc ctx.
|
||||
*/
|
||||
char *sdp_create_file(struct sip_call_leg *leg, struct call_leg *other, sdp_mode_t mode)
|
||||
{
|
||||
struct in_addr net = { .s_addr = other->ip };
|
||||
char *fmtp_str = NULL, *sdp;
|
||||
sdp_parser_t *parser;
|
||||
sdp_session_t *sdp;
|
||||
sdp_media_t *media;
|
||||
const char *sdp_data;
|
||||
sdp_printer_t *printer;
|
||||
char buf[1024];
|
||||
const char *sdp_str;
|
||||
char *ret;
|
||||
|
||||
sdp_data = other->rx_sdp;
|
||||
|
||||
if (!*sdp_data) {
|
||||
/* Legacy compat: We have not received any SDP from the other call leg. Compose some original SDP from
|
||||
* the RTP information we have. */
|
||||
char *fmtp_str = NULL;
|
||||
char *mode_attribute;
|
||||
char ip_addr[INET_ADDRSTRLEN];
|
||||
char ip_addr[INET6_ADDRSTRLEN];
|
||||
char ipv;
|
||||
|
||||
inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr));
|
||||
osmo_sockaddr_ntop((const struct sockaddr *)&other->addr, ip_addr);
|
||||
ipv = other->addr.ss_family == AF_INET6 ? '6' : '4';
|
||||
leg->wanted_codec = app_media_name(other->payload_msg_type);
|
||||
|
||||
if (strcmp(leg->wanted_codec, "AMR") == 0)
|
||||
fmtp_str = talloc_asprintf(leg, "a=fmtp:%d octet-align=1\r\n", other->payload_type);
|
||||
|
||||
|
@ -233,22 +287,65 @@ char *sdp_create_file(struct sip_call_leg *leg, struct call_leg *other, sdp_mode
|
|||
break;
|
||||
}
|
||||
|
||||
sdp = talloc_asprintf(leg,
|
||||
return talloc_asprintf(leg,
|
||||
"v=0\r\n"
|
||||
"o=Osmocom 0 0 IN IP4 %s\r\n"
|
||||
"o=Osmocom 0 0 IN IP%c %s\r\n"
|
||||
"s=GSM Call\r\n"
|
||||
"c=IN IP4 %s\r\n"
|
||||
"c=IN IP%c %s\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio %d RTP/AVP %d\r\n"
|
||||
"%s"
|
||||
"a=rtpmap:%d %s/8000\r\n"
|
||||
"%s",
|
||||
ip_addr, ip_addr,
|
||||
other->port, other->payload_type,
|
||||
ipv, ip_addr, ipv, ip_addr,
|
||||
osmo_sockaddr_port((const struct sockaddr *)&other->addr),
|
||||
other->payload_type,
|
||||
fmtp_str ? fmtp_str : "",
|
||||
other->payload_type,
|
||||
leg->wanted_codec,
|
||||
mode_attribute);
|
||||
talloc_free(fmtp_str);
|
||||
return sdp;
|
||||
}
|
||||
|
||||
/* We have received SDP from the other call leg. Forward this as-is, only apply the mode the caller requests:
|
||||
* parse SDP, set media mode, recompose. */
|
||||
/* TODO: currently, we often detect the media mode from parsing SDP, and then forward the same SDP, applying the
|
||||
* same mode to it below. It may make sense to completely skip parsing and composition: the mode is usually
|
||||
* already in the received SDP.
|
||||
* However, there are some invocations of this function (sdp_create_file()) with hardcoded modes. Take a look if
|
||||
* that is really necessary, and if not, just drop below parsing + recomposition and use the sdp_data as is.
|
||||
*/
|
||||
parser = sdp_parse(NULL, sdp_data, strlen(sdp_data), 0);
|
||||
if (!parser) {
|
||||
LOGP(DSIP, LOGL_ERROR, "leg(%p) failed to parse SDP\n", other);
|
||||
return talloc_strdup(leg, sdp_data);
|
||||
}
|
||||
|
||||
sdp = sdp_session(parser);
|
||||
if (!sdp) {
|
||||
LOGP(DSIP, LOGL_INFO, "leg(%p) no SDP session in %s, returning SDP unchanged\n", other, osmo_quote_str(sdp_data, -1));
|
||||
sdp_parser_free(parser);
|
||||
return talloc_strdup(leg, sdp_data);
|
||||
}
|
||||
|
||||
for (media = sdp->sdp_media; media; media = media->m_next)
|
||||
media->m_mode = mode;
|
||||
|
||||
printer = sdp_print(NULL, sdp, buf, sizeof(buf), sdp_f_mode_always);
|
||||
if (!printer) {
|
||||
LOGP(DSIP, LOGL_ERROR, "leg(%p) failed to print SDP\n", other);
|
||||
sdp_parser_free(parser);
|
||||
return talloc_strdup(leg, sdp_data);
|
||||
}
|
||||
|
||||
sdp_str = sdp_message(printer);
|
||||
if (!sdp_str) {
|
||||
LOGP(DSIP, LOGL_ERROR, "leg(%p) failed to print SDP: %s\n", other, sdp_printing_error(printer));
|
||||
sdp_str = sdp_data;
|
||||
}
|
||||
|
||||
ret = talloc_strdup(leg, sdp_str);
|
||||
|
||||
sdp_parser_free(parser);
|
||||
sdp_printer_free(printer);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -11,5 +11,4 @@ struct call_leg;
|
|||
bool sdp_get_sdp_mode(const sip_t *sip, sdp_mode_t *mode);
|
||||
bool sdp_screen_sdp(const sip_t *sip);
|
||||
bool sdp_extract_sdp(struct sip_call_leg *leg, const sip_t *sip, bool any_codec);
|
||||
|
||||
char *sdp_create_file(struct sip_call_leg *, struct call_leg *, sdp_mode_t mode);
|
||||
char *sdp_create_file(struct sip_call_leg *leg, struct call_leg *other, sdp_mode_t mode);
|
||||
|
|
126
src/sip.c
126
src/sip.c
|
@ -25,6 +25,8 @@
|
|||
#include "sdp.h"
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include <sofia-sip/sip_status.h>
|
||||
#include <sofia-sip/su_log.h>
|
||||
|
@ -33,6 +35,7 @@
|
|||
#include <talloc.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
extern void *tall_mncc_ctx;
|
||||
|
||||
|
@ -43,6 +46,12 @@ static void sip_dtmf_call(struct call_leg *_leg, int keypad);
|
|||
static void sip_hold_call(struct call_leg *_leg);
|
||||
static void sip_retrieve_call(struct call_leg *_leg);
|
||||
|
||||
static const char *sip_get_sdp(const sip_t *sip)
|
||||
{
|
||||
if (!sip || !sip->sip_payload)
|
||||
return NULL;
|
||||
return sip->sip_payload->pl_data;
|
||||
}
|
||||
|
||||
/* Find a SIP Call leg by given nua_handle */
|
||||
static struct sip_call_leg *sip_find_leg(nua_handle_t *nh)
|
||||
|
@ -110,10 +119,22 @@ static void new_call(struct sip_agent *agent, nua_handle_t *nh,
|
|||
struct call *call;
|
||||
struct sip_call_leg *leg;
|
||||
const char *from = NULL, *to = NULL;
|
||||
char ip_addr[INET_ADDRSTRLEN];
|
||||
char ip_addr[INET6_ADDRSTRLEN];
|
||||
bool xgcr_hdr_present = false;
|
||||
uint8_t xgcr_hdr[28] = { 0 };
|
||||
|
||||
LOGP(DSIP, LOGL_INFO, "Incoming call(%s) handle(%p)\n", sip->sip_call_id->i_id, nh);
|
||||
|
||||
sip_unknown_t *unknown_header = sip->sip_unknown;
|
||||
while (unknown_header != NULL) {
|
||||
if (!strcmp("X-Global-Call-Ref", unknown_header->un_name)) {
|
||||
osmo_hexparse(unknown_header->un_value, xgcr_hdr, sizeof(xgcr_hdr));
|
||||
xgcr_hdr_present = true;
|
||||
break;
|
||||
}
|
||||
unknown_header = unknown_header->un_next;
|
||||
}
|
||||
|
||||
if (!sdp_screen_sdp(sip)) {
|
||||
LOGP(DSIP, LOGL_ERROR, "No supported codec.\n");
|
||||
nua_respond(nh, SIP_406_NOT_ACCEPTABLE, TAG_END());
|
||||
|
@ -122,12 +143,18 @@ static void new_call(struct sip_agent *agent, nua_handle_t *nh,
|
|||
}
|
||||
|
||||
call = call_sip_create();
|
||||
if (!call) {
|
||||
LOGP(DSIP, LOGL_ERROR, "No supported codec.\n");
|
||||
nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
|
||||
OSMO_ASSERT(call);
|
||||
|
||||
/* Decode Decode the Global Call Reference (if present) */
|
||||
if (xgcr_hdr_present) {
|
||||
if (osmo_dec_gcr(&call->gcr, xgcr_hdr, sizeof(xgcr_hdr)) < 0) {
|
||||
LOGP(DSIP, LOGL_ERROR, "Failed to parse X-Global-Call-Ref.\n");
|
||||
nua_respond(nh, SIP_406_NOT_ACCEPTABLE, TAG_END());
|
||||
nua_handle_destroy(nh);
|
||||
return;
|
||||
}
|
||||
call->gcr_present = true;
|
||||
}
|
||||
|
||||
if (sip->sip_to)
|
||||
to = sip->sip_to->a_url->url_user;
|
||||
|
@ -159,11 +186,9 @@ static void new_call(struct sip_agent *agent, nua_handle_t *nh,
|
|||
call_leg_release(&leg->base);
|
||||
return;
|
||||
}
|
||||
struct in_addr net = { .s_addr = leg->base.ip };
|
||||
inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr));
|
||||
LOGP(DSIP, LOGL_INFO, "SDP Extracted: IP=(%s) PORT=(%u) PAYLOAD=(%u).\n",
|
||||
ip_addr,
|
||||
leg->base.port,
|
||||
osmo_sockaddr_ntop((const struct sockaddr *)&leg->base.addr, ip_addr),
|
||||
osmo_sockaddr_port((const struct sockaddr *)&leg->base.addr),
|
||||
leg->base.payload_type);
|
||||
|
||||
leg->base.release_call = sip_release_call;
|
||||
|
@ -177,6 +202,8 @@ static void new_call(struct sip_agent *agent, nua_handle_t *nh,
|
|||
nua_handle_bind(nh, leg);
|
||||
leg->sdp_payload = talloc_strdup(leg, sip->sip_payload->pl_data);
|
||||
|
||||
call_leg_rx_sdp(&leg->base, sip_get_sdp(sip));
|
||||
|
||||
app_route_call(call,
|
||||
talloc_strdup(leg, from),
|
||||
talloc_strdup(leg, to));
|
||||
|
@ -186,9 +213,8 @@ static void sip_handle_reinvite(struct sip_call_leg *leg, nua_handle_t *nh, cons
|
|||
|
||||
char *sdp;
|
||||
sdp_mode_t mode = sdp_sendrecv;
|
||||
uint32_t ip = leg->base.ip;
|
||||
uint16_t port = leg->base.port;
|
||||
char ip_addr[INET_ADDRSTRLEN];
|
||||
char ip_addr[INET6_ADDRSTRLEN];
|
||||
struct sockaddr_storage prev_addr = leg->base.addr;
|
||||
|
||||
LOGP(DSIP, LOGL_INFO, "re-INVITE for call %s\n", sip->sip_call_id->i_id);
|
||||
|
||||
|
@ -214,9 +240,11 @@ static void sip_handle_reinvite(struct sip_call_leg *leg, nua_handle_t *nh, cons
|
|||
return;
|
||||
}
|
||||
|
||||
struct in_addr net = { .s_addr = leg->base.ip };
|
||||
inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr));
|
||||
LOGP(DSIP, LOGL_DEBUG, "pre re-INVITE have IP:port (%s:%u)\n", ip_addr, leg->base.port);
|
||||
LOGP(DSIP, LOGL_DEBUG, "pre re-INVITE have IP:port (%s:%u)\n",
|
||||
osmo_sockaddr_ntop((struct sockaddr*)&prev_addr, ip_addr),
|
||||
osmo_sockaddr_port((struct sockaddr*)&prev_addr));
|
||||
|
||||
call_leg_rx_sdp(&leg->base, sip_get_sdp(sip));
|
||||
|
||||
if (mode == sdp_sendonly) {
|
||||
/* SIP side places call on HOLD */
|
||||
|
@ -231,13 +259,20 @@ static void sip_handle_reinvite(struct sip_call_leg *leg, nua_handle_t *nh, cons
|
|||
call_leg_release(&leg->base);
|
||||
return;
|
||||
}
|
||||
struct in_addr net = { .s_addr = leg->base.ip };
|
||||
inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr));
|
||||
LOGP(DSIP, LOGL_DEBUG, "Media IP:port in re-INVITE: (%s:%u)\n", ip_addr, leg->base.port);
|
||||
if (ip != leg->base.ip || port != leg->base.port) {
|
||||
LOGP(DSIP, LOGL_INFO, "re-INVITE changes media connection.\n");
|
||||
LOGP(DSIP, LOGL_DEBUG, "Media IP:port in re-INVITE: (%s:%u)\n",
|
||||
osmo_sockaddr_ntop((struct sockaddr*)&leg->base.addr, ip_addr),
|
||||
osmo_sockaddr_port((struct sockaddr*)&leg->base.addr));
|
||||
if (osmo_sockaddr_cmp((struct osmo_sockaddr *)&prev_addr,
|
||||
(struct osmo_sockaddr *)&leg->base.addr)) {
|
||||
LOGP(DSIP, LOGL_INFO, "re-INVITE changes media connection to %s:%u\n",
|
||||
osmo_sockaddr_ntop((struct sockaddr*)&leg->base.addr, ip_addr),
|
||||
osmo_sockaddr_port((struct sockaddr*)&leg->base.addr));
|
||||
if (other->update_rtp)
|
||||
other->update_rtp(leg->base.call->remote);
|
||||
} else {
|
||||
LOGP(DSIP, LOGL_INFO, "re-INVITE does not change media connection (%s:%u)\n",
|
||||
osmo_sockaddr_ntop((struct sockaddr*)&prev_addr, ip_addr),
|
||||
osmo_sockaddr_port((struct sockaddr*)&prev_addr));
|
||||
}
|
||||
sdp = sdp_create_file(leg, other, sdp_sendrecv);
|
||||
}
|
||||
|
@ -323,13 +358,15 @@ static int status2cause(int status)
|
|||
|
||||
void nua_callback(nua_event_t event, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
|
||||
{
|
||||
LOGP(DSIP, LOGL_DEBUG, "SIP event[%s] status(%d) phrase(%s) %p\n",
|
||||
nua_event_name(event), status, phrase, hmagic);
|
||||
LOGP(DSIP, LOGL_DEBUG, "SIP event[%s] status(%d) phrase(%s) SDP(%s) %p\n",
|
||||
nua_event_name(event), status, phrase, sip_get_sdp(sip), hmagic);
|
||||
|
||||
if (event == nua_r_invite) {
|
||||
struct sip_call_leg *leg;
|
||||
leg = (struct sip_call_leg *) hmagic;
|
||||
|
||||
call_leg_rx_sdp(&leg->base, sip_get_sdp(sip));
|
||||
|
||||
/* MT call is moving forward */
|
||||
|
||||
/* The dialogue is now confirmed */
|
||||
|
@ -350,7 +387,12 @@ void nua_callback(nua_event_t event, int status, char const *phrase, nua_t *nua,
|
|||
else if (status >= 300) {
|
||||
struct call_leg *other = call_leg_other(&leg->base);
|
||||
|
||||
LOGP(DSIP, LOGL_INFO, "INVITE got status(%d), releasing leg(%p).\n", status, leg);
|
||||
if (status < 400)
|
||||
LOGP(DSIP, LOGL_NOTICE, "INVITE got status(%d), releasing leg(%p) as redirect is not"
|
||||
" implemented\n", status, leg);
|
||||
else
|
||||
LOGP(DSIP, LOGL_ERROR, "INVITE got status(%d), releasing leg(%p)\n", status, leg);
|
||||
|
||||
nua_cancel(leg->nua_handle, TAG_END());
|
||||
nua_handle_destroy(leg->nua_handle);
|
||||
call_leg_release(&leg->base);
|
||||
|
@ -366,9 +408,11 @@ void nua_callback(nua_event_t event, int status, char const *phrase, nua_t *nua,
|
|||
* respond to the re-INVITE query. */
|
||||
if (sip->sip_payload && sip->sip_payload->pl_data) {
|
||||
struct sip_call_leg *leg = sip_find_leg(nh);
|
||||
if (leg)
|
||||
if (leg) {
|
||||
call_leg_rx_sdp(&leg->base, sip_get_sdp(sip));
|
||||
sip_handle_reinvite(leg, nh, sip);
|
||||
}
|
||||
}
|
||||
} else if (event == nua_r_bye || event == nua_r_cancel) {
|
||||
/* our bye or hang up is answered */
|
||||
struct sip_call_leg *leg = (struct sip_call_leg *) hmagic;
|
||||
|
@ -393,11 +437,13 @@ void nua_callback(nua_event_t event, int status, char const *phrase, nua_t *nua,
|
|||
|
||||
if (status == 100) {
|
||||
struct sip_call_leg *leg = sip_find_leg(nh);
|
||||
if (leg)
|
||||
if (leg) {
|
||||
call_leg_rx_sdp(&leg->base, sip_get_sdp(sip));
|
||||
sip_handle_reinvite(leg, nh, sip);
|
||||
else
|
||||
} else {
|
||||
new_call((struct sip_agent *) magic, nh, sip);
|
||||
}
|
||||
}
|
||||
} else if (event == nua_i_cancel) {
|
||||
struct sip_call_leg *leg;
|
||||
struct call_leg *other;
|
||||
|
@ -491,6 +537,7 @@ static void sip_ring_call(struct call_leg *_leg)
|
|||
OSMO_ASSERT(_leg->type == CALL_TYPE_SIP);
|
||||
leg = (struct sip_call_leg *) _leg;
|
||||
|
||||
/* 180 Ringing should not contain any SDP. */
|
||||
nua_respond(leg->nua_handle, SIP_180_RINGING, TAG_END());
|
||||
}
|
||||
|
||||
|
@ -599,6 +646,19 @@ static int send_invite(struct sip_agent *agent, struct sip_call_leg *leg,
|
|||
agent->app->sip.remote_port);
|
||||
char *sdp = sdp_create_file(leg, other, sdp_sendrecv);
|
||||
|
||||
/* Encode the Global Call Reference (if present) */
|
||||
char *x_gcr = NULL;
|
||||
|
||||
if (leg->base.call->gcr_present) {
|
||||
struct msgb *msg = msgb_alloc(16, "SIP GCR");
|
||||
|
||||
if (msg != NULL && osmo_enc_gcr(msg, &leg->base.call->gcr) > 0)
|
||||
x_gcr = talloc_asprintf(leg, "X-Global-Call-Ref: %s", msgb_hexdump(msg));
|
||||
else
|
||||
LOGP(DSIP, LOGL_ERROR, "Failed to encode GCR for leg(%p)\n", leg);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
leg->state = SIP_CC_INITIAL;
|
||||
leg->dir = SIP_DIR_MT;
|
||||
nua_invite(leg->nua_handle,
|
||||
|
@ -606,6 +666,7 @@ static int send_invite(struct sip_agent *agent, struct sip_call_leg *leg,
|
|||
SIPTAG_TO_STR(to),
|
||||
NUTAG_MEDIA_ENABLE(0),
|
||||
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
|
||||
TAG_IF(x_gcr, SIPTAG_HEADER_STR(x_gcr)),
|
||||
SIPTAG_PAYLOAD_STR(sdp),
|
||||
TAG_END());
|
||||
|
||||
|
@ -613,6 +674,7 @@ static int send_invite(struct sip_agent *agent, struct sip_call_leg *leg,
|
|||
talloc_free(from);
|
||||
talloc_free(to);
|
||||
talloc_free(sdp);
|
||||
talloc_free(x_gcr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -666,7 +728,19 @@ static void sip_logger(void *stream, char const *fmt, va_list ap)
|
|||
* the log handler call-back function, so we have no clue what log level the
|
||||
* currently logged message was sent for :( As a result, we can only use one
|
||||
* hard-coded LOGL_NOTICE here */
|
||||
osmo_vlogp(DSIP, LOGL_NOTICE, "", 0, 0, fmt, ap);
|
||||
if (!log_check_level(DSIP, LOGL_NOTICE))
|
||||
return;
|
||||
/* The sofia-sip log line *sometimes* lacks a terminating '\n'. Add it. */
|
||||
char log_line[256];
|
||||
int rc = vsnprintf(log_line, sizeof(log_line), fmt, ap);
|
||||
if (rc > 0) {
|
||||
/* since we're explicitly checking for sizeof(log_line), we can use vsnprintf()'s return value (which,
|
||||
* alone, would possibly cause writing past the buffer's end). */
|
||||
char *end = log_line + OSMO_MIN(rc, sizeof(log_line) - 2);
|
||||
osmo_strlcpy(end, "\n", 2);
|
||||
LOGP(DSIP, LOGL_NOTICE, "%s", log_line);
|
||||
} else
|
||||
LOGP(DSIP, LOGL_NOTICE, "unknown logging from sip\n");
|
||||
}
|
||||
|
||||
void sip_agent_init(struct sip_agent *agent, struct app_config *app)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
if ENABLE_EXT_TESTS
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
python-tests: $(top_builddir)/src/osmo-sip-connector
|
||||
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
else
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
python-tests:
|
||||
echo "Not running python-based tests (determined at configure-time)"
|
||||
endif
|
||||
|
||||
|
|
Loading…
Reference in New Issue