Compare commits

...

63 Commits

Author SHA1 Message Date
Oliver Smith d639836123 debian/postinst: add checks, be verbose
Do not attempt to change permissions/ownership if the package gets
upgraded from a version higher than the next release.

Do not fail if the user deleted the config file.

Be verbose when changing permissions.

Related: OS#4107
Change-Id: I2b01a7625cf66fbb7d203f939ddcc1cbab43cf33
2024-05-14 15:21:06 +02:00
Oliver Smith 22091130e2 contrib: remove rpm spec file
Related: https://osmocom.org/news/255
Related: OS#6446
Change-Id: I9e7a3beb861faab1b6852aa5b57847c590986976
2024-05-08 14:41:10 +02:00
Oliver Smith 58f3e032d0 .deb/.rpm: various fixes related to non-root
* Explicitly chown /var/lib/osmocom to osmocom:osmocom, instead of
  relying on systemd to do it when the service starts up. This does not
  work with the systemd versions in debian 10 and almalinux 8.
* deb: Use "useradd" instead of the interactive "adduser" perl script
  from Debian. This makes it consistent with how we do it in rpm, and
  avoids the dependency on "adduser".
* deb: Consistently use tabs through the file, instead of mixing tabs
  and spaces.
* deb: Remove support for the "dpkg-statoverride --list" logic. This
  seems to be a rather obscure feature to override permissions for
  certain files or directories, for which it does not seem to be a good
  idea to make the postinst script less maintainable. Something similar
  can be achieved by using your own Osmocom config file in a different
  path with different permissions.

Related: OS#4107
Change-Id: Ib129217f6aff713d1d0e7aa831b4b54823e9bade
2024-04-26 16:23:08 +02:00
Max e3dd323579 .deb/.rpm: add osmocom user during package install
Create osmocom user & group during package installation.
Fix the configuration dir/files permission to match.

Related: OS#4107
Tweaked-By: Oliver Smith <osmith@sysmocom.de>
Change-Id: I7826fedb1b055a282954e5774615245092ddae5d
2024-04-24 11:52:59 +02:00
Vadim Yanitskiy 6122400451 build: include README.md into the release tarball
Change-Id: I4e1f0fd213fcc9e3b7e4a34f3380504696b2e08b
2024-01-26 23:38:07 +07:00
Neels Hofmeyr a8a5bcf2ed verbosely log MNCC and SDP
Change-Id: Ie923117929c6b79b1eb61e5a9f02a169edabc599
2024-01-25 02:55:58 +01:00
Andreas Eversberg ef0b0fd368 Use uniform log format for default config files
Related: OS#6272
Change-Id: I1b1ebca276184158d585815a62da3b91fa9c3081
2023-12-01 12:52:17 +01:00
Neels Hofmeyr d50637eec6 forward SDP between SIP and MNCC
We have added support for sending SDP via MNCC a long time ago, but so
far the SDP section remained empty. Now, implement actually forwarding
SDP codec information between SIP and MNCC.

The aim is to let the MSC know about all codec choices the remote SIP
call leg has to offer, so that finding a codec match between local and
remote call leg becomes possible.

Store any SDP info contained in incoming SIP and MNCC messages, and send
the stored SDP to the other call leg in all outgoing SIP and MNCC
messages.

In sdp_create_file(), we used to compose fixed SDP -- instead, take the
other call leg's SDP as-is, only make sure to modify the mode (e.g.
"a=sendrecv") to reflect the current call hold state.

The RTP address and codec info in the MNCC structures is now essentially
a redundant / possibly less accurate copy of the SDP info, but leave all
of that as-is, for backwards compat.

There is codec checking that may reject unexpected codecs. The
overall/future aim is to leave all codec checking up to the MSC, but so
far just leave current behaviour unchanged, until we notice problems.

Related: SYS#5066
Related: osmo-ttcn3-hacks Ib2ae8449e673f5027f01d428d3718c006f76d93e
Change-Id: I3df5d06f38ee2d122706a9ebffde7db4f2bd6bae
2023-09-15 04:21:06 +02:00
Neels Hofmeyr e148da87ed sdp_get_sdp_mode(): fix wrong return value
Change-Id: Id27eb82a018293cf54d068877dc222e1c7eab253
2023-09-13 22:49:07 +02:00
Neels Hofmeyr da2c2207b2 drop misleading error message
call_sip_create() returns NULL only when memory allocation failed. Do
not log "No supported codec", just assert that a call was allocated,
like we usually do in such cases.

Related: SYS#5066
Change-Id: Id7fd16b92bbd10e886892d0c425cf1bc5d5429cf
2023-09-13 22:49:07 +02:00
Pau Espin a6fedff8a6 Bump version: 1.6.2.6-47e6-dirty → 1.6.3
Change-Id: I5263fa4d1c2edcd12a5521b5cc03753482ff76cc
2023-09-12 17:24:27 +02:00
Oliver Smith 47e6130cae debian: set compat level to 10
Related: OS#5958
Change-Id: Ib45ad7c44cdefa4c5acab2da09f24796253a133d
2023-04-25 16:48:33 +02:00
Vadim Yanitskiy a0a5f7de75 tests: $(BUILT_SOURCES) is not defined, depend on osmo-sip-connector
Change-Id: I3aeaace814a209dade5323f30a5a91e4a9d73595
2023-03-30 02:13:00 +07:00
Oliver Smith 89180de32e sip: tweak log for release caused by status >= 300
Make it easier to spot when the sip server answers an invite with
anything that would cause osmo-sip-connector to release the call.

Related: SYS#6386
Change-Id: I64889d6ce174dc17d44d85aac12e7ee6e6b06164
2023-03-23 13:03:15 +01:00
Harald Welte 95312c53c2 doc: Expand the virtually empty user manual with some basics
Change-Id: Id42904a183b045eefac15a94139221a3bc65ecdd
2023-03-21 20:00:04 +01:00
Harald Welte a935cbec7c doc: fix typo
Change-Id: Ie001645e9b44cb5e16090813ca4f053118ac39a4
2023-03-21 19:58:22 +01:00
arehbein c65f13af46 Transition to use of 'telnet_init_default'
Related: OS#5809
Change-Id: Icc57c68337d55c6594c1c36e9bf41624d11dab0a
2023-02-25 17:48:58 +01:00
Pau Espin ec505be5ee Bump version: 1.6.1.2-44b4-dirty → 1.6.2
Change-Id: Id050891c8465cbb3bb81c9907de509009b7131ff
2023-02-07 17:47:29 +01:00
Keith Whyte 44b4a05032 Set MNCC_F_GCR field when the gcr is present
Commit bd66804082 established that the
MNCC field flag MNCC_F_GCR should be set when the GCR is present,
but did not set it.

Change-Id: Idad3210055a0d25be6220301b965e3d4c7323692
2022-10-03 02:36:54 +01:00
Max 8120b11eaa Set working directory in systemd service file
By default systemd will execute service with root directory
(or home directory for user instance) which might result in
attempts to create files in unexpected place. Let's set it
to 'osmocom' subdir of state directory
(/var/lib for system instance) instead.

Related: OS#4821
Change-Id: Idad82186d6ef7f00487cf162306b0733886fc129
2022-09-09 22:49:04 +07:00
Pau Espin 3f4625586f Bump version: 1.6.0.4-c0af → 1.6.1
Change-Id: I5e247667968d80be72bfe890c2afb3a25c7c105c
2022-06-28 18:29:49 +02:00
Oliver Smith c0af8f90f9 contrib/testpbx: fix docker run --rm linter error
Fixes: SYS#5583
Change-Id: Ie3a9caa6d1e682e0080b3acc4a68d5ad3895629f
2022-06-20 12:26:22 +02:00
Harald Welte 2267178db3 Convert + Expand README file
README.md in-line with that of other osmocom CNI projects:
* markdown syntax
* link to manuals, issue tracker, gerrit contributions, etc.

Change-Id: Ieadbbe4d2963e713d569460d053460f4f99d40eb
2022-06-18 13:41:01 +02:00
Harald Welte bf3b705a2b update git URLs (git -> https; gitea)
Change-Id: Ic45226b2de537fe4d45324fbe346bd7ed6db92fd
2022-06-18 13:36:59 +02:00
Pau Espin b3d5007a53 Add TODO-RELEASE
Change-Id: I32acbec597ff8bb79b3576f9d13ab051d5c5b938
2022-01-03 12:26:58 +01:00
Pau Espin 03bc63a1da Bump version: 1.5.0.8-bd66 → 1.6.0
Change-Id: I72fa9fdc5c6f1c5cfc6e4cb0b7d0e8ea4557b1ab
2021-11-16 14:52:09 +01:00
Vadim Yanitskiy bd66804082 mncc: rework passing GCR over the MNCC interface
Using *unpacked* 'struct osmo_gcr_parsed' in the MNCC PDUs makes
the protocol even more complicated than it currently is, and
moreover complicates implementing MNCCv8 in the ttcn3-sip-test.

Replace 'struct osmo_gcr_parsed' in 'struct gsm_mncc' with a
fixed-length buffer, which is supposed to hold the Global Call
Reference encoded as per 3GPP TS 29.205.

Check / indicate presence of GCR using the MNCC_F_GCR flag.

Change-Id: Iaff46732948f8f5d03e42f17c35cbac8a80af49b
Fixes: Id40d7e0fed9356f801b3627c118150055e7232b1
Related: OS#5164, OS#5282
2021-10-27 17:03:42 +03:00
Vadim Yanitskiy f3eb44f54b sip: use osmo_{enc,dec}_gcr() from libosmocore
Change-Id: I46fa46fc79494d337f2f4657215b91c39207eea4
Depends: I06babb959fdc82f4e82d92260131d60c98b0abd2
Fixes: Id40d7e0fed9356f801b3627c118150055e7232b1
Related: OS#5164
2021-10-27 16:46:00 +03:00
Vadim Yanitskiy 5e178d2c55 sip: fix memory leak (x_gcr) in send_invite()
Change-Id: I6a2e5e15061010f6e62d0f3b0acd2c040dbe3249
Fixes: Id40d7e0fed9356f801b3627c118150055e7232b1
Related: OS#5164
2021-10-27 16:26:57 +03:00
Keith Whyte 364f237b42 MNCC v8: Implement Basic Support for Global Call Reference.
* Add GCR to mncc struct and therefore bump mncc version.
* Pass the GCR as a SIP Header to SIP UA and retrieve any such header
  from incoming SIP calls, passing the GCR on to MNCC

Related: #OS5164
Depends: osmo-msc I705c860e51637b4537cad65a330ecbaaca96dd5b
Change-Id: Id40d7e0fed9356f801b3627c118150055e7232b1
2021-10-05 20:30:16 +00:00
Oliver Smith 1c76aadeed debian/control: remove dh-systemd build-depend
Related: OS#5223
Change-Id: I769bf61a2f3a97d55c65999436d2cb079c9170a0
2021-09-01 16:07:07 +02:00
Neels Hofmeyr e7d05f40ba SIP re-INVITE: log media connection
Change-Id: I2d11be9fe65aa76e044a91f88ba0e9a1d8e756ec
2021-06-01 04:17:49 +02:00
Neels Hofmeyr 0294e38243 SIP re-INVITE: fix media connection change detection
Use the correct variable in address comparison.
The type cast hid the incompatible type from the compiler.

Change-Id: I701150f22f0eb49fae821996358568d60a385035
2021-06-01 04:16:19 +02:00
Neels Hofmeyr 97c7916892 fix use-after-free in SIP re-INVITE
Copy the m_mode before freeing the parser.
Address sanitizer aborted with:

20210601033017695 DSIP INFO re-INVITE for call 854A5CDA8037073 (sip.c:192)
=================================================================
==8583==ERROR: AddressSanitizer: heap-use-after-free on address 0x612000003250 at pc 0x55c3b4624dc5 bp 0x7ffe8a4464d0 sp 0x7ffe8a4464c8
READ of size 8 at 0x612000003250 thread T0
    #0 0x55c3b4624dc4 in sdp_get_sdp_mode ../../../src/osmo-sip-connector/src/sdp.c:72
    #1 0x55c3b462be9e in sip_handle_reinvite ../../../src/osmo-sip-connector/src/sip.c:202
    #2 0x55c3b462d676 in nua_callback ../../../src/osmo-sip-connector/src/sip.c:397
[...]

Change-Id: I4c48832f01e61e98536de8f164ab5a3caa64f34a
2021-06-01 03:35:53 +02:00
Pau Espin Pedrol d9e57e5f08 Bump version: 1.4.1.15-9484-dirty → 1.5.0
Change-Id: I2cafda295513b019cda424f09655061fa7a0fa10
2021-02-23 13:42:08 +01:00
Pau Espin Pedrol 9484ad9a87 mncc: Fix write len
Fixes bug introduced recently.

Closes: OS#4957
Fixes: 0f27b1bd39
Change-Id: Ibbff004e2b76fbfe8962d14628c916afb19f30e6
2021-02-02 16:32:55 +00:00
Pau Espin Pedrol d2a85888aa .gitignore: Get rid of new autofoo tmp files
Change-Id: I5e0735c2e99e868f8cbfffc19ac005df7c289dd8
2021-02-02 16:42:12 +01:00
Pau Espin Pedrol 462ec86ff3 mncc: Log write error code
Change-Id: If7b49bee6fec3a188c978c0606c524776a577927
2021-02-02 16:41:44 +01:00
Oliver Smith ad353b885e configure.ac: set -std=gnu11
Change-Id: Ic95981af622dd713638bec775514dc4ef25594d4
2021-01-28 09:29:00 +00:00
Alexander Couzens 1bd26902d2 mncc: mncc_create_remote_leg: use mncc_write instead of write direct to the fd
Change-Id: I870c16d7ee5e5424304f3c1c9fb78af418ae2577
2021-01-14 21:27:55 +01:00
Alexander Couzens 0f27b1bd39 mncc: Introduce mncc_rtp_write similiar to mncc_write
Concentrate the write() to the socket in 2 places.
One for struct gsm_mncc and one for struct gsm_mncc_rtp.
Improve debugging as now all function debug print the
MNCC primitiv.

Change-Id: Ia84602955b913a3bb13de7a6a92048799f2e1955
2021-01-14 21:27:50 +01:00
Alexander Couzens ef646b07e2 mncc: add return code for write/send function
Allow to get errors back for the caller to clean up state.

Change-Id: Ic926d192c238ef84fb3ad2be27e507e010b0e93f
2021-01-14 03:41:34 +01:00
Alexander Couzens 3b95463d09 mncc: remove callref argument from mncc_write()
The callref is always included in the struct mncc argument.

Change-Id: Ia156010194c1f4334a4966d01aadfd02fa7097a8
2021-01-14 03:41:34 +01:00
Oliver Smith e590dc15f4 contrib/jenkins: don't build osmo-gsm-manuals
Related: OS#4912
Change-Id: Iae17e6d6f4242f98676dd3a1b01c419ac7288047
2021-01-13 12:59:45 +01:00
Pau Espin 05eea1ef0a contrib/jenkins: Enable parallel make in make distcheck
Change-Id: I17e1adac40ac01daee0dd83da0a6aaebd78ea0dc
Related: OS#4421
2020-10-12 19:46:19 +02:00
Pau Espin cebab664a8 sdp: Don't select addresses failing to parse
Change-Id: Ibfc308bdfab77d0032d8f20c2f0df913e336b75b
2020-09-18 00:32:01 +02:00
Pau Espin b6cb607d12 mncc: Support IPv6 addresses (new version mncc 7)
Change-Id: I3b1bebbcc9e36be43d8d055c8d28cbb38ff21b37
2020-09-10 17:08:19 +02:00
Alexander Couzens f678caa819 Revert "sip: Specify invite contact tag"
This reverts commit 52b2afce2c.
The contact header is generated by the original sofia-sip library.
By adding the contact header explicit as user header it violates the
SIP RFC because sofia will add the Contact header to the BYE message as
well.

Let's fix the bugs in the freeswitch sofia-sip and make it compatible
(not bug compatible) with the original sofia-sip.

Change-Id: I712f17fecbc372d1e486e80673a548e281b37800
2020-09-05 23:13:55 +02:00
Matt Johnson 52b2afce2c sip: Specify invite contact tag
Version 1.12.12 of libsofia-sip-ua no longer automatically generates a
contact header element from the local ip address and port. Specifying
the contact tag does not break operation with the existing
library (1.12.11), but allows for operation on a system with
freeswitch 1.10.4 or later installed, which is built against this new
version of libsofia-sip-ua.

Change-Id: I5c35c5a4bad2fbe76c22ac6d7ee37c832e0ba246
2020-08-13 19:31:58 +00:00
Vadim Yanitskiy 39a2e19309 debian/control: change maintainer to the Osmocom team / mailing list
Change-Id: Id7a258c52e72101920a1c0de5534a2ed9f488203
2020-08-13 14:59:51 +00:00
Harald Welte 79d76480ac Bump version: 1.4.0.3-e02b → 1.4.1
Change-Id: I66c64cae47e32a16ef605348d02884172ef04d70
2020-08-13 12:38:17 +02:00
Oliver Smith e02b4dc84c Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
Change-Id: Ifee945e89b8b3d1a9ef9cf57e36cad306c244657
2020-05-22 13:46:54 +02:00
Oliver Smith d02c8f72b1 contrib: integrate RPM spec
Remove OpenSUSE bug report link, set version to @VERSION@, make it build
with CentOS 8 etc.

Related: OS#4550
Change-Id: I387b41b6c524cd3f6baad7e89b4b6b347d9998ac
2020-05-19 15:49:42 +02:00
Oliver Smith afb705309f contrib: import RPM spec
Copy the RPM spec file from:
https://build.opensuse.org/project/show/home:mnhauke:osmocom:nightly

Related: OS#4550
Change-Id: I00f13acd541ec492e4671cf88c92010f5cf61e9e
2020-05-14 11:53:26 +02:00
Pau Espin e58ead7ac4 Bump version: 1.3.1 → 1.4.0
Change-Id: Ia6693372994791bac4ab86808729f5e568dd9176
2020-01-02 21:35:01 +01:00
Neels Hofmeyr 037c6c1ba8 logging from sofia: add missing newline
Sometimes, logging from sofia lacks the final newline character, messing up log
output. First snprintf() to a buffer, add '\n' if necessary and then log.

Change-Id: Ia26c0b57a0166cf7de87c49471ce6f528a366dd5
2019-12-17 13:52:21 +00:00
Oliver Smith 18cf9f39c5 osmoappdesc.py: switch to python 3
Make build and external tests work with python3, so we can drop
the python2 dependency.

This should be merged shortly after osmo-python-tests was migrated to
python3, and the jenkins build slaves were (automatically) updated to
have the new osmo-python-tests installed.

Related: OS#2819
Depends: osmo-python-tests I3ffc3519bf6c22536a49dad7a966188ddad351a7
Change-Id: Ic913e336a5a962fe9515479b03eecdbef0917721
2019-12-11 09:43:11 +01:00
Neels Hofmeyr 2e2db3e0ad MNCC v6: add optional SDP to the socket protocol
Add the new SDP section to the MNCC socket protocol, but do not yet implement
forwarding SDP from SIP. Implementing SDP forwarding follows in a subsequent
patch.

It is still possible to establish a call with empty SDP: the new osmo-msc on
the MT side, receiving an MNCC_SETUP_REQ, will hit an error log:
"Got no information of remote audio codecs: neither SDP nor Bearer Capability.
Trying anyway."
and then hold thumbs to hit a codec match, analogous to previous behavior.

Note that osmo-sip-connector should actually always have encoded a Bearer
Capability in the MNCC protocol in the MT MNCC_SETUP_REQ message, but never
has. Now we are ready to leapfrog from zero codec info to full SDP.

This patch must be merged at the same time as osmo-msc patch
Ie16f0804c4d99760cd4a0c544d0889b6313eebb7, so that both sides have a matching
MNCC protocol version number.

Change-Id: Iaca9ed6611fc5ca8ca749bbbefc31f54bea5e925
2019-11-28 21:05:49 +01:00
Oliver Smith b544ea396a mncc.c: fix gsm_mncc_rtp size checks
Verify is the parsed data is at least the size of the struct, not
exactly the size. Make it accept messages with additional data, like
the SDP information the TTCN-3 testsuite is sending since
Ic9568c8927507e161aadfad1a4d20aa896d8ae30.

This change makes the size checks consistent with the two other size
checks in the file:
	if (rc < sizeof(*rtp)) {
	if (rc < sizeof(**mncc)) {

Related: OS#4282
Change-Id: I522ce7f206932a816a64f03d916799c3215bb8c7
2019-11-28 15:11:33 +01:00
Alexander Couzens 632156a4a8 exit if the sip agent failed to start
When the sip agent start fails (i.e. port can not bind
because IP doesn't exist) exit the sip-connector

Relates: OS#4197
Change-Id: I22ed16c77391b4f270df498dda587ed657279390
2019-11-23 08:07:01 +00:00
Alexander Couzens 5e3c9d7224 .service: require to ran after network-online.target
Relates: OS#4197
Change-Id: I09bf8139582ef198fdbe664be32b14d8c9c99f1a
2019-09-16 19:51:07 +02:00
Philipp Maier e49a3d714c mncc: check fd before closing a connection
The function close_connection() closes the fd without marking it as
closed. Lets set the fd to -1 and check at the beginning if it is
greater than zero. This prevents us from closing an already closed fd
again.

Related: OS#4159
Change-Id: I9742f31a37296fed15d54cf44c1f65b93abb8c8e
2019-09-02 14:23:36 +02:00
Keith Whyte 5319d4d979 coverity: Address issue found by coverity
Add NULL checks on the return value of call_leg_other() in
update_rtp()

If the remote side has requested media change and we cannot
find the other leg, then release call. This should not
happen.

Also, Add an assert to show that we cannot be here
without call type of SIP or MNCC (not related to coverity)

Fixes: CID#202863
Change-Id: I6f1f26533a25c93f243090bc02f1bc83b9108d42
2019-08-09 14:12:32 +02:00
26 changed files with 810 additions and 179 deletions

3
.gitignore vendored
View File

@ -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

View File

@ -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)

View File

@ -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.

82
README.md Normal file
View File

@ -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 us 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>

9
TODO-RELEASE Normal file
View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

122
debian/changelog vendored
View File

@ -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 ]

2
debian/compat vendored
View File

@ -1 +1 @@
7
10

13
debian/control vendored
View File

@ -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

38
debian/postinst vendored Executable file
View File

@ -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#

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# (C) 2016 by Holger Hans Peter Freyther

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -1,4 +1,4 @@
/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
* 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
@ -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 {

169
src/sdp.c
View File

@ -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;
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;
inet_aton(conn->c_address, &addr);
leg->base.ip = addr.s_addr;
}
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,27 +214,62 @@ 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;
char *mode_attribute;
char ip_addr[INET_ADDRSTRLEN];
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;
inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr));
leg->wanted_codec = app_media_name(other->payload_msg_type);
sdp_data = other->rx_sdp;
if (strcmp(leg->wanted_codec, "AMR") == 0)
fmtp_str = talloc_asprintf(leg, "a=fmtp:%d octet-align=1\r\n", other->payload_type);
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[INET6_ADDRSTRLEN];
char ipv;
switch (mode) {
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);
switch (mode) {
case sdp_inactive:
mode_attribute = "a=inactive\r\n";
break;
@ -231,24 +285,67 @@ char *sdp_create_file(struct sip_call_leg *leg, struct call_leg *other, sdp_mode
default:
OSMO_ASSERT(false);
break;
}
return talloc_asprintf(leg,
"v=0\r\n"
"o=Osmocom 0 0 IN IP%c %s\r\n"
"s=GSM Call\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",
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);
}
sdp = talloc_asprintf(leg,
"v=0\r\n"
"o=Osmocom 0 0 IN IP4 %s\r\n"
"s=GSM Call\r\n"
"c=IN IP4 %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,
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;
}

View File

@ -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);

130
src/sip.c
View File

@ -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,11 +143,17 @@ 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());
nua_handle_destroy(nh);
return;
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)
@ -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,8 +408,10 @@ 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 */
@ -393,10 +437,12 @@ 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;
@ -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)

View File

@ -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