mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr.git
synced 2025-11-02 13:13:29 +00:00
Compare commits
22 Commits
1.4.0
...
neels/dgsm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
045d7be024 | ||
|
|
adeb6e7b5f | ||
|
|
0f2cfe6c79 | ||
|
|
288a9ddd21 | ||
|
|
f3598c4b2c | ||
|
|
7ca9bc517a | ||
|
|
37ec56d39c | ||
|
|
f91e26ed02 | ||
|
|
2975bcbc2f | ||
|
|
749e42902d | ||
|
|
b0c00522b0 | ||
|
|
613e1b87f0 | ||
|
|
0a5eac957a | ||
|
|
82f4521319 | ||
|
|
c1863eb53b | ||
|
|
3e142ab230 | ||
|
|
774d114635 | ||
|
|
40b52d9b58 | ||
|
|
30c1590599 | ||
|
|
e8e2388ea7 | ||
|
|
f83c5a85e3 | ||
|
|
359bbcb9f7 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -26,7 +26,6 @@ m4
|
||||
*.m4
|
||||
missing
|
||||
.deps
|
||||
*~
|
||||
|
||||
*.pc
|
||||
.libs
|
||||
@@ -37,7 +36,6 @@ src/osmo-hlr
|
||||
src/osmo-hlr-db-tool
|
||||
src/osmo-euse-demo
|
||||
src/gsupclient/gsup-test-client
|
||||
src/mslookup/osmo-mslookup-client
|
||||
|
||||
tests/atconfig
|
||||
tests/testsuite
|
||||
@@ -52,11 +50,6 @@ tests/gsup_server/gsup_server_test
|
||||
tests/gsup/gsup_test
|
||||
tests/db/db_test
|
||||
tests/hlr_vty_test.db*
|
||||
tests/db_upgrade/*.dump
|
||||
tests/mslookup/mdns_test
|
||||
tests/mslookup/mslookup_client_mdns_test
|
||||
tests/mslookup/mslookup_client_test
|
||||
tests/mslookup/mslookup_test
|
||||
|
||||
# manuals
|
||||
doc/manuals/*.html
|
||||
@@ -68,5 +61,3 @@ doc/manuals/generated/
|
||||
doc/manuals/osmomsc-usermanual.xml
|
||||
doc/manuals/common
|
||||
doc/manuals/build
|
||||
|
||||
contrib/osmo-hlr.spec
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
AUTOMAKE_OPTIONS = foreign dist-bzip2
|
||||
|
||||
SUBDIRS = \
|
||||
doc \
|
||||
src \
|
||||
include \
|
||||
doc \
|
||||
sql \
|
||||
contrib \
|
||||
tests \
|
||||
@@ -11,16 +11,13 @@ SUBDIRS = \
|
||||
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
contrib/osmo-hlr.spec.in \
|
||||
debian \
|
||||
$(NULL)
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libosmo-gsup-client.pc \
|
||||
libosmo-mslookup.pc
|
||||
pkgconfig_DATA = libosmo-gsup-client.pc
|
||||
|
||||
@RELMAKE@
|
||||
|
||||
|
||||
66
README.md
66
README.md
@@ -1,66 +0,0 @@
|
||||
osmo-hlr - Osmocom HLR Implementation
|
||||
=====================================
|
||||
|
||||
This repository contains a C-language implementation of a GSM Home
|
||||
Location Register (HLR). It is part of the
|
||||
[Osmocom](https://osmocom.org/) Open Source Mobile Communications
|
||||
project.
|
||||
|
||||
Warning: While the HLR logical functionality is implemented, OsmoHLR
|
||||
does not use the ETSI/3GPP TCAP/MAP protocol stack. Instead, a much
|
||||
simpler custom protocol (GSUP) is used. This means, OsmoHLR is of
|
||||
no use outside the context of an Osmocom core network. You can use
|
||||
it with OsmoMSC, OsmoSGSN etc. - but not with third party components.
|
||||
|
||||
Homepage
|
||||
--------
|
||||
|
||||
The official homepage of the project is
|
||||
https://osmocom.org/projects/osmo-hlr/wiki
|
||||
|
||||
GIT Repository
|
||||
--------------
|
||||
|
||||
You can clone from the official osmo-hlr.git repository using
|
||||
|
||||
git clone git://git.osmocom.org/osmo-hlr.git
|
||||
git clone https://git.osmocom.org/osmo-hlr.git
|
||||
|
||||
There is a cgit interface at https://git.osmocom.org/osmo-hlr/
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
User Manuals and VTY reference manuals are [optionally] built in PDF form
|
||||
as part of the build process.
|
||||
|
||||
Pre-rendered PDF version of the current "master" can be found at
|
||||
[User Manual](https://ftp.osmocom.org/docs/latest/osmohlr-usermanual.pdf)
|
||||
as well as the VTY reference manuals
|
||||
* [VTY Reference Manual for osmo-hlr](https://ftp.osmocom.org/docs/latest/osmohlr-vty-reference.pdf)
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
Discussions related to osmo-hlr 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-hlr can be seen at
|
||||
https://gerrit.osmocom.org/#/q/project:osmo-hlr+status:open
|
||||
@@ -1,9 +0,0 @@
|
||||
# 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
|
||||
38
configure.ac
38
configure.ac
@@ -12,8 +12,6 @@ AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.9])
|
||||
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
@@ -27,11 +25,6 @@ AC_PROG_MKDIR_P
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
|
||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
|
||||
AS_CASE(["$LD"],[*clang*],
|
||||
[AS_CASE(["${host_os}"],
|
||||
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
@@ -41,11 +34,12 @@ PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.2.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.2.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.2.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.2.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOMSLOOKUP, libosmomslookup >= 1.2.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.6.0)
|
||||
|
||||
PKG_CHECK_MODULES(SQLITE3, sqlite3)
|
||||
|
||||
@@ -114,15 +108,6 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
|
||||
AC_MSG_RESULT([$enable_ext_tests])
|
||||
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
|
||||
|
||||
# mslookup_client_mdns_test (OS#4385: does not work everywhere)
|
||||
AC_ARG_ENABLE([mslookup_client_mdns_test],
|
||||
AC_HELP_STRING([--enable-mslookup-client-mdns-test],
|
||||
[Include the mslookup_client_mdns_test in make check [default=no]]),
|
||||
[enable_mslookup_client_mdns_test="$enableval"],[enable_mslookup_client_mdns_test="no"])
|
||||
AC_MSG_CHECKING([whether to enable mslookup_client_mdns_test])
|
||||
AC_MSG_RESULT([$enable_mslookup_client_mdns_test])
|
||||
AM_CONDITIONAL(ENABLE_MSLOOKUP_CLIENT_MDNS_TEST, test "x$enable_mslookup_client_mdns_test" = "xyes")
|
||||
|
||||
# Generate manuals
|
||||
AC_ARG_ENABLE(manuals,
|
||||
[AS_HELP_STRING(
|
||||
@@ -190,24 +175,17 @@ AC_OUTPUT(
|
||||
doc/examples/Makefile
|
||||
src/Makefile
|
||||
src/gsupclient/Makefile
|
||||
src/mslookup/Makefile
|
||||
include/Makefile
|
||||
include/osmocom/Makefile
|
||||
include/osmocom/hlr/Makefile
|
||||
include/osmocom/mslookup/Makefile
|
||||
libosmo-gsup-client.pc
|
||||
libosmo-mslookup.pc
|
||||
sql/Makefile
|
||||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
contrib/dgsm/Makefile
|
||||
contrib/osmo-hlr.spec
|
||||
tests/Makefile
|
||||
tests/auc/Makefile
|
||||
tests/auc/gen_ts_55_205_test_sets/Makefile
|
||||
tests/gsup/Makefile
|
||||
tests/gsup_server/Makefile
|
||||
tests/db/Makefile
|
||||
tests/db_upgrade/Makefile
|
||||
tests/mslookup/Makefile
|
||||
tests/mslookup_manual_test/Makefile
|
||||
)
|
||||
|
||||
@@ -1,14 +1 @@
|
||||
SUBDIRS = \
|
||||
systemd \
|
||||
dgsm \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = osmo-hlr-post-upgrade.sh
|
||||
|
||||
install-data-hook:
|
||||
install -Dm755 $(srcdir)/osmo-hlr-post-upgrade.sh \
|
||||
-t $(DESTDIR)$(datadir)/osmocom/
|
||||
|
||||
uninstall-hook:
|
||||
@$(PRE_UNINSTALL)
|
||||
$(RM) $(DESTDIR)$(datadir)/osmocom/osmo-hlr-post-upgrade.sh
|
||||
SUBDIRS = systemd
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
EXTRA_DIST = \
|
||||
esme_dgsm.py \
|
||||
freeswitch_dialplan_dgsm.py \
|
||||
osmo-mslookup-pipe.py \
|
||||
osmo-mslookup-socket.py \
|
||||
$(NULL)
|
||||
@@ -1,184 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright 2019 sysmocom s.f.m.c GmbH <info@sysmocom.de>
|
||||
|
||||
WARNING: this is just a proof-of-concept implementation, it blocks for every
|
||||
received SMPP request and is not suitable for servicing more than one request
|
||||
at a time.
|
||||
|
||||
Based on esme.py from RCCN (license changed with permission from author):
|
||||
https://github.com/Rhizomatica/rccn/blob/master/rccn/esme.py
|
||||
Copyright 2017 keith <keith@rhizomatica.org>
|
||||
|
||||
Forward SMS to the receiver's SMSC, as determined with mslookup.
|
||||
Requires smpplip (pip3 install --user smpplib) and osmo-mslookup-client.
|
||||
|
||||
Example SMPP configuration for osmo-msc.cfg:
|
||||
smpp
|
||||
local-tcp-ip 127.0.0.1 2775
|
||||
policy closed
|
||||
smpp-first
|
||||
# outgoing to esme_dgsm.py
|
||||
esme OSMPP
|
||||
no alert-notifications
|
||||
password foo
|
||||
default-route
|
||||
# incoming from esme_dgsm.py
|
||||
esme ISMPP
|
||||
no alert-notifications
|
||||
password foo
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import smpplib
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
|
||||
def can_handle_pdu(pdu):
|
||||
if not isinstance(pdu, smpplib.command.DeliverSM):
|
||||
logging.info('PDU is not a DeliverSM, ignoring')
|
||||
return False
|
||||
|
||||
if int(pdu.dest_addr_ton) == smpplib.consts.SMPP_TON_INTL:
|
||||
logging.info("Unable to handle SMS for %s: SMPP_TON_INTL" %
|
||||
(pdu.destination_addr))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def query_mslookup(service_type, id, id_type='msisdn'):
|
||||
query_str = '%s.%s.%s' % (service_type, id, id_type)
|
||||
logging.info('mslookup: ' + query_str)
|
||||
|
||||
result_line = subprocess.check_output(['osmo-mslookup-client', query_str,
|
||||
'-f', 'json'])
|
||||
if isinstance(result_line, bytes):
|
||||
result_line = result_line.decode('ascii')
|
||||
|
||||
logging.info('mslookup result: ' + result_line.rstrip())
|
||||
return json.loads(result_line)
|
||||
|
||||
|
||||
def tx_sms(dst_host, dst_port, source, destination, registered_delivery,
|
||||
unicode_text):
|
||||
smpp_client = smpplib.client.Client(dst_host, dst_port, 90)
|
||||
smpp_client.connect()
|
||||
smpp_client.bind_transceiver(system_id=args.dst_id, password=args.dst_pass)
|
||||
logging.info('Connected to destination SMSC (%s@%s:%s)' % (args.dst_id,
|
||||
dst_host, dst_port))
|
||||
|
||||
pdu = smpp_client.send_message(
|
||||
source_addr_ton=smpplib.consts.SMPP_TON_ALNUM,
|
||||
source_addr_npi=smpplib.consts.SMPP_NPI_UNK,
|
||||
source_addr=source.decode(),
|
||||
dest_addr_ton=smpplib.consts.SMPP_TON_SBSCR,
|
||||
dest_addr_npi=smpplib.consts.SMPP_NPI_ISDN,
|
||||
destination_addr=destination.decode(),
|
||||
short_message=unicode_text,
|
||||
registered_delivery=registered_delivery,
|
||||
)
|
||||
|
||||
smpp_client.unbind()
|
||||
smpp_client.disconnect()
|
||||
del pdu
|
||||
del smpp_client
|
||||
|
||||
|
||||
def rx_deliver_sm(pdu):
|
||||
if not can_handle_pdu(pdu):
|
||||
return smpplib.consts.SMPP_ESME_RSYSERR
|
||||
|
||||
msisdn = pdu.destination_addr.decode()
|
||||
logging.info("Incoming SMS for: " + msisdn)
|
||||
|
||||
if args.sleep:
|
||||
logging.info("Sleeping for %i seconds" % (args.sleep))
|
||||
time.sleep(args.sleep)
|
||||
logging.info("Sleep done")
|
||||
|
||||
if args.always_fail is not None:
|
||||
return args.always_fail
|
||||
|
||||
result = query_mslookup("smpp.sms", msisdn)
|
||||
if 'v4' not in result or not result['v4']:
|
||||
logging.info('No IPv4 result from mslookup! This example only'
|
||||
' makes use of IPv4, dropping.')
|
||||
return smpplib.consts.SMPP_ESME_RSYSERR
|
||||
|
||||
dst_host, dst_port = result['v4']
|
||||
tx_sms(dst_host, dst_port, pdu.source_addr,
|
||||
pdu.destination_addr, int(pdu.registered_delivery),
|
||||
pdu.short_message)
|
||||
|
||||
return smpplib.consts.SMPP_ESME_ROK
|
||||
|
||||
|
||||
def smpp_bind():
|
||||
client = smpplib.client.Client(args.src_host, args.src_port, 90)
|
||||
client.set_message_received_handler(rx_deliver_sm)
|
||||
client.connect()
|
||||
client.bind_transceiver(system_id=args.src_id, password=args.src_pass)
|
||||
logging.info('Connected to source SMSC (%s@%s:%s)' % (args.src_id,
|
||||
args.src_host, args.src_port))
|
||||
logging.info('Waiting for SMS...')
|
||||
client.listen()
|
||||
|
||||
|
||||
def main():
|
||||
global args
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--src-host', default='127.0.0.1',
|
||||
help='source SMSC (OsmoMSC) host (default: 127.0.0.1)')
|
||||
parser.add_argument('--src-port', default=2775, type=int,
|
||||
help='source SMSC (OsmoMSC) port (default: 2775)')
|
||||
parser.add_argument('--src-id', default='OSMPP',
|
||||
help='source system id, as configured in osmo-msc.cfg'
|
||||
' (default: OSMPP)')
|
||||
parser.add_argument('--src-pass', default='foo',
|
||||
help='source system password, as configured in'
|
||||
' osmo-msc.cfg (default: foo)')
|
||||
parser.add_argument('--dst-id', default='ISMPP',
|
||||
help='destination system id, as configured in'
|
||||
' osmo-msc.cfg (default: ISMPP)')
|
||||
parser.add_argument('--dst-pass', default='foo',
|
||||
help='destination system password, as configured in'
|
||||
' osmo-msc.cfg (default: foo)')
|
||||
parser.add_argument('--sleep', default=0, type=float,
|
||||
help='sleep time in seconds before forwarding an SMS,'
|
||||
' to test multithreading (default: 0)')
|
||||
parser.add_argument('--always-fail', default=None, metavar='SMPP_ESME_ERRCODE',
|
||||
help='test delivery failure: always return an error code on Deliver-SM,'
|
||||
' pass an smpplib error code name like RDELIVERYFAILURE (see smpplib/consts.py),'
|
||||
' or an SMPP error code in hex digits')
|
||||
args = parser.parse_args()
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='[%(asctime)s]'
|
||||
' (%(threadName)s) %(message)s', datefmt="%H:%M:%S")
|
||||
|
||||
if args.always_fail:
|
||||
resolved = None
|
||||
name = 'SMPP_ESME_' + args.always_fail
|
||||
if hasattr(smpplib.consts, name):
|
||||
resolved = getattr(smpplib.consts, name)
|
||||
if resolved is None:
|
||||
try:
|
||||
resolved = int(args.always_fail, 16)
|
||||
except ValueError:
|
||||
resolved = None
|
||||
if resolved is None:
|
||||
print('Invalid argument for --always-fail: %r' % args.always_fail)
|
||||
exit(1)
|
||||
args.always_fail = resolved
|
||||
logging.info('--always-fail: returning error code %s to all Deliver-SM' % hex(args.always_fail))
|
||||
|
||||
smpp_bind()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
# vim: expandtab tabstop=4 shiftwidth=4
|
||||
@@ -1,77 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright 2019 sysmocom s.f.m.c GmbH <info@sysmocom.de>
|
||||
|
||||
This is a freeswitch dialplan implementation, see:
|
||||
https://freeswitch.org/confluence/display/FREESWITCH/mod_python
|
||||
|
||||
Find the right SIP server with mslookup (depending on the destination number)
|
||||
and bridge calls accordingly.
|
||||
"""
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
|
||||
def query_mslookup(service_type, id, id_type='msisdn'):
|
||||
query_str = '%s.%s.%s' % (service_type, id, id_type)
|
||||
print('[dialplan-dgsm] mslookup: ' + query_str)
|
||||
|
||||
result_line = subprocess.check_output([
|
||||
'osmo-mslookup-client', query_str, '-f', 'json'])
|
||||
if isinstance(result_line, bytes):
|
||||
result_line = result_line.decode('ascii')
|
||||
|
||||
print('[dialplan-dgsm] mslookup result: ' + result_line)
|
||||
return json.loads(result_line)
|
||||
|
||||
|
||||
def handler(session, args):
|
||||
""" Handle calls: bridge to the SIP server found with mslookup. """
|
||||
print('[dialplan-dgsm] call handler')
|
||||
msisdn = session.getVariable('destination_number')
|
||||
|
||||
# Run osmo-mslookup-client binary. We have also tried to directly call the
|
||||
# C functions with ctypes but this has lead to hard-to-debug segfaults.
|
||||
try:
|
||||
result = query_mslookup("sip.voice", msisdn)
|
||||
|
||||
# This example only makes use of IPv4
|
||||
if not result['v4']:
|
||||
print('[dialplan-dgsm] no IPv4 result from mslookup')
|
||||
session.hangup('UNALLOCATED_NUMBER')
|
||||
return
|
||||
|
||||
sip_ip, sip_port = result['v4']
|
||||
dial_str = 'sofia/internal/sip:{}@{}:{}'.format(
|
||||
msisdn, sip_ip, sip_port)
|
||||
print('[dialplan-dgsm] dial_str: ' + str(dial_str))
|
||||
|
||||
session.execute('bridge', dial_str)
|
||||
except:
|
||||
print('[dialplan-dgsm]: exception during call handler')
|
||||
session.hangup('UNALLOCATED_NUMBER')
|
||||
|
||||
|
||||
def fsapi(session, stream, env, args):
|
||||
""" Freeswitch refuses to load the module without this. """
|
||||
stream.write(env.serialize())
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('id', type=int)
|
||||
parser.add_argument('-i', '--id-type', default='msisdn',
|
||||
help='default: "msisdn"')
|
||||
parser.add_argument('-s', '--service', default='sip.voice',
|
||||
help='default: "sip.voice"')
|
||||
args = parser.parse_args()
|
||||
|
||||
result = query_mslookup(args.service, args.id, args.id_type)
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# vim: shiftwidth=4 tabstop=4 expandtab
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
def query_mslookup(query_str):
|
||||
result = {'result': 'not-found'}
|
||||
proc = subprocess.Popen(('osmo-mslookup-client', '-f', 'json', query_str),
|
||||
stdout=subprocess.PIPE)
|
||||
for line in iter(proc.stdout.readline,''):
|
||||
if not line:
|
||||
break
|
||||
response = json.loads(line)
|
||||
if response.get('result') == 'result':
|
||||
result = response
|
||||
print('Response: %r' % response)
|
||||
return result
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
query_str = '1000-5000@sip.voice.12345.msisdn'
|
||||
if len(sys.argv) > 1:
|
||||
query_str = sys.argv[1]
|
||||
print('Final result: %r' % query_mslookup(query_str))
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# vim: shiftwidth=4 tabstop=4 expandtab
|
||||
import socket
|
||||
import time
|
||||
|
||||
MSLOOKUP_SOCKET_PATH = '/tmp/mslookup'
|
||||
|
||||
def query_mslookup_socket(query_str, socket_path=MSLOOKUP_SOCKET_PATH):
|
||||
mslookup_socket = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
|
||||
mslookup_socket.setblocking(True)
|
||||
mslookup_socket.connect(socket_path)
|
||||
result = {'result': 'not-found'}
|
||||
column_names = mslookup_socket.recv(1024).decode('ascii')
|
||||
if not column_names:
|
||||
return result
|
||||
column_names = column_names.split('\t')
|
||||
mslookup_socket.sendall(query_str.encode('ascii'))
|
||||
while True:
|
||||
csv = mslookup_socket.recv(1024).decode('ascii')
|
||||
if not csv:
|
||||
break
|
||||
response = dict(zip(column_names, csv.split('\t')))
|
||||
if response.get('result') == 'result':
|
||||
result = response
|
||||
print('Response: %r' % response)
|
||||
return result
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
print(
|
||||
'\nPlease run separately: osmo-mslookup-client --socket /tmp/mslookup -d\n')
|
||||
query_str = '1000-5000@sip.voice.12345.msisdn'
|
||||
if len(sys.argv) > 1:
|
||||
query_str = sys.argv[1]
|
||||
print('Final result: %r' % query_mslookup_socket(query_str))
|
||||
@@ -35,6 +35,7 @@ osmo-build-dep.sh libosmo-abis
|
||||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
if [ "$WITH_MANUALS" = "1" ]; then
|
||||
osmo-build-dep.sh osmo-gsm-manuals
|
||||
CONFIG="--enable-manuals"
|
||||
fi
|
||||
|
||||
@@ -48,19 +49,14 @@ set -x
|
||||
|
||||
cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure \
|
||||
--enable-sanitize \
|
||||
--enable-external-tests \
|
||||
--enable-mslookup-client-mdns-test \
|
||||
--enable-werror \
|
||||
$CONFIG
|
||||
./configure --enable-sanitize --enable-external-tests --enable-werror $CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
$MAKE check || cat-testlogs.sh
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck || cat-testlogs.sh
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck || cat-testlogs.sh
|
||||
|
||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||
make -C "$base/doc/manuals" publish
|
||||
fi
|
||||
|
||||
$MAKE $PARALLEL_MAKE maintainer-clean
|
||||
$MAKE maintainer-clean
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright 2021 sysmocom s.f.m.c GmbH <info@sysmocom.de>
|
||||
#
|
||||
# Packagers are supposed to call this script in post-upgrade, so it can safely
|
||||
# upgrade the database scheme if required.
|
||||
|
||||
DB="/var/lib/osmocom/hlr.db"
|
||||
IS_ACTIVE=0
|
||||
|
||||
msg() {
|
||||
echo "osmo-hlr-post-upgrade: $@"
|
||||
}
|
||||
|
||||
err() {
|
||||
msg "ERROR: $@"
|
||||
}
|
||||
|
||||
open_db() {
|
||||
# Attempt to open the database with osmo-hlr-db-tool, it will fail if
|
||||
# upgrading the schema is required
|
||||
osmo-hlr-db-tool -s -l "$DB" create
|
||||
}
|
||||
|
||||
check_upgrade_required() {
|
||||
if ! [ -e "$DB" ]; then
|
||||
msg "nothing to do (no existing database)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if open_db 2>/dev/null; then
|
||||
msg "nothing to do (database version is up to date)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
msg "database upgrade is required"
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
if systemctl is-active -q osmo-hlr; then
|
||||
IS_ACTIVE=1
|
||||
msg "stopping osmo-hlr service"
|
||||
systemctl stop osmo-hlr
|
||||
|
||||
# Verify that it stopped
|
||||
for i in $(seq 1 100); do
|
||||
if ! systemctl is-active -q osmo-hlr; then
|
||||
return
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
err "failed to stop osmo-hlr service"
|
||||
exit 1
|
||||
else
|
||||
msg "osmo-hlr service is not running"
|
||||
fi
|
||||
}
|
||||
|
||||
create_backup() {
|
||||
backup="$DB.$(date +%Y%m%d%H%M%S).bak"
|
||||
msg "creating backup: $backup"
|
||||
if [ -e "$backup" ]; then
|
||||
err "backup already exists: $backup"
|
||||
exit 1
|
||||
fi
|
||||
cp "$DB" "$backup"
|
||||
}
|
||||
|
||||
upgrade() {
|
||||
msg "performing database upgrade"
|
||||
osmo-hlr-db-tool -s -U -l "$DB" create
|
||||
|
||||
if ! open_db 2>/dev/null; then
|
||||
err "failed to open the database after upgrade"
|
||||
err "osmo-hlr-db-tool output:"
|
||||
open_db
|
||||
# exit because of "set -e"
|
||||
fi
|
||||
|
||||
msg "database upgrade successful"
|
||||
}
|
||||
|
||||
start_service() {
|
||||
if [ "$IS_ACTIVE" = "1" ]; then
|
||||
msg "starting osmo-hlr service"
|
||||
systemctl start osmo-hlr
|
||||
fi
|
||||
}
|
||||
|
||||
check_upgrade_required
|
||||
stop_service
|
||||
create_backup
|
||||
upgrade
|
||||
start_service
|
||||
@@ -1,195 +0,0 @@
|
||||
#
|
||||
# spec file for package osmo-hlr
|
||||
#
|
||||
# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||
# Copyright (c) 2016, Martin Hauke <mardnh@gmx.de>
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
# upon. The license for this file, and modifications and additions to the
|
||||
# file, is the same license as for the pristine package itself (unless the
|
||||
# license for the pristine package is not an Open Source License, in which
|
||||
# case the license is the MIT License). An "Open Source License" is a
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
Name: osmo-hlr
|
||||
Version: @VERSION@
|
||||
Release: 0
|
||||
Summary: Osmocom Home Location Register for GSUP protocol towards OsmoSGSN and OsmoCSCN
|
||||
License: AGPL-3.0-or-later AND GPL-2.0-or-later
|
||||
Group: Productivity/Telephony/Servers
|
||||
URL: https://osmocom.org/projects/osmo-hlr
|
||||
Source: %{name}-%{version}.tar.xz
|
||||
BuildRequires: autoconf
|
||||
BuildRequires: automake
|
||||
BuildRequires: libtool
|
||||
BuildRequires: pkgconfig >= 0.20
|
||||
BuildRequires: python3
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.5.0
|
||||
BuildRequires: pkgconfig(sqlite3)
|
||||
BuildRequires: pkgconfig(talloc) >= 2.0.1
|
||||
# only needed for populate_hlr_db.pl
|
||||
Requires: libdbi-drivers-dbd-sqlite3
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
The GSUP HLR is a stand-alone HLR (Home Location Register) for SIM
|
||||
and USIM based subscribers which exposes the GSUP protocol towards
|
||||
its users. OsmoSGSN supports this protocol.
|
||||
|
||||
osmo-gsup-hlr is still very simplistic. It is a single-threaded
|
||||
architecture and uses only sqlite3 tables as back-end. It is suitable
|
||||
for installations of the scale that OsmoNITB was able to handle. It
|
||||
also lacks various features like fine-grained control of subscribed
|
||||
services (like supplementary services).
|
||||
|
||||
%package -n libosmo-gsup-client0
|
||||
Summary: Osmocom GSUP (General Subscriber Update Protocol) client library
|
||||
License: GPL-2.0-or-later
|
||||
Group: System/Libraries
|
||||
|
||||
%description -n libosmo-gsup-client0
|
||||
This is a shared library that can be used to implement client programs for
|
||||
the GSUP protocol. The typical GSUP server is OsmoHLR, with OsmoMSC, OsmoSGSN
|
||||
and External USSD Entities (EUSEs) using this library to implement clients.
|
||||
|
||||
%package -n libosmo-gsup-client-devel
|
||||
Summary: Development files for the Osmocom GSUP client library
|
||||
License: GPL-2.0-or-later
|
||||
Group: Development/Libraries/C and C++
|
||||
Requires: libosmo-gsup-client0 = %{version}
|
||||
|
||||
%description -n libosmo-gsup-client-devel
|
||||
This is a shared library that can be used to implement client programs for
|
||||
the GSUP protocol. The typical GSUP server is OsmoHLR, with OsmoMSC, OsmoSGSN
|
||||
and External USSD Entities (EUSEs) using this library to implement clients.
|
||||
|
||||
This subpackage contains libraries and header files for developing
|
||||
applications that want to make use of libosmo-gsup-client.
|
||||
|
||||
%package -n libosmo-mslookup0
|
||||
Summary: Osmocom MS lookup library
|
||||
License: GPL-2.0-or-later
|
||||
Group: System/Libraries
|
||||
|
||||
%description -n libosmo-mslookup0
|
||||
This shared library contains routines for looking up mobile subscribers.
|
||||
|
||||
%package -n libosmo-mslookup-devel
|
||||
Summary: Development files for the Osmocom MS lookup library
|
||||
License: GPL-2.0-or-later
|
||||
Group: Development/Libraries/C and C++
|
||||
Requires: libosmo-mslookup0 = %{version}
|
||||
|
||||
%description -n libosmo-mslookup-devel
|
||||
This shared library contains routines for looking up mobile subscribers.
|
||||
|
||||
This subpackage contains libraries and header files for developing
|
||||
applications that want to make use of libosmo-mslookup.
|
||||
|
||||
|
||||
%package -n osmo-mslookup-client
|
||||
Summary: Standalone program using libosmo-mslookup
|
||||
License: GPL-2.0-or-later
|
||||
Group: Development/Libraries/C and C++
|
||||
|
||||
%description -n osmo-mslookup-client
|
||||
Standalone program using libosmo-mslookup to easily integrate with programs
|
||||
that want to connect services (SIP, SMS,...) to the current location of a
|
||||
subscriber.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
echo "%{version}" >.tarball-version
|
||||
autoreconf -fi
|
||||
%configure \
|
||||
--docdir="%{_docdir}/%{name}" \
|
||||
--with-systemdsystemunitdir=%{_unitdir} \
|
||||
--enable-shared \
|
||||
--disable-static
|
||||
make V=1 %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
%make_install
|
||||
install -d "%{buildroot}/%{_localstatedir}/lib/osmocom"
|
||||
find %{buildroot} -type f -name "*.la" -delete -print
|
||||
|
||||
%check
|
||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%preun
|
||||
%service_del_preun %{name}.service
|
||||
|
||||
%postun
|
||||
%service_del_postun %{name}.service
|
||||
|
||||
%pre
|
||||
%service_add_pre %{name}.service
|
||||
%endif
|
||||
|
||||
%post
|
||||
%if 0%{?suse_version}
|
||||
%service_add_post %{name}.service
|
||||
%endif
|
||||
/usr/share/osmocom/osmo-hlr-post-upgrade.sh
|
||||
|
||||
%post -n libosmo-gsup-client0 -p /sbin/ldconfig
|
||||
%postun -n libosmo-gsup-client0 -p /sbin/ldconfig
|
||||
%post -n libosmo-mslookup0 -p /sbin/ldconfig
|
||||
%postun -n libosmo-mslookup0 -p /sbin/ldconfig
|
||||
|
||||
%files
|
||||
%license COPYING
|
||||
%dir %{_docdir}/%{name}
|
||||
%dir %{_docdir}/%{name}/examples
|
||||
%{_docdir}/%{name}/examples/osmo-hlr.cfg
|
||||
%{_docdir}/%{name}/examples/osmo-hlr-dgsm.cfg
|
||||
%dir %{_docdir}/%{name}/sql
|
||||
%{_docdir}/%{name}/sql/hlr.sql
|
||||
%{_docdir}/%{name}/sql//hlr_data.sql
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%dir %{_localstatedir}/lib/osmocom
|
||||
%{_bindir}/osmo-hlr
|
||||
%{_bindir}/osmo-hlr-db-tool
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config %{_sysconfdir}/osmocom/osmo-hlr.cfg
|
||||
%{_unitdir}/osmo-hlr.service
|
||||
%dir %{_datadir}/osmocom
|
||||
%{_datadir}/osmocom/osmo-hlr-post-upgrade.sh
|
||||
|
||||
%files -n libosmo-gsup-client0
|
||||
%{_libdir}/libosmo-gsup-client.so.0*
|
||||
|
||||
%files -n libosmo-gsup-client-devel
|
||||
%{_bindir}/osmo-euse-demo
|
||||
%dir %{_includedir}/osmocom
|
||||
%dir %{_includedir}/osmocom/gsupclient
|
||||
%{_includedir}/osmocom/gsupclient/*.h
|
||||
%{_libdir}/libosmo-gsup-client.so
|
||||
%{_libdir}/pkgconfig/libosmo-gsup-client.pc
|
||||
|
||||
%files -n libosmo-mslookup0
|
||||
%{_libdir}/libosmo-mslookup.so.0*
|
||||
|
||||
%files -n libosmo-mslookup-devel
|
||||
%dir %{_includedir}/osmocom
|
||||
%dir %{_includedir}/osmocom/mslookup
|
||||
%{_includedir}/osmocom/mslookup/*.h
|
||||
%{_libdir}/libosmo-mslookup.so
|
||||
%{_libdir}/pkgconfig/libosmo-mslookup.pc
|
||||
|
||||
%files -n osmo-mslookup-client
|
||||
%{_bindir}/osmo-mslookup-client
|
||||
|
||||
%changelog
|
||||
163
debian/changelog
vendored
163
debian/changelog
vendored
@@ -1,166 +1,3 @@
|
||||
osmo-hlr (1.4.0) unstable; urgency=medium
|
||||
|
||||
[ Keith ]
|
||||
* Correct configuration written from vty
|
||||
* vty: enable show subscribers filtered by IMEI
|
||||
|
||||
[ Harald Welte ]
|
||||
* add README.md file as customary for cgit, github, gitlab, etc.
|
||||
|
||||
[ Oliver Smith ]
|
||||
* Add post-upgrade script for automatic db upgrade
|
||||
* debian/control: remove dh-systemd build-depend
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* db: Avoid use uninitialized rc if running 0 statements
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* db v6: determine 3G AUC IND from VLR name
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 14:56:41 +0100
|
||||
|
||||
osmo-hlr (1.3.0) unstable; urgency=medium
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* hlr: respect the num_auth_vectors requested
|
||||
* hlr: remove unused internal USSD list
|
||||
|
||||
[ Oliver Smith ]
|
||||
* add libosmo-mslookup abstract client
|
||||
* add mDNS lookup method to libosmo-mslookup
|
||||
* Makefile.am: fix pkgconfig_DATA
|
||||
* add mDNS lookup method to libosmo-mslookup (#2)
|
||||
* contrib/dgsm/ add example esme and dialplan
|
||||
* mslookup_client.c: fix dereferencing null pointer
|
||||
* mdns_msg.c: always call va_end
|
||||
* mslookup_client_mdns.c: fix dereferencing null
|
||||
* osmo-mslookup-client.c: fix dereferencing null
|
||||
* osmo-mslookup-client: fix dereferencing null
|
||||
* mdns_sock.c: fix resource leak of sock
|
||||
* mdns_rfc.c: fix possible access of uninit. mem
|
||||
* mslookup_client_mdns_test: disable by default
|
||||
* mslookup_client_mdns_test: no automatic skip
|
||||
* Cosmetic: mention OS#4491 in location cancel code
|
||||
* hlr_vty_subscr: prettier output for last LU seen
|
||||
* contrib: import RPM spec
|
||||
* contrib: integrate RPM spec
|
||||
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
|
||||
* contrib/jenkins: don't build osmo-gsm-manuals
|
||||
* configure.ac: set -std=gnu11
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* add osmo-mslookup-client program
|
||||
* add osmo-mslookup-client program (#2)
|
||||
* fix missing braces in LOGP_GSUP_FWD
|
||||
* gsup_client.c: fix deprecation for client create func
|
||||
* 1/2: refactor: add and use lu_fsm, osmo_gsup_req, osmo_ipa_name
|
||||
* 2/2: wrap ipa_name in osmo_cni_peer_id with type enum and union
|
||||
* gsup client: add up_down_cb(), add osmo_gsup_client_create3()
|
||||
* db v5: prep for D-GSM: add vlr_via_proxy and sgsn_via_proxy
|
||||
* enlarge the GSUP message headroom
|
||||
* test_nodes.vty: remove cruft
|
||||
* D-GSM 1/n: add mslookup server in osmo-hlr
|
||||
* D-GSM 2/n: implement mDNS method of mslookup server
|
||||
* D-GSM 3/n: implement roaming by mslookup in osmo-hlr
|
||||
* gsup_server: send routing error back to the correct peer
|
||||
* adoc: add D-GSM chapter to osmohlr-usermanual
|
||||
* drop error log for when a subscriber does not exist
|
||||
* vty: show subscriber: change format of 'last LU seen'
|
||||
* vty: show subscriber: show lu d,h,m,s ago, not just seconds
|
||||
* auc3g: officially wrap IND around IND_bitlen space
|
||||
* make osmo_cni_peer_id_cmp() NULL safe
|
||||
* osmo_gsup_req_new(): require from_peer != NULL
|
||||
* gsup_server.c: properly handle negative rc from osmo_gsup_conn_ccm_get()
|
||||
* osmo_mslookup_server_mdns_rx(): handle read() rc == 0
|
||||
* hlr_subscr_nam(): fix condition to fix nam=false notifications
|
||||
* esme_dgsm.py: add --always-fail option for debugging SMPP
|
||||
* osmo-mslookup-client: fix segfault for respond_error() caller
|
||||
* manual: describe subscriber import by SQL
|
||||
|
||||
[ Harald Welte ]
|
||||
* Revert "add osmo-mslookup-client program"
|
||||
* Revert "add mDNS lookup method to libosmo-mslookup"
|
||||
* Use OSMO_FD_* instead of deprecated BSC_FD_*
|
||||
* support the XOR algorithm for UMTS AKA
|
||||
* auc_test.c: Add some comments on what the test cases actually do
|
||||
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
|
||||
* manuals: generate vty reference xml at build time
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* db: fix possible SQLite3 allocated memory leak in db_open()
|
||||
* gsup_server: fix typo: s/omso_gsup_message/osmo_gsup_message/
|
||||
* debian/control: change maintainer to the Osmocom team / mailing list
|
||||
* cosmetic: fix spelling in logging message: existAnt -> existEnt
|
||||
* doc/manuals: fix s/There/The/ in 'USSD Configuration'
|
||||
* doc/manuals: re-organize description of internal USSD handlers
|
||||
* USSD: fix handle_ussd(): do not free() unconditionally
|
||||
* USSD: add special 'idle' handler to IUSE for testing
|
||||
|
||||
[ Eric ]
|
||||
* configure.ac: fix libtool issue with clang and sanitizer
|
||||
|
||||
[ Philipp Maier ]
|
||||
* doc: do not use loglevel info for log category ss
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* configure.ac: Fix trailing whitespace
|
||||
* doc: Update VTY reference xml file
|
||||
* Support setting rt-prio and cpu-affinity mask through VTY
|
||||
* Set TCP NODELAY sockopt to GSUP cli and srv connections
|
||||
* contrib/jenkins: Enable parallel make in make distcheck
|
||||
* .gitignore: Ignore new autofoo tmp files
|
||||
* tests: Replace deprecated API log_set_print_filename
|
||||
|
||||
[ Keith ]
|
||||
* osmo-hlr-db-tool: Make import from osmo-nitb less "lossy"
|
||||
* Correct vty inline help for show subscriber
|
||||
* Add vty command to show summary of all or filtered subscribers
|
||||
* Fix Coverity Warnings
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 18:13:53 +0100
|
||||
|
||||
osmo-hlr (1.2.0) unstable; urgency=medium
|
||||
|
||||
[ Ruben Undheim ]
|
||||
* Fix test for return codes on mipsel and alpha archs
|
||||
|
||||
[ Thorsten Alteholz ]
|
||||
* fix spelling errors detected by lintian
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* tests: Fix db_test err file to expect error code name instead of value
|
||||
|
||||
[ Oliver Smith ]
|
||||
* tests/test_nodes.vty: check less libosmocore cmds
|
||||
* tests/db_upgrade: disable for old sqlite versions
|
||||
* gitignore: add tests/db_upgrade/*.dump
|
||||
* gsup_client.h: fix license header: GPLv2+
|
||||
* tests/auc: change back to python3
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* fix double free in osmo_gsup_client_enc_send()
|
||||
* db upgrade to v2: log version 2, not 1
|
||||
* fix upgrade to version 2: imei column default value
|
||||
* add --db-check option
|
||||
* hlr.sql: move comment
|
||||
* add db_upgrade test
|
||||
* hlr db schema 3: hlr_number -> msc_number
|
||||
* db.c: code dup: add db_run_statements() for arrays of statements
|
||||
* move headers to include/osmocom/hlr
|
||||
* fix upgrade test in presence of ~/.sqliterc
|
||||
* db upgrade: remove some code dup
|
||||
* add osmo_gsup_msgb_alloc()
|
||||
* Makefile convenience: add VTY_TEST var to run only one test
|
||||
* remove gsup_test
|
||||
* test_nodes.vty: tweak: add some '?' checks
|
||||
* db v4: add column last_lu_seen_ps
|
||||
|
||||
[ Harald Welte ]
|
||||
* AUC: Add support for setting the AMF separation bit to '1' for EUTRAN
|
||||
* hlr: exit(2) on unsupported positional arguments on command line
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 03 Jan 2020 12:37:35 +0100
|
||||
|
||||
osmo-hlr (1.1.0) unstable; urgency=medium
|
||||
|
||||
[ Oliver Smith ]
|
||||
|
||||
35
debian/control
vendored
35
debian/control
vendored
@@ -1,18 +1,19 @@
|
||||
Source: osmo-hlr
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||
Maintainer: Max Suraev <msuraev@sysmocom.de>
|
||||
Build-Depends: debhelper (>= 9),
|
||||
pkg-config,
|
||||
dh-autoreconf,
|
||||
dh-systemd (>= 1.5),
|
||||
autotools-dev,
|
||||
python3-minimal,
|
||||
libosmocore-dev (>= 1.5.0),
|
||||
libosmo-abis-dev (>= 1.1.0),
|
||||
libosmo-netif-dev (>= 1.1.0),
|
||||
python-minimal,
|
||||
libosmocore-dev,
|
||||
libosmo-abis-dev,
|
||||
libosmo-netif-dev,
|
||||
libsqlite3-dev,
|
||||
sqlite3,
|
||||
osmo-gsm-manuals-dev (>= 1.1.0)
|
||||
osmo-gsm-manuals-dev
|
||||
Standards-Version: 3.9.6
|
||||
Vcs-Browser: http://cgit.osmocom.org/osmo-hlr
|
||||
Vcs-Git: git://git.osmocom.org/osmo-hlr
|
||||
@@ -58,28 +59,6 @@ Description: Development headers of Osmocom GSUP client library
|
||||
.
|
||||
This package contains the development headers.
|
||||
|
||||
Package: libosmo-mslookup0
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends}
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Description: Osmocom MS lookup library
|
||||
This shared library contains routines for looking up mobile subscribers.
|
||||
|
||||
Package: libosmo-mslookup-dev
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: ${misc:Depends},
|
||||
libosmo-mslookup0 (= ${binary:Version}),
|
||||
libosmocore-dev
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Description: Development headers of Osmocom MS lookup library
|
||||
This shared library contains routines for looking up mobile subscribers.
|
||||
.
|
||||
This package contains the development headers.
|
||||
|
||||
Package: osmo-hlr-doc
|
||||
Architecture: all
|
||||
Section: doc
|
||||
|
||||
5
debian/libosmo-mslookup-dev.install
vendored
5
debian/libosmo-mslookup-dev.install
vendored
@@ -1,5 +0,0 @@
|
||||
usr/include/osmocom/mslookup
|
||||
usr/lib/*/libosmo-mslookup*.a
|
||||
usr/lib/*/libosmo-mslookup*.so
|
||||
usr/lib/*/libosmo-mslookup*.la
|
||||
usr/lib/*/pkgconfig/libosmo-mslookup.pc
|
||||
1
debian/libosmo-mslookup0.install
vendored
1
debian/libosmo-mslookup0.install
vendored
@@ -1 +0,0 @@
|
||||
usr/lib/*/libosmo-mslookup*.so.*
|
||||
1
debian/osmo-hlr.install
vendored
1
debian/osmo-hlr.install
vendored
@@ -5,5 +5,4 @@
|
||||
/usr/share/doc/osmo-hlr/sql/hlr.sql
|
||||
/usr/share/doc/osmo-hlr/sql/hlr_data.sql
|
||||
/usr/share/doc/osmo-hlr/examples/osmo-hlr.cfg
|
||||
/usr/share/osmocom/osmo-hlr-post-upgrade.sh
|
||||
/var/lib/osmocom
|
||||
|
||||
5
debian/postinst
vendored
5
debian/postinst
vendored
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
# Debian's postinst script is called on both installation and upgrade. Call the
|
||||
# post-upgrade script in both cases, it won't do anything if there is nothing
|
||||
# to do.
|
||||
/usr/share/osmocom/osmo-hlr-post-upgrade.sh
|
||||
@@ -1,22 +0,0 @@
|
||||
# OsmoHLR example configuration for Distributed GSM (mslookup)
|
||||
hlr
|
||||
gsup
|
||||
# For D-GSM roaming, osmo-hlr's GSUP must listen on a public IP:
|
||||
bind ip 10.9.8.7
|
||||
# Each HLR should identify with a distinct name
|
||||
ipa-name hlr-23
|
||||
mslookup
|
||||
# Bind mslookup mDNS server and client on default multicast address and port:
|
||||
# 239.192.23.42 port 4266
|
||||
mdns bind
|
||||
# Tell the mslookup server in osmo-hlr which IP+ports to return when a
|
||||
# remote voice call or SMS wants to deliver to a local subscriber:
|
||||
server
|
||||
# local osmo-sip-connector SIP port
|
||||
service sip.voice at 10.9.8.7 5060
|
||||
# local osmo-msc SMPP port
|
||||
service smpp.sms at 10.9.8.7 2775
|
||||
# experimental: SMS-over-GSUP: this HLR's GSUP port
|
||||
service gsup.sms at 10.9.8.7 4222
|
||||
# only required if different from above 'gsup'/'bind ip':
|
||||
#service gsup.hlr at 10.9.8.7 4222
|
||||
@@ -12,7 +12,7 @@ log stderr
|
||||
logging level main notice
|
||||
logging level db notice
|
||||
logging level auc notice
|
||||
logging level ss notice
|
||||
logging level ss info
|
||||
logging level linp error
|
||||
!
|
||||
line vty
|
||||
|
||||
@@ -14,12 +14,6 @@ if BUILD_MANUALS
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
|
||||
|
||||
VTY_REFERENCE = osmohlr-vty-reference.xml
|
||||
|
||||
BUILT_REFERENCE_XML = $(builddir)/vty/hlr_vty_reference.xml
|
||||
$(builddir)/vty/hlr_vty_reference.xml: $(top_builddir)/src/osmo-hlr
|
||||
mkdir -p $(builddir)/vty
|
||||
$(top_builddir)/src/osmo-hlr --vty-ref-xml > $@
|
||||
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||
|
||||
OSMO_REPOSITORY = osmo-hlr
|
||||
|
||||
@@ -1,491 +0,0 @@
|
||||
== Distributed GSM / Multicast MS Lookup
|
||||
|
||||
Distributed GSM (D-GSM) allows independent mobile core network stacks to provide voice, SMS and Roaming services to each
|
||||
other, without the need for centralised entities or administration authority, and in a way that is resilient against
|
||||
unstable network links between sites.
|
||||
|
||||
D-GSM aims at communal networks, where several independent sites, let's call them villages, each have a full mobile core
|
||||
network infrastructure. It elegantly provides ad-hoc service for subscribers moving across villages, and allows villages
|
||||
to dynamically join or leave the cooperative network without the need for configuration changes at other sites.
|
||||
|
||||
A challenge for linking separate sites is to find the current location of a subscriber. Typically, in mobile networks, a
|
||||
centralized entity keeps track of where to Page for subscribers. Running several fully independent sites with unreliable
|
||||
links between them makes it hard to provide such centralisation.
|
||||
|
||||
D-GSM finds subscribers by mslookup, a service provided by OsmoHLR, typically using multicast DNS queries. This allows
|
||||
routing Location Updating requests, calls, and SMS to the right site without administrative delay nor the need for a
|
||||
reliable link to a central database.
|
||||
|
||||
D-GSM is highly resilient against single sites or links becoming temporarily unavailable. Service between still
|
||||
reachable sites simply continues; Service to a disconnected site resumes as soon as it becomes reachable again.
|
||||
|
||||
This brings an entirely new paradigm to mobile core network infrastructure: as sites become reachable on the IP network
|
||||
and join the common IP multicast group, services between them become available immediately. Basically, the only premise
|
||||
is that IP routing and multicast works across sites, and that each site uses unique IPA names in the GSUP config.
|
||||
|
||||
This chapter describes how D-GSM and mslookup work, and how to configure sites to use D-GSM, using Osmocom core network
|
||||
infrastructure.
|
||||
|
||||
=== Finding Subscribers: mslookup Clients
|
||||
|
||||
There are two fundamentally distinct subscriber lookups provided by the mslookup service.
|
||||
|
||||
==== Find the Current Location of an MSISDN
|
||||
|
||||
[[fig_dgsm_connect]]
|
||||
.mslookup for connecting subscribers: Alice is visiting village C; a phone call gets routed directly to her current location independently from her resident village infrastructure
|
||||
[graphviz]
|
||||
----
|
||||
digraph G {
|
||||
rankdir=LR
|
||||
|
||||
subgraph cluster_village_b {
|
||||
label="Village B"
|
||||
ms_bob [label="Bob\n(from village B)",shape=box]
|
||||
pbx_b [label="SIP B"]
|
||||
}
|
||||
|
||||
subgraph cluster_village_c {
|
||||
label="Village C"
|
||||
ms_alice [label="Alice\n(from village A)",shape=box]
|
||||
msc_c [label="MSC C"]
|
||||
hlr_c [label="HLR C"]
|
||||
sip_c [label="SIP C"]
|
||||
}
|
||||
|
||||
ms_alice -> msc_c [style=dashed,arrowhead=none]
|
||||
msc_c -> hlr_c [label="attached",style=dashed,arrowhead=none]
|
||||
ms_bob -> pbx_b [label="call Alice"]
|
||||
pbx_b -> hlr_c [label="mslookup by MSISDN",style=dotted,dir=both]
|
||||
pbx_b -> sip_c -> msc_c -> ms_alice [label="call"]
|
||||
}
|
||||
----
|
||||
|
||||
For example, if a subscriber is currently visiting another village, establish a phone call / send SMS towards that
|
||||
village.
|
||||
|
||||
- To deliver a phone call, a SIP agent integrates an mslookup client to request the SIP service of an MSISDN's current
|
||||
location (example: <<dgsm_conf_dialplan>>). It receives an IP address and port to send the SIP Invite to.
|
||||
|
||||
- To deliver an SMS, an ESME integrates an mslookup client to request the SMPP service of an MSISDN's current location
|
||||
(example: <<dgsm_conf_esme_smpp>>).
|
||||
|
||||
The current location of a subscriber may change at any time, and, when moving across locations, a subscriber may
|
||||
suddenly lose reception to the previous location without explicitly detaching. Hence an mslookup request for the current
|
||||
location of an MSISDN may get numerous responses. To find the currently valid location, mslookup includes the age of the
|
||||
subscriber record, i.e. how long ago the subscriber was last reached. The one response with the youngest age reflects
|
||||
the current location.
|
||||
|
||||
In order to evaluate several responses, mslookup always waits for a fixed amount of time (1 second), and then evaluates
|
||||
the available responses.
|
||||
|
||||
Services are not limited to SIP and SMPP, arbitrarily named services can be added to the mslookup configuration.
|
||||
|
||||
.Message sequence for locating an MSISDN to deliver a voice call
|
||||
["mscgen"]
|
||||
----
|
||||
msc {
|
||||
hscale="2";
|
||||
moms[label="MS,BSS\nvillage A"],momsc[label="MSC,MGW\nvillage A"],mosipcon[label="osmo-sip-connector\nvillage A"],mopbx[label="PBX\nvillage A"],mthlr[label="OsmoHLR\nvillage B"],mtsipcon[label="osmo-sip-connector\nvillage B"],mtmsc[label="MGW,MSC\nvillage B"],mtms[label="RAN,MS\nvillage B"];
|
||||
|
||||
moms =>> momsc [label="CC Setup"];
|
||||
momsc =>> mosipcon [label="MNCC_SETUP_IND"];
|
||||
mosipcon =>> mopbx [label="SIP INVITE"];
|
||||
mopbx rbox mopbx [label="dialplan: launch mslookup by MSISDN"];
|
||||
--- [label="multicast-DNS query to all connected sites"];
|
||||
...;
|
||||
mopbx <<= mthlr [label="mDNS response\n(age)"];
|
||||
mopbx rbox mopbx [label="wait ~ 1s for more mDNS responses"];
|
||||
...;
|
||||
mopbx =>> mtsipcon [label="SIP INVITE (MT)"];
|
||||
mtmsc <<= mtsipcon [label="MNCC_SETUP_REQ"];
|
||||
mtms <<= mtmsc [label="Paging (CC)"];
|
||||
moms rbox mtms [label="voice call commences"];
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
==== Find the Home HLR for an IMSI
|
||||
|
||||
[[fig_dgsm_roaming]]
|
||||
.mslookup for Roaming: Alice visits village B; she can attach to the local mobile network, which proxies HLR administration to her home village.
|
||||
[graphviz]
|
||||
----
|
||||
digraph G {
|
||||
rankdir=LR
|
||||
|
||||
subgraph cluster_village_b {
|
||||
label="Village B"
|
||||
|
||||
ms_alice [label="Alice\n(from village A)",shape=box]
|
||||
msc_b [label="MSC B"]
|
||||
hlr_b [label="HLR B"]
|
||||
}
|
||||
|
||||
subgraph cluster_village_a {
|
||||
label="Village A"
|
||||
hlr_alice [label="Alice's home HLR"]
|
||||
}
|
||||
|
||||
ms_alice -> msc_b -> hlr_b [label="Location\nUpdating"]
|
||||
hlr_b -> hlr_alice [label="mslookup by IMSI",style=dotted,dir=both]
|
||||
hlr_b -> hlr_alice [label="GSUP proxy forwarding"]
|
||||
}
|
||||
----
|
||||
|
||||
For example, when attaching to a local network, a local resident gets serviced directly by the local village's HLR,
|
||||
while a visitor from another village gets serviced by the remote village's HLR (Roaming).
|
||||
|
||||
A home HLR typically stays the same for a given IMSI. If the home site is reachable, there should be exactly one
|
||||
response to an mslookup request asking for it. The age of such a home-HLR response is always sent as zero.
|
||||
|
||||
If a response's age is zero, mslookup does not wait for further responses and immediately uses the result.
|
||||
|
||||
If there were more than one HLR accepting service for an IMSI, the one with the shortest response latency is used.
|
||||
|
||||
=== mslookup Configuration
|
||||
|
||||
OsmoHLR the main mslookup agent. It provides the responses for both current location services as well as for locating
|
||||
the fixed home-HLR. But naturally, depending on the mslookup request's purpose, different OsmoHLR instances will respond
|
||||
for a given subscriber.
|
||||
|
||||
- When querying the home HLR, it is always the (typically single) home HLR instance that sends the mslookup response. As
|
||||
soon as it finds the queried IMSI in the local HLR database, an OsmoHLR will respond to home-HLR requests.
|
||||
In <<fig_dgsm_roaming>>, Alice's home HLR responds to the Roaming request ("where is the home HLR?").
|
||||
|
||||
- When querying the location of an MSISDN, it is always the HLR proxy nearest to the servicing MSC that sends the
|
||||
mslookup response. Even though the home HLR keeps the Location Updating record also for Roaming cases, it will only
|
||||
respond to an mslookup service request if the subscriber has attached at a directly connected MSC. If attached at a
|
||||
remote MSC, that MSC's remote HLR will be the GSUP proxy for the home HLR, and the remote HLR is responsible for
|
||||
responding to service requests.
|
||||
In <<fig_dgsm_roaming>>, HLR B is the nearest proxy and will answer all service requests ("where is this MSISDN?").
|
||||
Alice's home HLR will not answer service requests, because it detects that the servicing MSC is connected via another
|
||||
HLR proxy.
|
||||
|
||||
[[dgsm_example_config]]
|
||||
==== Example
|
||||
|
||||
Here is an osmo-hlr.cfg mslookup configuration example for one site, which is explained in subsequent chapters.
|
||||
|
||||
hlr
|
||||
gsup
|
||||
bind ip 10.9.8.7
|
||||
ipa-name hlr-23
|
||||
mslookup
|
||||
mdns bind
|
||||
server
|
||||
service sip.voice at 10.9.8.7 5060
|
||||
service smpp.sms at 10.9.8.7 2775
|
||||
|
||||
OsmoHLR has both an mslookup server and a client.
|
||||
|
||||
- The server responds to incoming service and home-HLR requests, when the local HLR is responsible.
|
||||
- The client is used as GSUP proxy to a remote home HLR (found by mslookup upon a locally unknown IMSI).
|
||||
- The client may also be used for forwarding SMS-over-GSUP.
|
||||
|
||||
The mslookup service can be implemented by various methods.
|
||||
At the time of writing, the only method implemented is mDNS.
|
||||
|
||||
==== mDNS
|
||||
|
||||
The stock mslookup method is mDNS, multicast DNS. It consists of standard DNS encoding according to <<ietf-rfc1035>> and
|
||||
<<ietf-rfc3596>>, but sent and received on IP multicast. In the response, standard A and AAAA records return the
|
||||
service's IP address, while additional TXT records provide the service's port number and the MS attach age.
|
||||
|
||||
TIP: To watch D-GSM mDNS conversations in wireshark, select "udp.port == 4266" (the default mslookup mDNS port
|
||||
number), right click on the packet to "Decode as...", and select "DNS".
|
||||
|
||||
In OsmoHLR, the mDNS server and client are typically both enabled at the same time:
|
||||
|
||||
mslookup
|
||||
mdns bind
|
||||
|
||||
Server and client can also be enabled/disabled individually:
|
||||
|
||||
mslookup
|
||||
server
|
||||
mdns bind
|
||||
client
|
||||
mdns bind
|
||||
|
||||
These examples use the default mslookup multicast IP address and port. It is possible to configure custom IP address and
|
||||
port, but beware that the IP address must be from a multicast range, see <<ietf-rfc5771>>:
|
||||
|
||||
mslookup
|
||||
mdns bind 239.192.23.42 4266
|
||||
|
||||
Domain names generated from mslookup queries (e.g. "sip.voice.123.msisdn") should not collide with IANA permitted
|
||||
domains. Therefore we add the "mdns.osmocom.org" suffix. It can be overridden as follows:
|
||||
|
||||
mslookup
|
||||
mdns domain-suffix mdns.osmocom.org
|
||||
|
||||
==== Server: Site Services
|
||||
|
||||
The mslookup server requires a list of service addresses provided at the local site, in order to respond to service
|
||||
requests matching locally attached subscribers.
|
||||
|
||||
mslookup
|
||||
server
|
||||
service sip.voice at 10.9.8.7 5060
|
||||
service smpp.sms at 10.9.8.7 2775
|
||||
|
||||
In this example:
|
||||
|
||||
- "10.9.8.7 5060" are the IP address and port on which the local site's osmo-sip-connector is bound to receive SIP
|
||||
Invite requests.
|
||||
- "10.9.8.7 2775" are the local site's OsmoMSC SMPP bind address and port.
|
||||
|
||||
Obviously, these IP addresses must be routable back to this site from all other sites. Using link-local or "ANY"
|
||||
addresses, like 127.0.0.1 or 0.0.0.0, will not work here. Instead, each service config requires a public IP address that
|
||||
all remote requestors are able to reach (not necessarily on the host that osmo-hlr is running on).
|
||||
|
||||
If a site has more than one MSC, services can also be configured for each MSC individually, keyed by the IPA unit name
|
||||
that each MSC sends on the GSUP link:
|
||||
|
||||
mslookup
|
||||
server
|
||||
msc ipa-name msc-262-42-0
|
||||
service sip.voice at 10.11.12.13 5060
|
||||
service smpp.sms at 10.11.12.13 2775
|
||||
msc ipa-name msc-901-70-0
|
||||
service sip.voice at 10.9.8.7 5060
|
||||
service smpp.sms at 10.9.8.7 2775
|
||||
|
||||
Here, "msc-262-42-0" is the IPA name of a local OsmoMSC instance. To configure an OsmoMSC's IPA name on the GSUP link,
|
||||
see osmo-msc.cfg, setting `hlr` / `ipa-name`.
|
||||
|
||||
For mslookup service responses, only Location Updatings in the Circuit Switched domain are relevant. OsmoHLR does manage
|
||||
IMSIs attaching in the Packet Switched domain (via an SGSN) similarly to Circuit Switched (via an MSC), but mslookup
|
||||
completely ignores the Packet Switched attach status.
|
||||
|
||||
==== Server: Own GSUP Address
|
||||
|
||||
When responding to home-HLR requests, OsmoHLR implicitly by default responds with its locally configured GSUP bind
|
||||
address (setting `hlr` / `gsup` / `bind ip`). If required, an explicit local GSUP address and port can be configured,
|
||||
for example:
|
||||
|
||||
hlr
|
||||
gsup
|
||||
bind ip 0.0.0.0
|
||||
ipa-name hlr-23
|
||||
mslookup
|
||||
server
|
||||
# osmo-hlr's own GSUP address to send in mslookup responses:
|
||||
service gsup.hlr at 10.9.8.7 4222
|
||||
|
||||
The gsup.hlr service can only be configured globally (because requests come from arbitrary mDNS clients, before a
|
||||
Location Updating has associated the IMSI with the requesting MSC).
|
||||
|
||||
==== Client IPA Naming
|
||||
|
||||
For reliable GSUP proxy routing to a remote HLR (Roaming), it is important that each GSUP client, i.e. each HLR, MSC and
|
||||
SGSN instance, has a unique IPA name.
|
||||
|
||||
Example for configuring an OsmoHLR instance's IPA name:
|
||||
|
||||
hlr
|
||||
gsup
|
||||
ipa-name hlr-23
|
||||
|
||||
Here, "hlr-23" is the unique identification of this OsmoHLR instance across all potentially connected D-GSM sites.
|
||||
|
||||
Furthermore, each MSC and SGSN must have a uniquely distinct IPA name across all sites (here "msc-262-42-0" and
|
||||
"msc-901-70-0" are used as example IPA names for local MSCs).
|
||||
|
||||
When this OsmoHLR connects to a remote HLR, be it for GSUP proxying or SMS-over-GSUP, it communicates its own IPA name
|
||||
(on GSUP link-up) as well as the IPA name of the requesting client MSC/SGSN (as Source Name in each message) to the
|
||||
remote OsmoHLR GSUP server. These names are used to route GSUP responses back to the respective requesting peer.
|
||||
|
||||
If two MSCs were accidentally configured with identical names, a problem will occur as soon as both MSCs attempt to
|
||||
attach to the same OsmoHLR (either directly or via GSUP proxying). The MSC that shows up first will work normally, but
|
||||
any duplicate that shows up later will be rejected, since a route for its name already exists.
|
||||
|
||||
=== Queries
|
||||
|
||||
In URL notation, typical mslookup queries look like:
|
||||
|
||||
gsup.hlr.123456789.imsi
|
||||
sip.voice.123.msisdn
|
||||
smpp.sms.123.msisdn
|
||||
|
||||
A query consists of
|
||||
|
||||
- a service name ("gsup.hlr"),
|
||||
- an id ("123456789"),
|
||||
- the id type ("imsi").
|
||||
|
||||
The calling client also defines a timeout to wait for responses.
|
||||
|
||||
The mslookup ID types are fixed, while service names can be chosen arbitrarily.
|
||||
|
||||
.mslookup ID types, no other ID types are understood by mslookup
|
||||
[options="header",width="100%",cols="20%,80%"]
|
||||
|===
|
||||
|ID Type|Description
|
||||
|imsi|An IMSI as existing in an OsmoHLR subscriber database
|
||||
|msisdn|A phone number as configured in an OsmoHLR subscriber database
|
||||
|===
|
||||
|
||||
.mslookup service name conventions, arbitrary service names can be added as required
|
||||
[options="header",width="100%",cols="20%,20%,60%"]
|
||||
|===
|
||||
|Service Name|Protocol|Description
|
||||
|gsup.hlr | GSUP | Home HLR's GSUP server, to handle Location Updating related procedures
|
||||
|sip.voice | SIP | OsmoSIPConnector, to receive a SIP Invite (MT side of a call)
|
||||
|smpp.sms | SMPP | Destination OsmoMSC (or other SMPP server) to deliver an SMS to the recipient
|
||||
|gsup.sms | GSUP | GSUP peer to deliver an SMS to the recipient using SMS-over-GSUP
|
||||
|===
|
||||
|
||||
Arbitrarily named services can be added to the mslookup configuration and queried by mslookup clients; as soon as a
|
||||
service name is present in osmo-hlr.cfg, it can be queried from any mslookup client.
|
||||
|
||||
Service names should consist of a protocol name (like "sip", "gsup", "english") and an intended action/entity (like
|
||||
"voice", "hlr", "greeting").
|
||||
|
||||
=== Service Client Implementation
|
||||
|
||||
In principle, arbitrary services could query target addresses via mslookup, leaving it up to any and all kinds of
|
||||
clients to find their respective destination addresses. But of course, mslookup was designed with specific services in
|
||||
mind, namely:
|
||||
|
||||
- SIP call agents and
|
||||
- SMS delivery (an ESME or SMSC)
|
||||
|
||||
The following chapters describe examples of setting up a working distributed core network providing SIP voice calls and
|
||||
SMS forwarding across sites.
|
||||
|
||||
==== mslookup Library
|
||||
|
||||
The OsmoHLR provides an mslookup client C library, libosmo-mslookup. Service lookups can be integrated directly
|
||||
in client programs using this library. However, its mDNS implementation requires the libosmocore select() loop, which
|
||||
can be challenging to integrate in practice. An alternative solution is the osmo-mslookup-client tool.
|
||||
|
||||
[[dgsm_osmo_mslookup_client]]
|
||||
==== osmo-mslookup-client
|
||||
|
||||
The mslookup C library is available, but often, a simpler approach for client implementations is desirable:
|
||||
|
||||
- When querying for a service address, the client is typically interested in the single final best result (youngest age
|
||||
/ first responding home HLR).
|
||||
- Voice call and SMS clients typically would block until an mslookup result is known. For example, the FreeSwitch
|
||||
dialplan integration expects a result synchronously, i.e. without waiting for mslookup responses via a select() loop.
|
||||
- Integrating the libosmocore select() loop required for mDNS can break the already existing socket handling in the
|
||||
client program.
|
||||
|
||||
The osmo-mslookup-client cmdline tool provides a trivial way to synchronously acquire the single result for an mslookup
|
||||
request. The service client can invoke an osmo-mslookup-client process per request and read the result from stdout.
|
||||
|
||||
Each invocation obviously spawns a separate process and opens a multicast socket for mDNS. For better scalability,
|
||||
osmo-mslookup-client can also be run as a daemon, providing results via a unix domain socket. Using synchronous write()
|
||||
and recv() allows blocking until a result is received without interfering with the client program's select() setup.
|
||||
|
||||
By itself, osmo-mslookup-client is also helpful as a diagnostic tool:
|
||||
|
||||
----
|
||||
$ osmo-mslookup-client sip.voice.1001.msisdn
|
||||
sip.voice.1001.msisdn ok 10.9.8.7 5060
|
||||
|
||||
$ osmo-mslookup-client gsup.hlr.901700000014701.imsi
|
||||
gsup.hlr.901700000014701.imsi ok 10.9.8.7 4222
|
||||
|
||||
$ osmo-mslookup-client gsup.hlr.111111.imsi
|
||||
gsup.hlr.111111.imsi not-found
|
||||
|
||||
$ osmo-mslookup-client gsup.hlr.1001.msisdn sip.voice.1001.msisdn smpp.sms.1001.msisdn foo.1001.msisdn
|
||||
gsup.hlr.1001.msisdn ok 10.9.8.7 4222
|
||||
foo.1001.msisdn not-found
|
||||
smpp.sms.1001.msisdn ok 10.9.8.7 2775
|
||||
sip.voice.1001.msisdn ok 10.9.8.7 5060
|
||||
|
||||
$ osmo-mslookup-client --csv-headers gsup.hlr.901700000014701.imsi
|
||||
QUERY RESULT V4_IP V4_PORT V6_IP V6_PORT
|
||||
gsup.hlr.901700000014701.imsi ok 10.9.8.7 4222
|
||||
|
||||
$ osmo-mslookup-client -f json gsup.hlr.901700000014701.imsi
|
||||
{"query": "gsup.hlr.901700000014701.imsi", "result": "ok", "v4": ["10.9.8.7", "4222"]}
|
||||
----
|
||||
|
||||
For full help including example client invocations in Python, see the output of:
|
||||
|
||||
osmo-mslookup-client -h
|
||||
|
||||
==== SIP Service Client
|
||||
|
||||
[[dgsm_conf_dialplan]]
|
||||
===== FreeSwitch dialplan.py
|
||||
|
||||
The FreeSWITCH PBX software <<freeswitch_pbx>> offers a Python integration to determine a SIP call recipient by a custom
|
||||
dialplan implementation. An example dialplan implementation for FreeSWITCH that uses D-GSM mslookup is provided in the
|
||||
osmo-hlr source tree under `contrib`, called `freeswitch_dialplan_dgsm.py`.
|
||||
|
||||
To integrate it with your FREESWITCH setup, add a new `extension` block to your `dialplan/public.xml`:
|
||||
|
||||
----
|
||||
<extension name="outbound">
|
||||
<condition field="destination_number" expression=".*">
|
||||
<action application="set" data="hangup_after_bridge=true"/>
|
||||
<action application="set" data="session_in_hangup_hook=true"/>
|
||||
<action application="set" data="ringback=%(2000, 4000, 440.0, 480.0)"/>
|
||||
<action application="python" data="freeswitch_dialplan_dgsm"/>
|
||||
</condition>
|
||||
</extension>
|
||||
----
|
||||
|
||||
Make sure that the dir containing `freeswitch_dialplan_dgsm.py` is in your `PYTHONPATH` environment variable, and start
|
||||
the server:
|
||||
|
||||
----
|
||||
$ export PYTHONPATH="$PYTHONPATH:/home/user/code/osmo-hlr/contrib/dgsm"
|
||||
$ freeswitch -nf -nonat -nonatmap -nocal -nort -c
|
||||
----
|
||||
|
||||
==== SMS Service Client
|
||||
|
||||
[[dgsm_conf_esme_smpp]]
|
||||
===== SMS via SMPP Port
|
||||
|
||||
An example ESME using D-GSM mslookup, `esme_dgsm.py`, is provided in the osmo-hlr source tree under `contrib`. It
|
||||
attaches to OsmoMSC's SMPP port to send SMS to recipients determined by mslookup.
|
||||
|
||||
OsmoMSC should be configured as "smpp-first", so that all SMS routing is determined by mslookup. If configured without
|
||||
smpp-first, OsmoMSC may try to deliver an SMS locally, even though the recipient has recently moved to a different site.
|
||||
|
||||
An example OsmoMSC configuration to work with esme_dgsm.py:
|
||||
|
||||
----
|
||||
smpp
|
||||
local-tcp-ip 127.0.0.1 2775
|
||||
system-id test-msc
|
||||
policy closed
|
||||
smpp-first
|
||||
# outgoing to esme_dgsm.py
|
||||
esme OSMPP
|
||||
no alert-notifications
|
||||
password foo
|
||||
default-route
|
||||
# incoming from esme_dgsm.py
|
||||
esme ISMPP
|
||||
no alert-notifications
|
||||
password foo
|
||||
----
|
||||
|
||||
Launch esme_dgsm.py alongside OsmoMSC:
|
||||
|
||||
----
|
||||
./esme_dgsm.py --src-host 127.0.0.1
|
||||
----
|
||||
|
||||
esme_dgsm.py will be notified via SMPP for each SMS to be delivered, and will forward them either to a remote
|
||||
recipient, or back to the same OsmoMSC, depending on the mslookup result. If the MSISDN is not reachable (or
|
||||
esme_dgsm.py can't handle the message for other reasons), it returns the RSYSERR code back to OsmoMSC.
|
||||
|
||||
Note that the esme_dgsm.py is a proof of concept and should not be used in production. It has several limitations, such
|
||||
as not supporting multipart SMS messages.
|
||||
|
||||
===== SMS-Over-GSUP
|
||||
|
||||
The GSUP protocol defines SMS delivery messages. When OsmoMSC is configured to deliver SMS via GSUP, MO SMS are directly
|
||||
forwarded to the HLR, which will determine where to forward the SMS-over-GSUP messages using its mslookup client.
|
||||
|
||||
FIXME implement this
|
||||
@@ -54,7 +54,7 @@ this database file will be created in the current working directory.
|
||||
|
||||
Alternatively, you may use the `osmo-hlr-db-tool`, which is installed along
|
||||
with `osmo-hlr`, to bootstrap an empty database, or to migrate subscriber data
|
||||
from an old 'OsmoNITB' database. See <<db_import_nitb>>.
|
||||
from an old 'OsmoNITB' database. See `osmo-hlr-db-tool --help`.
|
||||
|
||||
=== Multiple instances
|
||||
|
||||
|
||||
@@ -127,83 +127,3 @@ OsmoHLR# subscriber imei 35761300444848 show
|
||||
----
|
||||
<1> Randomly generated 5 digit MSISDN
|
||||
<2> Disabled CS and PS NAM prevent the subscriber from accessing the network
|
||||
|
||||
|
||||
=== Import Subscriber Data
|
||||
|
||||
==== Scripted Import
|
||||
|
||||
WARNING: It is not generally a good idea to depend on the HLR database's internal table structure, but in the lack of an
|
||||
automated import procedure, this example is provided as an ad-hoc method to aid automated subscriber import. This is not
|
||||
guaranteed to remain valid.
|
||||
|
||||
NOTE: We may add CSV and other import methods to the `osmo-hlr-db-tool`, but so far that is not implemented. Contact the
|
||||
community if you are interested in such a feature being implemented.
|
||||
|
||||
NOTE: `sqlite3` is available from your distribution packages or `sqlite.org`.
|
||||
|
||||
Currently, probably the easiest way to automatically import subscribers to OsmoHLR is to write out a text file with SQL
|
||||
commands per subscriber, and feed that to `sqlite3`, as described below.
|
||||
|
||||
A difficulty is to always choose subscriber IDs that are not yet in use. For an initial import, the subscriber ID may be
|
||||
incremented per subscriber record. If adding more subscribers to an existing database, it is necessary to choose
|
||||
subscriber IDs that are not yet in use. Get the highest ID in use with:
|
||||
|
||||
----
|
||||
sqlite3 hlr.db 'select max(id) from subscriber'
|
||||
----
|
||||
|
||||
A full SQL example of adding a single subscriber with id 23, IMSI 001010123456789, MSISDN 1234, Ki for COMP128v1, and K
|
||||
and OPC for Milenage:
|
||||
|
||||
----
|
||||
INSERT subscriber (id, imsi, msisdn) VALUES (23, '001010123456789', '1234');
|
||||
|
||||
INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki)
|
||||
VALUES(23, 1, '0123456789abcdef0123456789abcdef');
|
||||
|
||||
INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc)
|
||||
VALUES(23, 5, '0123456789abcdef0123456789abcdef',NULL,'0123456789abcdef0123456789abcdef');
|
||||
----
|
||||
|
||||
Table entries to `auc_2g` and/or `auc_3g` may be omitted if no such key material is required.
|
||||
|
||||
UMTS Milenage auth (on both 2G and 3G RAN) is configured by the `auc_3g` table. `algo_id_3g` must currently always be 5
|
||||
(MILENAGE).
|
||||
|
||||
The algorithm IDs for `algo_id_2g` and `algo_id_3g` are:
|
||||
|
||||
.Algorithm IDs in OsmoHLR's database
|
||||
[options="header",width="50%",cols="40%,60%"]
|
||||
|===
|
||||
|`algo_id_2g` / `algo_id_3g` | Authentication Algorithm
|
||||
| 1 | COMP128v1
|
||||
| 2 | COMP128v2
|
||||
| 3 | COMP128v3
|
||||
| 4 | XOR
|
||||
| 5 | MILENAGE
|
||||
|===
|
||||
|
||||
Create an empty HLR database with
|
||||
|
||||
----
|
||||
osmo-hlr-db-tool -l hlr.db create
|
||||
----
|
||||
|
||||
Repeat above SQL commands per subscriber, incrementing the subscriber ID for each block, then feed the SQL commands for
|
||||
the subscribers to be imported to the `sqlite3` command line tool:
|
||||
|
||||
----
|
||||
sqlite3 hlr.db < subscribers.sql
|
||||
----
|
||||
|
||||
[[db_import_nitb]]
|
||||
==== Import OsmoNITB database
|
||||
|
||||
To upgrade from old OsmoNITB to OsmoHLR, use `osmo-hlr-db-tool`:
|
||||
|
||||
----
|
||||
osmo-hlr-db-tool -l hlr.db import-nitb-db nitb.db
|
||||
----
|
||||
|
||||
Be aware that the import is lossy, only the IMSI, MSISDN, nam_cs/ps and 2G auth data are set.
|
||||
|
||||
@@ -50,29 +50,15 @@ prefix route to the named EUSE. All USSD short codes starting with *123 will be
|
||||
routed to the named EUSE.
|
||||
|
||||
`ussd route prefix *#100# internal own-msisdn` installs a prefix route
|
||||
to the named internal USSD handler. The above command will restore
|
||||
to the named internal USSD handler. There above command will restore
|
||||
the old behavior, in which *#100# will return a text message containing
|
||||
the subscribers own phone number. More information on internal USSD
|
||||
handlers can be found in <<iuse_handlers>>.
|
||||
the subscribers own phone number. There is one other handler called
|
||||
`own-imsi` which will return the IMSI instead of the MSISDN.
|
||||
|
||||
`ussd default-route external foobar-00-00-00-00-00-00` installs a
|
||||
default route to the named EUSE. This means that all USSD codes for
|
||||
which no more specific route exists will be routed to the named EUSE.
|
||||
|
||||
[[iuse_handlers]]
|
||||
=== Built-in USSD handlers
|
||||
|
||||
OsmoHLR has an Internal USSD Entity (IUSE) that allows to handle some
|
||||
USSD requests internally. It features a set of simple handlers, which
|
||||
can be assigned to one or more USSD request prefixes:
|
||||
|
||||
* `own-msisdn` returns subscriber's MSISDN (if assigned);
|
||||
* `own-imsi` returns subscriber's IMSI;
|
||||
* `test-idle` keeps the session idle until the MS terminates it, or
|
||||
the guard timer expires (may be useful for testing).
|
||||
|
||||
Additional handlers can be added on request.
|
||||
|
||||
=== Example EUSE program
|
||||
|
||||
We have provided an example EUSE developed in C language using existing
|
||||
|
||||
@@ -24,12 +24,8 @@ include::{srcdir}/chapters/control.adoc[]
|
||||
|
||||
include::./common/chapters/control_if.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/dgsm.adoc[]
|
||||
|
||||
include::./common/chapters/gsup.adoc[]
|
||||
|
||||
include::./common/chapters/vty_cpu_sched.adoc[]
|
||||
|
||||
include::./common/chapters/port_numbers.adoc[]
|
||||
|
||||
include::./common/chapters/bibliography.adoc[]
|
||||
@@ -37,3 +33,4 @@ include::./common/chapters/bibliography.adoc[]
|
||||
include::./common/chapters/glossary.adoc[]
|
||||
|
||||
include::./common/chapters/gfdl.adoc[]
|
||||
|
||||
|
||||
1431
doc/manuals/vty/hlr_vty_reference.xml
Normal file
1431
doc/manuals/vty/hlr_vty_reference.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,2 @@
|
||||
SUBDIRS = osmocom
|
||||
nobase_include_HEADERS = osmocom/gsupclient/gsup_client.h
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
osmocom/gsupclient/cni_peer_id.h \
|
||||
osmocom/gsupclient/gsup_client.h \
|
||||
osmocom/gsupclient/gsup_req.h \
|
||||
osmocom/mslookup/mdns.h \
|
||||
osmocom/mslookup/mdns_sock.h \
|
||||
osmocom/mslookup/mslookup_client_fake.h \
|
||||
osmocom/mslookup/mslookup_client.h \
|
||||
osmocom/mslookup/mslookup_client_mdns.h \
|
||||
osmocom/mslookup/mslookup.h \
|
||||
$(NULL)
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
SUBDIRS = \
|
||||
hlr \
|
||||
mslookup \
|
||||
$(NULL)
|
||||
@@ -1,66 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
/*! IPA Name: Arbitrary length blob, not necessarily zero-terminated.
|
||||
* In osmo-hlr, struct hlr_subscriber is mostly used as static reference and cannot serve as talloc context, which is
|
||||
* why this is also implemented as a fixed-maximum-size buffer instead of a talloc'd arbitrary sized buffer.
|
||||
* NOTE: The length of val may be extended in the future if it becomes necessary.
|
||||
* At the time of writing, this holds IPA unit name strings of very limited length.
|
||||
*/
|
||||
struct osmo_ipa_name {
|
||||
size_t len;
|
||||
uint8_t val[128];
|
||||
};
|
||||
|
||||
bool osmo_ipa_name_is_empty(const struct osmo_ipa_name *ipa_name);
|
||||
int osmo_ipa_name_set(struct osmo_ipa_name *ipa_name, const uint8_t *val, size_t len);
|
||||
int osmo_ipa_name_set_str(struct osmo_ipa_name *ipa_name, const char *str_fmt, ...);
|
||||
int osmo_ipa_name_cmp(const struct osmo_ipa_name *a, const struct osmo_ipa_name *b);
|
||||
const char *osmo_ipa_name_to_str_c(void *ctx, const struct osmo_ipa_name *ipa_name);
|
||||
const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name);
|
||||
|
||||
enum osmo_cni_peer_id_type {
|
||||
OSMO_CNI_PEER_ID_EMPTY=0,
|
||||
OSMO_CNI_PEER_ID_IPA_NAME,
|
||||
/* OSMO_CNI_PEER_ID_GLOBAL_TITLE, <-- currently not implemented, but likely future possibility */
|
||||
};
|
||||
|
||||
extern const struct value_string osmo_cni_peer_id_type_names[];
|
||||
static inline const char *osmo_cni_peer_id_type_name(enum osmo_cni_peer_id_type val)
|
||||
{ return get_value_string(osmo_cni_peer_id_type_names, val); }
|
||||
|
||||
struct osmo_cni_peer_id {
|
||||
enum osmo_cni_peer_id_type type;
|
||||
union {
|
||||
struct osmo_ipa_name ipa_name;
|
||||
};
|
||||
};
|
||||
|
||||
bool osmo_cni_peer_id_is_empty(const struct osmo_cni_peer_id *cni_peer_id);
|
||||
int osmo_cni_peer_id_set(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni_peer_id_type type,
|
||||
const uint8_t *val, size_t len);
|
||||
int osmo_cni_peer_id_set_str(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni_peer_id_type type,
|
||||
const char *str_fmt, ...);
|
||||
int osmo_cni_peer_id_cmp(const struct osmo_cni_peer_id *a, const struct osmo_cni_peer_id *b);
|
||||
const char *osmo_cni_peer_id_to_str(const struct osmo_cni_peer_id *cni_peer_id);
|
||||
const char *osmo_cni_peer_id_to_str_c(void *ctx, const struct osmo_cni_peer_id *cni_peer_id);
|
||||
@@ -6,17 +6,18 @@
|
||||
* Author: Jacob Erlbeck
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
@@ -59,27 +60,14 @@ struct osmo_gsup_client {
|
||||
osmo_gsup_client_up_down_cb_t up_down_cb;
|
||||
};
|
||||
|
||||
struct osmo_gsup_client_config {
|
||||
/*! IP access unit which contains client identification information; must be allocated in talloc_ctx as well to
|
||||
* ensure it lives throughout the lifetime of the connection. */
|
||||
struct ipaccess_unit *ipa_dev;
|
||||
/*! GSUP server IP address to connect to. */
|
||||
const char *ip_addr;
|
||||
/*! GSUP server TCP port to connect to. */
|
||||
unsigned int tcp_port;
|
||||
/*! OPA client configuration, or NULL. */
|
||||
struct osmo_oap_client_config *oapc_config;
|
||||
/*! callback for reading from the GSUP connection. */
|
||||
osmo_gsup_client_read_cb_t read_cb;
|
||||
/*! Invoked when the GSUP link is ready for communication, and when the link drops. */
|
||||
osmo_gsup_client_up_down_cb_t up_down_cb;
|
||||
/*! User data stored in the returned gsupc->data, as context for the callbacks. */
|
||||
void *data;
|
||||
/*! Marker for future extension, always pass this as false. */
|
||||
bool more;
|
||||
};
|
||||
struct osmo_gsup_client *osmo_gsup_client_create3(void *talloc_ctx, struct osmo_gsup_client_config *config);
|
||||
|
||||
struct osmo_gsup_client *osmo_gsup_client_create3(void *talloc_ctx,
|
||||
struct ipaccess_unit *ipa_dev,
|
||||
const char *ip_addr,
|
||||
unsigned int tcp_port,
|
||||
struct osmo_oap_client_config *oapc_config,
|
||||
osmo_gsup_client_read_cb_t read_cb,
|
||||
osmo_gsup_client_up_down_cb_t up_down_cb,
|
||||
void *data);
|
||||
struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
|
||||
struct ipaccess_unit *ipa_dev,
|
||||
const char *ip_addr,
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
|
||||
struct osmo_gsup_req;
|
||||
|
||||
#define LOG_GSUP_REQ_CAT_SRC(req, subsys, level, file, line, fmt, args...) \
|
||||
LOGPSRC(subsys, level, file, line, "GSUP %u: %s: IMSI-%s %s: " fmt, \
|
||||
(req) ? (req)->nr : 0, \
|
||||
(req) ? osmo_cni_peer_id_to_str(&(req)->source_name) : "NULL", \
|
||||
(req) ? (req)->gsup.imsi : "NULL", \
|
||||
(req) ? osmo_gsup_message_type_name((req)->gsup.message_type) : "NULL", \
|
||||
##args)
|
||||
#define LOG_GSUP_REQ_CAT(req, subsys, level, fmt, args...) \
|
||||
LOG_GSUP_REQ_CAT_SRC(req, subsys, level, __FILE__, __LINE__, fmt, ##args)
|
||||
|
||||
#define LOG_GSUP_REQ_SRC(req, level, file, line, fmt, args...) \
|
||||
LOG_GSUP_REQ_CAT_SRC(req, DLGSUP, level, file, line, fmt, ##args)
|
||||
|
||||
#define LOG_GSUP_REQ(req, level, fmt, args...) \
|
||||
LOG_GSUP_REQ_SRC(req, level, __FILE__, __LINE__, fmt, ##args)
|
||||
|
||||
typedef void (*osmo_gsup_req_send_response_t)(struct osmo_gsup_req *req, struct osmo_gsup_message *response);
|
||||
|
||||
/* Keep track of an incoming request, to route back a response when it is ready.
|
||||
* Particularly, a GSUP response to a request must contain various bits of information that need to be copied from the
|
||||
* request for proxy/routing to work and for session states to remain valid. That is the main reason why (almost) all
|
||||
* GSUP request/response should go through an osmo_gsup_req, even if it is handled synchronously.
|
||||
*/
|
||||
struct osmo_gsup_req {
|
||||
/* The incoming GSUP message in decoded form. */
|
||||
const struct osmo_gsup_message gsup;
|
||||
|
||||
/* Decoding result code. If decoding failed, this will be != 0. */
|
||||
int decode_rc;
|
||||
|
||||
/* The ultimate source of this message: the source_name form the GSUP message, or, if not present, then the
|
||||
* immediate GSUP peer. GSUP messages going via a proxy reflect the initial source in the source_name.
|
||||
* This source_name is implicitly added to the routes for the conn the message was received on. */
|
||||
struct osmo_cni_peer_id source_name;
|
||||
|
||||
/* If the source_name is not an immediate GSUP peer, this is set to the closest intermediate peer between here
|
||||
* and source_name. */
|
||||
struct osmo_cni_peer_id via_proxy;
|
||||
|
||||
/* Identify this request by number, for logging. */
|
||||
unsigned int nr;
|
||||
|
||||
/* osmo_gsup_req can be used by both gsup_server and gsup_client. The individual method of actually sending a
|
||||
* GSUP message is provided by this callback. */
|
||||
osmo_gsup_req_send_response_t send_response_cb;
|
||||
|
||||
/* User supplied data pointer, may be used to provide context to send_response_cb(). */
|
||||
void *cb_data;
|
||||
|
||||
/* List entry that can be used to keep a list of osmo_gsup_req instances; not used directly by osmo_gsup_req.c,
|
||||
* it is up to using implementations to keep a list. If this is non-NULL, osmo_gsup_req_free() calls
|
||||
* llist_del() on this. */
|
||||
struct llist_head entry;
|
||||
|
||||
/* A decoded GSUP message still points into the received msgb. For a decoded osmo_gsup_message to remain valid,
|
||||
* we also need to keep the msgb. */
|
||||
struct msgb *msg;
|
||||
};
|
||||
|
||||
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id *from_peer, struct msgb *msg,
|
||||
osmo_gsup_req_send_response_t send_response_cb, void *cb_data,
|
||||
struct llist_head *add_to_list);
|
||||
void osmo_gsup_req_free(struct osmo_gsup_req *req);
|
||||
|
||||
/*! See _osmo_gsup_req_respond() for details.
|
||||
* Call _osmo_gsup_req_respond(), passing the caller's source file and line for logging. */
|
||||
#define osmo_gsup_req_respond(REQ, RESPONSE, ERROR, FINAL_RESPONSE) \
|
||||
_osmo_gsup_req_respond(REQ, RESPONSE, ERROR, FINAL_RESPONSE, __FILE__, __LINE__)
|
||||
int _osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response,
|
||||
bool error, bool final_response, const char *file, int line);
|
||||
|
||||
/*! See _osmo_gsup_req_respond_msgt() for details.
|
||||
* Call _osmo_gsup_req_respond_msgt(), passing the caller's source file and line for logging. */
|
||||
#define osmo_gsup_req_respond_msgt(REQ, MESSAGE_TYPE, FINAL_RESPONSE) \
|
||||
_osmo_gsup_req_respond_msgt(REQ, MESSAGE_TYPE, FINAL_RESPONSE, __FILE__, __LINE__)
|
||||
int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type,
|
||||
bool final_response, const char *file, int line);
|
||||
|
||||
/*! See _osmo_gsup_req_respond_err() for details.
|
||||
* Log an error message, and call _osmo_gsup_req_respond_err(), passing the caller's source file and line for logging.
|
||||
*/
|
||||
#define osmo_gsup_req_respond_err(REQ, CAUSE, FMT, args...) do { \
|
||||
LOG_GSUP_REQ(REQ, LOGL_ERROR, "%s: " FMT "\n", \
|
||||
get_value_string(gsm48_gmm_cause_names, CAUSE), ##args); \
|
||||
_osmo_gsup_req_respond_err(REQ, CAUSE, __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause,
|
||||
const char *file, int line);
|
||||
|
||||
int osmo_gsup_make_response(struct osmo_gsup_message *reply,
|
||||
const struct osmo_gsup_message *rx, bool error, bool final_response);
|
||||
|
||||
size_t osmo_gsup_message_to_str_buf(char *buf, size_t bufsize, const struct osmo_gsup_message *msg);
|
||||
char *osmo_gsup_message_to_str_c(void *ctx, const struct osmo_gsup_message *msg);
|
||||
@@ -1,20 +0,0 @@
|
||||
noinst_HEADERS = \
|
||||
auc.h \
|
||||
ctrl.h \
|
||||
db.h \
|
||||
dgsm.h \
|
||||
gsup_router.h \
|
||||
gsup_server.h \
|
||||
hlr.h \
|
||||
hlr_ussd.h \
|
||||
hlr_vty.h \
|
||||
hlr_vty_subscr.h \
|
||||
logging.h \
|
||||
lu_fsm.h \
|
||||
mslookup_server.h \
|
||||
mslookup_server_mdns.h \
|
||||
proxy.h \
|
||||
rand.h \
|
||||
remote_hlr.h \
|
||||
timestamp.h \
|
||||
$(NULL)
|
||||
@@ -1,46 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mslookup/mslookup.h>
|
||||
#include <osmocom/hlr/gsup_server.h>
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_req.h>
|
||||
|
||||
#define LOG_DGSM(imsi, level, fmt, args...) \
|
||||
LOGP(DDGSM, level, "(IMSI-%s) " fmt, imsi, ##args)
|
||||
|
||||
struct vty;
|
||||
struct remote_hlr;
|
||||
struct hlr_subscriber;
|
||||
|
||||
extern void *dgsm_ctx;
|
||||
|
||||
void dgsm_init(void *ctx);
|
||||
void dgsm_start(void *ctx);
|
||||
void dgsm_stop();
|
||||
|
||||
bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req);
|
||||
|
||||
void dgsm_vty_init();
|
||||
void dgsm_mdns_client_config_apply(void);
|
||||
|
||||
bool hlr_subscr_lu_age(const struct hlr_subscriber *subscr, uint32_t *age_p);
|
||||
@@ -1,76 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/abis/ipa.h>
|
||||
#include <osmocom/abis/ipaccess.h>
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_req.h>
|
||||
|
||||
#ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN
|
||||
#define OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN 43 /* TS 24.008 10.5.4.7 */
|
||||
#endif
|
||||
|
||||
struct osmo_gsup_conn;
|
||||
|
||||
/* Expects message in msg->l2h */
|
||||
typedef int (*osmo_gsup_read_cb_t)(struct osmo_gsup_conn *conn, struct msgb *msg);
|
||||
|
||||
struct osmo_gsup_server {
|
||||
/* private data of the application/user */
|
||||
void *priv;
|
||||
|
||||
/* list of osmo_gsup_conn */
|
||||
struct llist_head clients;
|
||||
|
||||
struct ipa_server_link *link;
|
||||
osmo_gsup_read_cb_t read_cb;
|
||||
struct llist_head routes;
|
||||
|
||||
/* Proxy requests from this server's clients to remote GSUP servers. */
|
||||
struct proxy *proxy;
|
||||
};
|
||||
|
||||
|
||||
/* a single connection to a given client (SGSN, MSC) */
|
||||
struct osmo_gsup_conn {
|
||||
struct llist_head list;
|
||||
|
||||
struct osmo_gsup_server *server;
|
||||
struct ipa_server_conn *conn;
|
||||
//struct oap_state oap_state;
|
||||
struct tlv_parsed ccm;
|
||||
|
||||
/* Set when Location Update is received: */
|
||||
bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */
|
||||
bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */
|
||||
|
||||
/* The IPA unit name received on this link. Routes with more unit names serviced by this link may exist in
|
||||
* osmo_gsup_server->routes, but this is the name the immediate peer identified as in the IPA handshake. */
|
||||
struct osmo_ipa_name peer_name;
|
||||
};
|
||||
|
||||
struct msgb *osmo_gsup_msgb_alloc(const char *label);
|
||||
|
||||
struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb *msg);
|
||||
int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg);
|
||||
int osmo_gsup_conn_ccm_get(const struct osmo_gsup_conn *clnt, uint8_t **addr,
|
||||
uint8_t tag);
|
||||
|
||||
struct osmo_gsup_server *osmo_gsup_server_create(void *ctx,
|
||||
const char *ip_addr,
|
||||
uint16_t tcp_port,
|
||||
osmo_gsup_read_cb_t read_cb,
|
||||
void *priv);
|
||||
|
||||
void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups);
|
||||
|
||||
int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
|
||||
uint8_t *apn_buf, size_t apn_buf_size);
|
||||
int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, const char *imsi, const char *msisdn,
|
||||
uint8_t *msisdn_enc, size_t msisdn_enc_size,
|
||||
uint8_t *apn_buf, size_t apn_buf_size,
|
||||
enum osmo_gsup_cn_domain cn_domain);
|
||||
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_cni_peer_id *to_peer,
|
||||
struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup);
|
||||
@@ -1,22 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void lu_rx_gsup(struct osmo_gsup_req *req);
|
||||
@@ -1,72 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/mslookup/mslookup.h>
|
||||
|
||||
struct osmo_mslookup_query;
|
||||
struct osmo_mslookup_result;
|
||||
|
||||
/*! mslookup service name used for roaming/proxying between osmo-hlr instances. */
|
||||
#define OSMO_MSLOOKUP_SERVICE_HLR_GSUP "gsup.hlr"
|
||||
|
||||
/*! What addresses to return to mslookup queries when a subscriber is attached at the local site.
|
||||
* Mapping of service name to IP address and port. This corresponds to the VTY config for
|
||||
* 'mslookup' / 'server' [/ 'msc MSC-1-2-3'] / 'service sip.voice at 1.2.3.4 1234'.
|
||||
*/
|
||||
struct mslookup_service_host {
|
||||
struct llist_head entry;
|
||||
char service[OSMO_MSLOOKUP_SERVICE_MAXLEN+1];
|
||||
struct osmo_sockaddr_str host_v4;
|
||||
struct osmo_sockaddr_str host_v6;
|
||||
};
|
||||
|
||||
/*! Sets of mslookup_service_host per connected MSC.
|
||||
* When there are more than one MSC connected to this osmo-hlr, this allows keeping separate sets of service addresses
|
||||
* for each MSC. The entry with mslookup_server_msc_wildcard as MSC name is used for all MSCs (if no match for that
|
||||
* particular MSC is found). This corresponds to the VTY config for
|
||||
* 'mslookup' / 'server' / 'msc MSC-1-2-3'.
|
||||
*/
|
||||
struct mslookup_server_msc_cfg {
|
||||
struct llist_head entry;
|
||||
struct osmo_ipa_name name;
|
||||
struct llist_head service_hosts;
|
||||
};
|
||||
|
||||
struct mslookup_service_host *mslookup_server_service_get(const struct osmo_ipa_name *msc_name, const char *service);
|
||||
|
||||
struct mslookup_service_host *mslookup_server_msc_service_get(struct mslookup_server_msc_cfg *msc, const char *service,
|
||||
bool create);
|
||||
int mslookup_server_msc_service_set(struct mslookup_server_msc_cfg *msc, const char *service,
|
||||
const struct osmo_sockaddr_str *addr);
|
||||
int mslookup_server_msc_service_del(struct mslookup_server_msc_cfg *msc, const char *service,
|
||||
const struct osmo_sockaddr_str *addr);
|
||||
|
||||
extern const struct osmo_ipa_name mslookup_server_msc_wildcard;
|
||||
struct mslookup_server_msc_cfg *mslookup_server_msc_get(const struct osmo_ipa_name *msc_name, bool create);
|
||||
|
||||
const struct mslookup_service_host *mslookup_server_get_local_gsup_addr();
|
||||
void mslookup_server_rx(const struct osmo_mslookup_query *query,
|
||||
struct osmo_mslookup_result *result);
|
||||
|
||||
bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query,
|
||||
uint32_t *lu_age_p, struct osmo_ipa_name *local_msc_name,
|
||||
char *ret_imsi, size_t ret_imsi_len);
|
||||
@@ -1,36 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/mslookup/mdns_sock.h>
|
||||
|
||||
struct osmo_mslookup_server_mdns {
|
||||
struct osmo_mslookup_server *mslookup;
|
||||
struct osmo_sockaddr_str bind_addr;
|
||||
char *domain_suffix;
|
||||
struct osmo_mdns_sock *sock;
|
||||
};
|
||||
|
||||
struct osmo_mslookup_server_mdns *osmo_mslookup_server_mdns_start(void *ctx, const struct osmo_sockaddr_str *bind_addr,
|
||||
const char *domain_suffix);
|
||||
void osmo_mslookup_server_mdns_stop(struct osmo_mslookup_server_mdns *server);
|
||||
void mslookup_server_mdns_config_apply();
|
||||
@@ -1,95 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <time.h>
|
||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/hlr/timestamp.h>
|
||||
|
||||
struct osmo_gsup_req;
|
||||
struct remote_hlr;
|
||||
|
||||
struct proxy {
|
||||
struct llist_head subscr_list;
|
||||
struct llist_head pending_gsup_reqs;
|
||||
|
||||
/* When messages arrive back from a remote HLR that this is the proxy for, reach the VLR to forward the response
|
||||
* to via this osmo_gsup_server. */
|
||||
struct osmo_gsup_server *gsup_server_to_vlr;
|
||||
|
||||
/* How long to keep proxy entries without a refresh, in seconds. */
|
||||
uint32_t fresh_time;
|
||||
|
||||
/* How often to garbage collect the proxy cache, period in seconds.
|
||||
* To change this and take effect immediately, rather use proxy_set_gc_period(). */
|
||||
uint32_t gc_period;
|
||||
|
||||
struct osmo_timer_list gc_timer;
|
||||
};
|
||||
|
||||
struct proxy_subscr_domain_state {
|
||||
struct osmo_ipa_name vlr_name;
|
||||
timestamp_t last_lu;
|
||||
|
||||
/* The name from which an Update Location Request was received. Copied to vlr_name as soon as the LU is
|
||||
* completed successfully. */
|
||||
struct osmo_ipa_name vlr_name_preliminary;
|
||||
|
||||
/* Set if this is a middle proxy, i.e. a proxy behind another proxy.
|
||||
* That is mostly to know whether the MS is attached at a local MSC/SGSN or further away.
|
||||
* It could be a boolean, but store the full name for logging. Set only at successful LU acceptance. */
|
||||
struct osmo_ipa_name vlr_via_proxy;
|
||||
};
|
||||
|
||||
struct proxy_subscr {
|
||||
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
|
||||
char msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
|
||||
struct osmo_sockaddr_str remote_hlr_addr;
|
||||
struct proxy_subscr_domain_state cs, ps;
|
||||
};
|
||||
|
||||
void proxy_init(struct osmo_gsup_server *gsup_server_to_vlr);
|
||||
void proxy_del(struct proxy *proxy);
|
||||
void proxy_set_gc_period(struct proxy *proxy, uint32_t gc_period);
|
||||
|
||||
/* The API to access / modify proxy entries keeps the implementation opaque, to make sure that we can easily move proxy
|
||||
* storage to SQLite db. */
|
||||
int proxy_subscr_get_by_imsi(struct proxy_subscr *dst, struct proxy *proxy, const char *imsi);
|
||||
int proxy_subscr_get_by_msisdn(struct proxy_subscr *dst, struct proxy *proxy, const char *msisdn);
|
||||
void proxy_subscrs_get_by_remote_hlr(struct proxy *proxy, const struct osmo_sockaddr_str *remote_hlr_addr,
|
||||
bool (*yield)(struct proxy *proxy, const struct proxy_subscr *subscr, void *data),
|
||||
void *data);
|
||||
int proxy_subscr_create_or_update(struct proxy *proxy, const struct proxy_subscr *proxy_subscr);
|
||||
int proxy_subscr_del(struct proxy *proxy, const char *imsi);
|
||||
|
||||
int proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
|
||||
struct osmo_gsup_req *req);
|
||||
void proxy_subscr_forward_to_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
|
||||
struct remote_hlr *remote_hlr, struct osmo_gsup_req *req);
|
||||
|
||||
int proxy_subscr_forward_to_vlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
|
||||
const struct osmo_gsup_message *gsup, struct remote_hlr *from_remote_hlr);
|
||||
|
||||
void proxy_subscr_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
|
||||
const struct osmo_sockaddr_str *remote_hlr_addr);
|
||||
void proxy_subscr_remote_hlr_up(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
|
||||
struct remote_hlr *remote_hlr);
|
||||
@@ -1,59 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
|
||||
struct osmo_gsup_client;
|
||||
struct osmo_gsup_message;
|
||||
struct osmo_gsup_req;
|
||||
struct msgb;
|
||||
|
||||
#define LOG_REMOTE_HLR(remote_hlr, level, fmt, args...) \
|
||||
LOGP(DDGSM, level, "(Proxy HLR-" OSMO_SOCKADDR_STR_FMT ") " fmt, \
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS((remote_hlr) ? &(remote_hlr)->addr : NULL), ##args)
|
||||
|
||||
#define LOG_REMOTE_HLR_MSG(remote_hlr, gsup_msg, level, fmt, args...) \
|
||||
LOG_REMOTE_HLR(remote_hlr, level, "%s: " fmt, osmo_gsup_message_type_name((gsup_msg)->message_type), ##args)
|
||||
|
||||
/* GSUP client link for proxying to a remote HLR. */
|
||||
struct remote_hlr {
|
||||
struct llist_head entry;
|
||||
struct osmo_sockaddr_str addr;
|
||||
struct osmo_gsup_client *gsupc;
|
||||
struct llist_head pending_up_callbacks;
|
||||
};
|
||||
|
||||
/*! Receive a remote_hlr address when connecting succeeded, or remote_hlr == NULL on error.
|
||||
* \param addr GSUP IP address and port for which the connection was requested.
|
||||
* \param remote_hlr The connected remote_hlr ready for sending, or NULL if connecting failed.
|
||||
* \param data Same a passed to remote_hlr_get_or_connect(). */
|
||||
typedef void (*remote_hlr_connect_result_cb_t)(const struct osmo_sockaddr_str *addr, struct remote_hlr *remote_hlr, void *data);
|
||||
|
||||
struct remote_hlr *remote_hlr_get_or_connect(const struct osmo_sockaddr_str *addr, bool connect,
|
||||
remote_hlr_connect_result_cb_t connect_result_cb, void *data);
|
||||
void remote_hlr_destroy(struct remote_hlr *remote_hlr);
|
||||
int remote_hlr_msgb_send(struct remote_hlr *remote_hlr, struct msgb *msg);
|
||||
void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct osmo_gsup_req *req,
|
||||
struct osmo_gsup_message *modified_gsup);
|
||||
|
||||
bool remote_hlr_is_up(struct remote_hlr *remote_hlr);
|
||||
@@ -1,28 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef time_t timestamp_t;
|
||||
void timestamp_update(timestamp_t *timestamp);
|
||||
bool timestamp_age(const timestamp_t *timestamp, uint32_t *age);
|
||||
@@ -1,6 +0,0 @@
|
||||
# most headers here are installed, see /include/Makefile.am
|
||||
|
||||
noinst_HEADERS = \
|
||||
mdns_msg.h \
|
||||
mdns_rfc.h \
|
||||
$(NULL)
|
||||
@@ -1,39 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! \file mdns.h */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/mslookup/mslookup.h>
|
||||
|
||||
#define OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT "mdns.osmocom.org"
|
||||
|
||||
struct msgb *osmo_mdns_query_encode(void *ctx, uint16_t packet_id, const struct osmo_mslookup_query *query,
|
||||
const char *domain_suffix);
|
||||
|
||||
struct osmo_mslookup_query *osmo_mdns_query_decode(void *ctx, const uint8_t *data, size_t data_len,
|
||||
uint16_t *packet_id, const char *domain_suffix);
|
||||
|
||||
struct msgb *osmo_mdns_result_encode(void *ctx, uint16_t packet_id, const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_result *result, const char *domain_suffix);
|
||||
|
||||
int osmo_mdns_result_decode(void *ctx, const uint8_t *data, size_t data_len, uint16_t *packet_id,
|
||||
struct osmo_mslookup_query *query, struct osmo_mslookup_result *result,
|
||||
const char *domain_suffix);
|
||||
@@ -1,54 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mdns_rfc.h"
|
||||
|
||||
struct osmo_mdns_record {
|
||||
struct llist_head list;
|
||||
enum osmo_mdns_rfc_record_type type;
|
||||
uint16_t length;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
struct osmo_mdns_msg_request {
|
||||
uint16_t id;
|
||||
char *domain;
|
||||
enum osmo_mdns_rfc_record_type type;
|
||||
};
|
||||
|
||||
struct osmo_mdns_msg_answer {
|
||||
uint16_t id;
|
||||
char *domain;
|
||||
/*! list of osmo_mdns_record. */
|
||||
struct llist_head records;
|
||||
};
|
||||
|
||||
int osmo_mdns_msg_request_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_msg_request *req);
|
||||
struct osmo_mdns_msg_request *osmo_mdns_msg_request_decode(void *ctx, const uint8_t *data, size_t data_len);
|
||||
|
||||
void osmo_mdns_msg_answer_init(struct osmo_mdns_msg_answer *answer);
|
||||
int osmo_mdns_msg_answer_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_msg_answer *ans);
|
||||
struct osmo_mdns_msg_answer *osmo_mdns_msg_answer_decode(void *ctx, const uint8_t *data, size_t data_len);
|
||||
int osmo_mdns_result_from_answer(struct osmo_mslookup_result *result, const struct osmo_mdns_msg_answer *ans);
|
||||
|
||||
struct osmo_mdns_record *osmo_mdns_record_txt_keyval_encode(void *ctx, const char *key, const char *value_fmt, ...);
|
||||
int osmo_mdns_record_txt_keyval_decode(const struct osmo_mdns_record *rec,
|
||||
char *key_buf, size_t key_size, char *value_buf, size_t value_size);
|
||||
@@ -1,113 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/endian.h>
|
||||
#include <osmocom/mslookup/mdns.h>
|
||||
|
||||
/* RFC 1035 2.3.4 */
|
||||
#define OSMO_MDNS_RFC_MAX_NAME_LEN 255
|
||||
|
||||
/* RFC 1035 3.3 <character-string> */
|
||||
#define OSMO_MDNS_RFC_MAX_CHARACTER_STRING_LEN 256
|
||||
|
||||
enum osmo_mdns_rfc_record_type {
|
||||
OSMO_MDNS_RFC_RECORD_TYPE_UNKNOWN = 0,
|
||||
|
||||
/* RFC 1035 3.2.2 */
|
||||
OSMO_MDNS_RFC_RECORD_TYPE_A = 1, /* IPv4 address */
|
||||
OSMO_MDNS_RFC_RECORD_TYPE_TXT = 16, /* Text strings */
|
||||
|
||||
/* RFC 3596 2.1 */
|
||||
OSMO_MDNS_RFC_RECORD_TYPE_AAAA = 28, /* IPv6 address */
|
||||
|
||||
/* RFC 1035 3.2.3 */
|
||||
OSMO_MDNS_RFC_RECORD_TYPE_ALL = 255, /* Request only: ask for all */
|
||||
};
|
||||
|
||||
enum osmo_mdns_rfc_class {
|
||||
OSMO_MDNS_RFC_CLASS_UNKNOWN = 0,
|
||||
|
||||
/* RFC 1035 3.2.4 */
|
||||
OSMO_MDNS_RFC_CLASS_IN = 1, /* Internet and IP networks */
|
||||
|
||||
/* RFC 1035 3.2.5 */
|
||||
OSMO_MDNS_RFC_CLASS_ALL = 255, /* Request only: ask for all */
|
||||
};
|
||||
|
||||
/* RFC 1035 4.1.1 */
|
||||
struct osmo_mdns_rfc_header {
|
||||
#if OSMO_IS_LITTLE_ENDIAN
|
||||
uint16_t id;
|
||||
uint8_t rd:1,
|
||||
tc:1,
|
||||
aa:1,
|
||||
opcode:4,
|
||||
qr:1; /* QR (0: query, 1: response) */
|
||||
uint8_t rcode:4,
|
||||
z:3,
|
||||
ra:1;
|
||||
uint16_t qdcount; /* Number of questions */
|
||||
uint16_t ancount; /* Number of answers */
|
||||
uint16_t nscount; /* Number of authority records */
|
||||
uint16_t arcount; /* Number of additional records */
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
|
||||
uint16_t id;
|
||||
uint8_t qr:1, opcode:4, aa:1, tc:1, rd:1;
|
||||
uint8_t ra:1, z:3, rcode:4;
|
||||
uint16_t qdcount;
|
||||
uint16_t ancount;
|
||||
uint16_t nscount;
|
||||
uint16_t arcount;
|
||||
#endif
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* RFC 1035 4.1.2 */
|
||||
struct osmo_mdns_rfc_question {
|
||||
char *domain; /* Domain to be encoded as qname (e.g. "gsup.hlr.1234567.imsi") */
|
||||
enum osmo_mdns_rfc_record_type qtype;
|
||||
enum osmo_mdns_rfc_class qclass;
|
||||
};
|
||||
|
||||
/* RFC 1035 4.1.3 */
|
||||
struct osmo_mdns_rfc_record {
|
||||
char *domain; /* Domain to be encoded as name (e.g. "gsup.hlr.1234567.imsi") */
|
||||
enum osmo_mdns_rfc_record_type type;
|
||||
enum osmo_mdns_rfc_class class;
|
||||
uint32_t ttl;
|
||||
uint16_t rdlength;
|
||||
uint8_t *rdata;
|
||||
};
|
||||
|
||||
char *osmo_mdns_rfc_qname_encode(void *ctx, const char *domain);
|
||||
char *osmo_mdns_rfc_qname_decode(void *ctx, const char *qname, size_t qname_len);
|
||||
|
||||
void osmo_mdns_rfc_header_encode(struct msgb *msg, const struct osmo_mdns_rfc_header *hdr);
|
||||
int osmo_mdns_rfc_header_decode(const uint8_t *data, size_t data_len, struct osmo_mdns_rfc_header *hdr);
|
||||
|
||||
int osmo_mdns_rfc_question_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_question *qst);
|
||||
struct osmo_mdns_rfc_question *osmo_mdns_rfc_question_decode(void *ctx, const uint8_t *data, size_t data_len);
|
||||
|
||||
int osmo_mdns_rfc_record_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_record *rec);
|
||||
struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_t *data, size_t data_len,
|
||||
size_t *record_len);
|
||||
@@ -1,33 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <netdb.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/select.h>
|
||||
|
||||
struct osmo_mdns_sock {
|
||||
struct osmo_fd osmo_fd;
|
||||
struct addrinfo *ai;
|
||||
};
|
||||
|
||||
struct osmo_mdns_sock *osmo_mdns_sock_init(void *ctx, const char *ip, unsigned int port,
|
||||
int (*cb)(struct osmo_fd *fd, unsigned int what),
|
||||
void *data, unsigned int priv_nr);
|
||||
int osmo_mdns_sock_send(const struct osmo_mdns_sock *mdns_sock, struct msgb *msg);
|
||||
void osmo_mdns_sock_cleanup(struct osmo_mdns_sock *mdns_sock);
|
||||
@@ -1,121 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! \defgroup mslookup Distributed GSM: finding subscribers
|
||||
* @{
|
||||
* \file mslookup.h
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||
|
||||
#define OSMO_MSLOOKUP_SERVICE_MAXLEN 64
|
||||
|
||||
bool osmo_mslookup_service_valid(const char *service);
|
||||
|
||||
enum osmo_mslookup_id_type {
|
||||
OSMO_MSLOOKUP_ID_NONE = 0,
|
||||
OSMO_MSLOOKUP_ID_IMSI,
|
||||
OSMO_MSLOOKUP_ID_MSISDN,
|
||||
};
|
||||
|
||||
extern const struct value_string osmo_mslookup_id_type_names[];
|
||||
static inline const char *osmo_mslookup_id_type_name(enum osmo_mslookup_id_type val)
|
||||
{ return get_value_string(osmo_mslookup_id_type_names, val); }
|
||||
|
||||
struct osmo_mslookup_id {
|
||||
enum osmo_mslookup_id_type type;
|
||||
union {
|
||||
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
|
||||
char msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
|
||||
};
|
||||
};
|
||||
|
||||
int osmo_mslookup_id_cmp(const struct osmo_mslookup_id *a, const struct osmo_mslookup_id *b);
|
||||
bool osmo_mslookup_id_valid(const struct osmo_mslookup_id *id);
|
||||
|
||||
enum osmo_mslookup_result_code {
|
||||
OSMO_MSLOOKUP_RC_NONE = 0,
|
||||
/*! An intermediate valid result. The request is still open for more results. */
|
||||
OSMO_MSLOOKUP_RC_RESULT,
|
||||
/*! Returned when the final request timeout has elapsed without results. */
|
||||
OSMO_MSLOOKUP_RC_NOT_FOUND,
|
||||
};
|
||||
|
||||
extern const struct value_string osmo_mslookup_result_code_names[];
|
||||
static inline const char *osmo_mslookup_result_code_name(enum osmo_mslookup_result_code val)
|
||||
{ return get_value_string(osmo_mslookup_result_code_names, val); }
|
||||
|
||||
/*! Information to request from a lookup. */
|
||||
struct osmo_mslookup_query {
|
||||
/*! Which service to request, by freely invented names. For service name conventions (for voice, SMS, HLR,...),
|
||||
* refer to the OsmoHLR user's manual http://ftp.osmocom.org/docs/latest/osmohlr-usermanual.pdf */
|
||||
char service[OSMO_MSLOOKUP_SERVICE_MAXLEN + 1];
|
||||
/*! IMSI or MSISDN to look up. */
|
||||
struct osmo_mslookup_id id;
|
||||
|
||||
/*! Caller provided private data, if desired. */
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/*! Result data as passed back to a lookup client that invoked an osmo_mslookup_client_request. */
|
||||
struct osmo_mslookup_result {
|
||||
/*! Outcome of the request. */
|
||||
enum osmo_mslookup_result_code rc;
|
||||
|
||||
/*! IP address and port to reach the given service via IPv4, if any. */
|
||||
struct osmo_sockaddr_str host_v4;
|
||||
|
||||
/*! IP address and port to reach the given service via IPv6, if any. */
|
||||
struct osmo_sockaddr_str host_v6;
|
||||
|
||||
/*! How long ago the service last verified presence of the subscriber, in seconds, or zero if the presence is
|
||||
* invariable (like the home HLR record for an IMSI).
|
||||
* If a subscriber has recently moved to a different location, we get multiple replies and want to choose the
|
||||
* most recent one. If this were a timestamp, firstly the time zones would need to be taken care of.
|
||||
* Even if we choose UTC, a service provider with an inaccurate date/time would end up affecting the result.
|
||||
* The least susceptible to configuration errors or difference in local and remote clock is a value that
|
||||
* indicates the actual age of the record in seconds. The time that the lookup query took to be answered should
|
||||
* be neglectable here, since we would typically wait one second (or very few seconds) for lookup replies,
|
||||
* while typical Location Updating periods are in the range of 15 minutes. */
|
||||
uint32_t age;
|
||||
|
||||
/*! Whether this is the last result returned for this request. */
|
||||
bool last;
|
||||
};
|
||||
|
||||
int osmo_mslookup_query_init_from_domain_str(struct osmo_mslookup_query *q, const char *domain);
|
||||
|
||||
size_t osmo_mslookup_id_name_buf(char *buf, size_t buflen, const struct osmo_mslookup_id *id);
|
||||
char *osmo_mslookup_id_name_c(void *ctx, const struct osmo_mslookup_id *id);
|
||||
char *osmo_mslookup_id_name_b(char *buf, size_t buflen, const struct osmo_mslookup_id *id);
|
||||
|
||||
size_t osmo_mslookup_result_to_str_buf(char *buf, size_t buflen,
|
||||
const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_result *result);
|
||||
char *osmo_mslookup_result_name_c(void *ctx,
|
||||
const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_result *result);
|
||||
char *osmo_mslookup_result_name_b(char *buf, size_t buflen,
|
||||
const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_result *result);
|
||||
|
||||
/*! @} */
|
||||
@@ -1,132 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/mslookup/mslookup.h>
|
||||
|
||||
struct osmo_mslookup_client;
|
||||
struct osmo_mslookup_result;
|
||||
|
||||
typedef void (*osmo_mslookup_cb_t)(struct osmo_mslookup_client *client,
|
||||
uint32_t request_handle,
|
||||
const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_result *result);
|
||||
|
||||
/*! This handling information is passed along with a lookup request.
|
||||
* It tells the osmo_mslookup_client layer how to handle responses received from various mslookup methods (at the time
|
||||
* of writing only mDNS exists as a method, but the intention is to easily allow adding other methods in the future).
|
||||
* This query handling info is not seen by the individual method implementations, to clarify that it is the
|
||||
* osmo_mslookup_client layer that takes care of these details. */
|
||||
struct osmo_mslookup_query_handling {
|
||||
/*! Wait at least this long before returning any results.
|
||||
*
|
||||
* If nonzero, result_cb will be called as soon as this delay has elapsed, either with the so far youngest age
|
||||
* result, or with a "not found yet" result. After this delay has elapsed, receiving results will continue
|
||||
* until result_timeout_milliseconds has elapsed.
|
||||
*
|
||||
* If zero, responses are fed to the result_cb right from the start, every time a younger aged result than
|
||||
* before comes in.
|
||||
*
|
||||
* If a result with age == 0 is received, min_wait_milliseconds is ignored, the result is returned immediately
|
||||
* and listening for responses ends.
|
||||
*
|
||||
* Rationale: If a subscriber has recently moved between sites, multiple results will arrive, and the youngest
|
||||
* age wins. It can make sense to wait a minimum time for responses before determining the winning result.
|
||||
*
|
||||
* However, if no result or no valid result has arrived within a short period, the subscriber may be at a site
|
||||
* that is far away or that is currently experiencing high latency. It is thus a good safety net to still
|
||||
* receive results for an extended period of time.
|
||||
*
|
||||
* For some services, it is possible to establish links to every received result, and whichever link succeeds
|
||||
* will be used (for example for SIP calls: first to pick up the call gets connected, the others are dropped
|
||||
* silently).
|
||||
*/
|
||||
uint32_t min_wait_milliseconds;
|
||||
|
||||
/*! Total time in milliseconds to listen for lookup responses.
|
||||
*
|
||||
* When this timeout elapses, osmo_mslookup_client_request_cancel() is called implicitly; Manually invoking
|
||||
* osmo_mslookup_client_request_cancel() after result_timeout_milliseconds has elapsed is not necessary, but is
|
||||
* still safe to do anyway.
|
||||
*
|
||||
* If zero, min_wait_milliseconds is also used as result_timeout_milliseconds; if that is also zero, a default
|
||||
* timeout value is used.
|
||||
*
|
||||
* If result_timeout_milliseconds <= min_wait_milliseconds, then min_wait_milliseconds is used as
|
||||
* result_timeout_milliseconds, i.e. the timeout triggers as soon as min_wait_milliseconds hits.
|
||||
*
|
||||
* osmo_mslookup_client_request_cancel() can be called any time to end the request.
|
||||
*/
|
||||
uint32_t result_timeout_milliseconds;
|
||||
|
||||
/*! Invoked every time a result with a younger age than the previous result has arrived.
|
||||
* To stop receiving results before result_timeout_milliseconds has elapsed, call
|
||||
* osmo_mslookup_client_request_cancel().
|
||||
*/
|
||||
osmo_mslookup_cb_t result_cb;
|
||||
};
|
||||
|
||||
uint32_t osmo_mslookup_client_request(struct osmo_mslookup_client *client,
|
||||
const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_query_handling *handling);
|
||||
|
||||
void osmo_mslookup_client_request_cancel(struct osmo_mslookup_client *client, uint32_t request_handle);
|
||||
|
||||
struct osmo_mslookup_client *osmo_mslookup_client_new(void *ctx);
|
||||
bool osmo_mslookup_client_active(struct osmo_mslookup_client *client);
|
||||
void osmo_mslookup_client_free(struct osmo_mslookup_client *client);
|
||||
|
||||
/*! Describe a specific mslookup client method implementation. This struct is only useful for a lookup method
|
||||
* implementation to add itself to an osmo_mslookup_client, see for example osmo_mslookup_client_add_mdns(). */
|
||||
struct osmo_mslookup_client_method {
|
||||
struct llist_head entry;
|
||||
|
||||
/*! Human readable name of this lookup method. */
|
||||
const char *name;
|
||||
|
||||
/*! Private data for the lookup method implementation. */
|
||||
void *priv;
|
||||
|
||||
/*! Backpointer to the client this method is added to. */
|
||||
struct osmo_mslookup_client *client;
|
||||
|
||||
/*! Launch a lookup query. Called from osmo_mslookup_client_request().
|
||||
* The implementation returns results by calling osmo_mslookup_client_rx_result(). */
|
||||
void (*request)(struct osmo_mslookup_client_method *method,
|
||||
const struct osmo_mslookup_query *query,
|
||||
uint32_t request_handle);
|
||||
/*! End a lookup query. Called from osmo_mslookup_client_request_cancel(). It is guaranteed to be called
|
||||
* exactly once per above request() invocation. (The API user is required to invoke
|
||||
* osmo_mslookup_client_request_cancel() exactly once per osmo_mslookup_client_request().) */
|
||||
void (*request_cleanup)(struct osmo_mslookup_client_method *method,
|
||||
uint32_t request_handle);
|
||||
|
||||
/*! The mslookup_client is removing this method, clean up all open requests, lists and allocations. */
|
||||
void (*destruct)(struct osmo_mslookup_client_method *method);
|
||||
};
|
||||
|
||||
void osmo_mslookup_client_method_add(struct osmo_mslookup_client *client,
|
||||
struct osmo_mslookup_client_method *method);
|
||||
bool osmo_mslookup_client_method_del(struct osmo_mslookup_client *client,
|
||||
struct osmo_mslookup_client_method *method);
|
||||
void osmo_mslookup_client_rx_result(struct osmo_mslookup_client *client, uint32_t request_handle,
|
||||
const struct osmo_mslookup_result *result);
|
||||
@@ -1,34 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*! MS lookup fake API for testing purposes. */
|
||||
#include <osmocom/mslookup/mslookup_client.h>
|
||||
|
||||
struct osmo_mslookup_fake_response {
|
||||
struct timeval time_to_reply;
|
||||
struct osmo_mslookup_id for_id;
|
||||
const char *for_service;
|
||||
struct osmo_mslookup_result result;
|
||||
bool sent;
|
||||
};
|
||||
|
||||
struct osmo_mslookup_client_method *osmo_mslookup_client_add_fake(struct osmo_mslookup_client *client,
|
||||
struct osmo_mslookup_fake_response *responses,
|
||||
size_t responses_len);
|
||||
@@ -1,38 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct osmo_mslookup_client;
|
||||
struct osmo_mslookup_client_method;
|
||||
|
||||
/*! MS Lookup mDNS server bind default IP. Taken from the Administratevly Scoped block, particularly the Organizational
|
||||
* Scoped range, https://tools.ietf.org/html/rfc2365 . */
|
||||
#define OSMO_MSLOOKUP_MDNS_IP4 "239.192.23.42"
|
||||
#define OSMO_MSLOOKUP_MDNS_IP6 "ff08::23:42" // <-- TODO: sane?
|
||||
#define OSMO_MSLOOKUP_MDNS_PORT 4266
|
||||
|
||||
struct osmo_mslookup_client_method *osmo_mslookup_client_add_mdns(struct osmo_mslookup_client *client, const char *ip,
|
||||
uint16_t port, int initial_packet_id,
|
||||
const char *domain_suffix);
|
||||
|
||||
const struct osmo_sockaddr_str *osmo_mslookup_client_method_mdns_get_bind_addr(struct osmo_mslookup_client_method *dns_method);
|
||||
|
||||
const char *osmo_mslookup_client_method_mdns_get_domain_suffix(struct osmo_mslookup_client_method *dns_method);
|
||||
@@ -1,11 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Osmocom MS Lookup Library
|
||||
Description: C Utility Library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} @TALLOC_LIBS@ -losmogsm -losmo-mslookup -losmocore
|
||||
Cflags: -I${includedir}/
|
||||
|
||||
12
sql/hlr.sql
12
sql/hlr.sql
@@ -43,8 +43,6 @@ CREATE TABLE subscriber (
|
||||
-- Timestamp of last location update seen from subscriber
|
||||
-- The value is a string which encodes a UTC timestamp in granularity of seconds.
|
||||
last_lu_seen TIMESTAMP default NULL,
|
||||
last_lu_seen_ps TIMESTAMP default NULL,
|
||||
|
||||
-- When a LU was received via a proxy, that proxy's hlr_number is stored here,
|
||||
-- while vlr_number reflects the MSC on the far side of that proxy.
|
||||
vlr_via_proxy VARCHAR,
|
||||
@@ -79,16 +77,8 @@ CREATE TABLE auc_3g (
|
||||
ind_bitlen INTEGER NOT NULL DEFAULT 5
|
||||
);
|
||||
|
||||
CREATE TABLE ind (
|
||||
-- 3G auth IND pool to be used for this VLR
|
||||
ind INTEGER PRIMARY KEY,
|
||||
-- VLR identification, usually the GSUP source_name
|
||||
vlr TEXT NOT NULL,
|
||||
UNIQUE (vlr)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
|
||||
|
||||
-- Set HLR database schema version number
|
||||
-- Note: This constant is currently duplicated in src/db.c and must be kept in sync!
|
||||
PRAGMA user_version = 6;
|
||||
PRAGMA user_version = 4;
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
SUBDIRS = \
|
||||
gsupclient \
|
||||
mslookup \
|
||||
$(NULL)
|
||||
SUBDIRS = gsupclient
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
@@ -15,7 +12,6 @@ AM_CFLAGS = \
|
||||
$(NULL)
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include \
|
||||
-I$(top_builddir)/include \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
@@ -29,7 +25,25 @@ BUILT_SOURCES = \
|
||||
CLEANFILES = $(BUILT_SOURCES)
|
||||
|
||||
noinst_HEADERS = \
|
||||
auc.h \
|
||||
db.h \
|
||||
hlr.h \
|
||||
gsup_router.h \
|
||||
gsup_server.h \
|
||||
logging.h \
|
||||
rand.h \
|
||||
ctrl.h \
|
||||
hlr_vty.h \
|
||||
hlr_vty_subscr.h \
|
||||
hlr_ussd.h \
|
||||
db_bootstrap.h \
|
||||
proxy.h \
|
||||
dgsm.h \
|
||||
remote_hlr.h \
|
||||
global_title.h \
|
||||
mslookup_server.h \
|
||||
mslookup_server_mdns.h \
|
||||
lu_fsm.h \
|
||||
$(NULL)
|
||||
|
||||
bin_PROGRAMS = \
|
||||
@@ -55,17 +69,16 @@ osmo_hlr_SOURCES = \
|
||||
hlr_ussd.c \
|
||||
proxy.c \
|
||||
dgsm.c \
|
||||
dgsm_vty.c \
|
||||
remote_hlr.c \
|
||||
lu_fsm.c \
|
||||
timestamp.c \
|
||||
mslookup_server.c \
|
||||
mslookup_server_mdns.c \
|
||||
dgsm_vty.c \
|
||||
global_title.c \
|
||||
lu_fsm.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_hlr_LDADD = \
|
||||
$(top_builddir)/src/gsupclient/libosmo-gsup-client.la \
|
||||
$(top_builddir)/src/mslookup/libosmo-mslookup.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
@@ -82,7 +95,7 @@ osmo_hlr_db_tool_SOURCES = \
|
||||
logging.c \
|
||||
rand_urandom.c \
|
||||
dbd_decode_binary.c \
|
||||
$(srcdir)/gsupclient/cni_peer_id.c \
|
||||
global_title.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_hlr_db_tool_LDADD = \
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/crypt/auth.h>
|
||||
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/rand.h>
|
||||
#include "logging.h"
|
||||
#include "rand.h"
|
||||
|
||||
#define hexb(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
|
||||
#define hex(buf,sz) osmo_hexdump_nospc((void*)buf, sz)
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/ctrl/ports.h>
|
||||
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/ctrl.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include "hlr.h"
|
||||
#include "ctrl.h"
|
||||
#include "db.h"
|
||||
|
||||
#define SEL_BY "by-"
|
||||
#define SEL_BY_IMSI SEL_BY "imsi-"
|
||||
|
||||
78
src/db.c
78
src/db.c
@@ -22,13 +22,14 @@
|
||||
#include <stdbool.h>
|
||||
#include <sqlite3.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include "logging.h"
|
||||
#include "db.h"
|
||||
#include "db_bootstrap.h"
|
||||
|
||||
/* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */
|
||||
#define CURRENT_SCHEMA_VERSION 6
|
||||
#define CURRENT_SCHEMA_VERSION 4
|
||||
|
||||
#define SEL_COLUMNS \
|
||||
"id," \
|
||||
@@ -46,19 +47,10 @@
|
||||
"ms_purged_cs," \
|
||||
"ms_purged_ps," \
|
||||
"last_lu_seen," \
|
||||
"last_lu_seen_ps," \
|
||||
"vlr_via_proxy," \
|
||||
"sgsn_via_proxy"
|
||||
|
||||
static const char *stmt_sql[] = {
|
||||
[DB_STMT_SEL_ALL] = "SELECT " SEL_COLUMNS " FROM subscriber;",
|
||||
[DB_STMT_SEL_ALL_ORDER_LAST_SEEN] = "SELECT " SEL_COLUMNS " FROM subscriber "
|
||||
"WHERE last_lu_seen IS NOT NULL ORDER BY last_lu_seen;",
|
||||
[DB_STMT_SEL_FILTER_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn LIKE $search ORDER BY msisdn",
|
||||
[DB_STMT_SEL_FILTER_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi LIKE $search ORDER BY imsi",
|
||||
[DB_STMT_SEL_FILTER_IMEI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imei LIKE $search ORDER BY imei",
|
||||
[DB_STMT_SEL_FILTER_CS] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE nam_cs = $search ORDER BY last_lu_seen",
|
||||
[DB_STMT_SEL_FILTER_PS] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE nam_ps = $search ORDER BY last_lu_seen",
|
||||
[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
|
||||
[DB_STMT_SEL_BY_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn = ?",
|
||||
[DB_STMT_SEL_BY_ID] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE id = ?",
|
||||
@@ -90,12 +82,15 @@ static const char *stmt_sql[] = {
|
||||
" VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)",
|
||||
[DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id",
|
||||
[DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch') WHERE id = $subscriber_id",
|
||||
[DB_STMT_SET_LAST_LU_SEEN_PS] = "UPDATE subscriber SET last_lu_seen_ps = datetime($val, 'unixepoch') WHERE id = $subscriber_id",
|
||||
[DB_STMT_EXISTS_BY_IMSI] = "SELECT 1 FROM subscriber WHERE imsi = $imsi",
|
||||
[DB_STMT_EXISTS_BY_MSISDN] = "SELECT 1 FROM subscriber WHERE msisdn = $msisdn",
|
||||
[DB_STMT_IND_ADD] = "INSERT INTO ind (vlr) VALUES ($vlr)",
|
||||
[DB_STMT_IND_SELECT] = "SELECT ind FROM ind WHERE vlr = $vlr",
|
||||
[DB_STMT_IND_DEL] = "DELETE FROM ind WHERE vlr = $vlr",
|
||||
|
||||
#if 0
|
||||
[DB_STMT_PROXY_UPDATE] = "INSERT OR REPLACE INTO"
|
||||
" proxy (imsi, remote_ip, remote_port)"
|
||||
" VALUES ($imsi, $remote_ip, $remote_port)",
|
||||
[DB_STMT_PROXY_GET_BY_IMSI] = "SELECT imsi, remote_ip, remote_port FROM proxy WHERE imsi = $imsi",
|
||||
#endif
|
||||
};
|
||||
|
||||
static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
|
||||
@@ -237,7 +232,7 @@ void db_close(struct db_context *dbc)
|
||||
|
||||
static int db_run_statements(struct db_context *dbc, const char **statements, size_t statements_count)
|
||||
{
|
||||
int rc = 0;
|
||||
int rc;
|
||||
int i;
|
||||
for (i = 0; i < statements_count; i++) {
|
||||
const char *stmt_str = statements[i];
|
||||
@@ -461,7 +456,8 @@ static int db_upgrade_v4(struct db_context *dbc)
|
||||
{
|
||||
int rc;
|
||||
const char *statements[] = {
|
||||
"ALTER TABLE subscriber ADD COLUMN last_lu_seen_ps TIMESTAMP default NULL",
|
||||
"ALTER TABLE subscriber ADD COLUMN vlr_via_proxy VARCHAR",
|
||||
"ALTER TABLE subscriber ADD COLUMN sgsn_via_proxy VARCHAR",
|
||||
"PRAGMA user_version = 4",
|
||||
};
|
||||
|
||||
@@ -473,54 +469,12 @@ static int db_upgrade_v4(struct db_context *dbc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int db_upgrade_v5(struct db_context *dbc)
|
||||
{
|
||||
int rc;
|
||||
const char *statements[] = {
|
||||
"ALTER TABLE subscriber ADD COLUMN vlr_via_proxy VARCHAR",
|
||||
"ALTER TABLE subscriber ADD COLUMN sgsn_via_proxy VARCHAR",
|
||||
"PRAGMA user_version = 5",
|
||||
};
|
||||
|
||||
rc = db_run_statements(dbc, statements, ARRAY_SIZE(statements));
|
||||
if (rc != SQLITE_DONE) {
|
||||
LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version 5\n");
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int db_upgrade_v6(struct db_context *dbc)
|
||||
{
|
||||
int rc;
|
||||
const char *statements[] = {
|
||||
"CREATE TABLE ind (\n"
|
||||
" -- 3G auth IND pool to be used for this VLR\n"
|
||||
" ind INTEGER PRIMARY KEY,\n"
|
||||
" -- VLR identification, usually the GSUP source_name\n"
|
||||
" vlr TEXT NOT NULL,\n"
|
||||
" UNIQUE (vlr)\n"
|
||||
")"
|
||||
,
|
||||
"PRAGMA user_version = 6",
|
||||
};
|
||||
|
||||
rc = db_run_statements(dbc, statements, ARRAY_SIZE(statements));
|
||||
if (rc != SQLITE_DONE) {
|
||||
LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version 6\n");
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef int (*db_upgrade_func_t)(struct db_context *dbc);
|
||||
static db_upgrade_func_t db_upgrade_path[] = {
|
||||
db_upgrade_v1,
|
||||
db_upgrade_v2,
|
||||
db_upgrade_v3,
|
||||
db_upgrade_v4,
|
||||
db_upgrade_v5,
|
||||
db_upgrade_v6,
|
||||
};
|
||||
|
||||
static int db_get_user_version(struct db_context *dbc)
|
||||
@@ -609,11 +563,9 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
|
||||
|
||||
char *err_msg;
|
||||
rc = sqlite3_exec(dbc->db, "PRAGMA journal_mode=WAL; PRAGMA synchonous = NORMAL;", 0, 0, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
if (rc != SQLITE_OK)
|
||||
LOGP(DDB, LOGL_ERROR, "Unable to set Write-Ahead Logging: %s\n",
|
||||
err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
}
|
||||
|
||||
version = db_get_user_version(dbc);
|
||||
if (version < 0) {
|
||||
|
||||
@@ -3,19 +3,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
#include "global_title.h"
|
||||
|
||||
struct hlr;
|
||||
|
||||
enum stmt_idx {
|
||||
DB_STMT_SEL_ALL,
|
||||
DB_STMT_SEL_ALL_ORDER_LAST_SEEN,
|
||||
DB_STMT_SEL_FILTER_MSISDN,
|
||||
DB_STMT_SEL_FILTER_IMSI,
|
||||
DB_STMT_SEL_FILTER_IMEI,
|
||||
DB_STMT_SEL_FILTER_CS,
|
||||
DB_STMT_SEL_FILTER_PS,
|
||||
DB_STMT_SEL_BY_IMSI,
|
||||
DB_STMT_SEL_BY_MSISDN,
|
||||
DB_STMT_SEL_BY_ID,
|
||||
@@ -38,12 +30,12 @@ enum stmt_idx {
|
||||
DB_STMT_AUC_3G_INSERT,
|
||||
DB_STMT_AUC_3G_DELETE,
|
||||
DB_STMT_SET_LAST_LU_SEEN,
|
||||
DB_STMT_SET_LAST_LU_SEEN_PS,
|
||||
DB_STMT_EXISTS_BY_IMSI,
|
||||
DB_STMT_EXISTS_BY_MSISDN,
|
||||
DB_STMT_IND_ADD,
|
||||
DB_STMT_IND_SELECT,
|
||||
DB_STMT_IND_DEL,
|
||||
#if 0
|
||||
DB_STMT_PROXY_UPDATE,
|
||||
DB_STMT_PROXY_GET_BY_IMSI,
|
||||
#endif
|
||||
_NUM_DB_STMT
|
||||
};
|
||||
|
||||
@@ -80,7 +72,7 @@ int db_update_sqn(struct db_context *dbc, int64_t id,
|
||||
int db_get_auc(struct db_context *dbc, const char *imsi,
|
||||
unsigned int auc_3g_ind, struct osmo_auth_vector *vec,
|
||||
unsigned int num_vec, const uint8_t *rand_auts,
|
||||
const uint8_t *auts, bool separation_bit);
|
||||
const uint8_t *auts);
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||
@@ -110,10 +102,8 @@ struct hlr_subscriber {
|
||||
bool ms_purged_cs;
|
||||
bool ms_purged_ps;
|
||||
time_t last_lu_seen;
|
||||
time_t last_lu_seen_ps;
|
||||
/* talloc'd IPA unit name */
|
||||
struct osmo_ipa_name vlr_via_proxy;
|
||||
struct osmo_ipa_name sgsn_via_proxy;
|
||||
struct global_title vlr_via_proxy;
|
||||
};
|
||||
|
||||
/* A format string for use with strptime(3). This format string is
|
||||
@@ -159,9 +149,6 @@ int db_subscr_update_imei_by_imsi(struct db_context *dbc, const char* imsi, cons
|
||||
int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi);
|
||||
int db_subscr_exists_by_msisdn(struct db_context *dbc, const char *msisdn);
|
||||
|
||||
int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,
|
||||
void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,
|
||||
int *count, const char **err);
|
||||
int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
|
||||
struct hlr_subscriber *subscr);
|
||||
int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
|
||||
@@ -171,15 +158,12 @@ int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
|
||||
int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_subscriber *subscr);
|
||||
int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps);
|
||||
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
|
||||
const struct osmo_ipa_name *vlr_name, bool is_ps,
|
||||
const struct osmo_ipa_name *via_proxy);
|
||||
const struct global_title *vlr_name, bool is_ps,
|
||||
const struct global_title *via_proxy);
|
||||
|
||||
int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
|
||||
bool purge_val, bool is_ps);
|
||||
|
||||
int db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr, unsigned int *ind);
|
||||
int db_ind_del(struct db_context *dbc, const struct osmo_cni_peer_id *vlr);
|
||||
|
||||
/*! Call sqlite3_column_text() and copy result to a char[].
|
||||
* \param[out] buf A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
|
||||
* \param[in] stmt An sqlite3_stmt*.
|
||||
@@ -191,13 +175,13 @@ int db_ind_del(struct db_context *dbc, const struct osmo_cni_peer_id *vlr);
|
||||
osmo_strlcpy(buf, _txt, sizeof(buf)); \
|
||||
} while (0)
|
||||
|
||||
/*! Call sqlite3_column_text() and copy result to a struct osmo_ipa_name.
|
||||
* \param[out] ipa_name A struct osmo_ipa_name* to write to.
|
||||
/*! Call sqlite3_column_text() and copy result to a struct global_title.
|
||||
* \param[out] gt A struct global_title* to write to.
|
||||
* \param[in] stmt An sqlite3_stmt*.
|
||||
* \param[in] idx Index in stmt's returned columns.
|
||||
*/
|
||||
#define copy_sqlite3_text_to_ipa_name(ipa_name, stmt, idx) \
|
||||
#define copy_sqlite3_text_to_gt(gt, stmt, idx) \
|
||||
do { \
|
||||
const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \
|
||||
osmo_ipa_name_set_str(ipa_name, _txt); \
|
||||
global_title_set_str(gt, _txt); \
|
||||
} while (0)
|
||||
33
src/db_auc.c
33
src/db_auc.c
@@ -26,10 +26,10 @@
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include <osmocom/hlr/auc.h>
|
||||
#include <osmocom/hlr/rand.h>
|
||||
#include "logging.h"
|
||||
#include "db.h"
|
||||
#include "auc.h"
|
||||
#include "rand.h"
|
||||
|
||||
#define LOGAUC(imsi, level, fmt, args ...) LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)
|
||||
|
||||
@@ -189,7 +189,7 @@ out:
|
||||
int db_get_auc(struct db_context *dbc, const char *imsi,
|
||||
unsigned int auc_3g_ind, struct osmo_auth_vector *vec,
|
||||
unsigned int num_vec, const uint8_t *rand_auts,
|
||||
const uint8_t *auts, bool separation_bit)
|
||||
const uint8_t *auts)
|
||||
{
|
||||
struct osmo_sub_auth_data aud2g, aud3g;
|
||||
int64_t subscr_id;
|
||||
@@ -200,22 +200,15 @@ int db_get_auc(struct db_context *dbc, const char *imsi,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* modulo by the IND bitlen value range. For example, ind_bitlen == 5 would modulo 32:
|
||||
* 1 << 5 == 0b0100000 == 32
|
||||
* - 1 == 0b0011111 == bitmask of 5 lowest bits
|
||||
* x &= 0b0011111 == modulo 32
|
||||
* Why do this? osmo-hlr cannot possibly choose individual VLR INDs always matching all subscribers' IND_bitlen,
|
||||
* which might vary wildly. Instead, let hlr.c pass in an arbitrarily high number here, and the modulo does a
|
||||
* round-robin if the IND pools that this subscriber has available. */
|
||||
auc_3g_ind &= (1U << aud3g.u.umts.ind_bitlen) - 1;
|
||||
aud3g.u.umts.ind = auc_3g_ind;
|
||||
|
||||
/* the first bit (bit0) cannot be used as AMF anymore, but has been
|
||||
* re-appropriated as the separation bit. See 3GPP TS 33.102 Annex H
|
||||
* together with 3GPP TS 33.401 / 33.402 / 33.501 */
|
||||
aud3g.u.umts.amf[0] = aud3g.u.umts.amf[0] & 0x7f;
|
||||
if (separation_bit)
|
||||
aud3g.u.umts.amf[0] |= 0x80;
|
||||
if (aud3g.type == OSMO_AUTH_TYPE_UMTS
|
||||
&& aud3g.u.umts.ind >= (1U << aud3g.u.umts.ind_bitlen)) {
|
||||
LOGAUC(imsi, LOGL_NOTICE, "3G auth: SQN's IND bitlen %u is"
|
||||
" too small to hold an index of %u. Truncating. This"
|
||||
" may cause numerous additional AUTS resyncing.\n",
|
||||
aud3g.u.umts.ind_bitlen, aud3g.u.umts.ind);
|
||||
aud3g.u.umts.ind &= (1U << aud3g.u.umts.ind_bitlen) - 1;
|
||||
}
|
||||
|
||||
LOGAUC(imsi, LOGL_DEBUG, "Calling to generate %u vectors\n", num_vec);
|
||||
rc = auc_compute_vectors(vec, num_vec, &aud2g, &aud3g, rand_auts, auts);
|
||||
|
||||
263
src/db_hlr.c
263
src/db_hlr.c
@@ -28,16 +28,15 @@
|
||||
#include <time.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/crypt/auth.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include "logging.h"
|
||||
#include "hlr.h"
|
||||
#include "db.h"
|
||||
#include "gsup_server.h"
|
||||
|
||||
#define LOGHLR(imsi, level, fmt, args ...) LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)
|
||||
|
||||
@@ -264,11 +263,11 @@ int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
|
||||
switch (aud->algo) {
|
||||
case OSMO_AUTH_ALG_NONE:
|
||||
case OSMO_AUTH_ALG_MILENAGE:
|
||||
case OSMO_AUTH_ALG_XOR:
|
||||
break;
|
||||
case OSMO_AUTH_ALG_COMP128v1:
|
||||
case OSMO_AUTH_ALG_COMP128v2:
|
||||
case OSMO_AUTH_ALG_COMP128v3:
|
||||
case OSMO_AUTH_ALG_XOR:
|
||||
LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
|
||||
" auth algo not suited for 3G: %s\n",
|
||||
osmo_auth_alg_name(aud->algo));
|
||||
@@ -438,36 +437,14 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void parse_last_lu_seen(time_t *dst, const char *last_lu_seen_str, const char *imsi, const char *label)
|
||||
{
|
||||
struct tm tm = {0};
|
||||
time_t val;
|
||||
if (!last_lu_seen_str || last_lu_seen_str[0] == '\0')
|
||||
return;
|
||||
|
||||
if (strptime(last_lu_seen_str, DB_LAST_LU_SEEN_FMT, &tm) == NULL) {
|
||||
LOGP(DAUC, LOGL_ERROR, "IMSI-%s: Last LU Seen %s: Cannot parse timestamp '%s'\n",
|
||||
imsi, label, last_lu_seen_str);
|
||||
return;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
val = mktime(&tm);
|
||||
if (val == -1) {
|
||||
LOGP(DAUC, LOGL_ERROR, "IMSI-%s: Last LU Seen %s: Cannot convert timestamp '%s' to time_t: %s\n",
|
||||
imsi, label, last_lu_seen_str, strerror(errno));
|
||||
val = 0;
|
||||
}
|
||||
|
||||
*dst = val;
|
||||
}
|
||||
|
||||
/* Common code for db_subscr_get_by_*() functions. */
|
||||
static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscriber *subscr,
|
||||
const char **err)
|
||||
{
|
||||
int rc;
|
||||
int ret = 0;
|
||||
const char *last_lu_seen_str;
|
||||
struct tm tm = {0};
|
||||
|
||||
/* execute the statement */
|
||||
rc = sqlite3_step(stmt);
|
||||
@@ -501,12 +478,21 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
|
||||
subscr->lmsi = sqlite3_column_int(stmt, 11);
|
||||
subscr->ms_purged_cs = sqlite3_column_int(stmt, 12);
|
||||
subscr->ms_purged_ps = sqlite3_column_int(stmt, 13);
|
||||
parse_last_lu_seen(&subscr->last_lu_seen, (const char *)sqlite3_column_text(stmt, 14),
|
||||
subscr->imsi, "CS");
|
||||
parse_last_lu_seen(&subscr->last_lu_seen_ps, (const char *)sqlite3_column_text(stmt, 15),
|
||||
subscr->imsi, "PS");
|
||||
copy_sqlite3_text_to_ipa_name(&subscr->vlr_via_proxy, stmt, 16);
|
||||
copy_sqlite3_text_to_ipa_name(&subscr->sgsn_via_proxy, stmt, 17);
|
||||
last_lu_seen_str = (const char *)sqlite3_column_text(stmt, 14);
|
||||
if (last_lu_seen_str && last_lu_seen_str[0] != '\0') {
|
||||
if (strptime(last_lu_seen_str, DB_LAST_LU_SEEN_FMT, &tm) == NULL) {
|
||||
LOGP(DAUC, LOGL_ERROR, "Cannot parse last LU timestamp '%s' of subscriber with IMSI='%s': %s\n",
|
||||
last_lu_seen_str, subscr->imsi, strerror(errno));
|
||||
} else {
|
||||
subscr->last_lu_seen = mktime(&tm);
|
||||
if (subscr->last_lu_seen == -1) {
|
||||
LOGP(DAUC, LOGL_ERROR, "Cannot convert LU timestamp '%s' to time_t: %s\n",
|
||||
last_lu_seen_str, strerror(errno));
|
||||
subscr->last_lu_seen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
copy_sqlite3_text_to_gt(&subscr->vlr_via_proxy, stmt, 15);
|
||||
|
||||
out:
|
||||
db_remove_reset(stmt);
|
||||
@@ -568,7 +554,7 @@ int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
|
||||
return -EIO;
|
||||
|
||||
rc = db_sel(dbc, stmt, subscr, &err);
|
||||
if (rc && rc != -ENOENT)
|
||||
if (rc)
|
||||
LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMSI='%s': %s\n",
|
||||
imsi, err);
|
||||
return rc;
|
||||
@@ -619,100 +605,12 @@ int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
|
||||
return -EIO;
|
||||
|
||||
rc = db_sel(dbc, stmt, subscr, &err);
|
||||
if (rc && rc != -ENOENT)
|
||||
if (rc)
|
||||
LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: MSISDN='%s': %s\n",
|
||||
msisdn, err);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Retrieve subscriber data from the HLR database.
|
||||
* \param[in,out] dbc database context.
|
||||
* \param[in] filter_type ASCII string of identifier type to search.
|
||||
* \param[in] filter ASCII string to search.
|
||||
* \param[in] get_cb pointer to call back function for data.
|
||||
* \param[in,out] data pointer to pass to callback function.
|
||||
* \param[in,out] count counter for number of matched subscribers.
|
||||
* \param[in,our] err
|
||||
* \returns 0 on success, -ENOENT if no subscriber was found, -EIO on
|
||||
* database error.
|
||||
*/
|
||||
int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,
|
||||
void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,
|
||||
int *count, const char **err)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
char search[256];
|
||||
int rc;
|
||||
struct hlr_subscriber subscr;
|
||||
bool show_ls = false;
|
||||
|
||||
if (!filter_type) {
|
||||
stmt = dbc->stmt[DB_STMT_SEL_ALL];
|
||||
} else if (strcmp(filter_type, "imei") == 0) {
|
||||
stmt = dbc->stmt[DB_STMT_SEL_FILTER_IMEI];
|
||||
} else if (strcmp(filter_type, "imsi") == 0) {
|
||||
stmt = dbc->stmt[DB_STMT_SEL_FILTER_IMSI];
|
||||
} else if (strcmp(filter_type, "msisdn") == 0) {
|
||||
stmt = dbc->stmt[DB_STMT_SEL_FILTER_MSISDN];
|
||||
} else if (strcmp(filter_type, "cs") == 0) {
|
||||
stmt = dbc->stmt[DB_STMT_SEL_FILTER_CS];
|
||||
} else if (strcmp(filter_type, "ps") == 0) {
|
||||
stmt = dbc->stmt[DB_STMT_SEL_FILTER_PS];
|
||||
} else if (strcmp(filter_type, "last_lu_seen") == 0) {
|
||||
show_ls = true;
|
||||
stmt = dbc->stmt[DB_STMT_SEL_ALL_ORDER_LAST_SEEN];
|
||||
} else {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (filter_type && filter && strcmp(filter_type, "last_lu_seen") != 0) {
|
||||
if (strcmp(filter, "on") == 0) {
|
||||
sprintf(search, "%s", "1");
|
||||
} else if (strcmp(filter, "off") == 0) {
|
||||
sprintf(search, "%s", "0");
|
||||
} else {
|
||||
sprintf(search, "%%%s%%", filter);
|
||||
}
|
||||
if (!db_bind_text(stmt, "$search", search)) {
|
||||
*err = sqlite3_errmsg(dbc->db);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
if (rc == SQLITE_DONE) {
|
||||
db_remove_reset(stmt);
|
||||
*err = "No matching subscriber(s)";
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
while (rc == SQLITE_ROW) {
|
||||
subscr = (struct hlr_subscriber){
|
||||
.id = sqlite3_column_int64(stmt, 0),};
|
||||
copy_sqlite3_text_to_buf(subscr.imsi, stmt, 1);
|
||||
copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
|
||||
copy_sqlite3_text_to_buf(subscr.imei, stmt, 3);
|
||||
subscr.nam_cs = sqlite3_column_int(stmt, 9);
|
||||
subscr.nam_ps = sqlite3_column_int(stmt, 10);
|
||||
if (show_ls)
|
||||
parse_last_lu_seen(&subscr.last_lu_seen, (const char *)sqlite3_column_text(stmt, 14),
|
||||
subscr.imsi, "CS");
|
||||
get_cb(&subscr, data);
|
||||
rc = sqlite3_step(stmt);
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
db_remove_reset(stmt);
|
||||
if (rc != SQLITE_DONE) {
|
||||
*err = sqlite3_errmsg(dbc->db);
|
||||
LOGP(DAUC, LOGL_ERROR, "Cannot read subscribers from db:: %s\n", *err);
|
||||
return rc;
|
||||
}
|
||||
*err = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Retrieve subscriber data from the HLR database.
|
||||
* \param[in,out] dbc database context.
|
||||
* \param[in] id ID of the subscriber in the HLR db.
|
||||
@@ -731,7 +629,7 @@ int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
|
||||
return -EIO;
|
||||
|
||||
rc = db_sel(dbc, stmt, subscr, &err);
|
||||
if (rc && rc != -ENOENT)
|
||||
if (rc)
|
||||
LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: ID=%" PRId64 ": %s\n",
|
||||
id, err);
|
||||
return rc;
|
||||
@@ -754,7 +652,7 @@ int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_s
|
||||
return -EIO;
|
||||
|
||||
rc = db_sel(dbc, stmt, subscr, &err);
|
||||
if (rc && rc != -ENOENT)
|
||||
if (rc)
|
||||
LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMEI=%s: %s\n", imei, err);
|
||||
return rc;
|
||||
}
|
||||
@@ -824,8 +722,8 @@ out:
|
||||
* -EIO on database errors.
|
||||
*/
|
||||
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
|
||||
const struct osmo_ipa_name *vlr_name, bool is_ps,
|
||||
const struct osmo_ipa_name *via_proxy)
|
||||
const struct global_title *vlr_name, bool is_ps,
|
||||
const struct global_title *via_proxy)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int rc, ret = 0;
|
||||
@@ -881,7 +779,7 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
|
||||
goto out;
|
||||
}
|
||||
|
||||
stmt = dbc->stmt[is_ps? DB_STMT_SET_LAST_LU_SEEN_PS : DB_STMT_SET_LAST_LU_SEEN];
|
||||
stmt = dbc->stmt[DB_STMT_SET_LAST_LU_SEEN];
|
||||
|
||||
if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
|
||||
return -EIO;
|
||||
@@ -972,106 +870,3 @@ out:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _db_ind_run(struct db_context *dbc, sqlite3_stmt *stmt, const char *vlr, bool reset)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!db_bind_text(stmt, "$vlr", vlr))
|
||||
return -EIO;
|
||||
|
||||
/* execute the statement */
|
||||
rc = sqlite3_step(stmt);
|
||||
if (reset)
|
||||
db_remove_reset(stmt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int _db_ind_add(struct db_context *dbc, const char *vlr)
|
||||
{
|
||||
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IND_ADD];
|
||||
if (_db_ind_run(dbc, stmt, vlr, true) != SQLITE_DONE) {
|
||||
LOGP(DDB, LOGL_ERROR, "Cannot create IND entry for %s\n", osmo_quote_str_c(OTC_SELECT, vlr, -1));
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _db_ind_del(struct db_context *dbc, const char *vlr)
|
||||
{
|
||||
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IND_DEL];
|
||||
_db_ind_run(dbc, stmt, vlr, true);
|
||||
/* We don't really care about the result. If it didn't exist, then that was the goal anyway. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _db_ind_get(struct db_context *dbc, const char *vlr, unsigned int *ind)
|
||||
{
|
||||
int ret = 0;
|
||||
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IND_SELECT];
|
||||
int rc = _db_ind_run(dbc, stmt, vlr, false);
|
||||
if (rc == SQLITE_DONE) {
|
||||
/* Does not exist yet */
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
} else if (rc != SQLITE_ROW) {
|
||||
LOGP(DDB, LOGL_ERROR, "Error executing SQL: %d\n", rc);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(ind);
|
||||
*ind = sqlite3_column_int64(stmt, 0);
|
||||
out:
|
||||
db_remove_reset(stmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int _db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr,
|
||||
unsigned int *ind, bool del)
|
||||
{
|
||||
const char *vlr_name = NULL;
|
||||
int rc;
|
||||
|
||||
switch (vlr->type) {
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
if (vlr->ipa_name.len < 2 || vlr->ipa_name.val[vlr->ipa_name.len - 1] != '\0') {
|
||||
LOGP(DDB, LOGL_ERROR, "Expecting VLR ipa_name to be zero terminated; found %s\n",
|
||||
osmo_ipa_name_to_str(&vlr->ipa_name));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
vlr_name = (const char*)vlr->ipa_name.val;
|
||||
break;
|
||||
default:
|
||||
LOGP(DDB, LOGL_ERROR, "Unsupported osmo_cni_peer_id type: %s\n",
|
||||
osmo_cni_peer_id_type_name(vlr->type));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (del)
|
||||
return _db_ind_del(dbc, vlr_name);
|
||||
|
||||
rc = _db_ind_get(dbc, vlr_name, ind);
|
||||
if (!rc)
|
||||
return 0;
|
||||
|
||||
/* Does not exist yet, create. */
|
||||
rc = _db_ind_add(dbc, vlr_name);
|
||||
if (rc) {
|
||||
LOGP(DDB, LOGL_ERROR, "Error creating IND entry for %s\n", osmo_quote_str_c(OTC_SELECT, vlr_name, -1));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* To be sure, query again from scratch. */
|
||||
return _db_ind_get(dbc, vlr_name, ind);
|
||||
}
|
||||
|
||||
int db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr, unsigned int *ind)
|
||||
{
|
||||
return _db_ind(dbc, vlr, ind, false);
|
||||
}
|
||||
|
||||
int db_ind_del(struct db_context *dbc, const struct osmo_cni_peer_id *vlr)
|
||||
{
|
||||
return _db_ind(dbc, vlr, NULL, true);
|
||||
}
|
||||
|
||||
528
src/dgsm.c
528
src/dgsm.c
@@ -1,50 +1,266 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/mslookup/mslookup_client.h>
|
||||
#include <osmocom/mslookup/mslookup_client_mdns.h>
|
||||
#include <osmocom/gsupclient/gsup_client.h>
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include <osmocom/hlr/gsup_router.h>
|
||||
#include <osmocom/hlr/gsup_server.h>
|
||||
#include <osmocom/hlr/dgsm.h>
|
||||
#include <osmocom/hlr/proxy.h>
|
||||
#include <osmocom/hlr/remote_hlr.h>
|
||||
#include <osmocom/hlr/mslookup_server.h>
|
||||
#include <osmocom/hlr/mslookup_server_mdns.h>
|
||||
#include <osmocom/hlr/dgsm.h>
|
||||
#include "logging.h"
|
||||
#include "hlr.h"
|
||||
#include "db.h"
|
||||
#include "gsup_router.h"
|
||||
#include "gsup_server.h"
|
||||
#include "dgsm.h"
|
||||
#include "proxy.h"
|
||||
#include "remote_hlr.h"
|
||||
#include "mslookup_server_mdns.h"
|
||||
#include "global_title.h"
|
||||
|
||||
void *dgsm_ctx = NULL;
|
||||
|
||||
const struct global_title dgsm_config_msc_wildcard = {};
|
||||
|
||||
struct dgsm_msc_config *dgsm_config_msc_get(const struct global_title *msc_name, bool create)
|
||||
{
|
||||
struct dgsm_msc_config *msc;
|
||||
|
||||
if (!msc_name)
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(msc, &g_hlr->mslookup.vty.server.msc_configs, entry) {
|
||||
if (global_title_cmp(&msc->name, msc_name))
|
||||
continue;
|
||||
return msc;
|
||||
}
|
||||
if (!create)
|
||||
return NULL;
|
||||
|
||||
msc = talloc_zero(dgsm_ctx, struct dgsm_msc_config);
|
||||
OSMO_ASSERT(msc);
|
||||
INIT_LLIST_HEAD(&msc->service_hosts);
|
||||
msc->name = *msc_name;
|
||||
return msc;
|
||||
}
|
||||
|
||||
struct dgsm_service_host *dgsm_config_msc_service_get(struct dgsm_msc_config *msc, const char *service, bool create)
|
||||
{
|
||||
struct dgsm_service_host *e;
|
||||
if (!msc)
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(e, &msc->service_hosts, entry) {
|
||||
if (!strcmp(e->service, service))
|
||||
return e;
|
||||
}
|
||||
|
||||
if (!create)
|
||||
return NULL;
|
||||
|
||||
e = talloc_zero(msc, struct dgsm_service_host);
|
||||
OSMO_ASSERT(e);
|
||||
OSMO_STRLCPY_ARRAY(e->service, service);
|
||||
llist_add_tail(&e->entry, &msc->service_hosts);
|
||||
return e;
|
||||
}
|
||||
|
||||
struct dgsm_service_host *dgsm_config_service_get(const struct global_title *msc_name, const char *service)
|
||||
{
|
||||
struct dgsm_msc_config *msc = dgsm_config_msc_get(msc_name, false);
|
||||
if (!msc)
|
||||
return NULL;
|
||||
return dgsm_config_msc_service_get(msc, service, false);
|
||||
}
|
||||
|
||||
int dgsm_config_msc_service_set(struct dgsm_msc_config *msc, const char *service, const struct osmo_sockaddr_str *addr)
|
||||
{
|
||||
struct dgsm_service_host *e;
|
||||
|
||||
if (!service || !service[0]
|
||||
|| strlen(service) > OSMO_MSLOOKUP_SERVICE_MAXLEN)
|
||||
return -EINVAL;
|
||||
if (!addr || !osmo_sockaddr_str_is_nonzero(addr))
|
||||
return -EINVAL;
|
||||
|
||||
e = dgsm_config_msc_service_get(msc, service, true);
|
||||
if (!e)
|
||||
return -EINVAL;
|
||||
|
||||
switch (addr->af) {
|
||||
case AF_INET:
|
||||
e->host_v4 = *addr;
|
||||
break;
|
||||
case AF_INET6:
|
||||
e->host_v6 = *addr;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dgsm_config_service_set(const struct global_title *msc_name, const char *service, const struct osmo_sockaddr_str *addr)
|
||||
{
|
||||
struct dgsm_msc_config *msc;
|
||||
|
||||
msc = dgsm_config_msc_get(msc_name, true);
|
||||
if (!msc)
|
||||
return -EINVAL;
|
||||
|
||||
return dgsm_config_msc_service_set(msc, service, addr);
|
||||
}
|
||||
|
||||
int dgsm_config_msc_service_del(struct dgsm_msc_config *msc, const char *service, const struct osmo_sockaddr_str *addr)
|
||||
{
|
||||
struct dgsm_service_host *e, *n;
|
||||
|
||||
if (!msc)
|
||||
return -ENOENT;
|
||||
|
||||
llist_for_each_entry_safe(e, n, &msc->service_hosts, entry) {
|
||||
if (service && strcmp(service, e->service))
|
||||
continue;
|
||||
|
||||
if (addr) {
|
||||
if (!osmo_sockaddr_str_cmp(addr, &e->host_v4)) {
|
||||
e->host_v4 = (struct osmo_sockaddr_str){};
|
||||
/* Removed one addr. If the other is still there, keep the entry. */
|
||||
if (osmo_sockaddr_str_is_nonzero(&e->host_v6))
|
||||
continue;
|
||||
} else if (!osmo_sockaddr_str_cmp(addr, &e->host_v6)) {
|
||||
e->host_v6 = (struct osmo_sockaddr_str){};
|
||||
/* Removed one addr. If the other is still there, keep the entry. */
|
||||
if (osmo_sockaddr_str_is_nonzero(&e->host_v4))
|
||||
continue;
|
||||
} else
|
||||
/* No addr match, keep the entry. */
|
||||
continue;
|
||||
/* Addr matched and none is left. Delete. */
|
||||
}
|
||||
llist_del(&e->entry);
|
||||
talloc_free(e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dgsm_config_service_del(const struct global_title *msc_name,
|
||||
const char *service, const struct osmo_sockaddr_str *addr)
|
||||
{
|
||||
return dgsm_config_msc_service_del(dgsm_config_msc_get(msc_name, false),
|
||||
service, addr);
|
||||
}
|
||||
|
||||
static void *dgsm_pending_messages_ctx = NULL;
|
||||
|
||||
struct pending_gsup_message {
|
||||
struct llist_head entry;
|
||||
struct osmo_gsup_req *req;
|
||||
struct timeval received_at;
|
||||
};
|
||||
static LLIST_HEAD(pending_gsup_messages);
|
||||
|
||||
/* Defer a GSUP message until we know a remote HLR to proxy to.
|
||||
* Where to send this GSUP message is indicated by its IMSI: as soon as an MS lookup has yielded the IMSI's home HLR,
|
||||
* that's where the message should go. */
|
||||
static void defer_gsup_req(struct osmo_gsup_req *req)
|
||||
{
|
||||
struct pending_gsup_message *m;
|
||||
|
||||
m = talloc_zero(dgsm_pending_messages_ctx, struct pending_gsup_message);
|
||||
OSMO_ASSERT(m);
|
||||
m->req = req;
|
||||
timestamp_update(&m->received_at);
|
||||
llist_add_tail(&m->entry, &pending_gsup_messages);
|
||||
}
|
||||
|
||||
/* Unable to resolve remote HLR for this IMSI, Answer with error back to the sender. */
|
||||
static void defer_gsup_message_err(struct pending_gsup_message *m)
|
||||
{
|
||||
osmo_gsup_req_respond_err(m->req, GMM_CAUSE_IMSI_UNKNOWN, "could not reach home HLR");
|
||||
m->req = NULL;
|
||||
}
|
||||
|
||||
/* Forward spooled message for this IMSI to remote HLR. */
|
||||
static void defer_gsup_message_send(struct pending_gsup_message *m, struct remote_hlr *remote_hlr)
|
||||
{
|
||||
LOG_GSUP_REQ(m->req, LOGL_INFO, "Forwarding deferred message to " OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
|
||||
|
||||
/* If sending fails, still discard. */
|
||||
if (!remote_hlr->gsupc || !remote_hlr->gsupc->is_connected) {
|
||||
LOGP(DDGSM, LOGL_ERROR, "GSUP link to remote HLR is not connected: " OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
|
||||
defer_gsup_message_err(m);
|
||||
return;
|
||||
}
|
||||
|
||||
remote_hlr_gsup_forward(remote_hlr, m->req);
|
||||
m->req = NULL;
|
||||
}
|
||||
|
||||
/* Result of looking for remote HLR. If it failed, pass remote_hlr as NULL. */
|
||||
static void defer_gsup_message_pop(const char *imsi, struct remote_hlr *remote_hlr)
|
||||
{
|
||||
struct pending_gsup_message *m, *n;
|
||||
|
||||
if (remote_hlr)
|
||||
LOG_DGSM(imsi, LOGL_DEBUG, "Sending spooled GSUP messages to remote HLR at " OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
|
||||
else
|
||||
LOG_DGSM(imsi, LOGL_ERROR, "No remote HLR found, dropping spooled GSUP messages\n");
|
||||
|
||||
llist_for_each_entry_safe(m, n, &pending_gsup_messages, entry) {
|
||||
if (strcmp(m->req->gsup.imsi, imsi))
|
||||
continue;
|
||||
|
||||
if (!remote_hlr)
|
||||
defer_gsup_message_err(m);
|
||||
else
|
||||
defer_gsup_message_send(m, remote_hlr);
|
||||
|
||||
llist_del(&m->entry);
|
||||
talloc_free(m);
|
||||
}
|
||||
}
|
||||
|
||||
void dgsm_send_to_remote_hlr(const struct proxy_subscr *proxy_subscr, struct osmo_gsup_req *req)
|
||||
{
|
||||
struct remote_hlr *remote_hlr;
|
||||
|
||||
if (!osmo_sockaddr_str_is_nonzero(&proxy_subscr->remote_hlr_addr)) {
|
||||
/* We don't know the remote target yet. Still waiting for an MS lookup response. */
|
||||
LOG_GSUP_REQ(req, LOGL_DEBUG, "Proxy: deferring until remote HLR is known\n");
|
||||
defer_gsup_req(req);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_GSUP_REQ(req, LOGL_INFO, "Proxy: forwarding to " OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr->remote_hlr_addr));
|
||||
|
||||
remote_hlr = remote_hlr_get(&proxy_subscr->remote_hlr_addr, true);
|
||||
if (!remote_hlr) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL,
|
||||
"Proxy: Failed to establish connection to remote HLR " OSMO_SOCKADDR_STR_FMT,
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr->remote_hlr_addr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!remote_hlr->gsupc || !remote_hlr->gsupc->is_connected) {
|
||||
/* GSUP link is still busy establishing... */
|
||||
LOG_GSUP_REQ(req, LOGL_DEBUG, "Proxy: deferring until link to remote HLR is up\n");
|
||||
defer_gsup_req(req);
|
||||
return;
|
||||
}
|
||||
|
||||
remote_hlr_gsup_forward(remote_hlr, req);
|
||||
}
|
||||
|
||||
static void resolve_hlr_result_cb(struct osmo_mslookup_client *client,
|
||||
uint32_t request_handle,
|
||||
const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_result *result)
|
||||
{
|
||||
struct proxy *proxy = g_hlr->gs->proxy;
|
||||
struct proxy_subscr proxy_subscr;
|
||||
const struct osmo_sockaddr_str *remote_hlr_addr;
|
||||
struct proxy *proxy = g_hlr->proxy;
|
||||
const struct proxy_subscr *proxy_subscr;
|
||||
struct proxy_subscr proxy_subscr_new;
|
||||
struct remote_hlr *remote_hlr;
|
||||
|
||||
/* A remote HLR is answering back, indicating that it is the home HLR for a given IMSI.
|
||||
* There should be a mostly empty proxy entry for that IMSI.
|
||||
@@ -55,38 +271,86 @@ static void resolve_hlr_result_cb(struct osmo_mslookup_client *client,
|
||||
return;
|
||||
}
|
||||
|
||||
if (result->rc != OSMO_MSLOOKUP_RC_RESULT) {
|
||||
LOG_DGSM(query->id.imsi, LOGL_ERROR, "Failed to resolve remote HLR: %s\n",
|
||||
proxy_subscr = proxy_subscr_get_by_imsi(proxy, query->id.imsi);
|
||||
if (!proxy_subscr) {
|
||||
LOG_DGSM(query->id.imsi, LOGL_ERROR, "No proxy entry for mslookup result: %s\n",
|
||||
osmo_mslookup_result_name_c(OTC_SELECT, query, result));
|
||||
proxy_subscr_del(proxy, query->id.imsi);
|
||||
defer_gsup_message_pop(query->id.imsi, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result->rc != OSMO_MSLOOKUP_RC_OK) {
|
||||
LOG_DGSM(query->id.imsi, LOGL_ERROR, "Failed to resolve remote HLR: %s\n",
|
||||
osmo_mslookup_result_name_c(OTC_SELECT, query, result));
|
||||
defer_gsup_message_pop(query->id.imsi, NULL);
|
||||
proxy_subscr_del(proxy, proxy_subscr->imsi);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store the address. Make a copy to modify. */
|
||||
proxy_subscr_new = *proxy_subscr;
|
||||
proxy_subscr = &proxy_subscr_new;
|
||||
if (osmo_sockaddr_str_is_nonzero(&result->host_v4))
|
||||
remote_hlr_addr = &result->host_v4;
|
||||
proxy_subscr_new.remote_hlr_addr = result->host_v4;
|
||||
else if (osmo_sockaddr_str_is_nonzero(&result->host_v6))
|
||||
remote_hlr_addr = &result->host_v6;
|
||||
proxy_subscr_new.remote_hlr_addr = result->host_v6;
|
||||
else {
|
||||
LOG_DGSM(query->id.imsi, LOGL_ERROR, "Invalid address for remote HLR: %s\n",
|
||||
osmo_mslookup_result_name_c(OTC_SELECT, query, result));
|
||||
proxy_subscr_del(proxy, query->id.imsi);
|
||||
defer_gsup_message_pop(query->id.imsi, NULL);
|
||||
proxy_subscr_del(proxy, proxy_subscr->imsi);
|
||||
return;
|
||||
}
|
||||
|
||||
if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, query->id.imsi)) {
|
||||
LOG_DGSM(query->id.imsi, LOGL_ERROR, "No proxy entry for mslookup result: %s\n",
|
||||
if (proxy_subscr_update(proxy, proxy_subscr)) {
|
||||
LOG_DGSM(query->id.imsi, LOGL_ERROR, "Failed to store proxy entry for remote HLR: %s\n",
|
||||
osmo_mslookup_result_name_c(OTC_SELECT, query, result));
|
||||
defer_gsup_message_pop(query->id.imsi, NULL);
|
||||
proxy_subscr_del(proxy, proxy_subscr->imsi);
|
||||
return;
|
||||
}
|
||||
LOG_DGSM(proxy_subscr->imsi, LOGL_DEBUG, "Stored remote hlr address for this IMSI: " OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr->remote_hlr_addr));
|
||||
|
||||
remote_hlr = remote_hlr_get(&proxy_subscr->remote_hlr_addr, true);
|
||||
if (!remote_hlr) {
|
||||
defer_gsup_message_pop(query->id.imsi, NULL);
|
||||
proxy_subscr_del(proxy, proxy_subscr->imsi);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!remote_hlr->gsupc || !remote_hlr->gsupc->is_connected) {
|
||||
LOG_DGSM(query->id.imsi, LOGL_DEBUG, "Resolved remote HLR, waiting for link-up: %s\n",
|
||||
osmo_mslookup_result_name_c(OTC_SELECT, query, result));
|
||||
return;
|
||||
}
|
||||
|
||||
proxy_subscr_remote_hlr_resolved(proxy, &proxy_subscr, remote_hlr_addr);
|
||||
LOG_DGSM(query->id.imsi, LOGL_DEBUG, "Resolved remote HLR, sending spooled GSUP messages: %s\n",
|
||||
osmo_mslookup_result_name_c(OTC_SELECT, query, result));
|
||||
defer_gsup_message_pop(query->id.imsi, remote_hlr);
|
||||
}
|
||||
|
||||
static bool remote_hlr_up_yield(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, void *data)
|
||||
{
|
||||
struct remote_hlr *remote_hlr = data;
|
||||
defer_gsup_message_pop(proxy_subscr->imsi, remote_hlr);
|
||||
return true;
|
||||
}
|
||||
|
||||
void dgsm_remote_hlr_up(struct remote_hlr *remote_hlr)
|
||||
{
|
||||
LOGP(DDGSM, LOGL_NOTICE, "link to remote HLR is up, sending spooled GSUP messages: " OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
|
||||
/* Send all spooled GSUP messaged for IMSIs that are waiting for this link to establish. */
|
||||
proxy_subscrs_get_by_remote_hlr(g_hlr->proxy, &remote_hlr->addr, remote_hlr_up_yield, remote_hlr);
|
||||
}
|
||||
|
||||
/* Return true when the message has been handled by D-GSM. */
|
||||
bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req)
|
||||
{
|
||||
struct proxy_subscr proxy_subscr;
|
||||
struct proxy *proxy = g_hlr->gs->proxy;
|
||||
const struct proxy_subscr *proxy_subscr;
|
||||
struct proxy_subscr proxy_subscr_new;
|
||||
struct proxy *proxy = g_hlr->proxy;
|
||||
struct osmo_mslookup_query query;
|
||||
struct osmo_mslookup_query_handling handling;
|
||||
uint32_t request_handle;
|
||||
@@ -96,59 +360,29 @@ bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req)
|
||||
return false;
|
||||
|
||||
/* Are we already forwarding this IMSI to a remote HLR? */
|
||||
if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, req->gsup.imsi) == 0) {
|
||||
proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req);
|
||||
return true;
|
||||
}
|
||||
proxy_subscr = proxy_subscr_get_by_imsi(proxy, req->gsup.imsi);
|
||||
if (proxy_subscr)
|
||||
goto yes_we_are_proxying;
|
||||
|
||||
/* The IMSI is not known locally, so we want to proxy to a remote HLR, but no proxy entry exists yet. We need to
|
||||
* look up the subscriber in remote HLRs via D-GSM mslookup, forward GSUP and reply once a result is back from
|
||||
* there. Defer message and kick off MS lookup. */
|
||||
|
||||
/* Add a proxy entry without a remote address to indicate that we are busy querying for a remote HLR. */
|
||||
proxy_subscr = (struct proxy_subscr){};
|
||||
OSMO_STRLCPY_ARRAY(proxy_subscr.imsi, req->gsup.imsi);
|
||||
if (proxy_subscr_create_or_update(proxy, &proxy_subscr)) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Failed to create proxy entry\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Is a fixed gateway proxy configured? */
|
||||
if (osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_gateway_proxy)) {
|
||||
proxy_subscr_remote_hlr_resolved(proxy, &proxy_subscr, &g_hlr->mslookup.client.gsup_gateway_proxy);
|
||||
|
||||
/* Proxy database modified, update info */
|
||||
if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, req->gsup.imsi)) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Internal proxy error\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Kick off an mslookup for the remote HLR? This check could be up first on the top, but do it only now so that
|
||||
* if the mslookup client disconnected, we still continue to service open proxy entries. */
|
||||
if (!osmo_mslookup_client_active(g_hlr->mslookup.client.client)) {
|
||||
/* Kick off an mslookup for the remote HLR. */
|
||||
if (!g_hlr->mslookup.client.client) {
|
||||
LOG_GSUP_REQ(req, LOGL_DEBUG, "mslookup client not running, cannot query remote home HLR\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* First spool message, then kick off mslookup. If the proxy denies this message type, then don't do anything. */
|
||||
if (proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req)) {
|
||||
/* If the proxy denied forwarding, an error response was already generated. */
|
||||
return true;
|
||||
}
|
||||
|
||||
query = (struct osmo_mslookup_query){
|
||||
.id = {
|
||||
.type = OSMO_MSLOOKUP_ID_IMSI,
|
||||
},
|
||||
}
|
||||
};
|
||||
OSMO_STRLCPY_ARRAY(query.id.imsi, req->gsup.imsi);
|
||||
OSMO_STRLCPY_ARRAY(query.service, OSMO_MSLOOKUP_SERVICE_HLR_GSUP);
|
||||
handling = (struct osmo_mslookup_query_handling){
|
||||
.min_wait_milliseconds = g_hlr->mslookup.client.result_timeout_milliseconds,
|
||||
.result_timeout_milliseconds = g_hlr->mslookup.client.result_timeout_milliseconds,
|
||||
.result_cb = resolve_hlr_result_cb,
|
||||
};
|
||||
request_handle = osmo_mslookup_client_request(g_hlr->mslookup.client.client, &query, &handling);
|
||||
@@ -156,19 +390,32 @@ bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req)
|
||||
LOG_DGSM(req->gsup.imsi, LOGL_ERROR, "Error dispatching mslookup query for home HLR: %s\n",
|
||||
osmo_mslookup_result_name_c(OTC_SELECT, &query, NULL));
|
||||
proxy_subscr_del(proxy, req->gsup.imsi);
|
||||
/* mslookup seems to not be working. Try handling it locally. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Add a proxy entry without a remote address to indicate that we are busy querying for a remote HLR. */
|
||||
proxy_subscr_new = (struct proxy_subscr){};
|
||||
OSMO_STRLCPY_ARRAY(proxy_subscr_new.imsi, req->gsup.imsi);
|
||||
proxy_subscr = &proxy_subscr_new;
|
||||
proxy_subscr_update(proxy, proxy_subscr);
|
||||
|
||||
yes_we_are_proxying:
|
||||
OSMO_ASSERT(proxy_subscr);
|
||||
|
||||
/* If the remote HLR is already known, directly forward the GSUP message; otherwise, spool the GSUP message
|
||||
* until the remote HLR will respond / until timeout aborts. */
|
||||
dgsm_send_to_remote_hlr(proxy_subscr, req);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void dgsm_init(void *ctx)
|
||||
{
|
||||
dgsm_ctx = talloc_named_const(ctx, 0, "dgsm");
|
||||
INIT_LLIST_HEAD(&g_hlr->mslookup.server.local_site_services);
|
||||
dgsm_pending_messages_ctx = talloc_named_const(dgsm_ctx, 0, "dgsm_pending_messages");
|
||||
INIT_LLIST_HEAD(&g_hlr->mslookup.vty.server.msc_configs);
|
||||
|
||||
g_hlr->mslookup.server.local_attach_max_age = 60 * 60;
|
||||
g_hlr->mslookup.server.max_age = 60 * 60;
|
||||
|
||||
g_hlr->mslookup.client.result_timeout_milliseconds = 2000;
|
||||
|
||||
@@ -176,9 +423,9 @@ void dgsm_init(void *ctx)
|
||||
g_hlr->gsup_unit_name.serno = "unnamed-HLR";
|
||||
g_hlr->gsup_unit_name.swversion = PACKAGE_NAME "-" PACKAGE_VERSION;
|
||||
|
||||
osmo_sockaddr_str_from_str(&g_hlr->mslookup.server.mdns.bind_addr,
|
||||
osmo_sockaddr_str_from_str(&g_hlr->mslookup.vty.server.mdns.bind_addr,
|
||||
OSMO_MSLOOKUP_MDNS_IP4, OSMO_MSLOOKUP_MDNS_PORT);
|
||||
osmo_sockaddr_str_from_str(&g_hlr->mslookup.client.mdns.query_addr,
|
||||
osmo_sockaddr_str_from_str(&g_hlr->mslookup.vty.client.mdns.query_addr,
|
||||
OSMO_MSLOOKUP_MDNS_IP4, OSMO_MSLOOKUP_MDNS_PORT);
|
||||
}
|
||||
|
||||
@@ -187,61 +434,82 @@ void dgsm_start(void *ctx)
|
||||
g_hlr->mslookup.client.client = osmo_mslookup_client_new(dgsm_ctx);
|
||||
OSMO_ASSERT(g_hlr->mslookup.client.client);
|
||||
g_hlr->mslookup.allow_startup = true;
|
||||
mslookup_server_mdns_config_apply();
|
||||
dgsm_mdns_client_config_apply();
|
||||
dgsm_config_apply();
|
||||
}
|
||||
|
||||
void dgsm_stop()
|
||||
static void dgsm_mdns_server_config_apply()
|
||||
{
|
||||
g_hlr->mslookup.allow_startup = false;
|
||||
mslookup_server_mdns_config_apply();
|
||||
dgsm_mdns_client_config_apply();
|
||||
}
|
||||
/* Check whether to start/stop/restart mDNS server */
|
||||
bool should_run;
|
||||
bool should_stop;
|
||||
if (!g_hlr->mslookup.allow_startup)
|
||||
return;
|
||||
|
||||
void dgsm_mdns_client_config_apply(void)
|
||||
{
|
||||
/* Check whether to start/stop/restart mDNS client */
|
||||
const struct osmo_sockaddr_str *current_bind_addr;
|
||||
const char *current_domain_suffix;
|
||||
current_bind_addr = osmo_mslookup_client_method_mdns_get_bind_addr(g_hlr->mslookup.client.mdns.running);
|
||||
current_domain_suffix = osmo_mslookup_client_method_mdns_get_domain_suffix(g_hlr->mslookup.client.mdns.running);
|
||||
|
||||
bool should_run = g_hlr->mslookup.allow_startup
|
||||
&& g_hlr->mslookup.client.enable && g_hlr->mslookup.client.mdns.enable;
|
||||
|
||||
bool should_stop = g_hlr->mslookup.client.mdns.running &&
|
||||
(!should_run
|
||||
|| osmo_sockaddr_str_cmp(&g_hlr->mslookup.client.mdns.query_addr,
|
||||
current_bind_addr)
|
||||
|| strcmp(g_hlr->mslookup.client.mdns.domain_suffix,
|
||||
current_domain_suffix));
|
||||
should_run = g_hlr->mslookup.vty.server.enable && g_hlr->mslookup.vty.server.mdns.enable;
|
||||
should_stop = g_hlr->mslookup.server.mdns
|
||||
&& (!should_run
|
||||
|| osmo_sockaddr_str_cmp(&g_hlr->mslookup.vty.server.mdns.bind_addr,
|
||||
&g_hlr->mslookup.server.mdns->bind_addr));
|
||||
|
||||
if (should_stop) {
|
||||
osmo_mslookup_client_method_del(g_hlr->mslookup.client.client, g_hlr->mslookup.client.mdns.running);
|
||||
g_hlr->mslookup.client.mdns.running = NULL;
|
||||
osmo_mslookup_server_mdns_stop(g_hlr->mslookup.server.mdns);
|
||||
g_hlr->mslookup.server.mdns = NULL;
|
||||
LOGP(DDGSM, LOGL_NOTICE, "Stopped mslookup mDNS server\n");
|
||||
}
|
||||
|
||||
if (should_run && !g_hlr->mslookup.server.mdns) {
|
||||
g_hlr->mslookup.server.mdns =
|
||||
osmo_mslookup_server_mdns_start(g_hlr, &g_hlr->mslookup.vty.server.mdns.bind_addr);
|
||||
if (!g_hlr->mslookup.server.mdns)
|
||||
LOGP(DDGSM, LOGL_ERROR, "Failed to start mslookup mDNS server on " OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.server.mdns->bind_addr));
|
||||
else
|
||||
LOGP(DDGSM, LOGL_NOTICE, "Started mslookup mDNS server, receiving mDNS requests at multicast "
|
||||
OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.server.mdns->bind_addr));
|
||||
}
|
||||
}
|
||||
|
||||
static void dgsm_mdns_client_config_apply()
|
||||
{
|
||||
if (!g_hlr->mslookup.allow_startup)
|
||||
return;
|
||||
|
||||
/* Check whether to start/stop/restart mDNS client */
|
||||
const struct osmo_sockaddr_str *current_bind_addr;
|
||||
current_bind_addr = osmo_mslookup_client_method_mdns_get_bind_addr(g_hlr->mslookup.client.mdns);
|
||||
|
||||
bool should_run = g_hlr->mslookup.vty.client.enable && g_hlr->mslookup.vty.client.mdns.enable;
|
||||
bool should_stop = g_hlr->mslookup.client.mdns &&
|
||||
(!should_run
|
||||
|| osmo_sockaddr_str_cmp(&g_hlr->mslookup.vty.client.mdns.query_addr,
|
||||
current_bind_addr));
|
||||
|
||||
if (should_stop) {
|
||||
osmo_mslookup_client_method_del(g_hlr->mslookup.client.client, g_hlr->mslookup.client.mdns);
|
||||
g_hlr->mslookup.client.mdns = NULL;
|
||||
LOGP(DDGSM, LOGL_NOTICE, "Stopped mslookup mDNS client\n");
|
||||
}
|
||||
|
||||
if (should_run && !g_hlr->mslookup.client.mdns.running) {
|
||||
g_hlr->mslookup.client.mdns.running =
|
||||
if (should_run && !g_hlr->mslookup.client.mdns) {
|
||||
g_hlr->mslookup.client.mdns =
|
||||
osmo_mslookup_client_add_mdns(g_hlr->mslookup.client.client,
|
||||
g_hlr->mslookup.client.mdns.query_addr.ip,
|
||||
g_hlr->mslookup.client.mdns.query_addr.port,
|
||||
-1,
|
||||
g_hlr->mslookup.client.mdns.domain_suffix);
|
||||
if (!g_hlr->mslookup.client.mdns.running)
|
||||
g_hlr->mslookup.vty.client.mdns.query_addr.ip,
|
||||
g_hlr->mslookup.vty.client.mdns.query_addr.port,
|
||||
true);
|
||||
if (!g_hlr->mslookup.client.mdns)
|
||||
LOGP(DDGSM, LOGL_ERROR, "Failed to start mslookup mDNS client with target "
|
||||
OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.client.mdns.query_addr));
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.vty.client.mdns.query_addr));
|
||||
else
|
||||
LOGP(DDGSM, LOGL_NOTICE, "Started mslookup mDNS client, sending mDNS requests to multicast "
|
||||
OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.client.mdns.query_addr));
|
||||
LOGP(DDGSM, LOGL_NOTICE, "Started mslookup mDNS client, sending mDNS requests to multicast " OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.vty.client.mdns.query_addr));
|
||||
}
|
||||
|
||||
if (g_hlr->mslookup.client.enable && osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_gateway_proxy))
|
||||
LOGP(DDGSM, LOGL_NOTICE,
|
||||
"mslookup client: all GSUP requests for unknown IMSIs will be forwarded to"
|
||||
" gateway-proxy " OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.client.gsup_gateway_proxy));
|
||||
}
|
||||
|
||||
void dgsm_config_apply()
|
||||
{
|
||||
dgsm_mdns_server_config_apply();
|
||||
dgsm_mdns_client_config_apply();
|
||||
}
|
||||
|
||||
|
||||
75
src/dgsm.h
Normal file
75
src/dgsm.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mslookup/mslookup.h>
|
||||
#include "gsup_server.h"
|
||||
#include "global_title.h"
|
||||
|
||||
#define LOG_DGSM(imsi, level, fmt, args...) \
|
||||
LOGP(DDGSM, level, "(IMSI-%s) " fmt, imsi, ##args)
|
||||
|
||||
struct vty;
|
||||
struct remote_hlr;
|
||||
|
||||
extern void *dgsm_ctx;
|
||||
|
||||
struct dgsm_service_host {
|
||||
struct llist_head entry;
|
||||
char service[OSMO_MSLOOKUP_SERVICE_MAXLEN+1];
|
||||
struct osmo_sockaddr_str host_v4;
|
||||
struct osmo_sockaddr_str host_v6;
|
||||
};
|
||||
|
||||
struct dgsm_msc_config {
|
||||
struct llist_head entry;
|
||||
struct global_title name;
|
||||
struct llist_head service_hosts;
|
||||
};
|
||||
|
||||
/* "Sketch pad" where the VTY can store config items without yet applying. The changes will be applied by e.g.
|
||||
* dgsm_mdns_server_config_apply() and dgsm_mdns_client_config_apply(). */
|
||||
struct dgsm_config {
|
||||
struct {
|
||||
/* Whether to listen for incoming MS Lookup requests */
|
||||
bool enable;
|
||||
|
||||
struct {
|
||||
bool enable;
|
||||
struct osmo_sockaddr_str bind_addr;
|
||||
} mdns;
|
||||
|
||||
struct llist_head msc_configs;
|
||||
} server;
|
||||
|
||||
struct {
|
||||
/* Whether to ask remote HLRs via MS Lookup if an IMSI is not known locally. */
|
||||
bool enable;
|
||||
struct timeval timeout;
|
||||
|
||||
struct {
|
||||
/* Whether to use mDNS for IMSI MS Lookup */
|
||||
bool enable;
|
||||
struct osmo_sockaddr_str query_addr;
|
||||
} mdns;
|
||||
} client;
|
||||
};
|
||||
|
||||
void dgsm_config_apply();
|
||||
|
||||
struct dgsm_service_host *dgsm_config_service_get(const struct global_title *msc_name, const char *service);
|
||||
int dgsm_config_service_set(const struct global_title *msc_name, const char *service, const struct osmo_sockaddr_str *addr);
|
||||
int dgsm_config_service_del(const struct global_title *msc_name, const char *service, const struct osmo_sockaddr_str *addr);
|
||||
|
||||
struct dgsm_service_host *dgsm_config_msc_service_get(struct dgsm_msc_config *msc, const char *service, bool create);
|
||||
int dgsm_config_msc_service_set(struct dgsm_msc_config *msc, const char *service, const struct osmo_sockaddr_str *addr);
|
||||
int dgsm_config_msc_service_del(struct dgsm_msc_config *msc, const char *service, const struct osmo_sockaddr_str *addr);
|
||||
|
||||
extern const struct global_title dgsm_config_msc_wildcard;
|
||||
struct dgsm_msc_config *dgsm_config_msc_get(const struct global_title *msc_name, bool create);
|
||||
|
||||
void dgsm_init(void *ctx);
|
||||
void dgsm_start(void *ctx);
|
||||
bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req);
|
||||
|
||||
void dgsm_vty_init();
|
||||
|
||||
void dgsm_remote_hlr_up(struct remote_hlr *remote_hlr);
|
||||
457
src/dgsm_vty.c
457
src/dgsm_vty.c
@@ -1,30 +1,7 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/mslookup/mslookup_client_mdns.h>
|
||||
#include <osmocom/mslookup/mdns.h>
|
||||
#include <osmocom/hlr/hlr_vty.h>
|
||||
#include <osmocom/hlr/mslookup_server.h>
|
||||
#include <osmocom/hlr/mslookup_server_mdns.h>
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include "hlr_vty.h"
|
||||
#include "dgsm.h"
|
||||
|
||||
struct cmd_node mslookup_node = {
|
||||
MSLOOKUP_NODE,
|
||||
@@ -35,97 +12,33 @@ struct cmd_node mslookup_node = {
|
||||
DEFUN(cfg_mslookup,
|
||||
cfg_mslookup_cmd,
|
||||
"mslookup",
|
||||
"Configure Distributed GSM mslookup")
|
||||
"Configure Distributed GSM / multicast MS Lookup")
|
||||
{
|
||||
vty->node = MSLOOKUP_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int mslookup_server_mdns_bind(struct vty *vty, int argc, const char **argv)
|
||||
{
|
||||
const char *ip_str = argc > 0? argv[0] : g_hlr->mslookup.server.mdns.bind_addr.ip;
|
||||
const char *port_str = argc > 1? argv[1] : NULL;
|
||||
uint16_t port_nr = port_str ? atoi(port_str) : g_hlr->mslookup.server.mdns.bind_addr.port;
|
||||
struct osmo_sockaddr_str addr;
|
||||
if (osmo_sockaddr_str_from_str(&addr, ip_str, port_nr)
|
||||
|| !osmo_sockaddr_str_is_nonzero(&addr)) {
|
||||
vty_out(vty, "%% mslookup server: Invalid mDNS bind address: %s %u%s",
|
||||
ip_str, port_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
g_hlr->mslookup.server.mdns.bind_addr = addr;
|
||||
g_hlr->mslookup.server.mdns.enable = true;
|
||||
g_hlr->mslookup.server.enable = true;
|
||||
mslookup_server_mdns_config_apply();
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int mslookup_client_mdns_to(struct vty *vty, int argc, const char **argv)
|
||||
{
|
||||
const char *ip_str = argc > 0? argv[0] : g_hlr->mslookup.client.mdns.query_addr.ip;
|
||||
const char *port_str = argc > 1? argv[1] : NULL;
|
||||
uint16_t port_nr = port_str ? atoi(port_str) : g_hlr->mslookup.client.mdns.query_addr.port;
|
||||
struct osmo_sockaddr_str addr;
|
||||
if (osmo_sockaddr_str_from_str(&addr, ip_str, port_nr)
|
||||
|| !osmo_sockaddr_str_is_nonzero(&addr)) {
|
||||
vty_out(vty, "%% mslookup client: Invalid mDNS target address: %s %u%s",
|
||||
ip_str, port_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
g_hlr->mslookup.client.mdns.query_addr = addr;
|
||||
g_hlr->mslookup.client.mdns.enable = true;
|
||||
g_hlr->mslookup.client.enable = true;
|
||||
dgsm_mdns_client_config_apply();
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define MDNS_STR "Multicast DNS related configuration\n"
|
||||
#define MDNS_IP46_STR "multicast IPv4 address like " OSMO_MSLOOKUP_MDNS_IP4 \
|
||||
" or IPv6 address like " OSMO_MSLOOKUP_MDNS_IP6 "\n"
|
||||
#define MDNS_PORT_STR "mDNS UDP Port number\n"
|
||||
#define MDNS_DOMAIN_SUFFIX_STR "mDNS domain suffix (default: " OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT "). This is appended" \
|
||||
" and stripped from mDNS packets during encoding/decoding, so we don't collide with" \
|
||||
" top-level domains administrated by IANA\n"
|
||||
#define IP46_STR "IPv4 address like 1.2.3.4 or IPv6 address like a:b:c:d::1\n"
|
||||
#define PORT_STR "Service-specific port number\n"
|
||||
|
||||
DEFUN(cfg_mslookup_mdns,
|
||||
cfg_mslookup_mdns_cmd,
|
||||
"mdns bind [IP] [<1-65535>]",
|
||||
MDNS_STR
|
||||
"Convenience shortcut: enable and configure both server and client for mDNS mslookup\n"
|
||||
MDNS_IP46_STR MDNS_PORT_STR)
|
||||
"mdns",
|
||||
"Convenience shortcut: enable both server and client for DNS/mDNS MS Lookup with default config\n")
|
||||
{
|
||||
int rc1 = mslookup_server_mdns_bind(vty, argc, argv);
|
||||
int rc2 = mslookup_client_mdns_to(vty, argc, argv);
|
||||
if (rc1 != CMD_SUCCESS)
|
||||
return rc1;
|
||||
return rc2;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mslookup_mdns_domain_suffix,
|
||||
cfg_mslookup_mdns_domain_suffix_cmd,
|
||||
"mdns domain-suffix DOMAIN_SUFFIX",
|
||||
MDNS_STR MDNS_DOMAIN_SUFFIX_STR MDNS_DOMAIN_SUFFIX_STR)
|
||||
{
|
||||
osmo_talloc_replace_string(g_hlr, &g_hlr->mslookup.server.mdns.domain_suffix, argv[0]);
|
||||
osmo_talloc_replace_string(g_hlr, &g_hlr->mslookup.client.mdns.domain_suffix, argv[0]);
|
||||
mslookup_server_mdns_config_apply();
|
||||
dgsm_mdns_client_config_apply();
|
||||
g_hlr->mslookup.vty.server.enable = true;
|
||||
g_hlr->mslookup.vty.server.mdns.enable = true;
|
||||
g_hlr->mslookup.vty.client.enable = true;
|
||||
g_hlr->mslookup.vty.client.mdns.enable = true;
|
||||
dgsm_config_apply();
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mslookup_no_mdns,
|
||||
cfg_mslookup_no_mdns_cmd,
|
||||
"no mdns bind",
|
||||
NO_STR "Disable both server and client for mDNS mslookup\n")
|
||||
"no mdns",
|
||||
NO_STR "Disable both server and client for DNS/mDNS MS Lookup\n")
|
||||
{
|
||||
g_hlr->mslookup.server.mdns.enable = false;
|
||||
g_hlr->mslookup.client.mdns.enable = false;
|
||||
mslookup_server_mdns_config_apply();
|
||||
dgsm_mdns_client_config_apply();
|
||||
g_hlr->mslookup.vty.server.mdns.enable = false;
|
||||
g_hlr->mslookup.vty.client.mdns.enable = false;
|
||||
dgsm_config_apply();
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -138,53 +51,58 @@ struct cmd_node mslookup_server_node = {
|
||||
DEFUN(cfg_mslookup_server,
|
||||
cfg_mslookup_server_cmd,
|
||||
"server",
|
||||
"Enable and configure Distributed GSM mslookup server")
|
||||
"Enable and configure Distributed GSM / multicast MS Lookup server")
|
||||
{
|
||||
vty->node = MSLOOKUP_SERVER_NODE;
|
||||
g_hlr->mslookup.server.enable = true;
|
||||
mslookup_server_mdns_config_apply();
|
||||
g_hlr->mslookup.vty.server.enable = true;
|
||||
dgsm_config_apply();
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mslookup_no_server,
|
||||
cfg_mslookup_no_server_cmd,
|
||||
"no server",
|
||||
NO_STR "Disable Distributed GSM mslookup server")
|
||||
NO_STR "Disable Distributed GSM / multicast MS Lookup server")
|
||||
{
|
||||
g_hlr->mslookup.server.enable = false;
|
||||
mslookup_server_mdns_config_apply();
|
||||
g_hlr->mslookup.vty.server.enable = false;
|
||||
dgsm_config_apply();
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define MDNS_STR "Configure mslookup by multicast DNS\n"
|
||||
#define MDNS_BIND_STR MDNS_STR "Configure where the mDNS server listens for MS Lookup requests\n"
|
||||
#define IP46_STR "IPv4 address like 1.2.3.4 or IPv6 address like a:b:c:d::1\n"
|
||||
#define PORT_STR "Port number\n"
|
||||
|
||||
DEFUN(cfg_mslookup_server_mdns_bind,
|
||||
cfg_mslookup_server_mdns_bind_cmd,
|
||||
"mdns bind [IP] [<1-65535>]",
|
||||
MDNS_STR
|
||||
"Configure where the mDNS server listens for mslookup requests\n"
|
||||
MDNS_IP46_STR MDNS_PORT_STR)
|
||||
"mdns [bind] [IP] [<1-65535>]",
|
||||
MDNS_BIND_STR IP46_STR PORT_STR)
|
||||
{
|
||||
return mslookup_server_mdns_bind(vty, argc, argv);
|
||||
}
|
||||
const char *ip_str = argc > 1? argv[1] : g_hlr->mslookup.vty.server.mdns.bind_addr.ip;
|
||||
const char *port_str = argc > 2? argv[2] : NULL;
|
||||
uint16_t port_nr = port_str ? atoi(port_str) : g_hlr->mslookup.vty.server.mdns.bind_addr.port;
|
||||
struct osmo_sockaddr_str addr;
|
||||
if (osmo_sockaddr_str_from_str(&addr, ip_str, port_nr)
|
||||
|| !osmo_sockaddr_str_is_nonzero(&addr)) {
|
||||
vty_out(vty, "%% MS Lookup server: Invalid mDNS bind address: %s %u%s",
|
||||
ip_str, port_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mslookup_server_mdns_domain_suffix,
|
||||
cfg_mslookup_server_mdns_domain_suffix_cmd,
|
||||
"mdns domain-suffix DOMAIN_SUFFIX",
|
||||
MDNS_STR
|
||||
MDNS_DOMAIN_SUFFIX_STR
|
||||
MDNS_DOMAIN_SUFFIX_STR)
|
||||
{
|
||||
osmo_talloc_replace_string(g_hlr, &g_hlr->mslookup.server.mdns.domain_suffix, argv[0]);
|
||||
mslookup_server_mdns_config_apply();
|
||||
g_hlr->mslookup.vty.server.mdns.bind_addr = addr;
|
||||
g_hlr->mslookup.vty.server.mdns.enable = true;
|
||||
dgsm_config_apply();
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mslookup_server_no_mdns_bind,
|
||||
cfg_mslookup_server_no_mdns_bind_cmd,
|
||||
"no mdns bind",
|
||||
NO_STR "Disable server for mDNS mslookup (do not answer remote requests)\n")
|
||||
DEFUN(cfg_mslookup_server_no_mdns,
|
||||
cfg_mslookup_server_no_mdns_cmd,
|
||||
"no mdns",
|
||||
NO_STR "Disable server for DNS/mDNS MS Lookup (do not answer remote requests)\n")
|
||||
{
|
||||
g_hlr->mslookup.server.mdns.enable = false;
|
||||
mslookup_server_mdns_config_apply();
|
||||
g_hlr->mslookup.vty.server.mdns.enable = false;
|
||||
dgsm_config_apply();
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -196,18 +114,17 @@ struct cmd_node mslookup_server_msc_node = {
|
||||
|
||||
DEFUN(cfg_mslookup_server_msc,
|
||||
cfg_mslookup_server_msc_cmd,
|
||||
"msc ipa-name .IPA_NAME",
|
||||
"msc .UNIT_NAME",
|
||||
"Configure services for individual local MSCs\n"
|
||||
"Identify locally connected MSC by IPA Unit Name\n"
|
||||
"IPA Unit Name of the local MSC to configure\n")
|
||||
{
|
||||
struct osmo_ipa_name msc_name;
|
||||
struct mslookup_server_msc_cfg *msc;
|
||||
osmo_ipa_name_set_str(&msc_name, argv_concat(argv, argc, 0));
|
||||
struct global_title msc_name;
|
||||
struct dgsm_msc_config *msc;
|
||||
global_title_set_str(&msc_name, argv_concat(argv, argc, 0));
|
||||
|
||||
msc = mslookup_server_msc_get(&msc_name, true);
|
||||
msc = dgsm_config_msc_get(&msc_name, true);
|
||||
if (!msc) {
|
||||
vty_out(vty, "%% Error creating MSC %s%s", osmo_ipa_name_to_str(&msc_name), VTY_NEWLINE);
|
||||
vty_out(vty, "%% Error creating MSC %s%s", global_title_name(&msc_name), VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
vty->node = MSLOOKUP_SERVER_MSC_NODE;
|
||||
@@ -216,35 +133,30 @@ DEFUN(cfg_mslookup_server_msc,
|
||||
}
|
||||
|
||||
#define SERVICE_NAME_STR \
|
||||
"mslookup service name, e.g. sip.voice or smpp.sms\n"
|
||||
"MS Lookup service name, e.g. " OSMO_MSLOOKUP_SERVICE_SIP " or " OSMO_MSLOOKUP_SERVICE_SMPP "\n"
|
||||
|
||||
#define SERVICE_AND_NAME_STR \
|
||||
"Configure addresses of local services, as sent in replies to remote MS Lookup requests.\n" \
|
||||
SERVICE_NAME_STR
|
||||
|
||||
static struct mslookup_server_msc_cfg *msc_from_node(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
case MSLOOKUP_SERVER_NODE:
|
||||
/* On the mslookup.server node, set services on the wildcard msc, without a particular name. */
|
||||
return mslookup_server_msc_get(&mslookup_server_msc_wildcard, true);
|
||||
case MSLOOKUP_SERVER_MSC_NODE:
|
||||
return vty->index;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DEFUN(cfg_mslookup_server_msc_service,
|
||||
cfg_mslookup_server_msc_service_cmd,
|
||||
"service NAME at IP <1-65535>",
|
||||
"Configure addresses of local services, as sent in replies to remote mslookup requests.\n"
|
||||
SERVICE_NAME_STR "at\n" IP46_STR PORT_STR)
|
||||
SERVICE_AND_NAME_STR "at\n" IP46_STR PORT_STR)
|
||||
{
|
||||
/* If this command is run on the 'server' node, it produces an empty unit name and serves as wildcard for all
|
||||
* MSCs. If on a 'server' / 'msc' node, set services only for that MSC Unit Name. */
|
||||
struct mslookup_server_msc_cfg *msc = msc_from_node(vty);
|
||||
struct dgsm_msc_config *msc = (vty->node == MSLOOKUP_SERVER_MSC_NODE) ? vty->index : NULL;
|
||||
const char *service = argv[0];
|
||||
const char *ip_str = argv[1];
|
||||
const char *port_str = argv[2];
|
||||
struct osmo_sockaddr_str addr;
|
||||
|
||||
/* On the mslookup.server node, set services on the wildcard msc, without a particular name. */
|
||||
if (vty->node == MSLOOKUP_SERVER_NODE)
|
||||
msc = dgsm_config_msc_get(&dgsm_config_msc_wildcard, true);
|
||||
|
||||
if (!msc) {
|
||||
vty_out(vty, "%% Error: no MSC object on this node%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
@@ -252,13 +164,13 @@ DEFUN(cfg_mslookup_server_msc_service,
|
||||
|
||||
if (osmo_sockaddr_str_from_str(&addr, ip_str, atoi(port_str))
|
||||
|| !osmo_sockaddr_str_is_nonzero(&addr)) {
|
||||
vty_out(vty, "%% mslookup server: Invalid address for service %s: %s %s%s",
|
||||
vty_out(vty, "%% MS Lookup server: Invalid address for service %s: %s %s%s",
|
||||
service, ip_str, port_str, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (mslookup_server_msc_service_set(msc, service, &addr)) {
|
||||
vty_out(vty, "%% mslookup server: Error setting service %s to %s %s%s",
|
||||
if (dgsm_config_msc_service_set(msc, service, &addr)) {
|
||||
vty_out(vty, "%% MS Lookup server: Error setting service %s to %s %s%s",
|
||||
service, ip_str, port_str, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
@@ -274,16 +186,11 @@ DEFUN(cfg_mslookup_server_msc_no_service,
|
||||
{
|
||||
/* If this command is run on the 'server' node, it produces an empty unit name and serves as wildcard for all
|
||||
* MSCs. If on a 'server' / 'msc' node, set services only for that MSC Unit Name. */
|
||||
struct mslookup_server_msc_cfg *msc = msc_from_node(vty);
|
||||
struct dgsm_msc_config *msc = (vty->node == MSLOOKUP_SERVER_MSC_NODE) ? vty->index : NULL;
|
||||
const char *service = argv[0];
|
||||
|
||||
if (!msc) {
|
||||
vty_out(vty, "%% Error: no MSC object on this node%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (mslookup_server_msc_service_del(msc, service, NULL) < 1) {
|
||||
vty_out(vty, "%% mslookup server: cannot remove service '%s'%s",
|
||||
if (dgsm_config_msc_service_del(msc, service, NULL)) {
|
||||
vty_out(vty, "%% MS Lookup server: Error removing service %s%s",
|
||||
service, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
@@ -297,26 +204,21 @@ DEFUN(cfg_mslookup_server_msc_no_service_addr,
|
||||
{
|
||||
/* If this command is run on the 'server' node, it produces an empty unit name and serves as wildcard for all
|
||||
* MSCs. If on a 'server' / 'msc' node, set services only for that MSC Unit Name. */
|
||||
struct mslookup_server_msc_cfg *msc = msc_from_node(vty);
|
||||
struct dgsm_msc_config *msc = (vty->node == MSLOOKUP_SERVER_MSC_NODE) ? vty->index : NULL;
|
||||
const char *service = argv[0];
|
||||
const char *ip_str = argv[1];
|
||||
const char *port_str = argv[2];
|
||||
struct osmo_sockaddr_str addr;
|
||||
|
||||
if (!msc) {
|
||||
vty_out(vty, "%% Error: no MSC object on this node%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (osmo_sockaddr_str_from_str(&addr, ip_str, atoi(port_str))
|
||||
|| !osmo_sockaddr_str_is_nonzero(&addr)) {
|
||||
vty_out(vty, "%% mslookup server: Invalid address for 'no service' %s: %s %s%s",
|
||||
vty_out(vty, "%% MS Lookup server: Invalid address for 'no service' %s: %s %s%s",
|
||||
service, ip_str, port_str, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (mslookup_server_msc_service_del(msc, service, &addr) < 1) {
|
||||
vty_out(vty, "%% mslookup server: cannot remove service '%s' to %s %s%s",
|
||||
if (dgsm_config_service_del(&msc->name, service, &addr)) {
|
||||
vty_out(vty, "%% MS Lookup server: Error removing service %s to %s %s%s",
|
||||
service, ip_str, port_str, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
@@ -332,24 +234,26 @@ struct cmd_node mslookup_client_node = {
|
||||
DEFUN(cfg_mslookup_client,
|
||||
cfg_mslookup_client_cmd,
|
||||
"client",
|
||||
"Enable and configure Distributed GSM mslookup client")
|
||||
"Enable and configure Distributed GSM / multicast MS Lookup client")
|
||||
{
|
||||
vty->node = MSLOOKUP_CLIENT_NODE;
|
||||
g_hlr->mslookup.client.enable = true;
|
||||
dgsm_mdns_client_config_apply();
|
||||
g_hlr->mslookup.vty.client.enable = true;
|
||||
dgsm_config_apply();
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mslookup_no_client,
|
||||
cfg_mslookup_no_client_cmd,
|
||||
"no client",
|
||||
NO_STR "Disable Distributed GSM mslookup client")
|
||||
NO_STR "Disable Distributed GSM / multicast MS Lookup client")
|
||||
{
|
||||
g_hlr->mslookup.client.enable = false;
|
||||
dgsm_mdns_client_config_apply();
|
||||
g_hlr->mslookup.vty.client.enable = false;
|
||||
dgsm_config_apply();
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define MDNS_TO_STR MDNS_STR "Configure to which multicast address mDNS MS Lookup requests are sent\n"
|
||||
|
||||
DEFUN(cfg_mslookup_client_timeout,
|
||||
cfg_mslookup_client_timeout_cmd,
|
||||
"timeout <1-100000>",
|
||||
@@ -357,7 +261,8 @@ DEFUN(cfg_mslookup_client_timeout,
|
||||
"timeout in milliseconds\n")
|
||||
{
|
||||
uint32_t val = atol(argv[0]);
|
||||
g_hlr->mslookup.client.result_timeout_milliseconds = val;
|
||||
g_hlr->mslookup.vty.client.timeout.tv_sec = val / 1000;
|
||||
g_hlr->mslookup.vty.client.timeout.tv_usec = (val % 1000) * 1000;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -365,216 +270,86 @@ DEFUN(cfg_mslookup_client_timeout,
|
||||
if (vty->type != VTY_FILE) \
|
||||
vty_out(vty, "%% 'exit' this node to apply changes%s", VTY_NEWLINE)
|
||||
|
||||
|
||||
DEFUN(cfg_mslookup_client_mdns_bind,
|
||||
cfg_mslookup_client_mdns_bind_cmd,
|
||||
"mdns bind [IP] [<1-65535>]",
|
||||
MDNS_STR
|
||||
"Enable mDNS client, and configure multicast address to send mDNS mslookup requests to\n"
|
||||
MDNS_IP46_STR MDNS_PORT_STR)
|
||||
DEFUN(cfg_mslookup_client_mdns,
|
||||
cfg_mslookup_client_mdns_cmd,
|
||||
"mdns [to] [IP] [<1-65535>]",
|
||||
MDNS_STR "Configure multicast address to send mDNS mslookup requests to\n" IP46_STR PORT_STR)
|
||||
{
|
||||
return mslookup_client_mdns_to(vty, argc, argv);
|
||||
}
|
||||
const char *ip_str = argc > 1? argv[1] : g_hlr->mslookup.vty.client.mdns.query_addr.ip;
|
||||
const char *port_str = argc > 2? argv[2] : NULL;
|
||||
uint16_t port_nr = port_str ? atoi(port_str) : g_hlr->mslookup.vty.client.mdns.query_addr.port;
|
||||
struct osmo_sockaddr_str addr;
|
||||
if (osmo_sockaddr_str_from_str(&addr, ip_str, port_nr)
|
||||
|| !osmo_sockaddr_str_is_nonzero(&addr)) {
|
||||
vty_out(vty, "%% MS Lookup client: Invalid mDNS target address: %s %u%s",
|
||||
ip_str, port_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mslookup_client_mdns_domain_suffix,
|
||||
cfg_mslookup_client_mdns_domain_suffix_cmd,
|
||||
"mdns domain-suffix DOMAIN_SUFFIX",
|
||||
MDNS_STR
|
||||
MDNS_DOMAIN_SUFFIX_STR
|
||||
MDNS_DOMAIN_SUFFIX_STR)
|
||||
{
|
||||
osmo_talloc_replace_string(g_hlr, &g_hlr->mslookup.client.mdns.domain_suffix, argv[0]);
|
||||
dgsm_mdns_client_config_apply();
|
||||
g_hlr->mslookup.vty.client.mdns.query_addr = addr;
|
||||
g_hlr->mslookup.vty.client.mdns.enable = true;
|
||||
dgsm_config_apply();
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mslookup_client_no_mdns_bind,
|
||||
cfg_mslookup_client_no_mdns_bind_cmd,
|
||||
"no mdns bind",
|
||||
DEFUN(cfg_mslookup_client_no_mdns,
|
||||
cfg_mslookup_client_no_mdns_cmd,
|
||||
"no mdns",
|
||||
NO_STR "Disable mDNS client, do not query remote services by mDNS\n")
|
||||
{
|
||||
g_hlr->mslookup.client.mdns.enable = false;
|
||||
dgsm_mdns_client_config_apply();
|
||||
g_hlr->mslookup.vty.client.mdns.enable = false;
|
||||
dgsm_config_apply();
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
void config_write_msc_services(struct vty *vty, const char *indent, struct mslookup_server_msc_cfg *msc)
|
||||
{
|
||||
struct mslookup_service_host *e;
|
||||
|
||||
llist_for_each_entry(e, &msc->service_hosts, entry) {
|
||||
if (osmo_sockaddr_str_is_nonzero(&e->host_v4))
|
||||
vty_out(vty, "%sservice %s at %s %u%s", indent, e->service, e->host_v4.ip, e->host_v4.port,
|
||||
VTY_NEWLINE);
|
||||
if (osmo_sockaddr_str_is_nonzero(&e->host_v6))
|
||||
vty_out(vty, "%sservice %s at %s %u%s", indent, e->service, e->host_v6.ip, e->host_v6.port,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
|
||||
int config_write_mslookup(struct vty *vty)
|
||||
{
|
||||
if (!g_hlr->mslookup.server.enable
|
||||
&& llist_empty(&g_hlr->mslookup.server.local_site_services)
|
||||
&& !g_hlr->mslookup.client.enable)
|
||||
return CMD_SUCCESS;
|
||||
|
||||
vty_out(vty, "mslookup%s", VTY_NEWLINE);
|
||||
|
||||
if (g_hlr->mslookup.server.enable || !llist_empty(&g_hlr->mslookup.server.local_site_services)) {
|
||||
struct mslookup_server_msc_cfg *msc;
|
||||
|
||||
vty_out(vty, " server%s", VTY_NEWLINE);
|
||||
|
||||
if (g_hlr->mslookup.server.mdns.enable) {
|
||||
vty_out(vty, " mdns bind");
|
||||
if (osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.server.mdns.bind_addr)) {
|
||||
vty_out(vty, " %s %u",
|
||||
g_hlr->mslookup.server.mdns.bind_addr.ip,
|
||||
g_hlr->mslookup.server.mdns.bind_addr.port);
|
||||
}
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
}
|
||||
if (strcmp(g_hlr->mslookup.server.mdns.domain_suffix, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT))
|
||||
vty_out(vty, " mdns domain-suffix %s%s",
|
||||
g_hlr->mslookup.server.mdns.domain_suffix,
|
||||
VTY_NEWLINE);
|
||||
|
||||
msc = mslookup_server_msc_get(&mslookup_server_msc_wildcard, false);
|
||||
if (msc)
|
||||
config_write_msc_services(vty, " ", msc);
|
||||
|
||||
llist_for_each_entry(msc, &g_hlr->mslookup.server.local_site_services, entry) {
|
||||
if (!osmo_ipa_name_cmp(&mslookup_server_msc_wildcard, &msc->name))
|
||||
continue;
|
||||
vty_out(vty, " msc %s%s", osmo_ipa_name_to_str(&msc->name), VTY_NEWLINE);
|
||||
config_write_msc_services(vty, " ", msc);
|
||||
}
|
||||
|
||||
/* If the server is disabled, still output the above to not lose the service config. */
|
||||
if (!g_hlr->mslookup.server.enable)
|
||||
vty_out(vty, " no server%s", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
if (g_hlr->mslookup.client.enable) {
|
||||
vty_out(vty, " client%s", VTY_NEWLINE);
|
||||
|
||||
if (osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_gateway_proxy))
|
||||
vty_out(vty, " gateway-proxy %s %u%s",
|
||||
g_hlr->mslookup.client.gsup_gateway_proxy.ip,
|
||||
g_hlr->mslookup.client.gsup_gateway_proxy.port,
|
||||
VTY_NEWLINE);
|
||||
|
||||
if (g_hlr->mslookup.client.mdns.enable
|
||||
&& osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.mdns.query_addr))
|
||||
vty_out(vty, " mdns bind %s %u%s",
|
||||
g_hlr->mslookup.client.mdns.query_addr.ip,
|
||||
g_hlr->mslookup.client.mdns.query_addr.port,
|
||||
VTY_NEWLINE);
|
||||
if (strcmp(g_hlr->mslookup.client.mdns.domain_suffix, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT))
|
||||
vty_out(vty, " mdns domain-suffix %s%s",
|
||||
g_hlr->mslookup.client.mdns.domain_suffix,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mslookup_client_gateway_proxy,
|
||||
cfg_mslookup_client_gateway_proxy_cmd,
|
||||
"gateway-proxy IP [<1-65535>]",
|
||||
"Configure a fixed IP address to send all GSUP requests for unknown IMSIs to, without invoking a lookup for IMSI\n"
|
||||
"IP address of the remote HLR\n" "GSUP port number (omit for default " OSMO_STRINGIFY_VAL(OSMO_GSUP_PORT) ")\n")
|
||||
int config_write_mslookup_server(struct vty *vty)
|
||||
{
|
||||
const char *ip_str = argv[0];
|
||||
const char *port_str = argc > 1 ? argv[1] : NULL;
|
||||
struct osmo_sockaddr_str addr;
|
||||
|
||||
if (osmo_sockaddr_str_from_str(&addr, ip_str, port_str ? atoi(port_str) : OSMO_GSUP_PORT)
|
||||
|| !osmo_sockaddr_str_is_nonzero(&addr)) {
|
||||
vty_out(vty, "%% mslookup client: Invalid address for gateway-proxy: %s %s%s",
|
||||
ip_str, port_str ? : "", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
g_hlr->mslookup.client.gsup_gateway_proxy = addr;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mslookup_client_no_gateway_proxy,
|
||||
cfg_mslookup_client_no_gateway_proxy_cmd,
|
||||
"no gateway-proxy",
|
||||
NO_STR "Disable gateway proxy for GSUP with unknown IMSIs\n")
|
||||
int config_write_mslookup_server_msc(struct vty *vty)
|
||||
{
|
||||
g_hlr->mslookup.client.gsup_gateway_proxy = (struct osmo_sockaddr_str){};
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(do_mslookup_show_services,
|
||||
do_mslookup_show_services_cmd,
|
||||
"show mslookup services",
|
||||
SHOW_STR "Distributed GSM / mslookup related information\n"
|
||||
"List configured service addresses as sent to remote mslookup requests\n")
|
||||
int config_write_mslookup_client(struct vty *vty)
|
||||
{
|
||||
struct mslookup_server_msc_cfg *msc;
|
||||
const struct mslookup_service_host *local_hlr = mslookup_server_get_local_gsup_addr();
|
||||
|
||||
vty_out(vty, "Local GSUP HLR address returned in mslookup responses for local IMSIs:");
|
||||
if (osmo_sockaddr_str_is_nonzero(&local_hlr->host_v4))
|
||||
vty_out(vty, " " OSMO_SOCKADDR_STR_FMT,
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&local_hlr->host_v4));
|
||||
if (osmo_sockaddr_str_is_nonzero(&local_hlr->host_v6))
|
||||
vty_out(vty, " " OSMO_SOCKADDR_STR_FMT,
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&local_hlr->host_v6));
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
|
||||
msc = mslookup_server_msc_get(&mslookup_server_msc_wildcard, false);
|
||||
if (msc)
|
||||
config_write_msc_services(vty, "", msc);
|
||||
|
||||
llist_for_each_entry(msc, &g_hlr->mslookup.server.local_site_services, entry) {
|
||||
if (!osmo_ipa_name_cmp(&mslookup_server_msc_wildcard, &msc->name))
|
||||
continue;
|
||||
vty_out(vty, "msc ipa-name %s%s", osmo_ipa_name_to_str(&msc->name), VTY_NEWLINE);
|
||||
config_write_msc_services(vty, " ", msc);
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
void dgsm_vty_init(void)
|
||||
void dgsm_vty_init()
|
||||
{
|
||||
install_element(CONFIG_NODE, &cfg_mslookup_cmd);
|
||||
|
||||
install_node(&mslookup_node, config_write_mslookup);
|
||||
install_element(MSLOOKUP_NODE, &cfg_mslookup_mdns_cmd);
|
||||
install_element(MSLOOKUP_NODE, &cfg_mslookup_mdns_domain_suffix_cmd);
|
||||
install_element(MSLOOKUP_NODE, &cfg_mslookup_no_mdns_cmd);
|
||||
install_element(MSLOOKUP_NODE, &cfg_mslookup_server_cmd);
|
||||
install_element(MSLOOKUP_NODE, &cfg_mslookup_no_server_cmd);
|
||||
|
||||
install_node(&mslookup_server_node, NULL);
|
||||
install_node(&mslookup_server_node, config_write_mslookup_server);
|
||||
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_mdns_bind_cmd);
|
||||
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_mdns_domain_suffix_cmd);
|
||||
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_no_mdns_bind_cmd);
|
||||
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_no_mdns_cmd);
|
||||
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_service_cmd);
|
||||
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_no_service_cmd);
|
||||
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_no_service_addr_cmd);
|
||||
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_cmd);
|
||||
|
||||
install_node(&mslookup_server_msc_node, NULL);
|
||||
install_node(&mslookup_server_msc_node, config_write_mslookup_server_msc);
|
||||
install_element(MSLOOKUP_SERVER_MSC_NODE, &cfg_mslookup_server_msc_service_cmd);
|
||||
install_element(MSLOOKUP_SERVER_MSC_NODE, &cfg_mslookup_server_msc_no_service_cmd);
|
||||
install_element(MSLOOKUP_SERVER_MSC_NODE, &cfg_mslookup_server_msc_no_service_addr_cmd);
|
||||
|
||||
install_element(MSLOOKUP_NODE, &cfg_mslookup_client_cmd);
|
||||
install_element(MSLOOKUP_NODE, &cfg_mslookup_no_client_cmd);
|
||||
install_node(&mslookup_client_node, NULL);
|
||||
install_node(&mslookup_client_node, config_write_mslookup_client);
|
||||
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_timeout_cmd);
|
||||
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_mdns_bind_cmd);
|
||||
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_mdns_domain_suffix_cmd);
|
||||
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_mdns_bind_cmd);
|
||||
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_gateway_proxy_cmd);
|
||||
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_gateway_proxy_cmd);
|
||||
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_mdns_cmd);
|
||||
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_mdns_cmd);
|
||||
|
||||
install_element_ve(&do_mslookup_show_services_cmd);
|
||||
}
|
||||
|
||||
68
src/global_title.c
Normal file
68
src/global_title.c
Normal file
@@ -0,0 +1,68 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include "global_title.h"
|
||||
|
||||
int global_title_set(struct global_title *gt, const uint8_t *val, size_t len)
|
||||
{
|
||||
if (!val || !len) {
|
||||
*gt = (struct global_title){};
|
||||
return 0;
|
||||
}
|
||||
if (len > sizeof(gt->val))
|
||||
return -ENOSPC;
|
||||
gt->len = len;
|
||||
memcpy(gt->val, val, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int global_title_set_str(struct global_title *gt, const char *str_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
if (!str_fmt)
|
||||
return global_title_set(gt, NULL, 0);
|
||||
|
||||
va_start(ap, str_fmt);
|
||||
vsnprintf((char*)(gt->val), sizeof(gt->val), str_fmt, ap);
|
||||
va_end(ap);
|
||||
gt->len = strlen((char*)(gt->val))+1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int global_title_cmp(const struct global_title *a, const struct global_title *b)
|
||||
{
|
||||
int cmp;
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
if (!a->len && !b->len)
|
||||
return 0;
|
||||
if (!a->len && b->len)
|
||||
return -1;
|
||||
if (!b->len && a->len)
|
||||
return 1;
|
||||
|
||||
if (a->len == b->len)
|
||||
return memcmp(a->val, b->val, a->len);
|
||||
else if (a->len < b->len) {
|
||||
cmp = memcmp(a->val, b->val, a->len);
|
||||
if (!cmp)
|
||||
cmp = -1;
|
||||
return cmp;
|
||||
} else {
|
||||
/* a->len > b->len */
|
||||
cmp = memcmp(a->val, b->val, b->len);
|
||||
if (!cmp)
|
||||
cmp = 1;
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
|
||||
const char *global_title_name(const struct global_title *gt)
|
||||
{
|
||||
return osmo_quote_str_c(OTC_SELECT, (char*)gt->val, gt->len);
|
||||
}
|
||||
|
||||
17
src/global_title.h
Normal file
17
src/global_title.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Arbitrary length blob, not necessarily zero-terminated.
|
||||
* In osmo-hlr, struct hlr_subscriber is mostly used as static reference and cannot serve as talloc context, which is
|
||||
* why this is also implemented as a fixed-maximum-size buffer instead of a talloc'd arbitrary sized buffer.
|
||||
*/
|
||||
struct global_title {
|
||||
size_t len;
|
||||
uint8_t val[128];
|
||||
};
|
||||
|
||||
int global_title_set(struct global_title *gt, const uint8_t *val, size_t len);
|
||||
int global_title_set_str(struct global_title *gt, const char *str_fmt, ...);
|
||||
int global_title_cmp(const struct global_title *a, const struct global_title *b);
|
||||
const char *global_title_name(const struct global_title *gt);
|
||||
@@ -23,9 +23,9 @@
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/gsup_server.h>
|
||||
#include <osmocom/hlr/gsup_router.h>
|
||||
#include "logging.h"
|
||||
#include "gsup_server.h"
|
||||
#include "gsup_router.h"
|
||||
|
||||
/*! Find a route for the given address.
|
||||
* \param[in] gs gsup server
|
||||
@@ -47,9 +47,9 @@ struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct osmo_gsup_conn *gsup_route_find_by_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name)
|
||||
struct osmo_gsup_conn *gsup_route_find_gt(struct osmo_gsup_server *gs, const struct global_title *gt)
|
||||
{
|
||||
return gsup_route_find(gs, ipa_name->val, ipa_name->len);
|
||||
return gsup_route_find(gs, gt->val, gt->len);
|
||||
}
|
||||
|
||||
/*! Find a GSUP connection's route (to read the IPA address from the route).
|
||||
@@ -96,9 +96,9 @@ int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addr
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gsup_route_add_ipa_name(struct osmo_gsup_conn *conn, const struct osmo_ipa_name *ipa_name)
|
||||
int gsup_route_add_gt(struct osmo_gsup_conn *conn, const struct global_title *gt)
|
||||
{
|
||||
return gsup_route_add(conn, ipa_name->val, ipa_name->len);
|
||||
return gsup_route_add(conn, gt->val, gt->len);
|
||||
}
|
||||
|
||||
/* delete all routes for the given connection */
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmocom/hlr/gsup_server.h>
|
||||
|
||||
struct osmo_ipa_name;
|
||||
#include "global_title.h"
|
||||
#include "gsup_server.h"
|
||||
|
||||
struct gsup_route {
|
||||
struct llist_head list;
|
||||
@@ -14,12 +13,12 @@ struct gsup_route {
|
||||
|
||||
struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
|
||||
const uint8_t *addr, size_t addrlen);
|
||||
struct osmo_gsup_conn *gsup_route_find_by_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name);
|
||||
struct osmo_gsup_conn *gsup_route_find_gt(struct osmo_gsup_server *gs, const struct global_title *gt);
|
||||
|
||||
struct gsup_route *gsup_route_find_by_conn(const struct osmo_gsup_conn *conn);
|
||||
|
||||
/* add a new route for the given address to the given conn */
|
||||
int gsup_route_add_ipa_name(struct osmo_gsup_conn *conn, const struct osmo_ipa_name *ipa_name);
|
||||
int gsup_route_add_gt(struct osmo_gsup_conn *conn, const struct global_title *gt);
|
||||
int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen);
|
||||
|
||||
/* delete all routes for the given connection */
|
||||
@@ -28,6 +27,6 @@ int gsup_route_del_conn(struct osmo_gsup_conn *conn);
|
||||
int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
|
||||
const uint8_t *addr, size_t addrlen,
|
||||
struct msgb *msg);
|
||||
int osmo_gsup_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name, struct msgb *msg);
|
||||
int osmo_gsup_enc_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name,
|
||||
int osmo_gsup_gt_send(struct osmo_gsup_server *gs, const struct global_title *gt, struct msgb *msg);
|
||||
int osmo_gsup_gt_enc_send(struct osmo_gsup_server *gs, const struct global_title *gt,
|
||||
const struct osmo_gsup_message *gsup);
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/hlr/gsup_server.h>
|
||||
#include <osmocom/hlr/gsup_router.h>
|
||||
#include "gsup_server.h"
|
||||
#include "gsup_router.h"
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
@@ -53,27 +53,27 @@ int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
|
||||
|
||||
/*! Send a msgb to a given address using routing.
|
||||
* \param[in] gs gsup server
|
||||
* \param[in] ipa_name IPA unit name of the client (SGSN, MSC/VLR, proxy).
|
||||
* \param[in] gt IPA unit name of the client (SGSN, MSC/VLR, proxy).
|
||||
* \param[in] msg message buffer
|
||||
*/
|
||||
int osmo_gsup_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name, struct msgb *msg)
|
||||
int osmo_gsup_gt_send(struct osmo_gsup_server *gs, const struct global_title *gt, struct msgb *msg)
|
||||
{
|
||||
if (ipa_name->val[ipa_name->len - 1]) {
|
||||
if (gt->val[gt->len - 1]) {
|
||||
/* Is not nul terminated. But for legacy reasons we (still) require that. */
|
||||
if (ipa_name->len >= sizeof(ipa_name->val)) {
|
||||
LOGP(DLGSUP, LOGL_ERROR, "IPA unit name is too long: %s\n",
|
||||
osmo_ipa_name_to_str(ipa_name));
|
||||
if (gt->len >= sizeof(gt->val)) {
|
||||
LOGP(DLGSUP, LOGL_ERROR, "Global title (IPA unit name) is too long: %s\n",
|
||||
global_title_name(gt));
|
||||
return -EINVAL;
|
||||
}
|
||||
struct osmo_ipa_name ipa_name2 = *ipa_name;
|
||||
ipa_name2.val[ipa_name->len] = '\0';
|
||||
ipa_name2.len++;
|
||||
return osmo_gsup_addr_send(gs, ipa_name2.val, ipa_name2.len, msg);
|
||||
struct global_title gt2 = *gt;
|
||||
gt2.val[gt->len] = '\0';
|
||||
gt2.len++;
|
||||
return osmo_gsup_addr_send(gs, gt2.val, gt2.len, msg);
|
||||
}
|
||||
return osmo_gsup_addr_send(gs, ipa_name->val, ipa_name->len, msg);
|
||||
return osmo_gsup_addr_send(gs, gt->val, gt->len, msg);
|
||||
}
|
||||
|
||||
int osmo_gsup_enc_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name,
|
||||
int osmo_gsup_gt_enc_send(struct osmo_gsup_server *gs, const struct global_title *gt,
|
||||
const struct osmo_gsup_message *gsup)
|
||||
{
|
||||
struct msgb *msg = osmo_gsup_msgb_alloc("GSUP Tx");
|
||||
@@ -87,5 +87,5 @@ int osmo_gsup_enc_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osm
|
||||
}
|
||||
|
||||
LOGP(DLGSUP, LOGL_DEBUG, "IMSI-%s: Tx: %s\n", gsup->imsi, osmo_gsup_message_type_name(gsup->message_type));
|
||||
return osmo_gsup_send_to_ipa_name(gs, ipa_name, msg);
|
||||
return osmo_gsup_gt_send(gs, gt, msg);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
@@ -30,12 +28,9 @@
|
||||
#include <osmocom/gsm/apn.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
|
||||
#include <osmocom/hlr/gsup_server.h>
|
||||
#include <osmocom/hlr/gsup_router.h>
|
||||
|
||||
#define LOG_GSUP_CONN(conn, level, fmt, args...) \
|
||||
LOGP(DLGSUP, level, "GSUP peer %s: " fmt, \
|
||||
(conn) ? osmo_ipa_name_to_str(&(conn)->peer_name) : "NULL", ##args)
|
||||
#include "hlr.h"
|
||||
#include "gsup_server.h"
|
||||
#include "gsup_router.h"
|
||||
|
||||
struct msgb *osmo_gsup_msgb_alloc(const char *label)
|
||||
{
|
||||
@@ -64,89 +59,227 @@ int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gsup_server_send_req_response(struct osmo_gsup_req *req, struct osmo_gsup_message *response)
|
||||
/* Only for requests originating here. When answering to a remote request, rather use osmo_gsup_req_respond() or
|
||||
* osmo_gsup_req_respond_err(). */
|
||||
int osmo_gsup_conn_enc_send(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
|
||||
{
|
||||
struct osmo_gsup_server *server = req->cb_data;
|
||||
struct osmo_cni_peer_id *routing;
|
||||
struct osmo_gsup_conn *conn = NULL;
|
||||
struct msgb *msg = osmo_gsup_msgb_alloc("GSUP Tx");
|
||||
int rc;
|
||||
|
||||
if (response->message_type == OSMO_GSUP_MSGT_ROUTING_ERROR
|
||||
&& !osmo_cni_peer_id_is_empty(&req->via_proxy)) {
|
||||
/* If a routing error occured, we need to route back via the immediate sending peer, not via the
|
||||
* intended final recipient -- because one source of routing errors is a duplicate name for a recipient.
|
||||
* If we resolve to req->source_name, we may send to a completely unrelated recipient. */
|
||||
routing = &req->via_proxy;
|
||||
} else {
|
||||
routing = &req->source_name;
|
||||
}
|
||||
switch (routing->type) {
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
conn = gsup_route_find_by_ipa_name(server, &routing->ipa_name);
|
||||
break;
|
||||
default:
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP peer id kind not supported: %s\n",
|
||||
osmo_cni_peer_id_type_name(routing->type));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!conn) {
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP client that sent this request not found, cannot respond\n");
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = osmo_gsup_encode(msg, response);
|
||||
rc = osmo_gsup_encode(msg, gsup);
|
||||
if (rc) {
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "Unable to encode: {%s}\n",
|
||||
osmo_gsup_message_to_str_c(OTC_SELECT, response));
|
||||
LOG_GSUP_CONN(conn, LOGL_ERROR, "Cannot encode GSUP: %s\n",
|
||||
osmo_gsup_message_type_name(gsup->message_type));
|
||||
msgb_free(msg);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOG_GSUP_CONN(conn, LOGL_DEBUG, "Tx: %s\n", osmo_gsup_message_type_name(gsup->message_type));
|
||||
rc = osmo_gsup_conn_send(conn, msg);
|
||||
if (rc)
|
||||
LOG_GSUP_CONN(conn, LOGL_ERROR, "Unable to send: %s\n", osmo_gsup_message_to_str_c(OTC_SELECT, response));
|
||||
LOG_GSUP_CONN(conn, LOGL_ERROR, "Unable to send: %s\n",
|
||||
osmo_gsup_message_type_name(gsup->message_type));
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb *msg)
|
||||
struct osmo_gsup_req *osmo_gsup_req_new(struct osmo_gsup_conn *conn, struct msgb *msg)
|
||||
{
|
||||
static unsigned int next_req_nr = 1;
|
||||
struct osmo_gsup_req *req;
|
||||
struct osmo_cni_peer_id cpi = {
|
||||
.type = OSMO_CNI_PEER_ID_IPA_NAME,
|
||||
.ipa_name = conn->peer_name,
|
||||
};
|
||||
struct osmo_gsup_message gsup_err;
|
||||
int rc;
|
||||
|
||||
req = osmo_gsup_req_new(conn->server, &cpi, msg, gsup_server_send_req_response, conn->server, NULL);
|
||||
if (!req)
|
||||
if (!msgb_l2(msg) || !msgb_l2len(msg)) {
|
||||
LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP message: missing or empty L2 data\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!osmo_cni_peer_id_is_empty(&req->via_proxy)) {
|
||||
switch (req->via_proxy.type) {
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
break;
|
||||
default:
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP peer id kind not supported: %s\n",
|
||||
osmo_cni_peer_id_type_name(req->source_name.type));
|
||||
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
|
||||
return NULL;
|
||||
req = talloc_zero(conn->server, struct osmo_gsup_req);
|
||||
OSMO_ASSERT(req);
|
||||
req->nr = next_req_nr++;
|
||||
req->server = conn->server;
|
||||
req->msg = msg;
|
||||
req->source_name = conn->peer_name;
|
||||
|
||||
rc = osmo_gsup_decode(msgb_l2(req->msg), msgb_l2len(req->msg), (struct osmo_gsup_message*)&req->gsup);
|
||||
if (rc < 0) {
|
||||
LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP message: cannot decode (rc=%d)\n", rc);
|
||||
osmo_gsup_req_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOG_GSUP_REQ(req, LOGL_DEBUG, "new\n");
|
||||
|
||||
if (req->gsup.source_name_len) {
|
||||
if (global_title_set(&req->source_name, req->gsup.source_name, req->gsup.source_name_len)) {
|
||||
LOGP(DLGSUP, LOGL_ERROR, "cannot decode GSUP message's source_name, message is not routable\n");
|
||||
goto unroutable_error;
|
||||
}
|
||||
|
||||
/* The source of the GSUP message is not the immediate GSUP peer, but that peer is our proxy for that
|
||||
* source. Add it to the routes for this conn (so we can route responses back). */
|
||||
if (gsup_route_add_ipa_name(conn, &req->source_name.ipa_name)) {
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR,
|
||||
"GSUP message received from %s via peer %s, but there already exists a"
|
||||
" different route to this source, message is not routable\n",
|
||||
osmo_cni_peer_id_to_str(&req->source_name),
|
||||
osmo_ipa_name_to_str(&conn->peer_name));
|
||||
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
|
||||
return NULL;
|
||||
if (global_title_cmp(&req->source_name, &conn->peer_name)) {
|
||||
/* The source of the GSUP message is not the immediate GSUP peer, but that peer is our proxy for that
|
||||
* source. Add it to the routes for this conn (so we can route responses back). */
|
||||
if (gsup_route_add_gt(conn, &req->source_name)) {
|
||||
LOGP(DLGSUP, LOGL_ERROR,
|
||||
"GSUP message received from %s via peer %s, but there already exists a different"
|
||||
" route to this source, message is not routable\n",
|
||||
global_title_name(&req->source_name),
|
||||
global_title_name(&conn->peer_name));
|
||||
goto unroutable_error;
|
||||
}
|
||||
|
||||
req->via_proxy = conn->peer_name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!osmo_imsi_str_valid(req->gsup.imsi)) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "invalid IMSI: %s",
|
||||
osmo_quote_str(req->gsup.imsi, -1));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return req;
|
||||
|
||||
unroutable_error:
|
||||
gsup_err = (struct osmo_gsup_message){
|
||||
.message_type = OSMO_GSUP_MSGT_E_ROUTING_ERROR,
|
||||
.destination_name = req->gsup.destination_name,
|
||||
.destination_name_len = req->gsup.destination_name_len,
|
||||
.source_name = req->gsup.source_name,
|
||||
.source_name_len = req->gsup.source_name_len,
|
||||
};
|
||||
osmo_gsup_set_reply(&req->gsup, &gsup_err);
|
||||
osmo_gsup_conn_enc_send(conn, &gsup_err);
|
||||
osmo_gsup_req_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void _osmo_gsup_req_free(struct osmo_gsup_req *req)
|
||||
{
|
||||
if (req->msg)
|
||||
msgb_free(req->msg);
|
||||
talloc_free(req);
|
||||
}
|
||||
|
||||
int osmo_gsup_req_respond_nonfinal(struct osmo_gsup_req *req, struct osmo_gsup_message *response)
|
||||
{
|
||||
struct osmo_gsup_conn *conn;
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
conn = gsup_route_find_gt(req->server, &req->source_name);
|
||||
if (!conn) {
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP client that sent this request was disconnected, cannot respond\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
osmo_gsup_set_reply(&req->gsup, response);
|
||||
|
||||
msg = osmo_gsup_msgb_alloc("GSUP response");
|
||||
rc = osmo_gsup_encode(msg, response);
|
||||
if (rc) {
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "Unable to encode response: %s\n",
|
||||
osmo_gsup_message_type_name(response->message_type));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOG_GSUP_REQ(req, LOGL_DEBUG, "Tx response: %s\n", osmo_gsup_message_type_name(response->message_type));
|
||||
|
||||
rc = osmo_gsup_conn_send(conn, msg);
|
||||
if (rc)
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "Unable to send response: %s\n",
|
||||
osmo_gsup_message_type_name(response->message_type));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Make sure the response message contains all IEs that are required to be a valid response for the received GSUP
|
||||
* request, and send back to the requesting peer. */
|
||||
int osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response)
|
||||
{
|
||||
int rc = osmo_gsup_req_respond_nonfinal(req, response);
|
||||
osmo_gsup_req_free(req);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type)
|
||||
{
|
||||
struct osmo_gsup_message response = {
|
||||
.message_type = message_type,
|
||||
};
|
||||
return osmo_gsup_req_respond(req, &response);
|
||||
}
|
||||
|
||||
#define osmo_gsup_req_respond_err(REQ, CAUSE, FMT, args...) do { \
|
||||
LOG_GSUP_REQ(REQ, LOGL_ERROR, "%s: " FMT "\n", \
|
||||
get_value_string(gsm48_gmm_cause_names, CAUSE), ##args); \
|
||||
_osmo_gsup_req_respond_err(REQ, CAUSE); \
|
||||
} while(0)
|
||||
void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause)
|
||||
{
|
||||
struct osmo_gsup_message response = {
|
||||
.cause = cause,
|
||||
.message_type = OSMO_GSUP_TO_MSGT_ERROR(req->gsup.message_type),
|
||||
};
|
||||
|
||||
/* No need to answer if we couldn't parse an ERROR message type, only REQUESTs need an error reply. */
|
||||
if (!OSMO_GSUP_IS_MSGT_REQUEST(req->gsup.message_type)) {
|
||||
osmo_gsup_req_free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->gsup.session_state != OSMO_GSUP_SESSION_STATE_NONE)
|
||||
response.session_state = OSMO_GSUP_SESSION_STATE_END;
|
||||
|
||||
osmo_gsup_req_respond(req, &response);
|
||||
}
|
||||
|
||||
/* Encode an error reponse to the given GSUP message with the given cause.
|
||||
* Determine the error message type via OSMO_GSUP_TO_MSGT_ERROR(gsup_orig->message_type).
|
||||
* Only send an error response if the original message is a Request message.
|
||||
* On failure, log an error, but don't return anything: if an error occurs while trying to report an earlier error,
|
||||
* there is nothing we can do really except log the error (there are no callers that would use the return code).
|
||||
*/
|
||||
void osmo_gsup_conn_send_err_reply(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup_orig,
|
||||
enum gsm48_gmm_cause cause)
|
||||
{
|
||||
struct osmo_gsup_message gsup_reply;
|
||||
struct msgb *msg_out;
|
||||
int rc;
|
||||
|
||||
/* No need to answer if we couldn't parse an ERROR message type, only REQUESTs need an error reply. */
|
||||
if (!OSMO_GSUP_IS_MSGT_REQUEST(gsup_orig->message_type))
|
||||
return;
|
||||
|
||||
gsup_reply = (struct osmo_gsup_message){
|
||||
.cause = cause,
|
||||
.message_type = OSMO_GSUP_TO_MSGT_ERROR(gsup_orig->message_type),
|
||||
.message_class = gsup_orig->message_class,
|
||||
|
||||
/* RP-Message-Reference is mandatory for SM Service */
|
||||
.sm_rp_mr = gsup_orig->sm_rp_mr,
|
||||
};
|
||||
osmo_gsup_set_reply(gsup_orig, &gsup_reply);
|
||||
|
||||
if (gsup_orig->session_state != OSMO_GSUP_SESSION_STATE_NONE)
|
||||
gsup_reply.session_state = OSMO_GSUP_SESSION_STATE_END;
|
||||
|
||||
msg_out = osmo_gsup_msgb_alloc("GSUP ERR response");
|
||||
rc = osmo_gsup_encode(msg_out, &gsup_reply);
|
||||
if (rc) {
|
||||
LOGP(DLGSUP, LOGL_ERROR, "%s: Unable to encode error response %s (rc=%d)\n",
|
||||
osmo_quote_str(gsup_orig->imsi, -1), osmo_gsup_message_type_name(gsup_reply.message_type),
|
||||
rc);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DLGSUP, LOGL_DEBUG, "%s: GSUP tx %s\n",
|
||||
osmo_quote_str(gsup_orig->imsi, -1), osmo_gsup_message_type_name(gsup_reply.message_type));
|
||||
|
||||
rc = osmo_gsup_conn_send(conn, msg_out);
|
||||
if (rc)
|
||||
LOGP(DLGSUP, LOGL_ERROR, "%s: Unable to send error response %s (rc=%d)\n",
|
||||
osmo_quote_str(gsup_orig->imsi, -1), osmo_gsup_message_type_name(gsup_reply.message_type),
|
||||
rc);
|
||||
}
|
||||
|
||||
static int osmo_gsup_conn_oap_handle(struct osmo_gsup_conn *conn,
|
||||
@@ -276,7 +409,7 @@ static int osmo_gsup_server_ccm_cb(struct ipa_server_conn *conn,
|
||||
{
|
||||
struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data;
|
||||
uint8_t *addr = NULL;
|
||||
int addr_len;
|
||||
size_t addr_len;
|
||||
|
||||
LOGP(DLGSUP, LOGL_INFO, "CCM Callback\n");
|
||||
|
||||
@@ -294,8 +427,8 @@ static int osmo_gsup_server_ccm_cb(struct ipa_server_conn *conn,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
osmo_ipa_name_set(&clnt->peer_name, addr, addr_len);
|
||||
gsup_route_add_ipa_name(clnt, &clnt->peer_name);
|
||||
global_title_set(&clnt->peer_name, addr, addr_len);
|
||||
gsup_route_add_gt(clnt, &clnt->peer_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -317,17 +450,41 @@ static int osmo_gsup_server_closed_cb(struct ipa_server_conn *conn)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_fd_settings(int fd)
|
||||
/* Add conn to the clients list in a way that conn->auc_3g_ind takes the lowest
|
||||
* unused integer and the list of clients remains sorted by auc_3g_ind.
|
||||
* Keep this function non-static to allow linking in a unit test. */
|
||||
void osmo_gsup_server_add_conn(struct llist_head *clients,
|
||||
struct osmo_gsup_conn *conn)
|
||||
{
|
||||
int ret;
|
||||
int val;
|
||||
struct osmo_gsup_conn *c;
|
||||
struct osmo_gsup_conn *prev_conn;
|
||||
|
||||
/*TODO: Set keepalive settings here. See OS#4312 */
|
||||
c = llist_first_entry_or_null(clients, struct osmo_gsup_conn, list);
|
||||
|
||||
val = 1;
|
||||
ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
|
||||
if (ret < 0)
|
||||
LOGP(DLGSUP, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
|
||||
/* Is the first index, 0, unused? */
|
||||
if (!c || c->auc_3g_ind > 0) {
|
||||
conn->auc_3g_ind = 0;
|
||||
llist_add(&conn->list, clients);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Look for a gap later on */
|
||||
prev_conn = NULL;
|
||||
llist_for_each_entry(c, clients, list) {
|
||||
/* skip first item, we know it has auc_3g_ind == 0. */
|
||||
if (!prev_conn) {
|
||||
prev_conn = c;
|
||||
continue;
|
||||
}
|
||||
if (c->auc_3g_ind > prev_conn->auc_3g_ind + 1)
|
||||
break;
|
||||
prev_conn = c;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(prev_conn);
|
||||
|
||||
conn->auc_3g_ind = prev_conn->auc_3g_ind + 1;
|
||||
llist_add(&conn->list, &prev_conn->list);
|
||||
}
|
||||
|
||||
/* a client has connected to the server socket and we have accept()ed it */
|
||||
@@ -349,11 +506,10 @@ static int osmo_gsup_server_accept_cb(struct ipa_server_link *link, int fd)
|
||||
|
||||
/* link data structure with server structure */
|
||||
conn->server = gsups;
|
||||
llist_add_tail(&conn->list, &gsups->clients);
|
||||
osmo_gsup_server_add_conn(&gsups->clients, conn);
|
||||
|
||||
LOGP(DLGSUP, LOGL_INFO, "New GSUP client %s:%d\n", conn->conn->addr, conn->conn->port);
|
||||
|
||||
update_fd_settings(fd);
|
||||
LOGP(DLGSUP, LOGL_INFO, "New GSUP client %s:%d (IND=%u)\n",
|
||||
conn->conn->addr, conn->conn->port, conn->auc_3g_ind);
|
||||
|
||||
/* request the identity of the client */
|
||||
rc = ipa_ccm_send_id_req(fd);
|
||||
@@ -438,9 +594,10 @@ int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Populate a gsup message structure with an Insert Subscriber Data Message.
|
||||
* All required memory buffers for data pointed to by pointers in struct osmo_gsup_message
|
||||
* All required memory buffers for data pointed to by pointers in struct omso_gsup_message
|
||||
* must be allocated by the caller and should have the same lifetime as the gsup parameter.
|
||||
*
|
||||
* \param[out] gsup The gsup message to populate.
|
||||
@@ -454,77 +611,41 @@ int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
|
||||
* \returns 0 on success, and negative on error.
|
||||
*/
|
||||
int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, const char *imsi, const char *msisdn,
|
||||
uint8_t *msisdn_enc, size_t msisdn_enc_size,
|
||||
uint8_t *apn_buf, size_t apn_buf_size,
|
||||
enum osmo_gsup_cn_domain cn_domain)
|
||||
uint8_t *msisdn_enc, size_t msisdn_enc_size,
|
||||
uint8_t *apn_buf, size_t apn_buf_size,
|
||||
enum osmo_gsup_cn_domain cn_domain)
|
||||
{
|
||||
int len;
|
||||
int len;
|
||||
|
||||
OSMO_ASSERT(gsup);
|
||||
*gsup = (struct osmo_gsup_message){
|
||||
.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST,
|
||||
};
|
||||
OSMO_ASSERT(gsup);
|
||||
*gsup = (struct osmo_gsup_message){
|
||||
.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST,
|
||||
};
|
||||
|
||||
osmo_strlcpy(gsup->imsi, imsi, sizeof(gsup->imsi));
|
||||
osmo_strlcpy(gsup->imsi, imsi, sizeof(gsup->imsi));
|
||||
|
||||
if (msisdn_enc_size < OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN)
|
||||
return -ENOSPC;
|
||||
if (msisdn_enc_size < OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN)
|
||||
return -ENOSPC;
|
||||
|
||||
OSMO_ASSERT(msisdn_enc);
|
||||
len = gsm48_encode_bcd_number(msisdn_enc, msisdn_enc_size, 0, msisdn);
|
||||
if (len < 1) {
|
||||
LOGP(DLGSUP, LOGL_ERROR, "%s: Error: cannot encode MSISDN '%s'\n", imsi, msisdn);
|
||||
return -ENOSPC;
|
||||
}
|
||||
gsup->msisdn_enc = msisdn_enc;
|
||||
gsup->msisdn_enc_len = len;
|
||||
OSMO_ASSERT(msisdn_enc);
|
||||
len = gsm48_encode_bcd_number(msisdn_enc, msisdn_enc_size, 0, msisdn);
|
||||
if (len < 1) {
|
||||
LOGP(DLGSUP, LOGL_ERROR, "%s: Error: cannot encode MSISDN '%s'\n", imsi, msisdn);
|
||||
return -ENOSPC;
|
||||
}
|
||||
gsup->msisdn_enc = msisdn_enc;
|
||||
gsup->msisdn_enc_len = len;
|
||||
|
||||
#pragma message "FIXME: deal with encoding the following data: gsup.hlr_enc"
|
||||
#pragma message "FIXME: deal with encoding the following data: gsup.hlr_enc"
|
||||
|
||||
gsup->cn_domain = cn_domain;
|
||||
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) {
|
||||
OSMO_ASSERT(apn_buf_size >= APN_MAXLEN);
|
||||
OSMO_ASSERT(apn_buf);
|
||||
/* FIXME: PDP infos - use more fine-grained access control
|
||||
instead of wildcard APN */
|
||||
osmo_gsup_configure_wildcard_apn(gsup, apn_buf, apn_buf_size);
|
||||
}
|
||||
gsup->cn_domain = cn_domain;
|
||||
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) {
|
||||
OSMO_ASSERT(apn_buf_size >= APN_MAXLEN);
|
||||
OSMO_ASSERT(apn_buf);
|
||||
/* FIXME: PDP infos - use more fine-grained access control
|
||||
instead of wildcard APN */
|
||||
osmo_gsup_configure_wildcard_apn(gsup, apn_buf, apn_buf_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_cni_peer_id *to_peer,
|
||||
struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup)
|
||||
{
|
||||
int rc;
|
||||
/* To forward to a remote entity (HLR, SMSC,...), we need to indicate the original source name in the Source
|
||||
* Name IE to make sure the reply can be routed back. Store the sender in gsup->source_name -- the remote entity
|
||||
* is required to return this as gsup->destination_name so that the reply gets routed to the original sender. */
|
||||
struct osmo_gsup_message forward = *(modified_gsup? : &req->gsup);
|
||||
|
||||
if (req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s",
|
||||
osmo_cni_peer_id_type_name(req->source_name.type));
|
||||
rc = -ENOTSUP;
|
||||
goto routing_error;
|
||||
}
|
||||
forward.source_name = req->source_name.ipa_name.val;
|
||||
forward.source_name_len = req->source_name.ipa_name.len;
|
||||
|
||||
if (to_peer->type != OSMO_CNI_PEER_ID_IPA_NAME) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s",
|
||||
osmo_cni_peer_id_type_name(to_peer->type));
|
||||
rc = -ENOTSUP;
|
||||
goto routing_error;
|
||||
}
|
||||
LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to %s\n", osmo_cni_peer_id_to_str(to_peer));
|
||||
rc = osmo_gsup_enc_send_to_ipa_name(server, &to_peer->ipa_name, &forward);
|
||||
if (rc)
|
||||
goto routing_error;
|
||||
osmo_gsup_req_free(req);
|
||||
return 0;
|
||||
|
||||
routing_error:
|
||||
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
140
src/gsup_server.h
Normal file
140
src/gsup_server.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/abis/ipa.h>
|
||||
#include <osmocom/abis/ipaccess.h>
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
#include "global_title.h"
|
||||
|
||||
#ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN
|
||||
#define OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN 43 /* TS 24.008 10.5.4.7 */
|
||||
#endif
|
||||
|
||||
#define LOG_GSUP_CONN(conn, level, fmt, args...) \
|
||||
LOGP(DLGSUP, level, "GSUP from %s: " fmt, \
|
||||
conn && conn->server && conn->server->link ? \
|
||||
osmo_sock_get_name2_c(OTC_SELECT, conn->server->link->ofd.fd) \
|
||||
: "?", \
|
||||
##args)
|
||||
|
||||
#if 0
|
||||
#define LOG_GSUP_CONN_MSG(conn, gsup_msg, level, fmt, args...) \
|
||||
LOG_GSUP_CONN(conn, level, "IMSI-%s %s: " fmt, (gsup_msg)->imsi, \
|
||||
osmo_gsup_message_type_name((gsup_msg)->message_type), \
|
||||
##args)
|
||||
|
||||
#endif
|
||||
|
||||
#define LOG_GSUP_REQ_CAT(req, subsys, level, fmt, args...) \
|
||||
LOGP(subsys, level, "GSUP %u: %s: IMSI-%s %s: " fmt, \
|
||||
(req) ? (req)->nr : 0, \
|
||||
(req) ? global_title_name(&(req)->source_name) : "NULL", \
|
||||
(req) ? (req)->gsup.imsi : "NULL", \
|
||||
(req) ? osmo_gsup_message_type_name((req)->gsup.message_type) : "NULL", \
|
||||
##args)
|
||||
|
||||
#define LOG_GSUP_REQ(req, level, fmt, args...) \
|
||||
LOG_GSUP_REQ_CAT(req, DLGSUP, level, fmt, ##args)
|
||||
|
||||
struct osmo_gsup_conn;
|
||||
|
||||
/* Expects message in msg->l2h */
|
||||
typedef int (*osmo_gsup_read_cb_t)(struct osmo_gsup_conn *conn, struct msgb *msg);
|
||||
|
||||
struct osmo_gsup_server {
|
||||
/* private data of the application/user */
|
||||
void *priv;
|
||||
|
||||
/* list of osmo_gsup_conn */
|
||||
struct llist_head clients;
|
||||
|
||||
struct ipa_server_link *link;
|
||||
osmo_gsup_read_cb_t read_cb;
|
||||
struct llist_head routes;
|
||||
};
|
||||
|
||||
|
||||
/* a single connection to a given client (SGSN, MSC) */
|
||||
struct osmo_gsup_conn {
|
||||
struct llist_head list;
|
||||
|
||||
struct osmo_gsup_server *server;
|
||||
struct ipa_server_conn *conn;
|
||||
//struct oap_state oap_state;
|
||||
struct tlv_parsed ccm;
|
||||
|
||||
unsigned int auc_3g_ind; /*!< IND index used for UMTS AKA SQN */
|
||||
|
||||
/* Set when Location Update is received: */
|
||||
bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */
|
||||
bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */
|
||||
|
||||
/* The IPA unit name received on this link. Routes with more unit names serviced by this link may exist in
|
||||
* osmo_gsup_server->routes, but this is the name the immediate peer identified as in the IPA handshake. */
|
||||
struct global_title peer_name;
|
||||
};
|
||||
|
||||
/* Keep track of an incoming request to which osmo-hlr composes (or routes back) a response.
|
||||
* Particularly, if a request contained a source_name, we need to add this as destination_name in the response for any
|
||||
* intermediate GSUP proxies to be able to route back to the initial requestor. */
|
||||
struct osmo_gsup_req {
|
||||
struct osmo_gsup_server *server;
|
||||
/* Identify this request by number, for logging. */
|
||||
unsigned int nr;
|
||||
|
||||
/* A decoded GSUP message still points into the received msgb. For a decoded osmo_gsup_message to remain valid,
|
||||
* we also need to keep the msgb. */
|
||||
struct msgb *msg;
|
||||
/* Decoded msg. */
|
||||
int decode_rc;
|
||||
const struct osmo_gsup_message gsup;
|
||||
/* The ultimate source of this message: the source_name form the GSUP message, or, if not present, then the
|
||||
* immediate GSUP peer. GSUP messages going via a proxy reflect the initial source in the source_name.
|
||||
* This source_name is implicitly added to the routes for the conn the message was received on. */
|
||||
struct global_title source_name;
|
||||
/* If the source_name is not an immediate GSUP peer, this is set to the closest intermediate peer between here
|
||||
* and source_name. */
|
||||
struct global_title via_proxy;
|
||||
};
|
||||
|
||||
struct osmo_gsup_req *osmo_gsup_req_new(struct osmo_gsup_conn *conn, struct msgb *msg);
|
||||
#define osmo_gsup_req_free(REQ) do { \
|
||||
LOG_GSUP_REQ(REQ, LOGL_DEBUG, "free\n"); \
|
||||
_osmo_gsup_req_free(REQ); \
|
||||
} while(0)
|
||||
void _osmo_gsup_req_free(struct osmo_gsup_req *req);
|
||||
|
||||
int osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response);
|
||||
int osmo_gsup_req_respond_nonfinal(struct osmo_gsup_req *req, struct osmo_gsup_message *response);
|
||||
int osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type);
|
||||
|
||||
#define osmo_gsup_req_respond_err(REQ, CAUSE, FMT, args...) do { \
|
||||
LOG_GSUP_REQ(REQ, LOGL_ERROR, "%s: " FMT "\n", \
|
||||
get_value_string(gsm48_gmm_cause_names, CAUSE), ##args); \
|
||||
_osmo_gsup_req_respond_err(REQ, CAUSE); \
|
||||
} while(0)
|
||||
void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause);
|
||||
|
||||
struct msgb *osmo_gsup_msgb_alloc(const char *label);
|
||||
|
||||
int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg);
|
||||
int osmo_gsup_conn_enc_send(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
|
||||
int osmo_gsup_conn_ccm_get(const struct osmo_gsup_conn *clnt, uint8_t **addr,
|
||||
uint8_t tag);
|
||||
|
||||
struct osmo_gsup_server *osmo_gsup_server_create(void *ctx,
|
||||
const char *ip_addr,
|
||||
uint16_t tcp_port,
|
||||
osmo_gsup_read_cb_t read_cb,
|
||||
void *priv);
|
||||
|
||||
void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups);
|
||||
|
||||
int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
|
||||
uint8_t *apn_buf, size_t apn_buf_size);
|
||||
int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, const char *imsi, const char *msisdn,
|
||||
uint8_t *msisdn_enc, size_t msisdn_enc_size,
|
||||
uint8_t *apn_buf, size_t apn_buf_size,
|
||||
enum osmo_gsup_cn_domain cn_domain);
|
||||
@@ -1,18 +1,14 @@
|
||||
# This is _NOT_ the library release version, it's an API version.
|
||||
# Please read chapter "Library interface versions" of the libtool documentation
|
||||
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
|
||||
LIBVERSION=1:0:1
|
||||
LIBVERSION=0:0:0
|
||||
|
||||
AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include \
|
||||
$(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOABIS_CFLAGS)
|
||||
|
||||
lib_LTLIBRARIES = libosmo-gsup-client.la
|
||||
|
||||
libosmo_gsup_client_la_SOURCES = \
|
||||
cni_peer_id.c \
|
||||
gsup_client.c \
|
||||
gsup_req.c \
|
||||
$(NULL)
|
||||
libosmo_gsup_client_la_SOURCES = gsup_client.c
|
||||
|
||||
libosmo_gsup_client_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
|
||||
libosmo_gsup_client_la_LIBADD = $(TALLOC_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS)
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
|
||||
bool osmo_ipa_name_is_empty(const struct osmo_ipa_name *ipa_name)
|
||||
{
|
||||
return (!ipa_name) || (!ipa_name->len);
|
||||
}
|
||||
|
||||
int osmo_ipa_name_set(struct osmo_ipa_name *ipa_name, const uint8_t *val, size_t len)
|
||||
{
|
||||
if (!val || !len) {
|
||||
*ipa_name = (struct osmo_ipa_name){};
|
||||
return 0;
|
||||
}
|
||||
if (len > sizeof(ipa_name->val))
|
||||
return -ENOSPC;
|
||||
ipa_name->len = len;
|
||||
memcpy(ipa_name->val, val, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int osmo_ipa_name_set_str_va(struct osmo_ipa_name *ipa_name, const char *str_fmt, va_list ap)
|
||||
{
|
||||
if (!str_fmt)
|
||||
return osmo_ipa_name_set(ipa_name, NULL, 0);
|
||||
vsnprintf((char*)(ipa_name->val), sizeof(ipa_name->val), str_fmt, ap);
|
||||
ipa_name->len = strlen((char*)(ipa_name->val))+1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_ipa_name_set_str(struct osmo_ipa_name *ipa_name, const char *str_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int rc;
|
||||
va_start(ap, str_fmt);
|
||||
rc = osmo_ipa_name_set_str_va(ipa_name, str_fmt, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int osmo_ipa_name_cmp(const struct osmo_ipa_name *a, const struct osmo_ipa_name *b)
|
||||
{
|
||||
int cmp;
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
if (!a->len && !b->len)
|
||||
return 0;
|
||||
if (!a->len && b->len)
|
||||
return -1;
|
||||
if (!b->len && a->len)
|
||||
return 1;
|
||||
|
||||
if (a->len == b->len)
|
||||
return memcmp(a->val, b->val, a->len);
|
||||
else if (a->len < b->len) {
|
||||
cmp = memcmp(a->val, b->val, a->len);
|
||||
if (!cmp)
|
||||
cmp = -1;
|
||||
return cmp;
|
||||
} else {
|
||||
/* a->len > b->len */
|
||||
cmp = memcmp(a->val, b->val, b->len);
|
||||
if (!cmp)
|
||||
cmp = 1;
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Call osmo_ipa_name_to_str_c with OTC_SELECT. */
|
||||
const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name)
|
||||
{
|
||||
return osmo_ipa_name_to_str_c(OTC_SELECT, ipa_name);
|
||||
}
|
||||
|
||||
/* Return an unquoted string, not including the terminating zero. Used for writing VTY config. */
|
||||
const char *osmo_ipa_name_to_str_c(void *ctx, const struct osmo_ipa_name *ipa_name)
|
||||
{
|
||||
size_t len = ipa_name->len;
|
||||
if (!len)
|
||||
return talloc_strdup(ctx, "");
|
||||
if (ipa_name->val[len-1] == '\0')
|
||||
len--;
|
||||
return osmo_escape_str_c(ctx, (char*)ipa_name->val, len);
|
||||
}
|
||||
|
||||
bool osmo_cni_peer_id_is_empty(const struct osmo_cni_peer_id *cni_peer_id)
|
||||
{
|
||||
if (!cni_peer_id)
|
||||
return true;
|
||||
switch (cni_peer_id->type) {
|
||||
case OSMO_CNI_PEER_ID_EMPTY:
|
||||
return true;
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
return osmo_ipa_name_is_empty(&cni_peer_id->ipa_name);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int osmo_cni_peer_id_set(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni_peer_id_type type,
|
||||
const uint8_t *val, size_t len)
|
||||
{
|
||||
cni_peer_id->type = type;
|
||||
switch (type) {
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
return osmo_ipa_name_set(&cni_peer_id->ipa_name, val, len);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int osmo_cni_peer_id_set_str(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni_peer_id_type type,
|
||||
const char *str_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
*cni_peer_id = (struct osmo_cni_peer_id){};
|
||||
|
||||
switch (type) {
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
cni_peer_id->type = OSMO_CNI_PEER_ID_IPA_NAME;
|
||||
va_start(ap, str_fmt);
|
||||
rc = osmo_ipa_name_set_str_va(&cni_peer_id->ipa_name, str_fmt, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int osmo_cni_peer_id_cmp(const struct osmo_cni_peer_id *a, const struct osmo_cni_peer_id *b)
|
||||
{
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
if (a->type != b->type)
|
||||
return OSMO_CMP(a->type, b->type);
|
||||
switch (a->type) {
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
return osmo_ipa_name_cmp(&a->ipa_name, &b->ipa_name);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
const struct value_string osmo_cni_peer_id_type_names[] = {
|
||||
{ OSMO_CNI_PEER_ID_IPA_NAME, "IPA-name" },
|
||||
{}
|
||||
};
|
||||
|
||||
/* Call osmo_cni_peer_id_to_str_c with OTC_SELECT */
|
||||
const char *osmo_cni_peer_id_to_str(const struct osmo_cni_peer_id *cpi)
|
||||
{
|
||||
return osmo_cni_peer_id_to_str_c(OTC_SELECT, cpi);
|
||||
}
|
||||
|
||||
/* Return an unquoted string, not including the terminating zero. Used for writing VTY config. */
|
||||
const char *osmo_cni_peer_id_to_str_c(void *ctx, const struct osmo_cni_peer_id *cpi)
|
||||
{
|
||||
switch (cpi->type) {
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
return osmo_ipa_name_to_str_c(ctx, &cpi->ipa_name);
|
||||
default:
|
||||
return talloc_strdup(ctx, osmo_cni_peer_id_type_name(cpi->type));
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,6 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
static void start_test_procedure(struct osmo_gsup_client *gsupc);
|
||||
|
||||
@@ -106,6 +104,7 @@ static void connect_timer_cb(void *gsupc_)
|
||||
}
|
||||
|
||||
gsup_client_connect(gsupc);
|
||||
|
||||
}
|
||||
|
||||
static void client_send(struct osmo_gsup_client *gsupc, int proto_ext,
|
||||
@@ -131,19 +130,6 @@ static void gsup_client_oap_register(struct osmo_gsup_client *gsupc)
|
||||
client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
|
||||
}
|
||||
|
||||
static void update_fd_settings(int fd)
|
||||
{
|
||||
int ret;
|
||||
int val;
|
||||
|
||||
/*TODO: Set keepalive settings here. See OS#4312 */
|
||||
|
||||
val = 1;
|
||||
ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
|
||||
if (ret < 0)
|
||||
LOGP(DLGSUP, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
|
||||
{
|
||||
struct osmo_gsup_client *gsupc = link->data;
|
||||
@@ -154,7 +140,6 @@ static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
|
||||
gsupc->is_connected = up;
|
||||
|
||||
if (up) {
|
||||
update_fd_settings(link->ofd->fd);
|
||||
start_test_procedure(gsupc);
|
||||
|
||||
if (gsupc->oap_state.state == OSMO_OAP_INITIALIZED)
|
||||
@@ -289,52 +274,48 @@ static void start_test_procedure(struct osmo_gsup_client *gsupc)
|
||||
gsup_client_send_ping(gsupc);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a gsup client connecting to the specified IP address and TCP port.
|
||||
* Use the provided ipaccess unit as the client-side identifier; ipa_dev should
|
||||
* be allocated in talloc_ctx talloc_ctx as well.
|
||||
* \param[in] talloc_ctx talloc context.
|
||||
* \param[in] config Parameters for setting up the GSUP client.
|
||||
* \return a GSUP client connection, or NULL on failure.
|
||||
*/
|
||||
struct osmo_gsup_client *osmo_gsup_client_create3(void *talloc_ctx, struct osmo_gsup_client_config *config)
|
||||
struct osmo_gsup_client *osmo_gsup_client_create3(void *talloc_ctx,
|
||||
struct ipaccess_unit *ipa_dev,
|
||||
const char *ip_addr,
|
||||
unsigned int tcp_port,
|
||||
struct osmo_oap_client_config *oapc_config,
|
||||
osmo_gsup_client_read_cb_t read_cb,
|
||||
osmo_gsup_client_up_down_cb_t up_down_cb,
|
||||
void *data)
|
||||
{
|
||||
struct osmo_gsup_client *gsupc;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(config->ipa_dev->unit_name);
|
||||
OSMO_ASSERT(ipa_dev->unit_name);
|
||||
|
||||
gsupc = talloc_zero(talloc_ctx, struct osmo_gsup_client);
|
||||
OSMO_ASSERT(gsupc);
|
||||
*gsupc = (struct osmo_gsup_client){
|
||||
.unit_name = (const char *)config->ipa_dev->unit_name, /* API backwards compat */
|
||||
.ipa_dev = config->ipa_dev,
|
||||
.read_cb = config->read_cb,
|
||||
.up_down_cb = config->up_down_cb,
|
||||
.data = config->data,
|
||||
.unit_name = (const char *)ipa_dev->unit_name, /* API backwards compat */
|
||||
.ipa_dev = ipa_dev,
|
||||
.read_cb = read_cb,
|
||||
.up_down_cb = up_down_cb,
|
||||
.data = data,
|
||||
};
|
||||
|
||||
/* a NULL oapc_config will mark oap_state disabled. */
|
||||
rc = osmo_oap_client_init(config->oapc_config, &gsupc->oap_state);
|
||||
rc = osmo_oap_client_init(oapc_config, &gsupc->oap_state);
|
||||
if (rc != 0)
|
||||
goto failed;
|
||||
|
||||
gsupc->link = ipa_client_conn_create2(gsupc,
|
||||
/* no e1inp */ NULL,
|
||||
0,
|
||||
/* no specific local IP:port */ NULL, 0,
|
||||
config->ip_addr, config->tcp_port,
|
||||
gsup_client_updown_cb,
|
||||
gsup_client_read_cb,
|
||||
/* default write_cb */ NULL,
|
||||
gsupc);
|
||||
gsupc->link = ipa_client_conn_create(gsupc,
|
||||
/* no e1inp */ NULL,
|
||||
0,
|
||||
ip_addr, tcp_port,
|
||||
gsup_client_updown_cb,
|
||||
gsup_client_read_cb,
|
||||
/* default write_cb */ NULL,
|
||||
gsupc);
|
||||
if (!gsupc->link)
|
||||
goto failed;
|
||||
|
||||
osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc);
|
||||
|
||||
rc = gsup_client_connect(gsupc);
|
||||
|
||||
if (rc < 0)
|
||||
goto failed;
|
||||
return gsupc;
|
||||
@@ -344,8 +325,18 @@ failed:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Like osmo_gsup_client_create3() but without the up_down_cb and data arguments, and with the oapc_config argument in
|
||||
* a different position.
|
||||
/*!
|
||||
* Create a gsup client connecting to the specified IP address and TCP port.
|
||||
* Use the provided ipaccess unit as the client-side identifier; ipa_dev should
|
||||
* be allocated in talloc_ctx talloc_ctx as well.
|
||||
* \param[in] talloc_ctx talloc context.
|
||||
* \param[in] ipa_dev IP access unit which contains client identification information; must be allocated
|
||||
* in talloc_ctx as well to ensure it lives throughout the lifetime of the connection.
|
||||
* \param[in] ip_addr GSUP server IP address.
|
||||
* \param[in] tcp_port GSUP server TCP port.
|
||||
* \param[in] read_cb callback for reading from the GSUP connection.
|
||||
* \param[in] oapc_config OPA client configuration.
|
||||
* \returns a GSUP client connection or NULL on failure.
|
||||
*/
|
||||
struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
|
||||
struct ipaccess_unit *ipa_dev,
|
||||
@@ -354,14 +345,8 @@ struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
|
||||
osmo_gsup_client_read_cb_t read_cb,
|
||||
struct osmo_oap_client_config *oapc_config)
|
||||
{
|
||||
struct osmo_gsup_client_config cfg = {
|
||||
.ipa_dev = ipa_dev,
|
||||
.ip_addr = ip_addr,
|
||||
.tcp_port = tcp_port,
|
||||
.oapc_config = oapc_config,
|
||||
.read_cb = read_cb,
|
||||
};
|
||||
return osmo_gsup_client_create3(talloc_ctx, &cfg);
|
||||
return osmo_gsup_client_create3(talloc_ctx, ipa_dev, ip_addr, tcp_port, oapc_config,
|
||||
read_cb, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,416 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
|
||||
#include <osmocom/gsupclient/gsup_req.h>
|
||||
|
||||
/*! Create a new osmo_gsup_req record, decode GSUP and add to a provided list of requests.
|
||||
*
|
||||
* Rationales:
|
||||
*
|
||||
* - osmo_gsup_req makes it easy to handle GSUP requests asynchronously. Before this, a GSUP message struct would be
|
||||
* valid only within a read callback function, and would not survive asynchronous handling, because the struct often
|
||||
* points directly into the received msgb. An osmo_gsup_req takes ownership of the msgb and ensures that the data
|
||||
* remains valid, so that it can easily be queued for later handling.
|
||||
* - osmo_gsup_req unifies the composition of response messages to ensure that all IEs that identify it to belong to
|
||||
* the initial request are preserved / derived, like the source_name, destination_name, session_id, etc (see
|
||||
* osmo_gsup_make_response() for details).
|
||||
* - Deallocation of an osmo_gsup_req is implicit upon sending a response. The idea is that msgb memory leaks are a
|
||||
* recurring source of bugs. By enforcing a request-response relation with implicit deallocation, osmo_gsup_req aims
|
||||
* to help avoid most such memory leaks implicitly.
|
||||
*
|
||||
* The typical GSUP message sequence is:
|
||||
* -> rx request,
|
||||
* <- tx response.
|
||||
*
|
||||
* With osmo_gsup_req we can easily expand to:
|
||||
* -> rx request,
|
||||
* ... wait asynchronously,
|
||||
* <- tx response.
|
||||
*
|
||||
* Only few GSUP conversations go beyond a 1:1 request-response match. But some have a session (e.g. USSD) or more
|
||||
* negotiation may happen before the initial request is completed (e.g. Update Location with interleaved Insert
|
||||
* Subscriber Data), so osmo_gsup_req also allows passing non-final responses.
|
||||
* The final_response flag allows for:
|
||||
* -> rx request,
|
||||
* ... wait async,
|
||||
* <- tx intermediate message to same peer (final_response = false, req remains open),
|
||||
* ... wait async,
|
||||
* -> rx intermediate response,
|
||||
* ... wait async,
|
||||
* <- tx final response (final_response = true, req is deallocated).
|
||||
*
|
||||
* This function takes ownership of the msgb, which will, on success, be owned by the returned osmo_gsup_req instance
|
||||
* until osmo_gsup_req_free(). If a decoding error occurs, send an error response immediately, and return NULL.
|
||||
*
|
||||
* The original CNI entity that sent the message is found in req->source_name. If the message was passed on by an
|
||||
* intermediate CNI peer, then req->via_proxy is set to the immediate peer, and it is the responsibility of the caller
|
||||
* to add req->source_name to the GSUP routes that are serviced by req->via_proxy (usually not relevant for clients with
|
||||
* a single GSUP conn).
|
||||
* Examples:
|
||||
*
|
||||
* "msc" ---> here
|
||||
* source_name = "msc"
|
||||
* via_proxy = <empty>
|
||||
*
|
||||
* "msc" ---> "proxy-HLR" ---> here (e.g. home HLR)
|
||||
* source_name = "msc"
|
||||
* via_proxy = "proxy-HLR"
|
||||
*
|
||||
* "msc" ---> "proxy-HLR" ---> "home-HLR" ---> here (e.g. EUSE)
|
||||
* source_name = "msc"
|
||||
* via_proxy = "home-HLR"
|
||||
*
|
||||
* An osmo_gsup_req must be concluded (and deallocated) by calling one of the osmo_gsup_req_respond* functions.
|
||||
*
|
||||
* Note: osmo_gsup_req API makes use of OTC_SELECT to allocate volatile buffers for logging. Use of
|
||||
* osmo_select_main_ctx() is mandatory when using osmo_gsup_req.
|
||||
*
|
||||
* \param[in] ctx Talloc context for allocation of the new request.
|
||||
* \param[in] from_peer The IPA unit name of the immediate GSUP peer from which this msgb was received.
|
||||
* \param[in] msg The message buffer containing the received GSUP message, where msgb_l2() shall point to the GSUP
|
||||
* message start. The caller no longer owns the msgb when it is passed to this function: on error, the
|
||||
* msgb is freed immediately, and on success, the msgb is owned by the returned osmo_gsup_req.
|
||||
* \param[in] send_response_cb User specific method to send a GSUP response message, invoked upon
|
||||
* osmo_gsup_req_respond*() functions. Typically this invokes encoding and transmitting the
|
||||
* GSUP message over a network socket. See for example gsup_server_send_req_response().
|
||||
* \param[inout] cb_data Context data to be used freely by the caller.
|
||||
* \param[inout] add_to_list List to which to append this request, or NULL for no list.
|
||||
* \return a newly allocated osmo_gsup_req, or NULL on error. If NULL is returned, an error response has already been
|
||||
* dispatched to the send_response_cb.
|
||||
*/
|
||||
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id *from_peer, struct msgb *msg,
|
||||
osmo_gsup_req_send_response_t send_response_cb, void *cb_data,
|
||||
struct llist_head *add_to_list)
|
||||
{
|
||||
static unsigned int next_req_nr = 1;
|
||||
struct osmo_gsup_req *req;
|
||||
int rc;
|
||||
|
||||
if (!from_peer) {
|
||||
LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from NULL peer is not allowed\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!msgb_l2(msg) || !msgb_l2len(msg)) {
|
||||
LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: missing or empty L2 data\n",
|
||||
osmo_cni_peer_id_to_str(from_peer));
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
req = talloc_zero(ctx, struct osmo_gsup_req);
|
||||
OSMO_ASSERT(req);
|
||||
/* Note: req->gsup is declared const, so that the incoming message cannot be modified by handlers. */
|
||||
req->nr = next_req_nr++;
|
||||
req->msg = msg;
|
||||
req->send_response_cb = send_response_cb;
|
||||
req->cb_data = cb_data;
|
||||
req->source_name = *from_peer;
|
||||
rc = osmo_gsup_decode(msgb_l2(req->msg), msgb_l2len(req->msg), (struct osmo_gsup_message*)&req->gsup);
|
||||
if (rc < 0) {
|
||||
LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: cannot decode (rc=%d)\n", osmo_cni_peer_id_to_str(from_peer), rc);
|
||||
osmo_gsup_req_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOG_GSUP_REQ(req, LOGL_DEBUG, "new request: {%s}\n", osmo_gsup_message_to_str_c(OTC_SELECT, &req->gsup));
|
||||
|
||||
if (req->gsup.source_name_len) {
|
||||
if (osmo_cni_peer_id_set(&req->source_name, OSMO_CNI_PEER_ID_IPA_NAME,
|
||||
req->gsup.source_name, req->gsup.source_name_len)) {
|
||||
LOGP(DLGSUP, LOGL_ERROR,
|
||||
"Rx GSUP from %s: failed to decode source_name, message is not routable\n",
|
||||
osmo_cni_peer_id_to_str(from_peer));
|
||||
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The source of the GSUP message is not the immediate GSUP peer; the peer is our proxy for that source.
|
||||
*/
|
||||
if (osmo_cni_peer_id_cmp(&req->source_name, from_peer))
|
||||
req->via_proxy = *from_peer;
|
||||
}
|
||||
|
||||
if (!osmo_imsi_str_valid(req->gsup.imsi)) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "invalid IMSI: %s",
|
||||
osmo_quote_str(req->gsup.imsi, -1));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (add_to_list)
|
||||
llist_add_tail(&req->entry, add_to_list);
|
||||
return req;
|
||||
}
|
||||
|
||||
/*! Free an osmo_gsup_req and its msgb -- this is usually implicit in osmo_gsup_req_resond_*(), it should not be
|
||||
* necessary to call this directly. */
|
||||
void osmo_gsup_req_free(struct osmo_gsup_req *req)
|
||||
{
|
||||
LOG_GSUP_REQ(req, LOGL_DEBUG, "free\n");
|
||||
if (req->msg)
|
||||
msgb_free(req->msg);
|
||||
if (req->entry.prev)
|
||||
llist_del(&req->entry);
|
||||
talloc_free(req);
|
||||
}
|
||||
|
||||
/*! Send a response to a GSUP request.
|
||||
*
|
||||
* Ensure that the response message contains all GSUP IEs that identify it as a response for the request req, by calling
|
||||
* osmo_gsup_make_response().
|
||||
*
|
||||
* The final complete response message is passed to req->send_response_cb() to take care of the transmission.
|
||||
*
|
||||
* \param req Request as previously initialized by osmo_gsup_req_new().
|
||||
* \param response Buffer to compose the response, possibly with some pre-configured IEs.
|
||||
* Any missing IEs are added via osmo_gsup_make_response().
|
||||
* Must not be NULL. Does not need to remain valid memory beyond the function call,
|
||||
* i.e. this can just be a local variable in the calling function.
|
||||
* \param error True when the response message indicates an error response (error message type).
|
||||
* \param final_response True when the request is concluded by this response, which deallocates the req.
|
||||
* False when the request should remain open after this response.
|
||||
* For most plain request->response GSUP messages, this should be True.
|
||||
* \param file Source file for logging as in __FILE__, added by osmo_gsup_req_respond() macro.
|
||||
* \param line Source line for logging as in __LINE__, added by osmo_gsup_req_respond() macro.
|
||||
*/
|
||||
int _osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response,
|
||||
bool error, bool final_response, const char *file, int line)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = osmo_gsup_make_response(response, &req->gsup, error, final_response);
|
||||
if (rc) {
|
||||
LOG_GSUP_REQ_SRC(req, LOGL_ERROR, file, line, "Invalid response (rc=%d): {%s}\n",
|
||||
rc, osmo_gsup_message_to_str_c(OTC_SELECT, response));
|
||||
rc = -EINVAL;
|
||||
goto exit_cleanup;
|
||||
}
|
||||
|
||||
if (!req->send_response_cb) {
|
||||
LOG_GSUP_REQ_SRC(req, LOGL_ERROR, file, line, "No send_response_cb set, cannot send: {%s}\n",
|
||||
osmo_gsup_message_to_str_c(OTC_SELECT, response));
|
||||
rc = -EINVAL;
|
||||
goto exit_cleanup;
|
||||
}
|
||||
|
||||
LOG_GSUP_REQ_SRC(req, LOGL_DEBUG, file, line, "Tx response: {%s}\n",
|
||||
osmo_gsup_message_to_str_c(OTC_SELECT, response));
|
||||
req->send_response_cb(req, response);
|
||||
|
||||
exit_cleanup:
|
||||
if (final_response)
|
||||
osmo_gsup_req_free(req);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Shorthand for _osmo_gsup_req_respond() with no additional IEs and a fixed message type.
|
||||
* Set the message type in a local osmo_gsup_message and feed it to _osmo_gsup_req_respond().
|
||||
* That will ensure to add all IEs that identify it as a response to req.
|
||||
*
|
||||
* \param req Request as previously initialized by osmo_gsup_req_new().
|
||||
* \param message_type The GSUP message type discriminator to respond with.
|
||||
* \param final_response True when the request is concluded by this response, which deallocates the req.
|
||||
* False when the request should remain open after this response.
|
||||
* For most plain request->response GSUP messages, this should be True.
|
||||
* \param file Source file for logging as in __FILE__, added by osmo_gsup_req_respond_msgt() macro.
|
||||
* \param line Source line for logging as in __LINE__, added by osmo_gsup_req_respond_msgt() macro.
|
||||
*/
|
||||
int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type,
|
||||
bool final_response, const char *file, int line)
|
||||
{
|
||||
struct osmo_gsup_message response = {
|
||||
.message_type = message_type,
|
||||
};
|
||||
return _osmo_gsup_req_respond(req, &response, OSMO_GSUP_IS_MSGT_ERROR(message_type), final_response,
|
||||
file, line);
|
||||
}
|
||||
|
||||
/*! Shorthand for _osmo_gsup_req_respond() with an error cause IEs and using the req's matched error message type.
|
||||
* Set the error cause in a local osmo_gsup_message and feed it to _osmo_gsup_req_respond().
|
||||
* That will ensure to add all IEs that identify it as a response to req.
|
||||
*
|
||||
* Responding with an error always implies a final response: req is implicitly deallocated.
|
||||
*
|
||||
* \param req Request as previously initialized by osmo_gsup_req_new().
|
||||
* \param cause The error cause to include in a OSMO_GSUP_CAUSE_IE.
|
||||
* \param file Source file for logging as in __FILE__, added by osmo_gsup_req_respond_err() macro.
|
||||
* \param line Source line for logging as in __LINE__, added by osmo_gsup_req_respond_err() macro.
|
||||
*/
|
||||
void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause,
|
||||
const char *file, int line)
|
||||
{
|
||||
struct osmo_gsup_message response = {
|
||||
.cause = cause,
|
||||
};
|
||||
|
||||
/* No need to answer if we couldn't parse an ERROR message type, only REQUESTs need an error reply. */
|
||||
if (!OSMO_GSUP_IS_MSGT_REQUEST(req->gsup.message_type)) {
|
||||
osmo_gsup_req_free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
osmo_gsup_req_respond(req, &response, true, true);
|
||||
}
|
||||
|
||||
/*! This function is implicitly called by the osmo_gsup_req API, if at all possible rather use osmo_gsup_req_respond().
|
||||
* This function is non-static mostly to allow unit testing.
|
||||
*
|
||||
* Set fields, if still unset, that need to be copied from a received message over to its response message, to ensure
|
||||
* the response can be routed back to the requesting peer even via GSUP proxies.
|
||||
*
|
||||
* Note: after calling this function, fields in the reply may reference the same memory as rx and are not deep-copied,
|
||||
* as is the usual way we are handling decoded GSUP messages.
|
||||
*
|
||||
* These fields are set in the reply message, iff they are still unset:
|
||||
* - Set reply->message_type to the rx's matching RESULT code (or ERROR code if error == true).
|
||||
* - IMSI,
|
||||
* - Set reply->destination_name to rx->source_name (for proxy routing),
|
||||
* - sm_rp_mr (for SMS),
|
||||
* - session_id (for SS/USSD),
|
||||
* - if rx->session_state is not NONE, set tx->session_state depending on the final_response argument:
|
||||
* If false, set to OSMO_GSUP_SESSION_STATE_CONTINUE, else OSMO_GSUP_SESSION_STATE_END.
|
||||
*
|
||||
* If values in reply are already set, they will not be overwritten. The return code is an optional way of finding out
|
||||
* whether all values that were already set in 'reply' are indeed matching the 'rx' values that would have been set.
|
||||
*
|
||||
* \param[in] rx Received GSUP message that is being replied to.
|
||||
* \param[inout] reply The message that should be the response to rx, either empty or with some values already set up.
|
||||
* \return 0 if the resulting message is a valid response for rx, nonzero otherwise. A nonzero rc has no effect on the
|
||||
* values set in the reply message: all unset fields are first updated, and then the rc is determined.
|
||||
* The rc is intended to merely warn if the reply message already contained data that is incompatible with rx,
|
||||
* e.g. a mismatching IMSI.
|
||||
*/
|
||||
int osmo_gsup_make_response(struct osmo_gsup_message *reply,
|
||||
const struct osmo_gsup_message *rx, bool error, bool final_response)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!reply->message_type) {
|
||||
if (error)
|
||||
reply->message_type = OSMO_GSUP_TO_MSGT_ERROR(rx->message_type);
|
||||
else
|
||||
reply->message_type = OSMO_GSUP_TO_MSGT_RESULT(rx->message_type);
|
||||
}
|
||||
|
||||
if (*reply->imsi == '\0')
|
||||
OSMO_STRLCPY_ARRAY(reply->imsi, rx->imsi);
|
||||
|
||||
if (reply->message_class == OSMO_GSUP_MESSAGE_CLASS_UNSET)
|
||||
reply->message_class = rx->message_class;
|
||||
|
||||
if (!reply->destination_name || !reply->destination_name_len) {
|
||||
reply->destination_name = rx->source_name;
|
||||
reply->destination_name_len = rx->source_name_len;
|
||||
}
|
||||
|
||||
/* RP-Message-Reference is mandatory for SM Service */
|
||||
if (!reply->sm_rp_mr)
|
||||
reply->sm_rp_mr = rx->sm_rp_mr;
|
||||
|
||||
/* For SS/USSD, it's important to keep both session state and ID IEs */
|
||||
if (!reply->session_id)
|
||||
reply->session_id = rx->session_id;
|
||||
if (rx->session_state != OSMO_GSUP_SESSION_STATE_NONE
|
||||
&& reply->session_state == OSMO_GSUP_SESSION_STATE_NONE) {
|
||||
if (final_response || rx->session_state == OSMO_GSUP_SESSION_STATE_END)
|
||||
reply->session_state = OSMO_GSUP_SESSION_STATE_END;
|
||||
else
|
||||
reply->session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
|
||||
}
|
||||
|
||||
if (strcmp(reply->imsi, rx->imsi))
|
||||
rc |= 1 << 0;
|
||||
if (reply->message_class != rx->message_class)
|
||||
rc |= 1 << 1;
|
||||
if (rx->sm_rp_mr && (!reply->sm_rp_mr || *rx->sm_rp_mr != *reply->sm_rp_mr))
|
||||
rc |= 1 << 2;
|
||||
if (reply->session_id != rx->session_id)
|
||||
rc |= 1 << 3;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Print the most important value of a GSUP message to a string buffer in human readable form.
|
||||
* \param[out] buf The buffer to write to.
|
||||
* \param[out] buflen sizeof(buf).
|
||||
* \param[in] msg GSUP message to print.
|
||||
*/
|
||||
size_t osmo_gsup_message_to_str_buf(char *buf, size_t buflen, const struct osmo_gsup_message *msg)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
if (!msg) {
|
||||
OSMO_STRBUF_PRINTF(sb, "NULL");
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
if (msg->message_class)
|
||||
OSMO_STRBUF_PRINTF(sb, "%s ", osmo_gsup_message_class_name(msg->message_class));
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "%s:", osmo_gsup_message_type_name(msg->message_type));
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, " imsi=");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_quote_cstr_buf, msg->imsi, strnlen(msg->imsi, sizeof(msg->imsi)));
|
||||
|
||||
if (msg->cause)
|
||||
OSMO_STRBUF_PRINTF(sb, " cause=%s", get_value_string(gsm48_gmm_cause_names, msg->cause));
|
||||
|
||||
switch (msg->cn_domain) {
|
||||
case OSMO_GSUP_CN_DOMAIN_CS:
|
||||
OSMO_STRBUF_PRINTF(sb, " cn_domain=CS");
|
||||
break;
|
||||
case OSMO_GSUP_CN_DOMAIN_PS:
|
||||
OSMO_STRBUF_PRINTF(sb, " cn_domain=PS");
|
||||
break;
|
||||
default:
|
||||
if (msg->cn_domain)
|
||||
OSMO_STRBUF_PRINTF(sb, " cn_domain=?(%d)", msg->cn_domain);
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg->source_name_len) {
|
||||
OSMO_STRBUF_PRINTF(sb, " source_name=");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_quote_cstr_buf, (char*)msg->source_name, msg->source_name_len);
|
||||
}
|
||||
|
||||
if (msg->destination_name_len) {
|
||||
OSMO_STRBUF_PRINTF(sb, " destination_name=");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_quote_cstr_buf, (char*)msg->destination_name, msg->destination_name_len);
|
||||
}
|
||||
|
||||
if (msg->session_id)
|
||||
OSMO_STRBUF_PRINTF(sb, " session_id=%" PRIu32, msg->session_id);
|
||||
if (msg->session_state)
|
||||
OSMO_STRBUF_PRINTF(sb, " session_state=%s", osmo_gsup_session_state_name(msg->session_state));
|
||||
|
||||
if (msg->sm_rp_mr)
|
||||
OSMO_STRBUF_PRINTF(sb, " sm_rp_mr=%" PRIu8, *msg->sm_rp_mr);
|
||||
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
/*! Same as osmo_gsup_message_to_str_buf() but returns a talloc allocated string. */
|
||||
char *osmo_gsup_message_to_str_c(void *ctx, const struct osmo_gsup_message *msg)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_gsup_message_to_str_buf, msg)
|
||||
}
|
||||
169
src/hlr.c
169
src/hlr.c
@@ -31,33 +31,34 @@
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/vty/cpu_sched_vty.h>
|
||||
#include <osmocom/ctrl/control_vty.h>
|
||||
#include <osmocom/gsm/apn.h>
|
||||
#include <osmocom/gsm/gsm48_ie.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/mslookup/mslookup_client.h>
|
||||
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/ctrl.h>
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/gsup_server.h>
|
||||
#include <osmocom/hlr/gsup_router.h>
|
||||
#include <osmocom/hlr/rand.h>
|
||||
#include <osmocom/hlr/hlr_vty.h>
|
||||
#include <osmocom/hlr/hlr_ussd.h>
|
||||
#include <osmocom/hlr/dgsm.h>
|
||||
#include <osmocom/hlr/proxy.h>
|
||||
#include <osmocom/hlr/lu_fsm.h>
|
||||
#include <osmocom/mslookup/mdns.h>
|
||||
#include "db.h"
|
||||
#include "hlr.h"
|
||||
#include "ctrl.h"
|
||||
#include "logging.h"
|
||||
#include "gsup_server.h"
|
||||
#include "gsup_router.h"
|
||||
#include "rand.h"
|
||||
#include "hlr_vty.h"
|
||||
#include "hlr_ussd.h"
|
||||
#include "dgsm.h"
|
||||
#include "proxy.h"
|
||||
#include "global_title.h"
|
||||
#include "lu_fsm.h"
|
||||
|
||||
struct hlr *g_hlr;
|
||||
static void *hlr_ctx = NULL;
|
||||
static int quit = 0;
|
||||
|
||||
#define RAN_TDEFS \
|
||||
|
||||
struct osmo_tdef g_hlr_tdefs[] = {
|
||||
/* 4222 is also the OSMO_GSUP_PORT */
|
||||
{ .T = -4222, .default_val = 30, .desc = "GSUP Update Location timeout" },
|
||||
@@ -248,7 +249,7 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val,
|
||||
{
|
||||
int rc;
|
||||
bool is_val = is_ps? subscr->nam_ps : subscr->nam_cs;
|
||||
struct osmo_ipa_name vlr_name;
|
||||
struct global_title vlr_name;
|
||||
struct osmo_gsup_message gsup_del_data = {
|
||||
.message_type = OSMO_GSUP_MSGT_DELETE_DATA_REQUEST,
|
||||
};
|
||||
@@ -269,10 +270,10 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val,
|
||||
if (nam_val)
|
||||
return 0;
|
||||
|
||||
if (subscr->vlr_number[0] && !osmo_ipa_name_set_str(&vlr_name, subscr->vlr_number))
|
||||
osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &vlr_name, &gsup_del_data);
|
||||
if (subscr->sgsn_number[0] && !osmo_ipa_name_set_str(&vlr_name, subscr->sgsn_number))
|
||||
osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &vlr_name, &gsup_del_data);
|
||||
if (subscr->vlr_number && global_title_set_str(&vlr_name, subscr->vlr_number))
|
||||
osmo_gsup_gt_enc_send(g_hlr->gs, &vlr_name, &gsup_del_data);
|
||||
if (subscr->sgsn_number && global_title_set_str(&vlr_name, subscr->sgsn_number))
|
||||
osmo_gsup_gt_enc_send(g_hlr->gs, &vlr_name, &gsup_del_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -281,38 +282,19 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val,
|
||||
***********************************************************************/
|
||||
|
||||
/* process an incoming SAI request */
|
||||
static int rx_send_auth_info(struct osmo_gsup_req *req)
|
||||
static int rx_send_auth_info(unsigned int auc_3g_ind, struct osmo_gsup_req *req)
|
||||
{
|
||||
struct osmo_gsup_message gsup_out = {
|
||||
.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT,
|
||||
};
|
||||
bool separation_bit = false;
|
||||
int num_auth_vectors = OSMO_GSUP_MAX_NUM_AUTH_INFO;
|
||||
unsigned int auc_3g_ind;
|
||||
int rc;
|
||||
|
||||
subscr_create_on_demand(req->gsup.imsi);
|
||||
|
||||
if (req->gsup.current_rat_type == OSMO_RAT_EUTRAN_SGS)
|
||||
separation_bit = true;
|
||||
|
||||
if (req->gsup.num_auth_vectors > 0 &&
|
||||
req->gsup.num_auth_vectors <= OSMO_GSUP_MAX_NUM_AUTH_INFO)
|
||||
num_auth_vectors = req->gsup.num_auth_vectors;
|
||||
rc = db_ind(g_hlr->dbc, &req->source_name, &auc_3g_ind);
|
||||
if (rc) {
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR,
|
||||
"Unable to determine 3G auth IND for source %s (rc=%d),"
|
||||
" generating tuples with IND = 0\n",
|
||||
osmo_cni_peer_id_to_str(&req->source_name), rc);
|
||||
auc_3g_ind = 0;
|
||||
}
|
||||
|
||||
rc = db_get_auc(g_hlr->dbc, req->gsup.imsi, auc_3g_ind,
|
||||
gsup_out.auth_vectors,
|
||||
num_auth_vectors,
|
||||
req->gsup.rand, req->gsup.auts, separation_bit);
|
||||
|
||||
ARRAY_SIZE(gsup_out.auth_vectors),
|
||||
req->gsup.rand, req->gsup.auts);
|
||||
if (rc <= 0) {
|
||||
switch (rc) {
|
||||
case 0:
|
||||
@@ -331,9 +313,9 @@ static int rx_send_auth_info(struct osmo_gsup_req *req)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
gsup_out.num_auth_vectors = rc;
|
||||
|
||||
osmo_gsup_req_respond(req, &gsup_out, false, true);
|
||||
gsup_out.num_auth_vectors = rc;
|
||||
osmo_gsup_req_respond(req, &gsup_out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -374,7 +356,7 @@ static int rx_purge_ms_req(struct osmo_gsup_req *req)
|
||||
rc = db_subscr_purge(g_hlr->dbc, req->gsup.imsi, true, is_ps);
|
||||
|
||||
if (rc == 0)
|
||||
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_PURGE_MS_RESULT, true);
|
||||
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_PURGE_MS_RESULT);
|
||||
else if (rc == -ENOENT)
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown");
|
||||
else
|
||||
@@ -436,7 +418,7 @@ static int rx_check_imei_req(struct osmo_gsup_req *req)
|
||||
.message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT,
|
||||
.imei_result = OSMO_GSUP_IMEI_RESULT_ACK,
|
||||
};
|
||||
return osmo_gsup_req_respond(req, &gsup_reply, false, true);
|
||||
return osmo_gsup_req_respond(req, &gsup_reply);
|
||||
}
|
||||
|
||||
static char namebuf[255];
|
||||
@@ -455,28 +437,28 @@ static int read_cb_forward(struct osmo_gsup_req *req)
|
||||
const struct osmo_gsup_message *gsup = &req->gsup;
|
||||
struct osmo_gsup_message gsup_err;
|
||||
struct msgb *forward_msg;
|
||||
struct osmo_ipa_name destination_name;
|
||||
struct global_title destination_name;
|
||||
|
||||
/* Check for routing IEs */
|
||||
if (!req->gsup.source_name || !req->gsup.source_name_len
|
||||
|| !req->gsup.destination_name || !req->gsup.destination_name_len) {
|
||||
if (!req->gsup.source_name[0] || !req->gsup.source_name_len
|
||||
|| !req->gsup.destination_name[0] || !req->gsup.destination_name_len) {
|
||||
LOGP_GSUP_FWD(&req->gsup, LOGL_ERROR, "missing routing IEs\n");
|
||||
goto routing_error;
|
||||
}
|
||||
|
||||
if (osmo_ipa_name_set(&destination_name, req->gsup.destination_name, req->gsup.destination_name_len)) {
|
||||
if (global_title_set(&destination_name, req->gsup.destination_name, req->gsup.destination_name_len)) {
|
||||
LOGP_GSUP_FWD(&req->gsup, LOGL_ERROR, "invalid destination name\n");
|
||||
goto routing_error;
|
||||
}
|
||||
|
||||
LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to %s\n", osmo_ipa_name_to_str(&destination_name));
|
||||
LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to %s\n", global_title_name(&destination_name));
|
||||
|
||||
/* Forward message without re-encoding (so we don't remove unknown IEs).
|
||||
* Copy GSUP part to forward, removing incoming IPA header to be able to prepend an outgoing IPA header */
|
||||
forward_msg = osmo_gsup_msgb_alloc("GSUP forward");
|
||||
forward_msg->l2h = msgb_put(forward_msg, msgb_l2len(req->msg));
|
||||
memcpy(forward_msg->l2h, msgb_l2(req->msg), msgb_l2len(req->msg));
|
||||
ret = osmo_gsup_send_to_ipa_name(g_hlr->gs, &destination_name, forward_msg);
|
||||
ret = osmo_gsup_gt_send(g_hlr->gs, &destination_name, forward_msg);
|
||||
if (ret) {
|
||||
LOGP_GSUP_FWD(gsup, LOGL_ERROR, "%s (rc=%d)\n",
|
||||
ret == -ENODEV ? "destination not connected" : "unknown error",
|
||||
@@ -488,27 +470,30 @@ static int read_cb_forward(struct osmo_gsup_req *req)
|
||||
|
||||
routing_error:
|
||||
gsup_err = (struct osmo_gsup_message){
|
||||
.message_type = OSMO_GSUP_MSGT_ROUTING_ERROR,
|
||||
.source_name = gsup->destination_name,
|
||||
.source_name_len = gsup->destination_name_len,
|
||||
.message_type = OSMO_GSUP_MSGT_E_ROUTING_ERROR,
|
||||
.destination_name = gsup->destination_name,
|
||||
.destination_name_len = gsup->destination_name_len,
|
||||
.source_name = gsup->source_name,
|
||||
.source_name_len = gsup->source_name_len,
|
||||
};
|
||||
osmo_gsup_req_respond(req, &gsup_err, true, true);
|
||||
osmo_gsup_req_respond(req, &gsup_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
|
||||
{
|
||||
struct osmo_gsup_req *req = osmo_gsup_conn_rx(conn, msg);
|
||||
struct osmo_gsup_req *req = osmo_gsup_req_new(conn, msg);
|
||||
|
||||
if (!req)
|
||||
return -EINVAL;
|
||||
|
||||
/* If the GSUP recipient is other than this HLR, forward. */
|
||||
if (req->gsup.destination_name_len) {
|
||||
struct osmo_ipa_name destination_name;
|
||||
struct osmo_ipa_name my_name;
|
||||
osmo_ipa_name_set_str(&my_name, g_hlr->gsup_unit_name.serno);
|
||||
if (!osmo_ipa_name_set(&destination_name, req->gsup.destination_name, req->gsup.destination_name_len)
|
||||
&& osmo_ipa_name_cmp(&destination_name, &my_name)) {
|
||||
struct global_title destination_name;
|
||||
struct global_title my_name;
|
||||
global_title_set_str(&my_name, g_hlr->gsup_unit_name.serno);
|
||||
if (!global_title_set(&destination_name, req->gsup.destination_name, req->gsup.destination_name_len)
|
||||
&& global_title_cmp(&destination_name, &my_name)) {
|
||||
return read_cb_forward(req);
|
||||
}
|
||||
}
|
||||
@@ -516,17 +501,15 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
|
||||
/* Distributed GSM: check whether to proxy for / lookup a remote HLR.
|
||||
* It would require less database hits to do this only if a local-only operation fails with "unknown IMSI", but
|
||||
* it becomes semantically easier if we do this once-off ahead of time. */
|
||||
if (osmo_mslookup_client_active(g_hlr->mslookup.client.client)
|
||||
|| osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_gateway_proxy)) {
|
||||
if (osmo_mslookup_client_active(g_hlr->mslookup.client.client)) {
|
||||
if (dgsm_check_forward_gsup_msg(req))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* HLR related messages that are handled at this HLR instance */
|
||||
switch (req->gsup.message_type) {
|
||||
/* requests sent to us */
|
||||
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
|
||||
rx_send_auth_info(req);
|
||||
rx_send_auth_info(conn->auc_3g_ind, req);
|
||||
break;
|
||||
case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
|
||||
rx_upd_loc_req(conn, req);
|
||||
@@ -586,10 +569,6 @@ static void print_help()
|
||||
printf(" -U --db-upgrade Allow HLR database schema upgrades.\n");
|
||||
printf(" -C --db-check Quit after opening (and upgrading) the database.\n");
|
||||
printf(" -V --version Print the version of OsmoHLR.\n");
|
||||
|
||||
printf("\nVTY reference generation:\n");
|
||||
printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
|
||||
printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
|
||||
}
|
||||
|
||||
static struct {
|
||||
@@ -605,37 +584,10 @@ static struct {
|
||||
.db_upgrade = false,
|
||||
};
|
||||
|
||||
static void handle_long_options(const char *prog_name, const int long_option)
|
||||
{
|
||||
static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
|
||||
|
||||
switch (long_option) {
|
||||
case 1:
|
||||
vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
|
||||
if (vty_ref_mode < 0) {
|
||||
fprintf(stderr, "%s: Unknown VTY reference generation "
|
||||
"mode '%s'\n", prog_name, optarg);
|
||||
exit(2);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
|
||||
get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
|
||||
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
|
||||
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
|
||||
exit(0);
|
||||
default:
|
||||
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static int long_option = 0;
|
||||
static struct option long_options[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"config-file", 1, 0, 'c'},
|
||||
@@ -648,8 +600,6 @@ static void handle_options(int argc, char **argv)
|
||||
{"db-upgrade", 0, 0, 'U' },
|
||||
{"db-check", 0, 0, 'C' },
|
||||
{"version", 0, 0, 'V' },
|
||||
{"vty-ref-mode", 1, &long_option, 1},
|
||||
{"vty-ref-xml", 0, &long_option, 2},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
@@ -659,9 +609,6 @@ static void handle_options(int argc, char **argv)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
handle_long_options(argv[0], long_option);
|
||||
break;
|
||||
case 'h':
|
||||
print_usage();
|
||||
print_help();
|
||||
@@ -704,11 +651,6 @@ static void handle_options(int argc, char **argv)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > optind) {
|
||||
fprintf(stderr, "Unsupported positional arguments on command line\n");
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
static void signal_hdlr(int signal)
|
||||
@@ -753,16 +695,16 @@ int main(int argc, char **argv)
|
||||
|
||||
g_hlr = talloc_zero(hlr_ctx, struct hlr);
|
||||
INIT_LLIST_HEAD(&g_hlr->euse_list);
|
||||
INIT_LLIST_HEAD(&g_hlr->iuse_list);
|
||||
INIT_LLIST_HEAD(&g_hlr->ss_sessions);
|
||||
INIT_LLIST_HEAD(&g_hlr->ussd_routes);
|
||||
INIT_LLIST_HEAD(&g_hlr->mslookup.server.local_site_services);
|
||||
g_hlr->db_file_path = talloc_strdup(g_hlr, HLR_DEFAULT_DB_FILE_PATH);
|
||||
g_hlr->mslookup.server.mdns.domain_suffix = talloc_strdup(g_hlr, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT);
|
||||
g_hlr->mslookup.client.mdns.domain_suffix = talloc_strdup(g_hlr, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT);
|
||||
|
||||
/* Init default (call independent) SS session guard timeout value */
|
||||
g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT;
|
||||
|
||||
g_hlr->proxy = proxy_init(g_hlr);
|
||||
|
||||
rc = osmo_init_logging2(hlr_ctx, &hlr_log_info);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error initializing logging\n");
|
||||
@@ -775,10 +717,8 @@ int main(int argc, char **argv)
|
||||
osmo_stats_init(hlr_ctx);
|
||||
vty_init(&vty_info);
|
||||
ctrl_vty_init(hlr_ctx);
|
||||
hlr_vty_init();
|
||||
dgsm_vty_init();
|
||||
osmo_cpu_sched_vty_init(hlr_ctx);
|
||||
handle_options(argc, argv);
|
||||
hlr_vty_init();
|
||||
|
||||
rc = vty_read_config_file(cmdline_opts.config_file, NULL);
|
||||
if (rc < 0) {
|
||||
@@ -828,7 +768,6 @@ int main(int argc, char **argv)
|
||||
LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
|
||||
exit(1);
|
||||
}
|
||||
proxy_init(g_hlr->gs);
|
||||
|
||||
g_hlr->ctrl_bind_addr = ctrl_vty_get_bind_addr();
|
||||
g_hlr->ctrl = hlr_controlif_setup(g_hlr);
|
||||
@@ -851,8 +790,6 @@ int main(int argc, char **argv)
|
||||
while (!quit)
|
||||
osmo_select_main_ctx(0);
|
||||
|
||||
dgsm_stop();
|
||||
|
||||
osmo_gsup_server_destroy(g_hlr->gs);
|
||||
db_close(g_hlr->dbc);
|
||||
log_fini();
|
||||
|
||||
@@ -26,9 +26,7 @@
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
|
||||
#include <osmocom/hlr/dgsm.h>
|
||||
#include "dgsm.h"
|
||||
|
||||
#define HLR_DEFAULT_DB_FILE_PATH "hlr.db"
|
||||
|
||||
@@ -56,6 +54,7 @@ struct hlr {
|
||||
|
||||
struct llist_head euse_list;
|
||||
struct hlr_euse *euse_default;
|
||||
struct llist_head iuse_list;
|
||||
|
||||
/* NCSS (call independent) session guard timeout value */
|
||||
int ncss_guard_timeout;
|
||||
@@ -73,44 +72,21 @@ struct hlr {
|
||||
|
||||
struct {
|
||||
bool allow_startup;
|
||||
struct dgsm_config vty;
|
||||
|
||||
struct {
|
||||
/* Whether the mslookup server should be active in general (all lookup methods) */
|
||||
bool enable;
|
||||
uint32_t local_attach_max_age;
|
||||
struct llist_head local_site_services;
|
||||
struct {
|
||||
/* Whether the mDNS method of the mslookup server should be active. */
|
||||
bool enable;
|
||||
/* The mDNS bind address and domain suffix as set by the VTY, not necessarily in use. */
|
||||
struct osmo_sockaddr_str bind_addr;
|
||||
char *domain_suffix;
|
||||
struct osmo_mslookup_server_mdns *running;
|
||||
} mdns;
|
||||
struct osmo_mslookup_server_mdns *mdns;
|
||||
uint32_t max_age;
|
||||
} server;
|
||||
|
||||
/* The mslookup client in osmo-hlr is used to find out which remote HLRs service a locally unknown IMSI.
|
||||
* (It may also be used to resolve recipients for SMS-over-GSUP in the future.) */
|
||||
struct {
|
||||
/* Whether to proxy/forward to remote HLRs */
|
||||
bool enable;
|
||||
|
||||
/* If this is set, all GSUP for unknown IMSIs is forwarded directly to this GSUP address,
|
||||
* unconditionally. */
|
||||
struct osmo_sockaddr_str gsup_gateway_proxy;
|
||||
|
||||
/* mslookup client request handling */
|
||||
unsigned int result_timeout_milliseconds;
|
||||
|
||||
struct osmo_mslookup_client *client;
|
||||
struct {
|
||||
/* Whether to use mDNS for IMSI MS Lookup */
|
||||
bool enable;
|
||||
struct osmo_sockaddr_str query_addr;
|
||||
char *domain_suffix;
|
||||
struct osmo_mslookup_client_method *running;
|
||||
} mdns;
|
||||
struct osmo_mslookup_client_method *mdns;
|
||||
} client;
|
||||
} mslookup;
|
||||
|
||||
struct proxy *proxy;
|
||||
};
|
||||
|
||||
extern struct hlr *g_hlr;
|
||||
@@ -25,14 +25,13 @@
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/application.h>
|
||||
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include <osmocom/hlr/rand.h>
|
||||
#include "logging.h"
|
||||
#include "db.h"
|
||||
#include "rand.h"
|
||||
|
||||
struct hlr_db_tool_ctx {
|
||||
/* DB context */
|
||||
@@ -71,9 +70,8 @@ static void print_help()
|
||||
printf(" (All commands imply this if none exists yet.)\n");
|
||||
printf("\n");
|
||||
printf(" import-nitb-db <nitb.db> Add OsmoNITB db's subscribers to OsmoHLR db.\n");
|
||||
printf(" Be aware that the import is somewhat lossy, only the IMSI,\n");
|
||||
printf(" MSISDN, IMEI, nam_cs/ps, 2G auth data and last seen LU are set.\n");
|
||||
printf(" The most recently associated IMEI from the Equipment table is used.\n");
|
||||
printf(" Be aware that the import is lossy, only the\n");
|
||||
printf(" IMSI, MSISDN, nam_cs/ps and 2G auth data are set.\n");
|
||||
}
|
||||
|
||||
static void print_version(int print_copyright)
|
||||
@@ -214,15 +212,9 @@ enum nitb_stmt {
|
||||
|
||||
static const char *nitb_stmt_sql[] = {
|
||||
[NITB_SELECT_SUBSCR] =
|
||||
"SELECT s.imsi, s.id, s.extension, s.authorized,"
|
||||
" SUBSTR(e.imei,0,15), STRFTIME('%s', s.expire_lu)"
|
||||
" FROM Subscriber s LEFT JOIN"
|
||||
" (SELECT imei, subscriber_id, MAX(Equipment.updated) AS updated"
|
||||
" FROM Equipment,EquipmentWatch"
|
||||
" WHERE Equipment.id = EquipmentWatch.equipment_id"
|
||||
" GROUP BY EquipmentWatch.subscriber_id) e"
|
||||
" ON e.subscriber_id = s.id"
|
||||
" ORDER by s.id",
|
||||
"SELECT imsi, id, extension, authorized"
|
||||
" FROM Subscriber"
|
||||
" ORDER BY id",
|
||||
[NITB_SELECT_AUTH_KEYS] =
|
||||
"SELECT algorithm_id, a3a8_ki from authkeys"
|
||||
" WHERE subscriber_id = $subscr_id",
|
||||
@@ -230,65 +222,8 @@ static const char *nitb_stmt_sql[] = {
|
||||
|
||||
sqlite3_stmt *nitb_stmt[ARRAY_SIZE(nitb_stmt_sql)] = {};
|
||||
|
||||
enum hlr_db_stmt {
|
||||
HLR_DB_STMT_SET_IMPLICIT_LU_BY_IMSI,
|
||||
};
|
||||
|
||||
static const char *hlr_db_stmt_sql[] = {
|
||||
[HLR_DB_STMT_SET_IMPLICIT_LU_BY_IMSI] =
|
||||
"UPDATE subscriber SET last_lu_seen = datetime($last_lu, 'unixepoch') WHERE imsi = $imsi",
|
||||
};
|
||||
|
||||
sqlite3_stmt *hlr_db_stmt[ARRAY_SIZE(hlr_db_stmt_sql)] = {};
|
||||
|
||||
size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out);
|
||||
|
||||
/*! Set a subscriber's LU timestamp in the HLR database.
|
||||
* In normal operations there is never any need to explicitly
|
||||
* update the value of last_lu_seen, so this function can live here.
|
||||
*
|
||||
* \param[in,out] dbc database context.
|
||||
* \param[in] imsi ASCII string of IMSI digits
|
||||
* \param[in] imei ASCII string of identifier digits, or NULL to remove the IMEI.
|
||||
* \returns 0 on success, -ENOENT when the given subscriber does not exist,
|
||||
* -EIO on database errors.
|
||||
*/
|
||||
int db_subscr_update_lu_by_imsi(struct db_context *dbc, const char* imsi, const int last_lu)
|
||||
{
|
||||
int rc, ret = 0;
|
||||
|
||||
sqlite3_stmt *stmt = hlr_db_stmt[HLR_DB_STMT_SET_IMPLICIT_LU_BY_IMSI];
|
||||
|
||||
if (!db_bind_text(stmt, "$imsi", imsi))
|
||||
return -EIO;
|
||||
if (last_lu && !db_bind_int(stmt, "$last_lu", last_lu))
|
||||
return -EIO;
|
||||
|
||||
/* execute the statement */
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_DONE) {
|
||||
LOGP(DAUC, LOGL_ERROR, "Update last_lu_seen for subscriber IMSI='%s': SQL Error: %s\n", imsi,
|
||||
sqlite3_errmsg(dbc->db));
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* verify execution result */
|
||||
rc = sqlite3_changes(dbc->db);
|
||||
if (!rc) {
|
||||
LOGP(DAUC, LOGL_ERROR, "Cannot update last_lu_seen for subscriber IMSI='%s': no such subscriber\n", imsi);
|
||||
ret = -ENOENT;
|
||||
} else if (rc != 1) {
|
||||
LOGP(DAUC, LOGL_ERROR, "Update last_lu_seen for subscriber IMSI='%s': SQL modified %d rows (expected 1)\n",
|
||||
imsi, rc);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
out:
|
||||
db_remove_reset(stmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void import_nitb_subscr_aud(sqlite3 *nitb_db, const char *imsi, int64_t nitb_id, int64_t hlr_id)
|
||||
{
|
||||
int rc;
|
||||
@@ -362,7 +297,6 @@ void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
|
||||
int64_t imsi;
|
||||
char imsi_str[32];
|
||||
bool authorized;
|
||||
int last_lu_int;
|
||||
|
||||
imsi = sqlite3_column_int64(stmt, 0);
|
||||
|
||||
@@ -381,18 +315,8 @@ void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
|
||||
nitb_id = sqlite3_column_int64(stmt, 1);
|
||||
copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
|
||||
authorized = sqlite3_column_int(stmt, 3) ? true : false;
|
||||
copy_sqlite3_text_to_buf(subscr.imei, stmt, 4);
|
||||
/* Default periodic LU was 30 mins and the expire_lu
|
||||
* was twice that + 1 min
|
||||
*/
|
||||
last_lu_int = sqlite3_column_int(stmt, 5) - 3660;
|
||||
|
||||
db_subscr_update_msisdn_by_imsi(dbc, imsi_str, subscr.msisdn);
|
||||
/* In case the subscriber was somehow never seen, invent an IMEI */
|
||||
if (strlen(subscr.imei) == 14)
|
||||
db_subscr_update_imei_by_imsi(dbc, imsi_str, subscr.imei);
|
||||
db_subscr_update_lu_by_imsi(dbc, imsi_str, last_lu_int);
|
||||
|
||||
db_subscr_nam(dbc, imsi_str, authorized, true);
|
||||
db_subscr_nam(dbc, imsi_str, authorized, false);
|
||||
|
||||
@@ -437,17 +361,6 @@ int import_nitb_db(void)
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hlr_db_stmt_sql); i++) {
|
||||
sql = hlr_db_stmt_sql[i];
|
||||
rc = sqlite3_prepare_v2(g_hlr_db_tool_ctx->dbc->db, hlr_db_stmt_sql[i], -1,
|
||||
&hlr_db_stmt[i], NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
LOGP(DDB, LOGL_ERROR, "OsmoHLR DB: Unable to prepare SQL statement '%s'\n", sql);
|
||||
ret = -1;
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
stmt = nitb_stmt[NITB_SELECT_SUBSCR];
|
||||
|
||||
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
|
||||
@@ -474,7 +387,6 @@ int main(int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
int (*main_action)(void);
|
||||
int i;
|
||||
main_action = NULL;
|
||||
|
||||
g_hlr_db_tool_ctx = talloc_zero(NULL, struct hlr_db_tool_ctx);
|
||||
@@ -518,11 +430,6 @@ int main(int argc, char **argv)
|
||||
if (main_action)
|
||||
rc = (*main_action)();
|
||||
|
||||
/* db_close will only finalize statments in g_hlr_db_tool_ctx->dbc->stmt
|
||||
* it is ok to call finalize on NULL */
|
||||
for (i = 0; i < ARRAY_SIZE(hlr_db_stmt); i++) {
|
||||
sqlite3_finalize(hlr_db_stmt[i]);
|
||||
}
|
||||
db_close(g_hlr_db_tool_ctx->dbc);
|
||||
log_fini();
|
||||
exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
|
||||
119
src/hlr_ussd.c
119
src/hlr_ussd.c
@@ -29,12 +29,12 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/hlr_ussd.h>
|
||||
#include <osmocom/hlr/gsup_server.h>
|
||||
#include <osmocom/hlr/gsup_router.h>
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include "hlr.h"
|
||||
#include "hlr_ussd.h"
|
||||
#include "gsup_server.h"
|
||||
#include "gsup_router.h"
|
||||
#include "logging.h"
|
||||
#include "db.h"
|
||||
|
||||
/***********************************************************************
|
||||
* core data structures expressing config from VTY
|
||||
@@ -170,7 +170,7 @@ struct ss_session {
|
||||
/* subscriber's vlr_number
|
||||
* MO USSD: originating MSC's vlr_number
|
||||
* MT USSD: looked up once per session and cached here */
|
||||
struct osmo_ipa_name vlr_name;
|
||||
struct global_title vlr_name;
|
||||
|
||||
/* we don't keep a pointer to the osmo_gsup_{route,conn} towards the MSC/VLR here,
|
||||
* as this might change during inter-VLR hand-over, and we simply look-up the serving MSC/VLR
|
||||
@@ -243,8 +243,10 @@ static int ss_gsup_send_to_ms(struct ss_session *ss, struct osmo_gsup_server *gs
|
||||
int rc;
|
||||
|
||||
if (ss->initial_req_from_ms) {
|
||||
/* Use non-final osmo_gsup_req_respond() to not deallocate the ss->initial_req_from_ms */
|
||||
osmo_gsup_req_respond(ss->initial_req_from_ms, gsup, false, false);
|
||||
/* If this is a response to an incoming GSUP request from the MS, respond via osmo_gsup_req_respond() to
|
||||
* make sure that all required routing information is kept intact.
|
||||
* Use osmo_gsup_req_respond_nonfinal() to not deallocate the ss->initial_req_from_ms */
|
||||
osmo_gsup_req_respond_nonfinal(ss->initial_req_from_ms, gsup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -252,7 +254,6 @@ static int ss_gsup_send_to_ms(struct ss_session *ss, struct osmo_gsup_server *gs
|
||||
rc = osmo_gsup_encode(msg, gsup);
|
||||
if (rc) {
|
||||
LOGPSS(ss, LOGL_ERROR, "Failed to encode GSUP message\n");
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -264,7 +265,7 @@ static int ss_gsup_send_to_ms(struct ss_session *ss, struct osmo_gsup_server *gs
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
osmo_ipa_name_set_str(&ss->vlr_name, subscr.vlr_number);
|
||||
global_title_set_str(&ss->vlr_name, subscr.vlr_number);
|
||||
}
|
||||
|
||||
/* Check for empty string (all vlr_number strings end in "\0", because otherwise gsup_route_find() fails) */
|
||||
@@ -274,25 +275,25 @@ static int ss_gsup_send_to_ms(struct ss_session *ss, struct osmo_gsup_server *gs
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOGPSS(ss, LOGL_DEBUG, "Tx SS/USSD to VLR %s\n", osmo_ipa_name_to_str(&ss->vlr_name));
|
||||
return osmo_gsup_send_to_ipa_name(gs, &ss->vlr_name, msg);
|
||||
LOGPSS(ss, LOGL_DEBUG, "Tx SS/USSD to VLR %s\n", global_title_name(&ss->vlr_name));
|
||||
return osmo_gsup_gt_send(gs, &ss->vlr_name, msg);
|
||||
}
|
||||
|
||||
static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_msg_type,
|
||||
struct msgb *ss_msg)
|
||||
bool final, struct msgb *ss_msg)
|
||||
|
||||
{
|
||||
struct osmo_gsup_message resp;
|
||||
int rc;
|
||||
|
||||
resp = (struct osmo_gsup_message) {
|
||||
struct osmo_gsup_message resp = {
|
||||
.message_type = gsup_msg_type,
|
||||
.session_id = ss->session_id,
|
||||
.session_state = ss->state,
|
||||
};
|
||||
int rc;
|
||||
|
||||
OSMO_STRLCPY_ARRAY(resp.imsi, ss->imsi);
|
||||
|
||||
if (final)
|
||||
resp.session_state = OSMO_GSUP_SESSION_STATE_END;
|
||||
else
|
||||
resp.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
|
||||
if (ss_msg) {
|
||||
resp.ss_info = msgb_data(ss_msg);
|
||||
resp.ss_info_len = msgb_length(ss_msg);
|
||||
@@ -312,8 +313,7 @@ static int ss_tx_reject(struct ss_session *ss, int invoke_id, uint8_t problem_ta
|
||||
LOGPSS(ss, LOGL_NOTICE, "Tx Reject(%u, 0x%02x, 0x%02x)\n", invoke_id,
|
||||
problem_tag, problem_code);
|
||||
OSMO_ASSERT(msg);
|
||||
ss->state = OSMO_GSUP_SESSION_STATE_END;
|
||||
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, msg);
|
||||
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -322,23 +322,22 @@ static int ss_tx_to_ms_error(struct ss_session *ss, uint8_t invoke_id, uint8_t e
|
||||
struct msgb *msg = gsm0480_gen_return_error(invoke_id, error_code);
|
||||
LOGPSS(ss, LOGL_NOTICE, "Tx ReturnError(%u, 0x%02x)\n", invoke_id, error_code);
|
||||
OSMO_ASSERT(msg);
|
||||
ss->state = OSMO_GSUP_SESSION_STATE_END;
|
||||
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, msg);
|
||||
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
|
||||
}
|
||||
|
||||
static int ss_tx_to_ms_ussd_7bit(struct ss_session *ss, uint8_t invoke_id, const char *text)
|
||||
static int ss_tx_to_ms_ussd_7bit(struct ss_session *ss, bool final, uint8_t invoke_id, const char *text)
|
||||
{
|
||||
struct msgb *msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text);
|
||||
LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text);
|
||||
OSMO_ASSERT(msg);
|
||||
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, msg);
|
||||
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, final, msg);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Internal USSD Handlers
|
||||
***********************************************************************/
|
||||
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include "db.h"
|
||||
|
||||
static int handle_ussd_own_msisdn(struct ss_session *ss,
|
||||
const struct osmo_gsup_message *gsup, const struct ss_request *req)
|
||||
@@ -347,8 +346,6 @@ static int handle_ussd_own_msisdn(struct ss_session *ss,
|
||||
char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
|
||||
int rc;
|
||||
|
||||
ss->state = OSMO_GSUP_SESSION_STATE_END;
|
||||
|
||||
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
@@ -356,7 +353,7 @@ static int handle_ussd_own_msisdn(struct ss_session *ss,
|
||||
snprintf(buf, sizeof(buf), "You have no MSISDN!");
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "Your extension is %s", subscr.msisdn);
|
||||
ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
|
||||
ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, buf);
|
||||
break;
|
||||
case -ENOENT:
|
||||
ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
|
||||
@@ -374,21 +371,7 @@ static int handle_ussd_own_imsi(struct ss_session *ss,
|
||||
{
|
||||
char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
|
||||
snprintf(buf, sizeof(buf), "Your IMSI is %s", ss->imsi);
|
||||
ss->state = OSMO_GSUP_SESSION_STATE_END;
|
||||
ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This handler just keeps the session idle unless the guard timer expires. */
|
||||
static int handle_ussd_test_idle(struct ss_session *ss,
|
||||
const struct osmo_gsup_message *gsup,
|
||||
const struct ss_request *req)
|
||||
{
|
||||
char buf[GSM0480_USSD_7BIT_STRING_LEN + 1];
|
||||
snprintf(buf, sizeof(buf), "Keeping your session idle, it will expire "
|
||||
"at most in %u seconds.", g_hlr->ncss_guard_timeout);
|
||||
ss->state = OSMO_GSUP_SESSION_STATE_CONTINUE;
|
||||
ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
|
||||
ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -402,10 +385,6 @@ static const struct hlr_iuse hlr_iuses[] = {
|
||||
.name = "own-imsi",
|
||||
.handle_ussd = handle_ussd_own_imsi,
|
||||
},
|
||||
{
|
||||
.name = "test-idle",
|
||||
.handle_ussd = handle_ussd_test_idle,
|
||||
},
|
||||
};
|
||||
|
||||
const struct hlr_iuse *iuse_find(const char *name)
|
||||
@@ -439,24 +418,22 @@ static bool ss_op_is_ussd(uint8_t opcode)
|
||||
}
|
||||
|
||||
/* is this GSUP connection an EUSE (true) or not (false)? */
|
||||
static bool peer_name_is_euse(const struct osmo_cni_peer_id *peer_name)
|
||||
static bool peer_name_is_euse(const struct global_title *peer_name)
|
||||
{
|
||||
if (peer_name->type != OSMO_CNI_PEER_ID_IPA_NAME)
|
||||
if (peer_name->len <= 5)
|
||||
return false;
|
||||
if (peer_name->ipa_name.len <= 5)
|
||||
if (!strncmp((char *)(peer_name->val), "EUSE-", 5))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
return strncmp((char *)(peer_name->ipa_name.val), "EUSE-", 5) == 0;
|
||||
}
|
||||
|
||||
static struct hlr_euse *euse_by_name(const struct osmo_cni_peer_id *peer_name)
|
||||
static struct hlr_euse *euse_by_name(const struct global_title *peer_name)
|
||||
{
|
||||
if (!peer_name_is_euse(peer_name))
|
||||
return NULL;
|
||||
|
||||
/* above peer_name_is_euse() ensures this: */
|
||||
OSMO_ASSERT(peer_name->type == OSMO_CNI_PEER_ID_IPA_NAME);
|
||||
|
||||
return euse_find(g_hlr, (const char*)(peer_name->ipa_name.val)+5);
|
||||
return euse_find(g_hlr, (const char*)(peer_name->val)+5);
|
||||
}
|
||||
|
||||
static int handle_ss(struct ss_session *ss, bool is_euse_originated, const struct osmo_gsup_message *gsup,
|
||||
@@ -503,25 +480,26 @@ static int handle_ussd(struct ss_session *ss, bool is_euse_originated, const str
|
||||
/* Received from VLR (MS) */
|
||||
if (ss->is_external) {
|
||||
/* Forward to EUSE */
|
||||
struct osmo_ipa_name euse_name;
|
||||
struct global_title euse_name;
|
||||
struct osmo_gsup_conn *conn;
|
||||
osmo_ipa_name_set_str(&euse_name, "EUSE-%s", ss->u.euse->name);
|
||||
conn = gsup_route_find_by_ipa_name(g_hlr->gs, &euse_name);
|
||||
global_title_set_str(&euse_name, "EUSE-%s", ss->u.euse->name);
|
||||
conn = gsup_route_find_gt(g_hlr->gs, &euse_name);
|
||||
if (!conn) {
|
||||
LOGPSS(ss, LOGL_ERROR, "Cannot find conn for EUSE %s\n",
|
||||
osmo_ipa_name_to_str(&euse_name));
|
||||
global_title_name(&euse_name));
|
||||
ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);
|
||||
} else {
|
||||
msg_out = osmo_gsup_msgb_alloc("GSUP USSD FW");
|
||||
OSMO_ASSERT(msg_out);
|
||||
osmo_gsup_encode(msg_out, gsup);
|
||||
osmo_gsup_conn_send(conn, msg_out);
|
||||
}
|
||||
} else {
|
||||
/* Handle internally */
|
||||
ss->u.iuse->handle_ussd(ss, gsup, req);
|
||||
/* Release session if the handler has changed its state to END */
|
||||
if (ss->state == OSMO_GSUP_SESSION_STATE_END)
|
||||
ss_session_free(ss);
|
||||
/* Release session immediately */
|
||||
ss_session_free(ss);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,14 +523,6 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
|
||||
LOGP(DSS, LOGL_DEBUG, "%s/0x%08x: Process SS (%s)\n", gsup->imsi, gsup->session_id,
|
||||
osmo_gsup_session_state_name(gsup->session_state));
|
||||
|
||||
if (gsup_req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) {
|
||||
LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Unable to process SS request: Unsupported GSUP peer id type%s\n",
|
||||
gsup->imsi, gsup->session_id,
|
||||
osmo_cni_peer_id_type_name(gsup_req->source_name.type));
|
||||
osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_PROTO_ERR_UNSPEC, "error processing SS request");
|
||||
return;
|
||||
}
|
||||
|
||||
/* decode and find out what kind of SS message it is */
|
||||
if (gsup->ss_info && gsup->ss_info_len) {
|
||||
if (gsm0480_parse_facility_ie(gsup->ss_info, gsup->ss_info_len, &req)) {
|
||||
@@ -590,8 +560,7 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
|
||||
if (!is_euse_originated) {
|
||||
ss->initial_req_from_ms = gsup_req;
|
||||
free_gsup_req = NULL;
|
||||
OSMO_ASSERT(gsup_req->source_name.type == OSMO_CNI_PEER_ID_IPA_NAME); /* checked above */
|
||||
ss->vlr_name = gsup_req->source_name.ipa_name;
|
||||
ss->vlr_name = gsup_req->source_name;
|
||||
} else {
|
||||
ss->initial_req_from_euse = gsup_req;
|
||||
free_gsup_req = NULL;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
|
||||
#include <osmocom/hlr/gsup_server.h>
|
||||
#include "gsup_server.h"
|
||||
|
||||
#define NCSS_GUARD_TIMEOUT_DEFAULT 30
|
||||
|
||||
@@ -33,12 +33,13 @@
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/abis/ipa.h>
|
||||
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/hlr_vty.h>
|
||||
#include <osmocom/hlr/hlr_vty_subscr.h>
|
||||
#include <osmocom/hlr/hlr_ussd.h>
|
||||
#include <osmocom/hlr/gsup_server.h>
|
||||
#include "db.h"
|
||||
#include "hlr.h"
|
||||
#include "hlr_vty.h"
|
||||
#include "hlr_vty_subscr.h"
|
||||
#include "hlr_ussd.h"
|
||||
#include "gsup_server.h"
|
||||
#include "dgsm.h"
|
||||
|
||||
struct cmd_node hlr_node = {
|
||||
HLR_NODE,
|
||||
@@ -102,8 +103,6 @@ static int config_write_hlr_gsup(struct vty *vty)
|
||||
vty_out(vty, " gsup%s", VTY_NEWLINE);
|
||||
if (g_hlr->gsup_bind_addr)
|
||||
vty_out(vty, " bind ip %s%s", g_hlr->gsup_bind_addr, VTY_NEWLINE);
|
||||
if (g_hlr->gsup_unit_name.serno)
|
||||
vty_out(vty, " ipa-name %s%s", g_hlr->gsup_unit_name.serno, VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -116,8 +115,8 @@ static void show_one_conn(struct vty *vty, const struct osmo_gsup_conn *conn)
|
||||
rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &name, IPAC_IDTAG_SERNR);
|
||||
OSMO_ASSERT(rc);
|
||||
|
||||
vty_out(vty, " '%s' from %s:%5u, CS=%u, PS=%u%s",
|
||||
name, isc->addr, isc->port, conn->supports_cs, conn->supports_ps,
|
||||
vty_out(vty, " '%s' from %s:%5u, CS=%u, PS=%u, 3G_IND=%u%s",
|
||||
name, isc->addr, isc->port, conn->supports_cs, conn->supports_ps, conn->auc_3g_ind,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
@@ -170,17 +169,16 @@ DEFUN(cfg_hlr_gsup_ipa_name,
|
||||
* USSD Entity
|
||||
***********************************************************************/
|
||||
|
||||
#include <osmocom/hlr/hlr_ussd.h>
|
||||
#include "hlr_ussd.h"
|
||||
|
||||
#define USSD_STR "USSD Configuration\n"
|
||||
#define UROUTE_STR "Routing Configuration\n"
|
||||
#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
|
||||
|
||||
#define INT_CHOICE "(own-msisdn|own-imsi|test-idle)"
|
||||
#define INT_CHOICE "(own-msisdn|own-imsi)"
|
||||
#define INT_STR "Internal USSD Handler\n" \
|
||||
"Respond with subscribers' own MSISDN\n" \
|
||||
"Respond with subscribers' own IMSI\n" \
|
||||
"Keep the session idle (useful for testing)\n"
|
||||
"Respond with subscribers' own IMSI\n"
|
||||
|
||||
#define EXT_STR "External USSD Handler\n" \
|
||||
"Name of External USSD Handler (IPA CCM ID)\n"
|
||||
@@ -308,7 +306,7 @@ DEFUN(cfg_no_euse, cfg_no_euse_cmd,
|
||||
{
|
||||
struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
|
||||
if (!euse) {
|
||||
vty_out(vty, "%% Cannot remove non-existent EUSE %s%s", argv[0], VTY_NEWLINE);
|
||||
vty_out(vty, "%% Cannot remove non-existant EUSE %s%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
if (g_hlr->euse_default == euse) {
|
||||
@@ -424,6 +422,17 @@ int hlr_vty_go_parent(struct vty *vty)
|
||||
vty->index = NULL;
|
||||
vty->index_sub = NULL;
|
||||
break;
|
||||
case MSLOOKUP_CLIENT_NODE:
|
||||
case MSLOOKUP_SERVER_NODE:
|
||||
vty->node = CONFIG_NODE;
|
||||
vty->index = NULL;
|
||||
vty->index_sub = NULL;
|
||||
break;
|
||||
case MSLOOKUP_SERVER_MSC_NODE:
|
||||
vty->node = CONFIG_NODE;
|
||||
vty->index = NULL;
|
||||
vty->index_sub = NULL;
|
||||
break;
|
||||
default:
|
||||
case HLR_NODE:
|
||||
vty->node = CONFIG_NODE;
|
||||
@@ -484,4 +493,5 @@ void hlr_vty_init(void)
|
||||
install_element(HLR_NODE, &cfg_no_subscr_create_on_demand_cmd);
|
||||
|
||||
hlr_vty_subscriber_init();
|
||||
dgsm_vty_init();
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include "hlr.h"
|
||||
|
||||
enum hlr_vty_node {
|
||||
HLR_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
@@ -40,4 +40,3 @@ enum hlr_vty_node {
|
||||
int hlr_vty_is_config_node(struct vty *vty, int node);
|
||||
int hlr_vty_go_parent(struct vty *vty);
|
||||
void hlr_vty_init(void);
|
||||
void dgsm_vty_init(void);
|
||||
@@ -28,49 +28,23 @@
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include <osmocom/hlr/timestamp.h>
|
||||
#include "hlr.h"
|
||||
#include "db.h"
|
||||
|
||||
struct vty;
|
||||
|
||||
#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
|
||||
|
||||
static char *get_datestr(const time_t *t, char *buf, size_t bufsize)
|
||||
static char *
|
||||
get_datestr(const time_t *t, char *datebuf)
|
||||
{
|
||||
struct tm tm;
|
||||
gmtime_r(t, &tm);
|
||||
strftime(buf, bufsize, "%FT%T+00:00", &tm);
|
||||
return buf;
|
||||
}
|
||||
char *p, *s = ctime_r(t, datebuf);
|
||||
|
||||
static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen, bool only_age)
|
||||
{
|
||||
uint32_t age;
|
||||
char datebuf[32];
|
||||
if (!last_lu_seen)
|
||||
return;
|
||||
if (!only_age)
|
||||
vty_out(vty, " last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));
|
||||
if (!timestamp_age(&last_lu_seen, &age))
|
||||
vty_out(vty, " (invalid timestamp)%s", VTY_NEWLINE);
|
||||
else {
|
||||
vty_out(vty, " (");
|
||||
#define UNIT_AGO(UNITNAME, UNITVAL) \
|
||||
if (age >= (UNITVAL)) { \
|
||||
vty_out(vty, "%u%s", age / (UNITVAL), UNITNAME); \
|
||||
age = age % (UNITVAL); \
|
||||
}
|
||||
UNIT_AGO("d", 60*60*24);
|
||||
UNIT_AGO("h", 60*60);
|
||||
UNIT_AGO("m", 60);
|
||||
UNIT_AGO("s", 1);
|
||||
if (!only_age)
|
||||
vty_out(vty, " ago)%s", VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " ago)");
|
||||
#undef UNIT_AGO
|
||||
}
|
||||
/* Strip trailing newline. */
|
||||
p = strchr(s, '\n');
|
||||
if (p)
|
||||
*p = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
|
||||
@@ -78,6 +52,7 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
|
||||
int rc;
|
||||
struct osmo_sub_auth_data aud2g;
|
||||
struct osmo_sub_auth_data aud3g;
|
||||
char datebuf[26]; /* for ctime_r(3) */
|
||||
|
||||
vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
|
||||
|
||||
@@ -112,8 +87,8 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
|
||||
vty_out(vty, " PS disabled%s", VTY_NEWLINE);
|
||||
if (subscr->ms_purged_ps)
|
||||
vty_out(vty, " PS purged%s", VTY_NEWLINE);
|
||||
dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, false);
|
||||
dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps, false);
|
||||
if (subscr->last_lu_seen)
|
||||
vty_out(vty, " last LU seen: %s UTC%s", get_datestr(&subscr->last_lu_seen, datebuf), VTY_NEWLINE);
|
||||
|
||||
if (!*subscr->imsi)
|
||||
return;
|
||||
@@ -163,28 +138,6 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
|
||||
}
|
||||
}
|
||||
|
||||
static void subscr_dump_summary_vty(struct hlr_subscriber *subscr, void *data)
|
||||
{
|
||||
struct vty *vty = data;
|
||||
vty_out(vty, "%-5"PRIu64" %-12s %-16s", subscr->id,
|
||||
*subscr->msisdn ? subscr->msisdn : "none",
|
||||
*subscr->imsi ? subscr->imsi : "none");
|
||||
|
||||
if (*subscr->imei) {
|
||||
char checksum = osmo_luhn(subscr->imei, 14);
|
||||
if (checksum == -EINVAL)
|
||||
vty_out(vty, " %-14s (INVALID LENGTH!)", subscr->imei);
|
||||
else
|
||||
vty_out(vty, " %-14s%c", subscr->imei, checksum);
|
||||
} else {
|
||||
vty_out(vty," ------------- ");
|
||||
}
|
||||
vty_out(vty, " %-2s%-2s ", subscr->nam_cs ? "CS" : "", subscr->nam_ps ? "PS" : "");
|
||||
if (subscr->last_lu_seen)
|
||||
dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, true);
|
||||
vty_out_newline(vty);
|
||||
}
|
||||
|
||||
static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
|
||||
{
|
||||
char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
|
||||
@@ -212,52 +165,10 @@ static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void dump_summary_table_vty(struct vty *vty, bool header, bool show_ls)
|
||||
{
|
||||
const char *texts = "ID MSISDN IMSI IMEI NAM";
|
||||
const char *lines = "----- ------------ ---------------- ---------------- -----";
|
||||
const char *ls_text = " LAST SEEN";
|
||||
const char *ls_line = " ------------";
|
||||
if (header) {
|
||||
if (!show_ls)
|
||||
vty_out(vty, "%s%s%s%s", texts, VTY_NEWLINE, lines, VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, "%s%s%s%s%s%s", texts, ls_text, VTY_NEWLINE, lines, ls_line, VTY_NEWLINE);
|
||||
} else {
|
||||
if (!show_ls)
|
||||
vty_out(vty, "%s%s%s%s", lines, VTY_NEWLINE, texts, VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, "%s%s%s%s%s%s", lines, ls_line, VTY_NEWLINE, texts, ls_text, VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_subscrs(struct vty *vty, const char *filter_type, const char *filter)
|
||||
{
|
||||
int rc = -1;
|
||||
int count = 0;
|
||||
const char *err;
|
||||
bool show_ls = (filter_type && strcmp(filter_type, "last_lu_seen") == 0);
|
||||
dump_summary_table_vty(vty, true, show_ls);
|
||||
rc = db_subscrs_get(g_hlr->dbc, filter_type, filter, subscr_dump_summary_vty, vty, &count, &err);
|
||||
if (count > 40) {
|
||||
dump_summary_table_vty(vty, false, show_ls);
|
||||
}
|
||||
if (count > 0)
|
||||
vty_out(vty, " Subscribers Shown: %d%s", count, VTY_NEWLINE);
|
||||
if (rc)
|
||||
vty_out(vty, "%% %s%s", err, VTY_NEWLINE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#define SUBSCR_CMD "subscriber "
|
||||
#define SUBSCR_CMD_HELP "Subscriber management commands\n"
|
||||
#define SUBSCR_SHOW_HELP "Show subscriber information\n"
|
||||
#define SUBSCRS_SHOW_HELP "Show all subscribers (with filter possibility)\n"
|
||||
|
||||
#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
|
||||
#define SUBSCR_FILTER "(imei|imsi|msisdn) FILTER"
|
||||
|
||||
#define SUBSCR_ID_HELP \
|
||||
"Identify subscriber by IMSI\n" \
|
||||
"Identify subscriber by MSISDN (phone number)\n" \
|
||||
@@ -275,7 +186,7 @@ static int get_subscrs(struct vty *vty, const char *filter_type, const char *fil
|
||||
DEFUN(subscriber_show,
|
||||
subscriber_show_cmd,
|
||||
SUBSCR "show",
|
||||
SUBSCR_HELP SUBSCR_SHOW_HELP)
|
||||
SUBSCR_HELP "Show subscriber information\n")
|
||||
{
|
||||
struct hlr_subscriber subscr;
|
||||
const char *id_type = argv[0];
|
||||
@@ -290,50 +201,7 @@ DEFUN(subscriber_show,
|
||||
|
||||
ALIAS(subscriber_show, show_subscriber_cmd,
|
||||
"show " SUBSCR_CMD SUBSCR_ID,
|
||||
SHOW_STR SUBSCR_SHOW_HELP SUBSCR_ID_HELP);
|
||||
|
||||
DEFUN(show_subscriber_all,
|
||||
show_subscriber_all_cmd,
|
||||
"show subscribers all",
|
||||
SHOW_STR SUBSCRS_SHOW_HELP "Show summary of all subscribers\n")
|
||||
{
|
||||
if (get_subscrs(vty, NULL, NULL))
|
||||
return CMD_WARNING;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_subscriber_filtered,
|
||||
show_subscriber_filtered_cmd,
|
||||
"show subscribers " SUBSCR_FILTER,
|
||||
SHOW_STR SUBSCRS_SHOW_HELP
|
||||
"Filter Subscribers by IMEI\n" "Filter Subscribers by IMSI\n" "Filter Subscribers by MSISDN\n"
|
||||
"String to match in imei, imsi or msisdn\n")
|
||||
{
|
||||
const char *filter_type = argv[0];
|
||||
const char *filter = argv[1];
|
||||
|
||||
if (get_subscrs(vty, filter_type, filter))
|
||||
return CMD_WARNING;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
ALIAS(show_subscriber_filtered, show_subscriber_filtered_cmd2,
|
||||
"show subscribers (cs|ps) (on|off)",
|
||||
SHOW_STR SUBSCR_SHOW_HELP
|
||||
"Filter Subscribers by CS Network Access Mode\n" "Filter Subscribers by PS Network Access Mode\n"
|
||||
"Authorised\n" "Not Authorised\n");
|
||||
|
||||
DEFUN(show_subscriber_order_last_seen, show_subscriber_order_last_seen_cmd,
|
||||
"show subscribers last-seen",
|
||||
SHOW_STR SUBSCR_SHOW_HELP "Show Subscribers Ordered by Last Seen Time\n")
|
||||
{
|
||||
if (get_subscrs(vty, "last_lu_seen", NULL))
|
||||
return CMD_WARNING;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
|
||||
|
||||
DEFUN(subscriber_create,
|
||||
subscriber_create_cmd,
|
||||
@@ -662,55 +530,6 @@ DEFUN(subscriber_aud3g,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(subscriber_aud3g_xor,
|
||||
subscriber_aud3g_xor_cmd,
|
||||
SUBSCR_UPDATE "aud3g xor k K"
|
||||
" [ind-bitlen] [<0-28>]",
|
||||
SUBSCR_UPDATE_HELP
|
||||
"Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
|
||||
"Use XOR algorithm\n"
|
||||
"Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
|
||||
"Set IND bit length\n" "IND bit length value (default: 5)\n")
|
||||
{
|
||||
struct hlr_subscriber subscr;
|
||||
int minlen = 0;
|
||||
int maxlen = 0;
|
||||
int rc;
|
||||
const char *id_type = argv[0];
|
||||
const char *id = argv[1];
|
||||
const char *k = argv[2];
|
||||
int ind_bitlen = argc > 4? atoi(argv[4]) : 5;
|
||||
struct sub_auth_data_str aud3g = {
|
||||
.type = OSMO_AUTH_TYPE_UMTS,
|
||||
.u.umts = {
|
||||
.k = k,
|
||||
.opc_is_op = 0,
|
||||
.opc = "00000000000000000000000000000000",
|
||||
.ind_bitlen = ind_bitlen,
|
||||
},
|
||||
};
|
||||
|
||||
if (!auth_algo_parse("xor", &aud3g.algo, &minlen, &maxlen)) {
|
||||
vty_out(vty, "%% Unknown auth algorithm: '%s'%s", "xor", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
|
||||
return CMD_WARNING;
|
||||
|
||||
if (get_subscr_by_argv(vty, id_type, id, &subscr))
|
||||
return CMD_WARNING;
|
||||
|
||||
rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
|
||||
|
||||
if (rc) {
|
||||
vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
|
||||
subscr.imsi, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(subscriber_imei,
|
||||
subscriber_imei_cmd,
|
||||
SUBSCR_UPDATE "imei (none|IMEI)",
|
||||
@@ -788,10 +607,6 @@ DEFUN(subscriber_nam,
|
||||
|
||||
void hlr_vty_subscriber_init(void)
|
||||
{
|
||||
install_element_ve(&show_subscriber_all_cmd);
|
||||
install_element_ve(&show_subscriber_filtered_cmd);
|
||||
install_element_ve(&show_subscriber_filtered_cmd2);
|
||||
install_element_ve(&show_subscriber_order_last_seen_cmd);
|
||||
install_element_ve(&subscriber_show_cmd);
|
||||
install_element_ve(&show_subscriber_cmd);
|
||||
install_element(ENABLE_NODE, &subscriber_create_cmd);
|
||||
@@ -801,7 +616,6 @@ void hlr_vty_subscriber_init(void)
|
||||
install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
|
||||
install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
|
||||
install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
|
||||
install_element(ENABLE_NODE, &subscriber_aud3g_xor_cmd);
|
||||
install_element(ENABLE_NODE, &subscriber_imei_cmd);
|
||||
install_element(ENABLE_NODE, &subscriber_nam_cmd);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include "logging.h"
|
||||
|
||||
const struct log_info_cat hlr_log_info_cat[] = {
|
||||
[DMAIN] = {
|
||||
@@ -25,9 +25,9 @@ const struct log_info_cat hlr_log_info_cat[] = {
|
||||
.color = "\033[1;34m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DMSLOOKUP] = {
|
||||
.name = "DMSLOOKUP",
|
||||
.description = "Mobile Subscriber Lookup",
|
||||
[DDGSM] = {
|
||||
.name = "DDGSM",
|
||||
.description = "Distributed GSM: MS lookup and proxy",
|
||||
.color = "\033[1;35m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
@@ -37,12 +37,7 @@ const struct log_info_cat hlr_log_info_cat[] = {
|
||||
.color = "\033[1;33m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DDGSM] = {
|
||||
.name = "DDGSM",
|
||||
.description = "Distributed GSM: MS lookup and proxy",
|
||||
.color = "\033[1;35m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
const struct log_info hlr_log_info = {
|
||||
|
||||
@@ -8,9 +8,8 @@ enum {
|
||||
DGSUP,
|
||||
DAUC,
|
||||
DSS,
|
||||
DMSLOOKUP,
|
||||
DLU,
|
||||
DDGSM,
|
||||
DLU,
|
||||
};
|
||||
|
||||
extern const struct log_info hlr_log_info;
|
||||
65
src/lu_fsm.c
65
src/lu_fsm.c
@@ -1,23 +1,4 @@
|
||||
/* Roughly following "Process Update_Location_HLR" of TS 09.02 */
|
||||
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
@@ -26,13 +7,11 @@
|
||||
#include <osmocom/gsm/apn.h>
|
||||
#include <osmocom/gsm/gsm48_ie.h>
|
||||
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_req.h>
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/gsup_server.h>
|
||||
#include "logging.h"
|
||||
#include "hlr.h"
|
||||
#include "gsup_server.h"
|
||||
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include "db.h"
|
||||
|
||||
#define LOG_LU(lu, level, fmt, args...) \
|
||||
LOGPFSML((lu)? (lu)->fi : NULL, level, fmt, ##args)
|
||||
@@ -52,11 +31,11 @@ struct lu {
|
||||
bool is_ps;
|
||||
|
||||
/* VLR requesting the LU. */
|
||||
struct osmo_cni_peer_id vlr_name;
|
||||
struct global_title vlr_name;
|
||||
|
||||
/* If the LU request was received via a proxy and not immediately from a local VLR, this indicates the closest
|
||||
* peer that forwarded the GSUP message. */
|
||||
struct osmo_cni_peer_id via_proxy;
|
||||
struct global_title via_proxy;
|
||||
};
|
||||
LLIST_HEAD(g_all_lu);
|
||||
|
||||
@@ -88,7 +67,7 @@ static void lu_success(struct lu *lu)
|
||||
if (!lu->update_location_req)
|
||||
LOG_LU(lu, LOGL_ERROR, "No request for this LU\n");
|
||||
else
|
||||
osmo_gsup_req_respond_msgt(lu->update_location_req, OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT, true);
|
||||
osmo_gsup_req_respond_msgt(lu->update_location_req, OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT);
|
||||
lu->update_location_req = NULL;
|
||||
osmo_fsm_inst_term(lu->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
@@ -130,7 +109,7 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
|
||||
|
||||
osmo_fsm_inst_update_id_f_sanitize(fi, '_', "%s:IMSI-%s", lu->is_ps ? "PS" : "CS", update_location_req->gsup.imsi);
|
||||
|
||||
if (osmo_cni_peer_id_is_empty(&lu->vlr_name)) {
|
||||
if (!lu->vlr_name.len) {
|
||||
lu_failure(lu, GMM_CAUSE_NET_FAIL, "LU without a VLR");
|
||||
return;
|
||||
}
|
||||
@@ -154,7 +133,7 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
|
||||
/* TODO: Set subscriber tracing = deactive in VLR/SGSN */
|
||||
|
||||
#if 0
|
||||
/* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old (FIXME: OS#4491) */
|
||||
/* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
|
||||
if (!lu->is_ps && strcmp(subscr->vlr_number, vlr_number)) {
|
||||
lu_op_tx_cancel_old(lu);
|
||||
} else if (lu->is_ps && strcmp(subscr->sgsn_number, sgsn_number)) {
|
||||
@@ -163,30 +142,18 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
|
||||
#endif
|
||||
|
||||
/* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */
|
||||
if (!osmo_cni_peer_id_is_empty(&lu->via_proxy)) {
|
||||
if (lu->via_proxy.len) {
|
||||
LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s, via proxy %s\n",
|
||||
lu->is_ps ? "SGSN number" : "VLR number",
|
||||
osmo_cni_peer_id_to_str(&lu->vlr_name),
|
||||
osmo_cni_peer_id_to_str(&lu->via_proxy));
|
||||
global_title_name(&lu->vlr_name),
|
||||
global_title_name(&lu->via_proxy));
|
||||
} else {
|
||||
LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s\n",
|
||||
lu->is_ps ? "SGSN number" : "VLR number",
|
||||
osmo_cni_peer_id_to_str(&lu->vlr_name));
|
||||
global_title_name(&lu->vlr_name));
|
||||
}
|
||||
|
||||
if (osmo_cni_peer_id_is_empty(&lu->vlr_name)
|
||||
|| (lu->vlr_name.type != OSMO_CNI_PEER_ID_IPA_NAME)) {
|
||||
lu_failure(lu, GMM_CAUSE_PROTO_ERR_UNSPEC, "Unsupported GSUP peer id type for vlr_name: %s",
|
||||
osmo_cni_peer_id_type_name(lu->vlr_name.type));
|
||||
return;
|
||||
}
|
||||
if (!osmo_cni_peer_id_is_empty(&lu->via_proxy) && (lu->via_proxy.type != OSMO_CNI_PEER_ID_IPA_NAME)) {
|
||||
lu_failure(lu, GMM_CAUSE_PROTO_ERR_UNSPEC, "Unsupported GSUP peer id type for via_proxy: %s",
|
||||
osmo_cni_peer_id_type_name(lu->via_proxy.type));
|
||||
return;
|
||||
}
|
||||
if (db_subscr_lu(g_hlr->dbc, lu->subscr.id, &lu->vlr_name.ipa_name, lu->is_ps,
|
||||
osmo_cni_peer_id_is_empty(&lu->via_proxy)? NULL : &lu->via_proxy.ipa_name)) {
|
||||
if (db_subscr_lu(g_hlr->dbc, lu->subscr.id, &lu->vlr_name, lu->is_ps, &lu->via_proxy)) {
|
||||
lu_failure(lu, GMM_CAUSE_NET_FAIL, "Cannot update %s in the database",
|
||||
lu->is_ps ? "SGSN number" : "VLR number");
|
||||
return;
|
||||
@@ -252,7 +219,7 @@ static void lu_fsm_wait_insert_data_result_onenter(struct osmo_fsm_inst *fi, uin
|
||||
return;
|
||||
}
|
||||
|
||||
if (osmo_gsup_req_respond(lu->update_location_req, &gsup, false, false))
|
||||
if (osmo_gsup_req_respond_nonfinal(lu->update_location_req, &gsup))
|
||||
lu_failure(lu, GMM_CAUSE_NET_FAIL, "cannot send %s", osmo_gsup_message_type_name(gsup.message_type));
|
||||
}
|
||||
|
||||
|
||||
2
src/lu_fsm.h
Normal file
2
src/lu_fsm.h
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
void lu_rx_gsup(struct osmo_gsup_req *req);
|
||||
@@ -1,37 +0,0 @@
|
||||
# This is _NOT_ the library release version, it's an API version.
|
||||
# Please read chapter "Library interface versions" of the libtool documentation
|
||||
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
|
||||
LIBVERSION=0:0:0
|
||||
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include
|
||||
AM_CFLAGS = -fPIC -Wall $(PCSC_CFLAGS) $(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS)
|
||||
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
|
||||
|
||||
lib_LTLIBRARIES = libosmo-mslookup.la
|
||||
|
||||
libosmo_mslookup_la_SOURCES = \
|
||||
mdns.c \
|
||||
mdns_msg.c \
|
||||
mdns_rfc.c \
|
||||
mdns_sock.c \
|
||||
mslookup.c \
|
||||
mslookup_client.c \
|
||||
mslookup_client_fake.c \
|
||||
mslookup_client_mdns.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mslookup_la_LDFLAGS = -version-info $(LIBVERSION)
|
||||
libosmo_mslookup_la_LIBADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(TALLOC_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
bin_PROGRAMS = osmo-mslookup-client
|
||||
osmo_mslookup_client_SOURCES = osmo-mslookup-client.c
|
||||
osmo_mslookup_client_LDADD = \
|
||||
libosmo-mslookup.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(TALLOC_LIBS) \
|
||||
$(NULL)
|
||||
osmo_mslookup_client_CFLAGS = $(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS)
|
||||
@@ -1,425 +0,0 @@
|
||||
/* mslookup specific functions for encoding and decoding mslookup queries/results into mDNS packets, using the high
|
||||
* level functions from mdns_msg.c and mdns_record.c to build the request/answer messages. */
|
||||
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/mslookup/mslookup.h>
|
||||
#include <osmocom/mslookup/mdns_msg.h>
|
||||
#include <osmocom/mslookup/mdns_rfc.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
static struct msgb *osmo_mdns_msgb_alloc(const char *label)
|
||||
{
|
||||
return msgb_alloc(1024, label);
|
||||
}
|
||||
|
||||
/*! Combine the mslookup query service, ID and ID type into a domain string.
|
||||
* \param[in] domain_suffix is appended to each domain in the queries to avoid colliding with the top-level domains
|
||||
* administrated by IANA. Example: "mdns.osmocom.org"
|
||||
* \returns allocated buffer with the resulting domain (i.e. "sip.voice.123.msisdn.mdns.osmocom.org") on success,
|
||||
* NULL on failure.
|
||||
*/
|
||||
static char *domain_from_query(void *ctx, const struct osmo_mslookup_query *query, const char *domain_suffix)
|
||||
{
|
||||
const char *id;
|
||||
|
||||
/* Get id from query */
|
||||
switch (query->id.type) {
|
||||
case OSMO_MSLOOKUP_ID_IMSI:
|
||||
id = query->id.imsi;
|
||||
break;
|
||||
case OSMO_MSLOOKUP_ID_MSISDN:
|
||||
id = query->id.msisdn;
|
||||
break;
|
||||
default:
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "can't encode mslookup query id type %i", query->id.type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return talloc_asprintf(ctx, "%s.%s.%s.%s", query->service, id, osmo_mslookup_id_type_name(query->id.type),
|
||||
domain_suffix);
|
||||
}
|
||||
|
||||
/*! Split up query service, ID and ID type from a domain string into a mslookup query.
|
||||
* \param[in] domain with domain_suffix, e.g. "sip.voice.123.msisdn.mdns.osmocom.org"
|
||||
* \param[in] domain_suffix is appended to each domain in the queries to avoid colliding with the top-level domains
|
||||
* administrated by IANA. It is not part of the resulting struct osmo_mslookup_query, so we
|
||||
* remove it in this function. Example: "mdns.osmocom.org"
|
||||
*/
|
||||
int query_from_domain(struct osmo_mslookup_query *query, const char *domain, const char *domain_suffix)
|
||||
{
|
||||
int domain_len = strlen(domain) - strlen(domain_suffix) - 1;
|
||||
char domain_buf[OSMO_MDNS_RFC_MAX_NAME_LEN];
|
||||
|
||||
if (domain_len <= 0 || domain_len >= sizeof(domain_buf))
|
||||
return -EINVAL;
|
||||
|
||||
if (domain[domain_len] != '.' || strcmp(domain + domain_len + 1, domain_suffix) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(domain_buf, domain, domain_len);
|
||||
domain_buf[domain_len] = '\0';
|
||||
return osmo_mslookup_query_init_from_domain_str(query, domain_buf);
|
||||
}
|
||||
|
||||
/*! Encode a mslookup query into a mDNS packet.
|
||||
* \param[in] domain_suffix is appended to each domain in the queries to avoid colliding with the top-level domains
|
||||
* administrated by IANA. Example: "mdns.osmocom.org"
|
||||
* \returns msgb, or NULL on error.
|
||||
*/
|
||||
struct msgb *osmo_mdns_query_encode(void *ctx, uint16_t packet_id, const struct osmo_mslookup_query *query,
|
||||
const char *domain_suffix)
|
||||
{
|
||||
struct osmo_mdns_msg_request req = {0};
|
||||
struct msgb *msg = osmo_mdns_msgb_alloc(__func__);
|
||||
|
||||
req.id = packet_id;
|
||||
req.type = OSMO_MDNS_RFC_RECORD_TYPE_ALL;
|
||||
req.domain = domain_from_query(ctx, query, domain_suffix);
|
||||
if (!req.domain)
|
||||
goto error;
|
||||
if (osmo_mdns_msg_request_encode(ctx, msg, &req))
|
||||
goto error;
|
||||
talloc_free(req.domain);
|
||||
return msg;
|
||||
error:
|
||||
msgb_free(msg);
|
||||
talloc_free(req.domain);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Decode a mDNS request packet into a mslookup query.
|
||||
* \param[out] packet_id the result must be sent with the same packet_id.
|
||||
* \param[in] domain_suffix is appended to each domain in the queries to avoid colliding with the top-level domains
|
||||
* administrated by IANA. Example: "mdns.osmocom.org"
|
||||
* \returns allocated mslookup query on success, NULL on error.
|
||||
*/
|
||||
struct osmo_mslookup_query *osmo_mdns_query_decode(void *ctx, const uint8_t *data, size_t data_len,
|
||||
uint16_t *packet_id, const char *domain_suffix)
|
||||
{
|
||||
struct osmo_mdns_msg_request *req = NULL;
|
||||
struct osmo_mslookup_query *query = NULL;
|
||||
|
||||
req = osmo_mdns_msg_request_decode(ctx, data, data_len);
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
query = talloc_zero(ctx, struct osmo_mslookup_query);
|
||||
OSMO_ASSERT(query);
|
||||
if (query_from_domain(query, req->domain, domain_suffix) < 0)
|
||||
goto error_free;
|
||||
|
||||
*packet_id = req->id;
|
||||
talloc_free(req);
|
||||
return query;
|
||||
error_free:
|
||||
talloc_free(req);
|
||||
talloc_free(query);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Parse sockaddr_str from mDNS record, so the mslookup result can be filled with it.
|
||||
* \param[out] sockaddr_str resulting IPv4 or IPv6 sockaddr_str.
|
||||
* \param[in] rec single record of the abstracted list of mDNS records
|
||||
* \returns 0 on success, -EINVAL on error.
|
||||
*/
|
||||
static int sockaddr_str_from_mdns_record(struct osmo_sockaddr_str *sockaddr_str, struct osmo_mdns_record *rec)
|
||||
{
|
||||
switch (rec->type) {
|
||||
case OSMO_MDNS_RFC_RECORD_TYPE_A:
|
||||
if (rec->length != 4) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "unexpected length of A record\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
osmo_sockaddr_str_from_32(sockaddr_str, *(uint32_t *)rec->data, 0);
|
||||
break;
|
||||
case OSMO_MDNS_RFC_RECORD_TYPE_AAAA:
|
||||
if (rec->length != 16) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "unexpected length of AAAA record\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
osmo_sockaddr_str_from_in6_addr(sockaddr_str, (struct in6_addr*)rec->data, 0);
|
||||
break;
|
||||
default:
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "unexpected record type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Encode a successful mslookup result, along with the original query and packet_id into one mDNS answer packet.
|
||||
*
|
||||
* The records in the packet are ordered as follows:
|
||||
* 1) "age", ip_v4/v6, "port" (only IPv4 or IPv6 present) or
|
||||
* 2) "age", ip_v4, "port", ip_v6, "port" (both IPv4 and v6 present).
|
||||
* "age" and "port" are TXT records, ip_v4 is an A record, ip_v6 is an AAAA record.
|
||||
*
|
||||
* \param[in] packet_id as received in osmo_mdns_query_decode().
|
||||
* \param[in] query the original query, so we can send the domain back in the answer (i.e. "sip.voice.1234.msisdn").
|
||||
* \param[in] result holds the age, IPs and ports of the queried service.
|
||||
* \param[in] domain_suffix is appended to each domain in the queries to avoid colliding with the top-level domains
|
||||
* administrated by IANA. Example: "mdns.osmocom.org"
|
||||
* \returns msg on success, NULL on error.
|
||||
*/
|
||||
struct msgb *osmo_mdns_result_encode(void *ctx, uint16_t packet_id, const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_result *result, const char *domain_suffix)
|
||||
{
|
||||
struct osmo_mdns_msg_answer ans = {};
|
||||
struct osmo_mdns_record *rec_age = NULL;
|
||||
struct osmo_mdns_record rec_ip_v4 = {0};
|
||||
struct osmo_mdns_record rec_ip_v6 = {0};
|
||||
struct osmo_mdns_record *rec_ip_v4_port = NULL;
|
||||
struct osmo_mdns_record *rec_ip_v6_port = NULL;
|
||||
struct in_addr rec_ip_v4_in;
|
||||
struct in6_addr rec_ip_v6_in;
|
||||
struct msgb *msg = osmo_mdns_msgb_alloc(__func__);
|
||||
char buf[256];
|
||||
|
||||
ctx = talloc_named(ctx, 0, "osmo_mdns_result_encode");
|
||||
|
||||
/* Prepare answer (ans) */
|
||||
ans.domain = domain_from_query(ctx, query, domain_suffix);
|
||||
if (!ans.domain)
|
||||
goto error;
|
||||
ans.id = packet_id;
|
||||
INIT_LLIST_HEAD(&ans.records);
|
||||
|
||||
/* Record for age */
|
||||
rec_age = osmo_mdns_record_txt_keyval_encode(ctx, "age", "%"PRIu32, result->age);
|
||||
OSMO_ASSERT(rec_age);
|
||||
llist_add_tail(&rec_age->list, &ans.records);
|
||||
|
||||
/* Records for IPv4 */
|
||||
if (osmo_sockaddr_str_is_set(&result->host_v4)) {
|
||||
if (osmo_sockaddr_str_to_in_addr(&result->host_v4, &rec_ip_v4_in) < 0) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "failed to encode ipv4: %s\n",
|
||||
osmo_mslookup_result_name_b(buf, sizeof(buf), query, result));
|
||||
goto error;
|
||||
}
|
||||
rec_ip_v4.type = OSMO_MDNS_RFC_RECORD_TYPE_A;
|
||||
rec_ip_v4.data = (uint8_t *)&rec_ip_v4_in;
|
||||
rec_ip_v4.length = sizeof(rec_ip_v4_in);
|
||||
llist_add_tail(&rec_ip_v4.list, &ans.records);
|
||||
|
||||
rec_ip_v4_port = osmo_mdns_record_txt_keyval_encode(ctx, "port", "%"PRIu16, result->host_v4.port);
|
||||
OSMO_ASSERT(rec_ip_v4_port);
|
||||
llist_add_tail(&rec_ip_v4_port->list, &ans.records);
|
||||
}
|
||||
|
||||
/* Records for IPv6 */
|
||||
if (osmo_sockaddr_str_is_set(&result->host_v6)) {
|
||||
if (osmo_sockaddr_str_to_in6_addr(&result->host_v6, &rec_ip_v6_in) < 0) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "failed to encode ipv6: %s\n",
|
||||
osmo_mslookup_result_name_b(buf, sizeof(buf), query, result));
|
||||
goto error;
|
||||
}
|
||||
rec_ip_v6.type = OSMO_MDNS_RFC_RECORD_TYPE_AAAA;
|
||||
rec_ip_v6.data = (uint8_t *)&rec_ip_v6_in;
|
||||
rec_ip_v6.length = sizeof(rec_ip_v6_in);
|
||||
llist_add_tail(&rec_ip_v6.list, &ans.records);
|
||||
|
||||
rec_ip_v6_port = osmo_mdns_record_txt_keyval_encode(ctx, "port", "%"PRIu16, result->host_v6.port);
|
||||
OSMO_ASSERT(rec_ip_v6_port);
|
||||
llist_add_tail(&rec_ip_v6_port->list, &ans.records);
|
||||
}
|
||||
|
||||
if (osmo_mdns_msg_answer_encode(ctx, msg, &ans)) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "failed to encode mDNS answer: %s\n",
|
||||
osmo_mslookup_result_name_b(buf, sizeof(buf), query, result));
|
||||
goto error;
|
||||
}
|
||||
talloc_free(ctx);
|
||||
return msg;
|
||||
error:
|
||||
msgb_free(msg);
|
||||
talloc_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int decode_uint32_t(const char *str, uint32_t *val)
|
||||
{
|
||||
long long int lld;
|
||||
char *endptr = NULL;
|
||||
*val = 0;
|
||||
errno = 0;
|
||||
lld = strtoll(str, &endptr, 10);
|
||||
if (errno || !endptr || *endptr)
|
||||
return -EINVAL;
|
||||
if (lld < 0 || lld > UINT32_MAX)
|
||||
return -EINVAL;
|
||||
*val = lld;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode_port(const char *str, uint16_t *port)
|
||||
{
|
||||
uint32_t val;
|
||||
if (decode_uint32_t(str, &val))
|
||||
return -EINVAL;
|
||||
if (val > 65535)
|
||||
return -EINVAL;
|
||||
*port = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Read expected mDNS records into mslookup result.
|
||||
*
|
||||
* The records in the packet must be ordered as follows:
|
||||
* 1) "age", ip_v4/v6, "port" (only IPv4 or IPv6 present) or
|
||||
* 2) "age", ip_v4, "port", ip_v6, "port" (both IPv4 and v6 present).
|
||||
* "age" and "port" are TXT records, ip_v4 is an A record, ip_v6 is an AAAA record.
|
||||
*
|
||||
* \param[out] result holds the age, IPs and ports of the queried service.
|
||||
* \param[in] ans abstracted mDNS answer with a list of resource records.
|
||||
* \returns 0 on success, -EINVAL on error.
|
||||
*/
|
||||
int osmo_mdns_result_from_answer(struct osmo_mslookup_result *result, const struct osmo_mdns_msg_answer *ans)
|
||||
{
|
||||
struct osmo_mdns_record *rec;
|
||||
char txt_key[64];
|
||||
char txt_value[64];
|
||||
bool found_age = false;
|
||||
bool found_ip_v4 = false;
|
||||
bool found_ip_v6 = false;
|
||||
struct osmo_sockaddr_str *expect_port_for = NULL;
|
||||
|
||||
*result = (struct osmo_mslookup_result){};
|
||||
|
||||
result->rc = OSMO_MSLOOKUP_RC_NONE;
|
||||
|
||||
llist_for_each_entry(rec, &ans->records, list) {
|
||||
switch (rec->type) {
|
||||
case OSMO_MDNS_RFC_RECORD_TYPE_A:
|
||||
if (expect_port_for) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR,
|
||||
"'A' record found, but still expecting a 'port' value first\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (found_ip_v4) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "'A' record found twice in mDNS answer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
found_ip_v4 = true;
|
||||
expect_port_for = &result->host_v4;
|
||||
if (sockaddr_str_from_mdns_record(expect_port_for, rec)) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "'A' record with invalid address data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case OSMO_MDNS_RFC_RECORD_TYPE_AAAA:
|
||||
if (expect_port_for) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR,
|
||||
"'AAAA' record found, but still expecting a 'port' value first\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (found_ip_v6) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "'AAAA' record found twice in mDNS answer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
found_ip_v6 = true;
|
||||
expect_port_for = &result->host_v6;
|
||||
if (sockaddr_str_from_mdns_record(expect_port_for, rec) != 0) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "'AAAA' record with invalid address data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case OSMO_MDNS_RFC_RECORD_TYPE_TXT:
|
||||
if (osmo_mdns_record_txt_keyval_decode(rec, txt_key, sizeof(txt_key),
|
||||
txt_value, sizeof(txt_value)) != 0) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "failed to decode txt record\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (strcmp(txt_key, "age") == 0) {
|
||||
if (found_age) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "duplicate 'TXT' record for 'age'\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
found_age = true;
|
||||
if (decode_uint32_t(txt_value, &result->age)) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR,
|
||||
"'TXT' record: invalid 'age' value ('age=%s')\n", txt_value);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (strcmp(txt_key, "port") == 0) {
|
||||
if (!expect_port_for) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR,
|
||||
"'TXT' record for 'port' without previous 'A' or 'AAAA' record\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (decode_port(txt_value, &expect_port_for->port)) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR,
|
||||
"'TXT' record: invalid 'port' value ('port=%s')\n", txt_value);
|
||||
return -EINVAL;
|
||||
}
|
||||
expect_port_for = NULL;
|
||||
} else {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "unexpected key '%s' in TXT record\n", txt_key);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "unexpected record type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if everything was found */
|
||||
if (!found_age || !(found_ip_v4 || found_ip_v6) || expect_port_for) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "missing resource records in mDNS answer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
result->rc = OSMO_MSLOOKUP_RC_RESULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Decode a mDNS answer packet into a mslookup result, query and packet_id.
|
||||
* \param[out] packet_id same ID as sent in the request packet.
|
||||
* \param[out] query the original query (service, ID, ID type).
|
||||
* \param[out] result holds the age, IPs and ports of the queried service.
|
||||
* \param[in] domain_suffix is appended to each domain in the queries to avoid colliding with the top-level domains
|
||||
* administrated by IANA. Example: "mdns.osmocom.org"
|
||||
* \returns 0 on success, -EINVAL on error.
|
||||
*/
|
||||
int osmo_mdns_result_decode(void *ctx, const uint8_t *data, size_t data_len, uint16_t *packet_id,
|
||||
struct osmo_mslookup_query *query, struct osmo_mslookup_result *result,
|
||||
const char *domain_suffix)
|
||||
{
|
||||
int rc = -EINVAL;
|
||||
struct osmo_mdns_msg_answer *ans;
|
||||
ans = osmo_mdns_msg_answer_decode(ctx, data, data_len);
|
||||
if (!ans)
|
||||
goto exit_free;
|
||||
|
||||
if (query_from_domain(query, ans->domain, domain_suffix) < 0)
|
||||
goto exit_free;
|
||||
|
||||
if (osmo_mdns_result_from_answer(result, ans) < 0)
|
||||
goto exit_free;
|
||||
|
||||
*packet_id = ans->id;
|
||||
rc = 0;
|
||||
|
||||
exit_free:
|
||||
talloc_free(ans);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/* High level mDNS encoding and decoding functions for whole messages:
|
||||
* Request message (header, question)
|
||||
* Answer message (header, resource record 1, ... resource record N)*/
|
||||
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/mslookup/mdns_msg.h>
|
||||
|
||||
/*! Encode request message into one mDNS packet, consisting of the header section and one question section.
|
||||
* \returns 0 on success, -EINVAL on error.
|
||||
*/
|
||||
int osmo_mdns_msg_request_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_msg_request *req)
|
||||
{
|
||||
struct osmo_mdns_rfc_header hdr = {0};
|
||||
struct osmo_mdns_rfc_question qst = {0};
|
||||
|
||||
hdr.id = req->id;
|
||||
hdr.qdcount = 1;
|
||||
osmo_mdns_rfc_header_encode(msg, &hdr);
|
||||
|
||||
qst.domain = req->domain;
|
||||
qst.qtype = req->type;
|
||||
qst.qclass = OSMO_MDNS_RFC_CLASS_IN;
|
||||
if (osmo_mdns_rfc_question_encode(ctx, msg, &qst) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Decode request message from a mDNS packet, consisting of the header section and one question section.
|
||||
* \returns allocated request message on success, NULL on error.
|
||||
*/
|
||||
struct osmo_mdns_msg_request *osmo_mdns_msg_request_decode(void *ctx, const uint8_t *data, size_t data_len)
|
||||
{
|
||||
struct osmo_mdns_rfc_header hdr = {0};
|
||||
size_t hdr_len = sizeof(struct osmo_mdns_rfc_header);
|
||||
struct osmo_mdns_rfc_question* qst = NULL;
|
||||
struct osmo_mdns_msg_request *ret = NULL;
|
||||
|
||||
if (data_len < hdr_len || osmo_mdns_rfc_header_decode(data, hdr_len, &hdr) != 0 || hdr.qr != 0)
|
||||
return NULL;
|
||||
|
||||
qst = osmo_mdns_rfc_question_decode(ctx, data + hdr_len, data_len - hdr_len);
|
||||
if (!qst)
|
||||
return NULL;
|
||||
|
||||
ret = talloc_zero(ctx, struct osmo_mdns_msg_request);
|
||||
ret->id = hdr.id;
|
||||
ret->domain = talloc_strdup(ret, qst->domain);
|
||||
ret->type = qst->qtype;
|
||||
|
||||
talloc_free(qst);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! Initialize the linked list for resource records in a answer message. */
|
||||
void osmo_mdns_msg_answer_init(struct osmo_mdns_msg_answer *ans)
|
||||
{
|
||||
*ans = (struct osmo_mdns_msg_answer){};
|
||||
INIT_LLIST_HEAD(&ans->records);
|
||||
}
|
||||
|
||||
/*! Encode answer message into one mDNS packet, consisting of the header section and N resource records.
|
||||
*
|
||||
* To keep things simple, this sends the domain with each resource record. Other DNS implementations make use of
|
||||
* "message compression", which would send a question section with the domain before the resource records, and then
|
||||
* point inside each resource record with an offset back to the domain in the question section (RFC 1035 4.1.4).
|
||||
* \returns 0 on success, -EINVAL on error.
|
||||
*/
|
||||
int osmo_mdns_msg_answer_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_msg_answer *ans)
|
||||
{
|
||||
struct osmo_mdns_rfc_header hdr = {0};
|
||||
struct osmo_mdns_record *ans_record;
|
||||
|
||||
hdr.id = ans->id;
|
||||
hdr.qr = 1;
|
||||
hdr.ancount = llist_count(&ans->records);
|
||||
osmo_mdns_rfc_header_encode(msg, &hdr);
|
||||
|
||||
llist_for_each_entry(ans_record, &ans->records, list) {
|
||||
struct osmo_mdns_rfc_record rec = {0};
|
||||
|
||||
rec.domain = ans->domain;
|
||||
rec.type = ans_record->type;
|
||||
rec.class = OSMO_MDNS_RFC_CLASS_IN;
|
||||
rec.ttl = 0;
|
||||
rec.rdlength = ans_record->length;
|
||||
rec.rdata = ans_record->data;
|
||||
|
||||
if (osmo_mdns_rfc_record_encode(ctx, msg, &rec) != 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Decode answer message from a mDNS packet.
|
||||
*
|
||||
* Answer messages must consist of one header and one or more resource records. An additional question section or
|
||||
* message compression (RFC 1035 4.1.4) are not supported.
|
||||
* \returns allocated answer message on success, NULL on error.
|
||||
*/
|
||||
struct osmo_mdns_msg_answer *osmo_mdns_msg_answer_decode(void *ctx, const uint8_t *data, size_t data_len)
|
||||
{
|
||||
struct osmo_mdns_rfc_header hdr = {};
|
||||
size_t hdr_len = sizeof(struct osmo_mdns_rfc_header);
|
||||
struct osmo_mdns_msg_answer *ret = talloc_zero(ctx, struct osmo_mdns_msg_answer);
|
||||
|
||||
/* Parse header section */
|
||||
if (data_len < hdr_len || osmo_mdns_rfc_header_decode(data, hdr_len, &hdr) != 0 || hdr.qr != 1)
|
||||
goto error;
|
||||
ret->id = hdr.id;
|
||||
data_len -= hdr_len;
|
||||
data += hdr_len;
|
||||
|
||||
/* Parse resource records */
|
||||
INIT_LLIST_HEAD(&ret->records);
|
||||
while (data_len) {
|
||||
size_t record_len;
|
||||
struct osmo_mdns_rfc_record *rec;
|
||||
struct osmo_mdns_record* ret_record;
|
||||
|
||||
rec = osmo_mdns_rfc_record_decode(ret, data, data_len, &record_len);
|
||||
if (!rec)
|
||||
goto error;
|
||||
|
||||
/* Copy domain to ret */
|
||||
if (ret->domain) {
|
||||
if (strcmp(ret->domain, rec->domain) != 0) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "domain mismatch in resource records ('%s' vs '%s')\n",
|
||||
ret->domain, rec->domain);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else
|
||||
ret->domain = talloc_strdup(ret, rec->domain);
|
||||
|
||||
/* Add simplified record to ret */
|
||||
ret_record = talloc_zero(ret, struct osmo_mdns_record);
|
||||
ret_record->type = rec->type;
|
||||
ret_record->length = rec->rdlength;
|
||||
ret_record->data = talloc_memdup(ret_record, rec->rdata, rec->rdlength);
|
||||
llist_add_tail(&ret_record->list, &ret->records);
|
||||
|
||||
data += record_len;
|
||||
data_len -= record_len;
|
||||
talloc_free(rec);
|
||||
}
|
||||
|
||||
/* Verify record count */
|
||||
if (llist_count(&ret->records) != hdr.ancount) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "amount of parsed records (%i) doesn't match count in header (%i)\n",
|
||||
llist_count(&ret->records), hdr.ancount);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return ret;
|
||||
error:
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Get a TXT resource record, which stores a key=value string.
|
||||
* \returns allocated resource record on success, NULL on error.
|
||||
*/
|
||||
static struct osmo_mdns_record *_osmo_mdns_record_txt_encode(void *ctx, const char *key, const char *value)
|
||||
{
|
||||
struct osmo_mdns_record *ret = talloc_zero(ctx, struct osmo_mdns_record);
|
||||
size_t len = strlen(key) + 1 + strlen(value);
|
||||
|
||||
if (len > OSMO_MDNS_RFC_MAX_CHARACTER_STRING_LEN - 1)
|
||||
return NULL;
|
||||
|
||||
/* redundant len is required, see RFC 1035 3.3.14 and 3.3. */
|
||||
ret->data = (uint8_t *)talloc_asprintf(ctx, "%c%s=%s", (char)len, key, value);
|
||||
if (!ret->data)
|
||||
return NULL;
|
||||
ret->type = OSMO_MDNS_RFC_RECORD_TYPE_TXT;
|
||||
ret->length = len + 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! Get a TXT resource record, which stores a key=value string, but build value from a format string.
|
||||
* \returns allocated resource record on success, NULL on error.
|
||||
*/
|
||||
struct osmo_mdns_record *osmo_mdns_record_txt_keyval_encode(void *ctx, const char *key, const char *value_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *value = NULL;
|
||||
struct osmo_mdns_record *r;
|
||||
|
||||
if (!value_fmt)
|
||||
return _osmo_mdns_record_txt_encode(ctx, key, "");
|
||||
|
||||
va_start(ap, value_fmt);
|
||||
value = talloc_vasprintf(ctx, value_fmt, ap);
|
||||
va_end(ap);
|
||||
if (!value)
|
||||
return NULL;
|
||||
r = _osmo_mdns_record_txt_encode(ctx, key, value);
|
||||
talloc_free(value);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*! Decode a TXT resource record, which stores a key=value string.
|
||||
* \returns 0 on success, -EINVAL on error.
|
||||
*/
|
||||
int osmo_mdns_record_txt_keyval_decode(const struct osmo_mdns_record *rec,
|
||||
char *key_buf, size_t key_size, char *value_buf, size_t value_size)
|
||||
{
|
||||
const char *key_value;
|
||||
const char *key_value_end;
|
||||
const char *sep;
|
||||
const char *value;
|
||||
|
||||
if (rec->type != OSMO_MDNS_RFC_RECORD_TYPE_TXT)
|
||||
return -EINVAL;
|
||||
|
||||
key_value = (const char *)rec->data;
|
||||
key_value_end = key_value + rec->length;
|
||||
|
||||
/* Verify and then skip the redundant string length byte */
|
||||
if (*key_value != rec->length - 1)
|
||||
return -EINVAL;
|
||||
key_value++;
|
||||
|
||||
if (key_value >= key_value_end)
|
||||
return -EINVAL;
|
||||
|
||||
/* Find equals sign */
|
||||
sep = osmo_strnchr(key_value, key_value_end - key_value, '=');
|
||||
if (!sep)
|
||||
return -EINVAL;
|
||||
|
||||
/* Parse key */
|
||||
osmo_print_n(key_buf, key_size, key_value, sep - key_value);
|
||||
|
||||
/* Parse value */
|
||||
value = sep + 1;
|
||||
osmo_print_n(value_buf, value_size, value, key_value_end - value);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
/* Low level mDNS encoding and decoding functions of the qname IE, header/question sections and resource records,
|
||||
* as described in these RFCs:
|
||||
* - RFC 1035 (Domain names - implementation and specification)
|
||||
* - RFC 3596 (DNS Extensions to Support IP Version 6) */
|
||||
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/bitvec.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/mslookup/mdns_rfc.h>
|
||||
|
||||
/*
|
||||
* Encode/decode IEs
|
||||
*/
|
||||
|
||||
/*! Encode a domain string as qname (RFC 1035 4.1.2).
|
||||
* \param[in] domain multiple labels separated by dots, e.g. "sip.voice.1234.msisdn".
|
||||
* \returns allocated buffer with length-value pairs for each label (e.g. 0x03 "sip" 0x05 "voice" ...), NULL on error.
|
||||
*/
|
||||
char *osmo_mdns_rfc_qname_encode(void *ctx, const char *domain)
|
||||
{
|
||||
char *domain_dup;
|
||||
char *domain_iter;
|
||||
char buf[OSMO_MDNS_RFC_MAX_NAME_LEN + 2] = ""; /* len(qname) is len(domain) +1 */
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
|
||||
char *label;
|
||||
|
||||
if (strlen(domain) > OSMO_MDNS_RFC_MAX_NAME_LEN)
|
||||
return NULL;
|
||||
|
||||
domain_iter = domain_dup = talloc_strdup(ctx, domain);
|
||||
while ((label = strsep(&domain_iter, "."))) {
|
||||
size_t len = strlen(label);
|
||||
|
||||
/* Empty domain, dot at start, two dots in a row, or ending with a dot */
|
||||
if (!len)
|
||||
goto error;
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "%c%s", (char)len, label);
|
||||
}
|
||||
|
||||
talloc_free(domain_dup);
|
||||
return talloc_strdup(ctx, buf);
|
||||
|
||||
error:
|
||||
talloc_free(domain_dup);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Decode a domain string from a qname (RFC 1035 4.1.2).
|
||||
* \param[in] qname buffer with length-value pairs for each label (e.g. 0x03 "sip" 0x05 "voice" ...)
|
||||
* \param[in] qname_max_len amount of bytes that can be read at most from the memory location that qname points to.
|
||||
* \returns allocated buffer with domain string, multiple labels separated by dots (e.g. "sip.voice.1234.msisdn"),
|
||||
* NULL on error.
|
||||
*/
|
||||
char *osmo_mdns_rfc_qname_decode(void *ctx, const char *qname, size_t qname_max_len)
|
||||
{
|
||||
const char *next_label, *qname_end = qname + qname_max_len;
|
||||
char buf[OSMO_MDNS_RFC_MAX_NAME_LEN + 1];
|
||||
int i = 0;
|
||||
|
||||
if (qname_max_len < 1)
|
||||
return NULL;
|
||||
|
||||
while (*qname) {
|
||||
size_t len;
|
||||
|
||||
if (i >= qname_max_len)
|
||||
return NULL;
|
||||
|
||||
len = *qname;
|
||||
next_label = qname + len + 1;
|
||||
|
||||
if (next_label >= qname_end || i + len > OSMO_MDNS_RFC_MAX_NAME_LEN)
|
||||
return NULL;
|
||||
|
||||
if (i) {
|
||||
/* Two dots in a row is not allowed */
|
||||
if (buf[i - 1] == '.')
|
||||
return NULL;
|
||||
|
||||
buf[i] = '.';
|
||||
i++;
|
||||
}
|
||||
|
||||
memcpy(buf + i, qname + 1, len);
|
||||
i += len;
|
||||
qname = next_label;
|
||||
}
|
||||
buf[i] = '\0';
|
||||
|
||||
return talloc_strdup(ctx, buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode/decode message sections
|
||||
*/
|
||||
|
||||
/*! Encode header section (RFC 1035 4.1.1).
|
||||
* \param[in] msgb mesage buffer to which the encoded data will be appended.
|
||||
*/
|
||||
void osmo_mdns_rfc_header_encode(struct msgb *msg, const struct osmo_mdns_rfc_header *hdr)
|
||||
{
|
||||
struct osmo_mdns_rfc_header *buf = (struct osmo_mdns_rfc_header *) msgb_put(msg, sizeof(*hdr));
|
||||
memcpy(buf, hdr, sizeof(*hdr));
|
||||
|
||||
osmo_store16be(buf->id, &buf->id);
|
||||
osmo_store16be(buf->qdcount, &buf->qdcount);
|
||||
osmo_store16be(buf->ancount, &buf->ancount);
|
||||
osmo_store16be(buf->nscount, &buf->nscount);
|
||||
osmo_store16be(buf->arcount, &buf->arcount);
|
||||
}
|
||||
|
||||
/*! Decode header section (RFC 1035 4.1.1). */
|
||||
int osmo_mdns_rfc_header_decode(const uint8_t *data, size_t data_len, struct osmo_mdns_rfc_header *hdr)
|
||||
{
|
||||
if (data_len != sizeof(*hdr))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(hdr, data, data_len);
|
||||
|
||||
hdr->id = osmo_load16be(&hdr->id);
|
||||
hdr->qdcount = osmo_load16be(&hdr->qdcount);
|
||||
hdr->ancount = osmo_load16be(&hdr->ancount);
|
||||
hdr->nscount = osmo_load16be(&hdr->nscount);
|
||||
hdr->arcount = osmo_load16be(&hdr->arcount);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Encode question section (RFC 1035 4.1.2).
|
||||
* \param[in] msgb mesage buffer to which the encoded data will be appended.
|
||||
*/
|
||||
int osmo_mdns_rfc_question_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_question *qst)
|
||||
{
|
||||
char *qname;
|
||||
size_t qname_len;
|
||||
uint8_t *qname_buf;
|
||||
|
||||
/* qname */
|
||||
qname = osmo_mdns_rfc_qname_encode(ctx, qst->domain);
|
||||
if (!qname)
|
||||
return -EINVAL;
|
||||
qname_len = strlen(qname) + 1;
|
||||
qname_buf = msgb_put(msg, qname_len);
|
||||
memcpy(qname_buf, qname, qname_len);
|
||||
talloc_free(qname);
|
||||
|
||||
/* qtype and qclass */
|
||||
msgb_put_u16(msg, qst->qtype);
|
||||
msgb_put_u16(msg, qst->qclass);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Decode question section (RFC 1035 4.1.2). */
|
||||
struct osmo_mdns_rfc_question *osmo_mdns_rfc_question_decode(void *ctx, const uint8_t *data, size_t data_len)
|
||||
{
|
||||
struct osmo_mdns_rfc_question *ret;
|
||||
size_t qname_len = data_len - 4;
|
||||
|
||||
if (data_len < 6)
|
||||
return NULL;
|
||||
|
||||
/* qname */
|
||||
ret = talloc_zero(ctx, struct osmo_mdns_rfc_question);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
ret->domain = osmo_mdns_rfc_qname_decode(ret, (const char *)data, qname_len);
|
||||
if (!ret->domain) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* qtype and qclass */
|
||||
ret->qtype = osmo_load16be(data + qname_len);
|
||||
ret->qclass = osmo_load16be(data + qname_len + 2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode/decode resource records
|
||||
*/
|
||||
|
||||
/*! Encode one resource record (RFC 1035 4.1.3).
|
||||
* \param[in] msgb mesage buffer to which the encoded data will be appended.
|
||||
*/
|
||||
int osmo_mdns_rfc_record_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_record *rec)
|
||||
{
|
||||
char *name;
|
||||
size_t name_len;
|
||||
uint8_t *buf;
|
||||
|
||||
/* name */
|
||||
name = osmo_mdns_rfc_qname_encode(ctx, rec->domain);
|
||||
if (!name)
|
||||
return -EINVAL;
|
||||
name_len = strlen(name) + 1;
|
||||
buf = msgb_put(msg, name_len);
|
||||
memcpy(buf, name, name_len);
|
||||
talloc_free(name);
|
||||
|
||||
/* type, class, ttl, rdlength */
|
||||
msgb_put_u16(msg, rec->type);
|
||||
msgb_put_u16(msg, rec->class);
|
||||
msgb_put_u32(msg, rec->ttl);
|
||||
msgb_put_u16(msg, rec->rdlength);
|
||||
|
||||
/* rdata */
|
||||
buf = msgb_put(msg, rec->rdlength);
|
||||
memcpy(buf, rec->rdata, rec->rdlength);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Decode one resource record (RFC 1035 4.1.3). */
|
||||
struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_t *data, size_t data_len,
|
||||
size_t *record_len)
|
||||
{
|
||||
struct osmo_mdns_rfc_record *ret = talloc_zero(ctx, struct osmo_mdns_rfc_record);
|
||||
size_t name_len;
|
||||
|
||||
/* name */
|
||||
ret->domain = osmo_mdns_rfc_qname_decode(ret, (const char *)data, data_len - 10);
|
||||
if (!ret->domain)
|
||||
goto error;
|
||||
name_len = strlen(ret->domain) + 2;
|
||||
if (name_len + 10 > data_len)
|
||||
goto error;
|
||||
|
||||
/* type, class, ttl, rdlength */
|
||||
ret->type = osmo_load16be(data + name_len);
|
||||
ret->class = osmo_load16be(data + name_len + 2);
|
||||
ret->ttl = osmo_load32be(data + name_len + 4);
|
||||
ret->rdlength = osmo_load16be(data + name_len + 8);
|
||||
if (name_len + 10 + ret->rdlength > data_len)
|
||||
goto error;
|
||||
|
||||
/* rdata */
|
||||
ret->rdata = talloc_memdup(ret, data + name_len + 10, ret->rdlength);
|
||||
if (!ret->rdata)
|
||||
return NULL;
|
||||
|
||||
*record_len = name_len + 10 + ret->rdlength;
|
||||
return ret;
|
||||
error:
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <stdbool.h>
|
||||
#include <talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/mslookup/mdns_sock.h>
|
||||
|
||||
/*! Open socket to send and receive multicast data.
|
||||
*
|
||||
* The socket is opened with SO_REUSEADDR, so we can bind to the same IP and port multiple times. This socket receives
|
||||
* everything sent to that multicast IP/port, including its own data data sent from osmo_mdns_sock_send(). So whenever
|
||||
* sending something, the receive callback will be called with the same data and should discard it.
|
||||
*
|
||||
* \param[in] ip multicast IPv4 or IPv6 address.
|
||||
* \param[in] port port number.
|
||||
* \param[in] cb callback for incoming data that will be passed to osmo_fd_setup (should read from osmo_fd->fd).
|
||||
* \param[in] data userdata passed to osmo_fd (available in cb as osmo_fd->data).
|
||||
* \param[in] priv_nr additional userdata integer passed to osmo_fd (available in cb as osmo_fd->priv_nr).
|
||||
* \returns allocated osmo_mdns_sock, NULL on error.
|
||||
*/
|
||||
struct osmo_mdns_sock *osmo_mdns_sock_init(void *ctx, const char *ip, unsigned int port,
|
||||
int (*cb)(struct osmo_fd *fd, unsigned int what),
|
||||
void *data, unsigned int priv_nr)
|
||||
{
|
||||
struct osmo_mdns_sock *ret;
|
||||
int sock, rc;
|
||||
struct addrinfo hints = {0};
|
||||
struct ip_mreq multicast_req = {0};
|
||||
in_addr_t iface = INADDR_ANY;
|
||||
char portbuf[10];
|
||||
int y = 1;
|
||||
|
||||
snprintf(portbuf, sizeof(portbuf) -1, "%u", port);
|
||||
ret = talloc_zero(ctx, struct osmo_mdns_sock);
|
||||
OSMO_ASSERT(ret);
|
||||
|
||||
/* Fill addrinfo */
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = (AI_PASSIVE | AI_NUMERICHOST);
|
||||
rc = getaddrinfo(ip, portbuf, &hints, &ret->ai);
|
||||
if (rc != 0) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: getaddrinfo: %s\n", gai_strerror(rc));
|
||||
ret->ai = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Open socket */
|
||||
sock = socket(ret->ai->ai_family, ret->ai->ai_socktype, 0);
|
||||
if (sock == -1) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: socket: %s\n", strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Set multicast options */
|
||||
rc = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char*)&iface, sizeof(iface));
|
||||
if (rc == -1) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
|
||||
goto error_sock;
|
||||
}
|
||||
memcpy(&multicast_req.imr_multiaddr, &((struct sockaddr_in*)(ret->ai->ai_addr))->sin_addr,
|
||||
sizeof(multicast_req.imr_multiaddr));
|
||||
multicast_req.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||
rc = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multicast_req, sizeof(multicast_req));
|
||||
if (rc == -1) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
|
||||
goto error_sock;
|
||||
}
|
||||
|
||||
/* Always allow binding the same IP and port twice. This is needed in OsmoHLR (where the code becomes cleaner by
|
||||
* just using a different socket for server and client code) and in the mslookup_client_mdns_test. Also for
|
||||
* osmo-mslookup-client if it is running multiple times in parallel (i.e. two incoming calls almost at the same
|
||||
* time need to be resolved with the simple dialplan example that just starts new processes). */
|
||||
rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&y, sizeof(y));
|
||||
if (rc == -1) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
|
||||
goto error_sock;
|
||||
}
|
||||
|
||||
/* Bind and register osmo_fd callback */
|
||||
rc = bind(sock, ret->ai->ai_addr, ret->ai->ai_addrlen);
|
||||
if (rc == -1) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: bind: %s\n", strerror(errno));
|
||||
goto error_sock;
|
||||
}
|
||||
osmo_fd_setup(&ret->osmo_fd, sock, OSMO_FD_READ, cb, data, priv_nr);
|
||||
if (osmo_fd_register(&ret->osmo_fd) != 0)
|
||||
goto error_sock;
|
||||
|
||||
return ret;
|
||||
error_sock:
|
||||
close(sock);
|
||||
error:
|
||||
if (ret->ai)
|
||||
freeaddrinfo(ret->ai);
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Send msgb over mdns_sock and consume msgb.
|
||||
* \returns 0 on success, -1 on error.
|
||||
*/
|
||||
int osmo_mdns_sock_send(const struct osmo_mdns_sock *mdns_sock, struct msgb *msg)
|
||||
{
|
||||
size_t len = msgb_length(msg);
|
||||
int rc = sendto(mdns_sock->osmo_fd.fd, msgb_data(msg), len, 0, mdns_sock->ai->ai_addr,
|
||||
mdns_sock->ai->ai_addrlen);
|
||||
msgb_free(msg);
|
||||
return (rc == len) ? 0 : -1;
|
||||
}
|
||||
|
||||
/*! Tear down osmo_mdns_sock. */
|
||||
void osmo_mdns_sock_cleanup(struct osmo_mdns_sock *mdns_sock)
|
||||
{
|
||||
osmo_fd_close(&mdns_sock->osmo_fd);
|
||||
freeaddrinfo(mdns_sock->ai);
|
||||
talloc_free(mdns_sock);
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/mslookup/mslookup.h>
|
||||
|
||||
/*! \addtogroup mslookup
|
||||
*
|
||||
* Distributed GSM: finding subscribers
|
||||
*
|
||||
* There are various aspects of the D-GSM code base in osmo-hlr.git, here is an overview:
|
||||
*
|
||||
* mslookup is the main enabler of D-GSM, a concept for connecting services between independent core network stacks.
|
||||
*
|
||||
* D-GSM consists of:
|
||||
* (1) mslookup client to find subscribers:
|
||||
* (a) external clients like ESME, SIP PBX, ... ask osmo-hlr to tell where to send SMS, voice calls, ...
|
||||
* (b) osmo-hlr's own mslookup client asks remote osmo-hlrs whether they know a given IMSI.
|
||||
* (2) when a subscriber was found at a remote HLR, GSUP gets forwarded there:
|
||||
* (a) to deliver messages for the GSUP proxy, osmo-hlr manages many GSUP clients to establish links to remote HLRs.
|
||||
* (b) osmo-hlr has a GSUP proxy layer that caches data of IMSIs that get proxied to a remote HLR.
|
||||
* (c) decision making to distinguish local IMSIs from ones proxied to a remote HLR.
|
||||
*
|
||||
* (1) mslookup is a method of finding subscribers using (multicast) queries, by MSISDN or by IMSI.
|
||||
* It is open to various lookup methods, the first one being multicast DNS.
|
||||
* An mslookup client sends a request, and an mslookup server responds.
|
||||
* The mslookup server is implemented by osmo-hlr. mslookup clients are arbitrary programs, like an ESME or a SIP PBX.
|
||||
* Hence the mslookup client is public API, while the mslookup server is implemented "privately" in osmo-hlr.
|
||||
*
|
||||
* (1a) Public mslookup client: libosmo-mslookup
|
||||
* src/mslookup/mslookup.c Things useful for both client and server.
|
||||
* src/mslookup/mslookup_client.c The client API, which can use various lookup methods,
|
||||
* and consolidates results from various responders.
|
||||
* src/mslookup/mslookup_client_mdns.c lookup method implementing multicast DNS, client side.
|
||||
*
|
||||
* src/mslookup/osmo-mslookup-client.c Utility program to ease invocation for (blocking) mslookup clients.
|
||||
*
|
||||
* src/mslookup/mslookup_client_fake.c lookup method generating fake results, for testing client implementations.
|
||||
*
|
||||
* src/mslookup/mdns*.c implementation of DNS to be used by mslookup_client_mdns.c,
|
||||
* and the mslookup_server.c.
|
||||
*
|
||||
* contrib/dgsm/esme_dgsm.py Example implementation for an mslookup enabled SMS handler.
|
||||
* contrib/dgsm/freeswitch_dialplan_dgsm.py Example implementation for an mslookup enabled FreeSWITCH dialplan.
|
||||
* contrib/dgsm/osmo-mslookup-pipe.py Example for writing a python client using the osmo-mslookup-client
|
||||
* cmdline.
|
||||
* contrib/dgsm/osmo-mslookup-socket.py Example for writing a python client using the osmo-mslookup-client
|
||||
* unix domain socket.
|
||||
*
|
||||
* (1b) "Private" mslookup server in osmo-hlr:
|
||||
* src/mslookup_server.c Respond to mslookup queries, independent from the particular lookup method.
|
||||
* src/mslookup_server_mdns.c mDNS specific implementation for mslookup_server.c.
|
||||
* src/dgsm_vty.c Configure services that mslookup server sends to remote requests.
|
||||
*
|
||||
* (2) Proxy and GSUP clients to remote HLR instances:
|
||||
*
|
||||
* (a) Be a GSUP client to forward to a remote HLR:
|
||||
* src/gsupclient/ The same API that is used by osmo-{msc,sgsn} is also used to forward GSUP to remote osmo-hlrs.
|
||||
* src/remote_hlr.c Establish links to remote osmo-hlrs, where this osmo-hlr is a client (proxying e.g. for an MSC).
|
||||
*
|
||||
* (b) Keep track of remotely handled IMSIs:
|
||||
* src/proxy.c Keep track of proxied IMSIs and cache important subscriber data.
|
||||
*
|
||||
* (c) Direct GSUP request to the right destination: either the local or a remote HLR:
|
||||
* src/dgsm.c The glue that makes osmo-hlr distinguish between local IMSIs and those that are proxied to another
|
||||
* osmo-hlr.
|
||||
* src/dgsm_vty.c Config.
|
||||
*
|
||||
* @{
|
||||
* \file mslookup.c
|
||||
*/
|
||||
|
||||
const struct value_string osmo_mslookup_id_type_names[] = {
|
||||
{ OSMO_MSLOOKUP_ID_NONE, "none" },
|
||||
{ OSMO_MSLOOKUP_ID_IMSI, "imsi" },
|
||||
{ OSMO_MSLOOKUP_ID_MSISDN, "msisdn" },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct value_string osmo_mslookup_result_code_names[] = {
|
||||
{ OSMO_MSLOOKUP_RC_NONE, "none" },
|
||||
{ OSMO_MSLOOKUP_RC_RESULT, "result" },
|
||||
{ OSMO_MSLOOKUP_RC_NOT_FOUND, "not-found" },
|
||||
{}
|
||||
};
|
||||
|
||||
/*! Compare two struct osmo_mslookup_id.
|
||||
* \returns 0 if a and b are equal,
|
||||
* < 0 if a (or the ID type / start of ID) is < b,
|
||||
* > 0 if a (or the ID type / start of ID) is > b.
|
||||
*/
|
||||
int osmo_mslookup_id_cmp(const struct osmo_mslookup_id *a, const struct osmo_mslookup_id *b)
|
||||
{
|
||||
int cmp;
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
|
||||
cmp = OSMO_CMP(a->type, b->type);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
switch (a->type) {
|
||||
case OSMO_MSLOOKUP_ID_IMSI:
|
||||
return strncmp(a->imsi, b->imsi, sizeof(a->imsi));
|
||||
case OSMO_MSLOOKUP_ID_MSISDN:
|
||||
return strncmp(a->msisdn, b->msisdn, sizeof(a->msisdn));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool osmo_mslookup_id_valid(const struct osmo_mslookup_id *id)
|
||||
{
|
||||
switch (id->type) {
|
||||
case OSMO_MSLOOKUP_ID_IMSI:
|
||||
return osmo_imsi_str_valid(id->imsi);
|
||||
case OSMO_MSLOOKUP_ID_MSISDN:
|
||||
return osmo_msisdn_str_valid(id->msisdn);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool osmo_mslookup_service_valid(const char *service)
|
||||
{
|
||||
return strlen(service) > 0;
|
||||
}
|
||||
|
||||
/*! Write ID and ID type to a buffer.
|
||||
* \param[out] buf nul-terminated {id}.{id_type} string (e.g. "1234.msisdn") or
|
||||
* "?.none" if the ID type is invalid.
|
||||
* \returns amount of bytes written to buf.
|
||||
*/
|
||||
size_t osmo_mslookup_id_name_buf(char *buf, size_t buflen, const struct osmo_mslookup_id *id)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
switch (id->type) {
|
||||
case OSMO_MSLOOKUP_ID_IMSI:
|
||||
OSMO_STRBUF_PRINTF(sb, "%s", id->imsi);
|
||||
break;
|
||||
case OSMO_MSLOOKUP_ID_MSISDN:
|
||||
OSMO_STRBUF_PRINTF(sb, "%s", id->msisdn);
|
||||
break;
|
||||
default:
|
||||
OSMO_STRBUF_PRINTF(sb, "?");
|
||||
break;
|
||||
}
|
||||
OSMO_STRBUF_PRINTF(sb, ".%s", osmo_mslookup_id_type_name(id->type));
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
/*! Same as osmo_mslookup_id_name_buf(), but return a talloc allocated string of sufficient size. */
|
||||
char *osmo_mslookup_id_name_c(void *ctx, const struct osmo_mslookup_id *id)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_mslookup_id_name_buf, id)
|
||||
}
|
||||
|
||||
/*! Same as osmo_mslookup_id_name_buf(), but directly return the char* (for printf-like string formats). */
|
||||
char *osmo_mslookup_id_name_b(char *buf, size_t buflen, const struct osmo_mslookup_id *id)
|
||||
{
|
||||
int rc = osmo_mslookup_id_name_buf(buf, buflen, id);
|
||||
if (rc < 0 && buflen)
|
||||
buf[0] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*! Write mslookup result string to buffer.
|
||||
* \param[in] query with the service, ID and ID type to be written to buf like a domain string, or NULL to omit.
|
||||
* \param[in] result with the result code, IPv4/v6 and age to be written to buf or NULL to omit.
|
||||
* \param[out] buf result as flat string, which looks like the following for a valid query and result with IPv4 and v6
|
||||
* answer: "sip.voice.1234.msisdn -> ipv4: 42.42.42.42:1337 -> ipv6: [1234:5678:9ABC::]:1338 (age=1)",
|
||||
* the result part can also be " -> timeout" or " -> rc=5" depending on the result code.
|
||||
* \returns amount of bytes written to buf.
|
||||
*/
|
||||
size_t osmo_mslookup_result_to_str_buf(char *buf, size_t buflen,
|
||||
const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_result *result)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
if (query) {
|
||||
OSMO_STRBUF_PRINTF(sb, "%s.", query->service);
|
||||
OSMO_STRBUF_APPEND(sb, osmo_mslookup_id_name_buf, &query->id);
|
||||
}
|
||||
if (result && result->rc == OSMO_MSLOOKUP_RC_NONE)
|
||||
result = NULL;
|
||||
if (result) {
|
||||
if (result->rc != OSMO_MSLOOKUP_RC_RESULT) {
|
||||
OSMO_STRBUF_PRINTF(sb, " %s", osmo_mslookup_result_code_name(result->rc));
|
||||
} else {
|
||||
if (result->host_v4.ip[0]) {
|
||||
OSMO_STRBUF_PRINTF(sb, " -> ipv4: " OSMO_SOCKADDR_STR_FMT,
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&result->host_v4));
|
||||
}
|
||||
if (result->host_v6.ip[0]) {
|
||||
OSMO_STRBUF_PRINTF(sb, " -> ipv6: " OSMO_SOCKADDR_STR_FMT,
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&result->host_v6));
|
||||
}
|
||||
OSMO_STRBUF_PRINTF(sb, " (age=%u)", result->age);
|
||||
}
|
||||
OSMO_STRBUF_PRINTF(sb, " %s", result->last ? "(last)" : "(not-last)");
|
||||
}
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
/*! Same as osmo_mslookup_result_to_str_buf(), but return a talloc allocated string of sufficient size. */
|
||||
char *osmo_mslookup_result_name_c(void *ctx,
|
||||
const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_result *result)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_mslookup_result_to_str_buf, query, result)
|
||||
}
|
||||
|
||||
/*! Same as osmo_mslookup_result_to_str_buf(), but directly return the char* (for printf-like string formats). */
|
||||
char *osmo_mslookup_result_name_b(char *buf, size_t buflen,
|
||||
const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_result *result)
|
||||
{
|
||||
int rc = osmo_mslookup_result_to_str_buf(buf, buflen, query, result);
|
||||
if (rc < 0 && buflen)
|
||||
buf[0] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*! Copy part of a string to a buffer and nul-terminate it.
|
||||
* \returns 0 on success, negative on error.
|
||||
*/
|
||||
static int token(char *dest, size_t dest_size, const char *start, const char *end)
|
||||
{
|
||||
int len;
|
||||
if (start >= end)
|
||||
return -10;
|
||||
len = end - start;
|
||||
if (len >= dest_size)
|
||||
return -11;
|
||||
strncpy(dest, start, len);
|
||||
dest[len] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Parse a string like "foo.moo.goo.123456789012345.msisdn" into service="foo.moo.goo", id="123456789012345" and
|
||||
* id_type="msisdn", placed in a struct osmo_mslookup_query.
|
||||
* \param q Write parsed query to this osmo_mslookup_query.
|
||||
* \param domain Human readable domain string like "sip.voice.12345678.msisdn".
|
||||
* \returns 0 on success, negative on error.
|
||||
*/
|
||||
int osmo_mslookup_query_init_from_domain_str(struct osmo_mslookup_query *q, const char *domain)
|
||||
{
|
||||
const char *last_dot;
|
||||
const char *second_last_dot;
|
||||
const char *id_type;
|
||||
const char *id;
|
||||
int rc;
|
||||
|
||||
*q = (struct osmo_mslookup_query){};
|
||||
|
||||
if (!domain)
|
||||
return -1;
|
||||
|
||||
last_dot = strrchr(domain, '.');
|
||||
|
||||
if (!last_dot)
|
||||
return -2;
|
||||
|
||||
if (last_dot <= domain)
|
||||
return -3;
|
||||
|
||||
for (second_last_dot = last_dot - 1; second_last_dot > domain && *second_last_dot != '.'; second_last_dot--);
|
||||
if (second_last_dot == domain || *second_last_dot != '.')
|
||||
return -3;
|
||||
|
||||
id_type = last_dot + 1;
|
||||
if (!*id_type)
|
||||
return -4;
|
||||
|
||||
q->id.type = get_string_value(osmo_mslookup_id_type_names, id_type);
|
||||
|
||||
id = second_last_dot + 1;
|
||||
switch (q->id.type) {
|
||||
case OSMO_MSLOOKUP_ID_IMSI:
|
||||
rc = token(q->id.imsi, sizeof(q->id.imsi), id, last_dot);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!osmo_imsi_str_valid(q->id.imsi))
|
||||
return -5;
|
||||
break;
|
||||
case OSMO_MSLOOKUP_ID_MSISDN:
|
||||
rc = token(q->id.msisdn, sizeof(q->id.msisdn), id, last_dot);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!osmo_msisdn_str_valid(q->id.msisdn))
|
||||
return -6;
|
||||
break;
|
||||
default:
|
||||
return -7;
|
||||
}
|
||||
|
||||
return token(q->service, sizeof(q->service), domain, second_last_dot);
|
||||
}
|
||||
|
||||
/*! @} */
|
||||
@@ -1,310 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/mslookup/mslookup_client.h>
|
||||
|
||||
/*! Lookup client's internal data for a query. */
|
||||
struct osmo_mslookup_client {
|
||||
struct llist_head lookup_methods;
|
||||
struct llist_head requests;
|
||||
uint32_t next_request_handle;
|
||||
};
|
||||
|
||||
/*! Lookup client's internal data for a query.
|
||||
* The request methods only get to see the query part, and result handling is done commonly for all request methods. */
|
||||
struct osmo_mslookup_client_request {
|
||||
struct llist_head entry;
|
||||
struct osmo_mslookup_client *client;
|
||||
uint32_t request_handle;
|
||||
|
||||
struct osmo_mslookup_query query;
|
||||
struct osmo_mslookup_query_handling handling;
|
||||
struct osmo_timer_list timeout;
|
||||
bool waiting_min_delay;
|
||||
|
||||
struct osmo_mslookup_result result;
|
||||
};
|
||||
|
||||
static struct osmo_mslookup_client_request *get_request(struct osmo_mslookup_client *client, uint32_t request_handle)
|
||||
{
|
||||
struct osmo_mslookup_client_request *r;
|
||||
if (!request_handle)
|
||||
return NULL;
|
||||
llist_for_each_entry(r, &client->requests, entry) {
|
||||
if (r->request_handle == request_handle)
|
||||
return r;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct osmo_mslookup_client *osmo_mslookup_client_new(void *ctx)
|
||||
{
|
||||
struct osmo_mslookup_client *client = talloc_zero(ctx, struct osmo_mslookup_client);
|
||||
OSMO_ASSERT(client);
|
||||
INIT_LLIST_HEAD(&client->lookup_methods);
|
||||
INIT_LLIST_HEAD(&client->requests);
|
||||
return client;
|
||||
}
|
||||
|
||||
/*! Return whether any lookup methods are available.
|
||||
* \param[in] client Client to query.
|
||||
* \return true when a client is present that has at least one osmo_mslookup_client_method registered.
|
||||
*/
|
||||
bool osmo_mslookup_client_active(struct osmo_mslookup_client *client)
|
||||
{
|
||||
if (!client)
|
||||
return false;
|
||||
if (llist_empty(&client->lookup_methods))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _osmo_mslookup_client_method_del(struct osmo_mslookup_client_method *method)
|
||||
{
|
||||
if (method->destruct)
|
||||
method->destruct(method);
|
||||
llist_del(&method->entry);
|
||||
talloc_free(method);
|
||||
}
|
||||
|
||||
/*! Stop and free mslookup client and all registered lookup methods.
|
||||
*/
|
||||
void osmo_mslookup_client_free(struct osmo_mslookup_client *client)
|
||||
{
|
||||
struct osmo_mslookup_client_method *m, *n;
|
||||
if (!client)
|
||||
return;
|
||||
llist_for_each_entry_safe(m, n, &client->lookup_methods, entry) {
|
||||
_osmo_mslookup_client_method_del(m);
|
||||
}
|
||||
talloc_free(client);
|
||||
}
|
||||
|
||||
/*! Add an osmo_mslookup_client_method to service MS Lookup requests.
|
||||
* Note, osmo_mslookup_client_method_del() will talloc_free() the method pointer, so it needs to be dynamically
|
||||
* allocated.
|
||||
* \param client The osmo_mslookup_client instance to add to.
|
||||
* \param method A fully initialized method struct, allocated by talloc.
|
||||
*/
|
||||
void osmo_mslookup_client_method_add(struct osmo_mslookup_client *client,
|
||||
struct osmo_mslookup_client_method *method)
|
||||
{
|
||||
method->client = client;
|
||||
llist_add_tail(&method->entry, &client->lookup_methods);
|
||||
}
|
||||
|
||||
/*! \return false if the method was not listed, true if the method was listed, removed and talloc_free()d.
|
||||
*/
|
||||
bool osmo_mslookup_client_method_del(struct osmo_mslookup_client *client,
|
||||
struct osmo_mslookup_client_method *method)
|
||||
{
|
||||
struct osmo_mslookup_client_method *m;
|
||||
llist_for_each_entry(m, &client->lookup_methods, entry) {
|
||||
if (m == method) {
|
||||
_osmo_mslookup_client_method_del(method);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void osmo_mslookup_request_send_result(struct osmo_mslookup_client_request *r, bool finish)
|
||||
{
|
||||
struct osmo_mslookup_client *client = r->client;
|
||||
uint32_t request_handle = r->request_handle;
|
||||
|
||||
r->result.last = finish;
|
||||
r->handling.result_cb(r->client, r->request_handle, &r->query, &r->result);
|
||||
|
||||
/* Make sure the request struct is discarded.
|
||||
* The result_cb() may already have triggered a cleanup, so query by request_handle. */
|
||||
if (finish)
|
||||
osmo_mslookup_client_request_cancel(client, request_handle);
|
||||
}
|
||||
|
||||
void osmo_mslookup_client_rx_result(struct osmo_mslookup_client *client, uint32_t request_handle,
|
||||
const struct osmo_mslookup_result *result)
|
||||
{
|
||||
struct osmo_mslookup_client_request *req = get_request(client, request_handle);
|
||||
|
||||
if (!req) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR,
|
||||
"Internal error: Got mslookup result for a request that does not exist (handle %u)\n",
|
||||
request_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore incoming results that are not successful */
|
||||
if (result->rc != OSMO_MSLOOKUP_RC_RESULT)
|
||||
return;
|
||||
|
||||
/* If we already stored an earlier successful result, keep that if its age is younger. */
|
||||
if (req->result.rc == OSMO_MSLOOKUP_RC_RESULT
|
||||
&& result->age >= req->result.age)
|
||||
return;
|
||||
|
||||
req->result = *result;
|
||||
|
||||
/* If age == 0, it doesn't get any better, so return the result immediately. */
|
||||
if (req->result.age == 0) {
|
||||
osmo_mslookup_request_send_result(req, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->waiting_min_delay)
|
||||
return;
|
||||
|
||||
osmo_mslookup_request_send_result(req, false);
|
||||
}
|
||||
|
||||
static void _osmo_mslookup_client_request_cleanup(struct osmo_mslookup_client_request *r)
|
||||
{
|
||||
struct osmo_mslookup_client_method *m;
|
||||
osmo_timer_del(&r->timeout);
|
||||
llist_for_each_entry(m, &r->client->lookup_methods, entry) {
|
||||
if (!m->request_cleanup)
|
||||
continue;
|
||||
m->request_cleanup(m, r->request_handle);
|
||||
}
|
||||
llist_del(&r->entry);
|
||||
talloc_free(r);
|
||||
}
|
||||
|
||||
static void timeout_cb(void *data);
|
||||
|
||||
static void set_timer(struct osmo_mslookup_client_request *r, unsigned long milliseconds)
|
||||
{
|
||||
osmo_timer_setup(&r->timeout, timeout_cb, r);
|
||||
osmo_timer_schedule(&r->timeout, milliseconds / 1000, (milliseconds % 1000) * 1000);
|
||||
}
|
||||
|
||||
static void timeout_cb(void *data)
|
||||
{
|
||||
struct osmo_mslookup_client_request *r = data;
|
||||
if (r->waiting_min_delay) {
|
||||
/* The initial delay has passed. See if it stops here, or whether the overall timeout continues. */
|
||||
r->waiting_min_delay = false;
|
||||
|
||||
if (r->handling.result_timeout_milliseconds <= r->handling.min_wait_milliseconds) {
|
||||
/* It ends here. Return a final result. */
|
||||
if (r->result.rc != OSMO_MSLOOKUP_RC_RESULT)
|
||||
r->result.rc = OSMO_MSLOOKUP_RC_NOT_FOUND;
|
||||
osmo_mslookup_request_send_result(r, true);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We continue to listen for results. If one is already on record, send it now. */
|
||||
if (r->result.rc == OSMO_MSLOOKUP_RC_RESULT)
|
||||
osmo_mslookup_request_send_result(r, false);
|
||||
|
||||
set_timer(r, r->handling.result_timeout_milliseconds - r->handling.min_wait_milliseconds);
|
||||
return;
|
||||
}
|
||||
/* The final timeout has passed, finish and clean up the request. */
|
||||
switch (r->result.rc) {
|
||||
case OSMO_MSLOOKUP_RC_RESULT:
|
||||
/* If the rc == OSMO_MSLOOKUP_RC_RESULT, this result has already been sent.
|
||||
* Don't send it again, instead send an RC_NONE, last=true result. */
|
||||
r->result.rc = OSMO_MSLOOKUP_RC_NONE;
|
||||
break;
|
||||
default:
|
||||
r->result.rc = OSMO_MSLOOKUP_RC_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
osmo_mslookup_request_send_result(r, true);
|
||||
}
|
||||
|
||||
/*! Launch a subscriber lookup with the provided query.
|
||||
* A request is cleared implicitly when the handling->result_cb is invoked; if the quer->priv pointer becomes invalid
|
||||
* before that, a request should be canceled by calling osmo_mslookup_client_request_cancel() with the returned
|
||||
* request_handle. A request handle of zero indicates error.
|
||||
* \return a nonzero request_handle that allows ending the request, or 0 on invalid query data. */
|
||||
uint32_t osmo_mslookup_client_request(struct osmo_mslookup_client *client,
|
||||
const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_query_handling *handling)
|
||||
{
|
||||
struct osmo_mslookup_client_request *r;
|
||||
struct osmo_mslookup_client_request *other;
|
||||
struct osmo_mslookup_client_method *m;
|
||||
|
||||
if (!osmo_mslookup_service_valid(query->service)
|
||||
|| !osmo_mslookup_id_valid(&query->id)) {
|
||||
char buf[256];
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "Invalid query: %s\n",
|
||||
osmo_mslookup_result_name_b(buf, sizeof(buf), query, NULL));
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = talloc_zero(client, struct osmo_mslookup_client_request);
|
||||
OSMO_ASSERT(r);
|
||||
|
||||
/* A request_handle of zero means error, so make sure we don't use a zero handle. */
|
||||
if (!client->next_request_handle)
|
||||
client->next_request_handle++;
|
||||
*r = (struct osmo_mslookup_client_request){
|
||||
.client = client,
|
||||
.query = *query,
|
||||
.handling = *handling,
|
||||
.request_handle = client->next_request_handle++,
|
||||
};
|
||||
|
||||
if (!r->handling.result_timeout_milliseconds)
|
||||
r->handling.result_timeout_milliseconds = r->handling.min_wait_milliseconds;
|
||||
if (!r->handling.result_timeout_milliseconds)
|
||||
r->handling.result_timeout_milliseconds = 1000;
|
||||
|
||||
/* Paranoia: make sure a request_handle exists only once, by expiring an already existing one. This is unlikely
|
||||
* to happen in practice: before we get near wrapping a uint32_t range, previous requests should long have
|
||||
* timed out or ended. */
|
||||
llist_for_each_entry(other, &client->requests, entry) {
|
||||
if (other->request_handle != r->request_handle)
|
||||
continue;
|
||||
osmo_mslookup_request_send_result(other, true);
|
||||
/* we're sure it exists only once. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now sure that the new request_handle does not exist a second time. */
|
||||
llist_add_tail(&r->entry, &client->requests);
|
||||
|
||||
if (r->handling.min_wait_milliseconds) {
|
||||
r->waiting_min_delay = true;
|
||||
set_timer(r, r->handling.min_wait_milliseconds);
|
||||
} else {
|
||||
set_timer(r, r->handling.result_timeout_milliseconds);
|
||||
}
|
||||
|
||||
/* Let the lookup implementations know */
|
||||
llist_for_each_entry(m, &client->lookup_methods, entry) {
|
||||
m->request(m, query, r->request_handle);
|
||||
}
|
||||
return r->request_handle;
|
||||
}
|
||||
|
||||
/*! End or cancel a subscriber lookup. This *must* be invoked exactly once per osmo_mslookup_client_request() invocation,
|
||||
* either after a lookup has concluded or to abort an ongoing lookup.
|
||||
* \param[in] request_handle The request_handle returned by an osmo_mslookup_client_request() invocation.
|
||||
*/
|
||||
void osmo_mslookup_client_request_cancel(struct osmo_mslookup_client *client, uint32_t request_handle)
|
||||
{
|
||||
struct osmo_mslookup_client_request *r = get_request(client, request_handle);
|
||||
if (!r)
|
||||
return;
|
||||
_osmo_mslookup_client_request_cleanup(r);
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/mslookup/mslookup_client.h>
|
||||
#include <osmocom/mslookup/mslookup_client_fake.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Fake mslookup method */
|
||||
|
||||
struct fake_lookup_state {
|
||||
struct osmo_mslookup_client *client;
|
||||
struct llist_head requests;
|
||||
struct osmo_timer_list async_response_timer;
|
||||
struct osmo_mslookup_fake_response *responses;
|
||||
size_t responses_len;
|
||||
};
|
||||
|
||||
struct fake_lookup_request {
|
||||
struct llist_head entry;
|
||||
uint32_t request_handle;
|
||||
struct osmo_mslookup_query query;
|
||||
struct timeval received_at;
|
||||
};
|
||||
|
||||
/*! Args for osmo_timer_schedule: seconds and microseconds. */
|
||||
#define ASYNC_RESPONSE_PERIOD 0, (1e6 / 10)
|
||||
static void fake_lookup_async_response(void *state);
|
||||
|
||||
static void fake_lookup_request(struct osmo_mslookup_client_method *method,
|
||||
const struct osmo_mslookup_query *query,
|
||||
uint32_t request_handle)
|
||||
{
|
||||
struct fake_lookup_state *state = method->priv;
|
||||
char buf[256];
|
||||
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_mslookup_result_name_b(buf, sizeof(buf), query, NULL));
|
||||
|
||||
/* A real implementation would send packets to some remote server.
|
||||
* Here this is simulated: add to the list of requests, which fake_lookup_async_response() will reply upon
|
||||
* according to the test data listing the replies that the test wants to generate. */
|
||||
|
||||
struct fake_lookup_request *r = talloc_zero(method->client, struct fake_lookup_request);
|
||||
*r = (struct fake_lookup_request){
|
||||
.request_handle = request_handle,
|
||||
.query = *query,
|
||||
};
|
||||
osmo_gettimeofday(&r->received_at, NULL);
|
||||
llist_add_tail(&r->entry, &state->requests);
|
||||
}
|
||||
|
||||
static void fake_lookup_request_cleanup(struct osmo_mslookup_client_method *method,
|
||||
uint32_t request_handle)
|
||||
{
|
||||
struct fake_lookup_state *state = method->priv;
|
||||
|
||||
/* Tear down any state associated with this handle. */
|
||||
struct fake_lookup_request *r;
|
||||
llist_for_each_entry(r, &state->requests, entry) {
|
||||
if (r->request_handle != request_handle)
|
||||
continue;
|
||||
llist_del(&r->entry);
|
||||
talloc_free(r);
|
||||
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s() ok\n", __func__);
|
||||
return;
|
||||
}
|
||||
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s() FAILED\n", __func__);
|
||||
}
|
||||
|
||||
static void fake_lookup_async_response(void *data)
|
||||
{
|
||||
struct fake_lookup_state *state = data;
|
||||
struct fake_lookup_request *req, *n;
|
||||
struct timeval now;
|
||||
char str[256];
|
||||
|
||||
osmo_gettimeofday(&now, NULL);
|
||||
|
||||
llist_for_each_entry_safe(req, n, &state->requests, entry) {
|
||||
struct osmo_mslookup_fake_response *resp;
|
||||
|
||||
for (resp = state->responses;
|
||||
(resp - state->responses) < state->responses_len;
|
||||
resp++) {
|
||||
struct timeval diff;
|
||||
|
||||
if (resp->sent)
|
||||
continue;
|
||||
if (osmo_mslookup_id_cmp(&req->query.id, &resp->for_id) != 0)
|
||||
continue;
|
||||
if (strcmp(req->query.service, resp->for_service) != 0)
|
||||
continue;
|
||||
|
||||
timersub(&now, &req->received_at, &diff);
|
||||
if (timercmp(&diff, &resp->time_to_reply, <))
|
||||
continue;
|
||||
|
||||
/* It's time to reply to this request. */
|
||||
LOGP(DMSLOOKUP, LOGL_DEBUG, "osmo_mslookup_client_rx_result(): %s\n",
|
||||
osmo_mslookup_result_name_b(str, sizeof(str), &req->query, &resp->result));
|
||||
osmo_mslookup_client_rx_result(state->client, req->request_handle, &resp->result);
|
||||
resp->sent = true;
|
||||
|
||||
/* The req will have been cleaned up now, so we must not iterate over state->responses anymore
|
||||
* with this req. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
osmo_timer_schedule(&state->async_response_timer, ASYNC_RESPONSE_PERIOD);
|
||||
}
|
||||
|
||||
struct osmo_mslookup_client_method *osmo_mslookup_client_add_fake(struct osmo_mslookup_client *client,
|
||||
struct osmo_mslookup_fake_response *responses,
|
||||
size_t responses_len)
|
||||
{
|
||||
struct osmo_mslookup_client_method *method = talloc_zero(client, struct osmo_mslookup_client_method);
|
||||
OSMO_ASSERT(method);
|
||||
|
||||
struct fake_lookup_state *state = talloc_zero(method, struct fake_lookup_state);
|
||||
OSMO_ASSERT(state);
|
||||
*state = (struct fake_lookup_state){
|
||||
.client = client,
|
||||
.responses = responses,
|
||||
.responses_len = responses_len,
|
||||
};
|
||||
INIT_LLIST_HEAD(&state->requests);
|
||||
|
||||
*method = (struct osmo_mslookup_client_method){
|
||||
.name = "fake",
|
||||
.priv = state,
|
||||
.request = fake_lookup_request,
|
||||
.request_cleanup = fake_lookup_request_cleanup,
|
||||
};
|
||||
|
||||
osmo_timer_setup(&state->async_response_timer, fake_lookup_async_response, state);
|
||||
osmo_mslookup_client_method_add(client, method);
|
||||
|
||||
osmo_timer_schedule(&state->async_response_timer, ASYNC_RESPONSE_PERIOD);
|
||||
return method;
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/mslookup/mdns.h>
|
||||
#include <osmocom/mslookup/mdns_sock.h>
|
||||
#include <osmocom/mslookup/mslookup_client.h>
|
||||
#include <osmocom/mslookup/mslookup_client_mdns.h>
|
||||
|
||||
struct osmo_mdns_method_state {
|
||||
/* Parameters passed by _add_method_dns() */
|
||||
struct osmo_sockaddr_str bind_addr;
|
||||
const char *domain_suffix;
|
||||
|
||||
struct osmo_mdns_sock *mc;
|
||||
|
||||
struct osmo_mslookup_client *client;
|
||||
struct llist_head requests;
|
||||
uint16_t next_packet_id;
|
||||
};
|
||||
|
||||
struct osmo_mdns_method_request {
|
||||
struct llist_head entry;
|
||||
uint32_t request_handle;
|
||||
struct osmo_mslookup_query query;
|
||||
uint16_t packet_id;
|
||||
};
|
||||
|
||||
static int request_handle_by_query(uint32_t *request_handle, struct osmo_mdns_method_state *state,
|
||||
struct osmo_mslookup_query *query, uint16_t packet_id)
|
||||
{
|
||||
struct osmo_mdns_method_request *request;
|
||||
|
||||
llist_for_each_entry(request, &state->requests, entry) {
|
||||
if (strcmp(request->query.service, query->service) != 0)
|
||||
continue;
|
||||
if (osmo_mslookup_id_cmp(&request->query.id, &query->id) != 0)
|
||||
continue;
|
||||
|
||||
/* Match! */
|
||||
*request_handle = request->request_handle;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int mdns_method_recv(struct osmo_fd *osmo_fd, unsigned int what)
|
||||
{
|
||||
struct osmo_mdns_method_state *state = osmo_fd->data;
|
||||
struct osmo_mslookup_result result;
|
||||
struct osmo_mslookup_query query;
|
||||
uint16_t packet_id;
|
||||
int n;
|
||||
uint8_t buffer[1024];
|
||||
uint32_t request_handle = 0;
|
||||
void *ctx = state;
|
||||
|
||||
n = read(osmo_fd->fd, buffer, sizeof(buffer));
|
||||
if (n < 0) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "failed to read from socket\n");
|
||||
return n;
|
||||
}
|
||||
|
||||
if (osmo_mdns_result_decode(ctx, buffer, n, &packet_id, &query, &result, state->domain_suffix) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (request_handle_by_query(&request_handle, state, &query, packet_id) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
osmo_mslookup_client_rx_result(state->client, request_handle, &result);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void mdns_method_request(struct osmo_mslookup_client_method *method, const struct osmo_mslookup_query *query,
|
||||
uint32_t request_handle)
|
||||
{
|
||||
char buf[256];
|
||||
struct osmo_mdns_method_state *state = method->priv;
|
||||
struct msgb *msg;
|
||||
struct osmo_mdns_method_request *r = talloc_zero(method->client, struct osmo_mdns_method_request);
|
||||
|
||||
*r = (struct osmo_mdns_method_request){
|
||||
.request_handle = request_handle,
|
||||
.query = *query,
|
||||
.packet_id = state->next_packet_id,
|
||||
};
|
||||
llist_add(&r->entry, &state->requests);
|
||||
state->next_packet_id++;
|
||||
|
||||
msg = osmo_mdns_query_encode(method->client, r->packet_id, query, state->domain_suffix);
|
||||
if (!msg) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "Cannot encode request: %s\n",
|
||||
osmo_mslookup_result_name_b(buf, sizeof(buf), query, NULL));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send over the wire */
|
||||
LOGP(DMSLOOKUP, LOGL_DEBUG, "sending mDNS query: %s.%s\n", query->service,
|
||||
osmo_mslookup_id_name_b(buf, sizeof(buf), &query->id));
|
||||
if (osmo_mdns_sock_send(state->mc, msg) == -1)
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "sending mDNS query failed\n");
|
||||
}
|
||||
|
||||
static void mdns_method_request_cleanup(struct osmo_mslookup_client_method *method, uint32_t request_handle)
|
||||
{
|
||||
struct osmo_mdns_method_state *state = method->priv;
|
||||
|
||||
/* Tear down any state associated with this handle. */
|
||||
struct osmo_mdns_method_request *r;
|
||||
llist_for_each_entry(r, &state->requests, entry) {
|
||||
if (r->request_handle != request_handle)
|
||||
continue;
|
||||
llist_del(&r->entry);
|
||||
talloc_free(r);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void mdns_method_destruct(struct osmo_mslookup_client_method *method)
|
||||
{
|
||||
struct osmo_mdns_method_state *state = method->priv;
|
||||
struct osmo_mdns_method_request *e, *n;
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
/* Drop all DNS lookup request state. Triggering a timeout event and cleanup for mslookup client users will
|
||||
* happen in the mslookup_client.c, we will simply stop responding from this lookup method. */
|
||||
llist_for_each_entry_safe(e, n, &state->requests, entry) {
|
||||
llist_del(&e->entry);
|
||||
}
|
||||
|
||||
osmo_mdns_sock_cleanup(state->mc);
|
||||
}
|
||||
|
||||
/*! Initialize the mDNS lookup method.
|
||||
* \param[in] client the client to attach the method to.
|
||||
* \param[in] ip IPv4 or IPv6 address string.
|
||||
* \param[in] port The port to bind to.
|
||||
* \param[in] initial_packet_id Used in the first mslookup query, then increased by one in each following query. All
|
||||
* servers answer to each query with the same packet ID. Set to -1 to use a random
|
||||
* initial ID (recommended unless you need deterministic output). This ID is for visually
|
||||
* distinguishing the packets in packet sniffers, the mslookup client uses not just the
|
||||
* ID, but all query parameters (service type, ID, ID type), to determine if a reply is
|
||||
* relevant.
|
||||
* \param[in] domain_suffix is appended to each domain in the queries to avoid colliding with the top-level domains
|
||||
* administrated by IANA. Example: "mdns.osmocom.org" */
|
||||
struct osmo_mslookup_client_method *osmo_mslookup_client_add_mdns(struct osmo_mslookup_client *client, const char *ip,
|
||||
uint16_t port, int initial_packet_id,
|
||||
const char *domain_suffix)
|
||||
{
|
||||
struct osmo_mdns_method_state *state;
|
||||
struct osmo_mslookup_client_method *m;
|
||||
|
||||
m = talloc_zero(client, struct osmo_mslookup_client_method);
|
||||
OSMO_ASSERT(m);
|
||||
|
||||
state = talloc_zero(m, struct osmo_mdns_method_state);
|
||||
OSMO_ASSERT(state);
|
||||
INIT_LLIST_HEAD(&state->requests);
|
||||
if (osmo_sockaddr_str_from_str(&state->bind_addr, ip, port)) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "mslookup mDNS: invalid address/port: %s %u\n",
|
||||
ip, port);
|
||||
goto error_cleanup;
|
||||
}
|
||||
|
||||
if (initial_packet_id == -1) {
|
||||
if (osmo_get_rand_id((uint8_t *)&state->next_packet_id, 2) < 0) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR, "mslookup mDNS: failed to generate random initial packet ID\n");
|
||||
goto error_cleanup;
|
||||
}
|
||||
} else
|
||||
state->next_packet_id = initial_packet_id;
|
||||
|
||||
state->client = client;
|
||||
state->domain_suffix = domain_suffix;
|
||||
|
||||
state->mc = osmo_mdns_sock_init(state, ip, port, mdns_method_recv, state, 0);
|
||||
if (!state->mc)
|
||||
goto error_cleanup;
|
||||
|
||||
*m = (struct osmo_mslookup_client_method){
|
||||
.name = "mDNS",
|
||||
.priv = state,
|
||||
.request = mdns_method_request,
|
||||
.request_cleanup = mdns_method_request_cleanup,
|
||||
.destruct = mdns_method_destruct,
|
||||
};
|
||||
|
||||
osmo_mslookup_client_method_add(client, m);
|
||||
return m;
|
||||
|
||||
error_cleanup:
|
||||
talloc_free(m);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct osmo_sockaddr_str *osmo_mslookup_client_method_mdns_get_bind_addr(struct osmo_mslookup_client_method *dns_method)
|
||||
{
|
||||
struct osmo_mdns_method_state *state;
|
||||
if (!dns_method || !dns_method->priv)
|
||||
return NULL;
|
||||
state = dns_method->priv;
|
||||
return &state->bind_addr;
|
||||
}
|
||||
|
||||
const char *osmo_mslookup_client_method_mdns_get_domain_suffix(struct osmo_mslookup_client_method *dns_method)
|
||||
{
|
||||
struct osmo_mdns_method_state *state;
|
||||
if (!dns_method || !dns_method->priv)
|
||||
return NULL;
|
||||
state = dns_method->priv;
|
||||
return state->domain_suffix;
|
||||
}
|
||||
@@ -1,885 +0,0 @@
|
||||
/*! \file osmo-mslookup-client.c
|
||||
* Distributed GSM: find the location of subscribers, for example by multicast DNS,
|
||||
* to obtain HLR, SIP or SMPP server addresses (or arbitrary service names).
|
||||
*/
|
||||
/*
|
||||
* (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* (C) 2019 by Neels Hofmeyr <neels@hofmeyr.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <talloc.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/mslookup/mslookup_client.h>
|
||||
#include <osmocom/mslookup/mslookup_client_mdns.h>
|
||||
#include <osmocom/mslookup/mdns_sock.h>
|
||||
#include <osmocom/mslookup/mdns.h>
|
||||
|
||||
#define CSV_HEADERS "query\tresult\tlast\tage\tv4_ip\tv4_port\tv6_ip\tv6_port"
|
||||
|
||||
static void print_version(void)
|
||||
{
|
||||
printf("osmo-mslookup-client version %s\n", PACKAGE_VERSION);
|
||||
printf("\n"
|
||||
"Copyright (C) 2019 by sysmocom - s.f.m.c. GmbH\n"
|
||||
"Copyright (C) 2019 by Neels Hofmeyr <neels@hofmeyr.de>\n"
|
||||
"This program is free software; you can redistribute it and/or modify\n"
|
||||
"it under the terms of the GNU General Public License as published by\n"
|
||||
"the Free Software Foundation; either version 2 of the License, or\n"
|
||||
"(at your option) any later version.\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
print_version();
|
||||
printf(
|
||||
"Standalone mslookup client for Distributed GSM\n"
|
||||
"\n"
|
||||
"Receiving mslookup results means listening for responses on a socket. Often,\n"
|
||||
"integration (e.g. FreeSwitch dialplan.py) makes it hard to select() on a socket\n"
|
||||
"to read responses, because that interferes with the main program (e.g.\n"
|
||||
"FreeSwitch's dialplan.py seems to be integrated with an own select() main loop\n"
|
||||
"that interferes with osmo_select_main(), or an smpp.py uses\n"
|
||||
"smpplib.client.listen() as main loop, etc.).\n"
|
||||
"\n"
|
||||
"This program provides a trivial solution, by outsourcing the mslookup main loop\n"
|
||||
"to a separate process. Communication is done via cmdline arg and stdout pipe or\n"
|
||||
"a (blocking) unix domain socket, results are returned in CSV or JSON format.\n"
|
||||
"\n"
|
||||
"This can be done one-shot, i.e. exit as soon as the response has been\n"
|
||||
"determined, or in daemon form, i.e. continuously listen for requests and return\n"
|
||||
"responses.\n"
|
||||
"\n"
|
||||
"About running a local daemon: it is unintuitive to connect to a socket to solve\n"
|
||||
"a problem of reading from a socket -- it seems like just more of the same\n"
|
||||
"problem. The reasons why the daemon is in fact useful are:\n"
|
||||
"- The osmo-mslookup-client daemon will return only those results matching\n"
|
||||
" requests issued on that socket connection.\n"
|
||||
"- A program can simply blockingly recv() from the osmo-mslookup-client socket\n"
|
||||
" instead of needing to run osmo_select_main() so that libosmo-mslookup is able\n"
|
||||
" to asynchronously receive responses from remote servers.\n"
|
||||
"- Only one long-lived multicast socket needs to be opened instead of a new\n"
|
||||
" socket for each request.\n"
|
||||
"\n"
|
||||
"Output is in CSV or json, see --format. The default is tab-separated CSV\n"
|
||||
"with these columns:\n"
|
||||
CSV_HEADERS "\n"
|
||||
"\n"
|
||||
"One-shot operation example:\n"
|
||||
"$ osmo-mslookup-client 1000-@sip.voice.12345.msisdn -f json\n"
|
||||
"{\"query\": \"sip.voice.12345.msisdn\", \"result\": \"result\", \"last\": true, \"age\": 5, \"v4\": [\"1.2.3.7\", \"23\"]}\n"
|
||||
"$\n"
|
||||
"\n"
|
||||
"Daemon operation example:\n"
|
||||
"$ osmo-mslookup-client -s /tmp/mslookup -d\n"
|
||||
"(and a client program then connects to /tmp/mslookup, find an implementation\n"
|
||||
"example below)\n"
|
||||
"\n"
|
||||
"Integrating with calling programs can be done by:\n"
|
||||
"- call osmo-mslookup-client with the query string as argument.\n"
|
||||
" It will open a multicast DNS socket, send out a query and wait for the\n"
|
||||
" matching response. It will print the result on stdout and exit.\n"
|
||||
" This method launches a new process for every mslookup query,\n"
|
||||
" and creates a short-lived multicast listener for each invocation.\n"
|
||||
" This is fine for low activity, but does not scale well.\n"
|
||||
"\n"
|
||||
"- invoke osmo-mslookup-client --socket /tmp/mslookup -d.\n"
|
||||
" Individual queries can be sent by connecting to that unix domain socket,\n"
|
||||
" blockingly reading the response when it arrives and disconnecting.\n"
|
||||
" This way only one process keeps one multicast listener open.\n"
|
||||
" Callers can connect to this socket without spawning processes.\n"
|
||||
" This is recommended for scale.\n"
|
||||
"\n"
|
||||
"Python example clients for {CSV,JSON}x{cmdline,socket} can be found here:\n"
|
||||
"http://git.osmocom.org/osmo-hlr/tree/contrib/dgsm/osmo-mslookup-pipe.py\n"
|
||||
"http://git.osmocom.org/osmo-hlr/tree/contrib/dgsm/osmo-mslookup-socket.py\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
"\n"
|
||||
"[[delay-][timeout]@]service.number.id\n"
|
||||
" A service query string with optional individual timeout.\n"
|
||||
" The same format is also used on a daemon socket, if any.\n"
|
||||
" The timeout consists of the min-delay and the timeout numbers,\n"
|
||||
" corresponding to the --min-delay and --timeout options, in milliseconds.\n"
|
||||
" These options apply if a query string lacks own numbers.\n"
|
||||
" Examples:\n"
|
||||
" gsup.hlr.1234567.imsi Use cmdline timeout settings\n"
|
||||
" 5000@gsup.hlr.1234567.imsi Return N results for 5 seconds\n"
|
||||
" 1000-5000@sip.voice.123.msisdn Same, but silent for first second\n"
|
||||
" 10000-@smpp.sms.567.msisdn Return 1 result after 10 seconds\n"
|
||||
"\n"
|
||||
"--format -f csv (default)\n"
|
||||
" Format result lines in CSV format.\n"
|
||||
"--no-csv-headers -H\n"
|
||||
" If the format is 'csv', by default, the first output line prints the\n"
|
||||
" CSV headers used for CSV output format. This option disables these CSV\n"
|
||||
" headers.\n"
|
||||
"\n"
|
||||
"--format -f json\n"
|
||||
" Format result lines in json instead of semicolon separated, like:\n"
|
||||
" {\"query\": \"sip.voice.12345.msisdn\", \"result\": \"ok\", \"v4\": [\"10.9.8.7\", \"5060\"]}\n"
|
||||
"\n"
|
||||
"--daemon -d\n"
|
||||
" Keep running after a request has been serviced\n"
|
||||
"\n"
|
||||
"--mdns-ip -m " OSMO_MSLOOKUP_MDNS_IP4 " -m " OSMO_MSLOOKUP_MDNS_IP6 "\n"
|
||||
"--mdns-port -M " OSMO_STRINGIFY_VAL(OSMO_MSLOOKUP_MDNS_PORT) "\n"
|
||||
" Set multicast IP address / port to send mDNS requests and listen for\n"
|
||||
" mDNS reponses\n"
|
||||
"--mdns-domain-suffix -D " OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT "\n"
|
||||
" Append this suffix to each mDNS query's domain to avoid colliding with the\n"
|
||||
" top-level domains administrated by IANA.\n"
|
||||
"\n"
|
||||
"--min-delay -t 1000 (in milliseconds)\n"
|
||||
" Set minimum delay to wait before returning any results.\n"
|
||||
" When this timeout has elapsed, the best current result is returned,\n"
|
||||
" if any is available.\n"
|
||||
" Responses arriving after the min-delay has elapsed which have a younger\n"
|
||||
" age than previous results are returned immediately.\n"
|
||||
" Note: When a response with age of zero comes in, the result is returned\n"
|
||||
" immediately and the request is discarded: non-daemon mode exits, daemon\n"
|
||||
" mode ignores later results.\n"
|
||||
"\n"
|
||||
"--timeout -T 1000 (in milliseconds)\n"
|
||||
" Set timeout after which to stop listening for responses.\n"
|
||||
" If this is smaller than -t, the value from -t will be used for -T as well.\n"
|
||||
" Note: When a response with age of zero comes in, the result is returned\n"
|
||||
" immediately and the request is discarded: non-daemon mode exits, daemon\n"
|
||||
" mode ignores later results.\n"
|
||||
"\n"
|
||||
"--socket -s /path/to/unix-domain-socket\n"
|
||||
" Listen to requests from and write responses to a UNIX domain socket.\n"
|
||||
"\n"
|
||||
"--send -S <query> <age> <ip1> <port1> <ip2> <port2>\n"
|
||||
" Do not query, but send an mslookup result. This is useful only for\n"
|
||||
" testing. Examples:\n"
|
||||
" --send foo.123.msisdn 300 23.42.17.11 1234\n"
|
||||
" --send foo.123.msisdn 300 2323:4242:1717:1111::42 1234\n"
|
||||
" --send foo.123.msisdn 300 23.42.17.11 1234 2323:4242:1717:1111::42 1234\n"
|
||||
"\n"
|
||||
"--quiet -q\n"
|
||||
" Do not print errors to stderr, do not log to stderr.\n"
|
||||
"\n"
|
||||
"--help -h\n"
|
||||
" This help\n"
|
||||
);
|
||||
}
|
||||
|
||||
enum result_format {
|
||||
FORMAT_CSV = 0,
|
||||
FORMAT_JSON,
|
||||
};
|
||||
|
||||
static struct {
|
||||
bool daemon;
|
||||
struct osmo_sockaddr_str mdns_addr;
|
||||
uint32_t min_delay;
|
||||
uint32_t timeout;
|
||||
const char *socket_path;
|
||||
const char *format_str;
|
||||
const char *mdns_domain_suffix;
|
||||
bool csv_headers;
|
||||
bool send;
|
||||
bool quiet;
|
||||
} cmdline_opts = {
|
||||
.mdns_addr = { .af=AF_INET, .ip=OSMO_MSLOOKUP_MDNS_IP4, .port=OSMO_MSLOOKUP_MDNS_PORT },
|
||||
.min_delay = 1000,
|
||||
.timeout = 1000,
|
||||
.csv_headers = true,
|
||||
.mdns_domain_suffix = OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT,
|
||||
};
|
||||
|
||||
#define print_error(fmt, args...) do { \
|
||||
if (!cmdline_opts.quiet) \
|
||||
fprintf(stderr, fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
char g_buf[1024];
|
||||
|
||||
long long int parse_int(long long int minval, long long int maxval, const char *arg, int *rc)
|
||||
{
|
||||
long long int val;
|
||||
char *endptr;
|
||||
if (rc)
|
||||
*rc = -1;
|
||||
if (!arg)
|
||||
return -1;
|
||||
errno = 0;
|
||||
val = strtoll(arg, &endptr, 10);
|
||||
if (errno || val < minval || val > maxval || *endptr)
|
||||
return -1;
|
||||
if (rc)
|
||||
*rc = 0;
|
||||
return val;
|
||||
}
|
||||
|
||||
int cb_doing_nothing(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --send: Just send a response, for manual testing. */
|
||||
int do_send(int argc, char ** argv)
|
||||
{
|
||||
/* parse args <query> <age> <v4-ip> <v4-port> <v6-ip> <v6-port> */
|
||||
#define ARG(NR) ((argc > NR)? argv[NR] : NULL)
|
||||
const char *query_str = ARG(0);
|
||||
const char *age_str = ARG(1);
|
||||
const char *ip_strs[2][2] = {
|
||||
{ ARG(2), ARG(3) },
|
||||
{ ARG(4), ARG(5) },
|
||||
};
|
||||
struct osmo_mslookup_query q = {};
|
||||
struct osmo_mslookup_result r = { .rc = OSMO_MSLOOKUP_RC_RESULT };
|
||||
int i;
|
||||
int rc;
|
||||
void *ctx = talloc_named_const(NULL, 0, __func__);
|
||||
struct osmo_mdns_sock *sock;
|
||||
|
||||
if (!query_str) {
|
||||
print_error("--send needs a query string like foo.123456.imsi\n");
|
||||
return 1;
|
||||
}
|
||||
if (osmo_mslookup_query_init_from_domain_str(&q, query_str)) {
|
||||
print_error("Invalid query string '%s', need a query string like foo.123456.imsi\n",
|
||||
query_str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!age_str) {
|
||||
print_error("--send needs an age\n");
|
||||
return 1;
|
||||
}
|
||||
r.age = parse_int(0, UINT32_MAX, age_str, &rc);
|
||||
if (rc) {
|
||||
print_error("invalid age\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
struct osmo_sockaddr_str addr;
|
||||
uint16_t port;
|
||||
if (!ip_strs[i][0])
|
||||
continue;
|
||||
port = parse_int(1, 65535, ip_strs[i][1] ? : "2342", &rc);
|
||||
if (rc) {
|
||||
print_error("invalid port: %s\n", ip_strs[i][1] ? : "NULL");
|
||||
return 1;
|
||||
}
|
||||
if (osmo_sockaddr_str_from_str(&addr, ip_strs[i][0], port)) {
|
||||
print_error("invalid IP addr: %s\n", ip_strs[i][0]);
|
||||
return 1;
|
||||
}
|
||||
if (addr.af == AF_INET)
|
||||
r.host_v4 = addr;
|
||||
else
|
||||
r.host_v6 = addr;
|
||||
}
|
||||
|
||||
printf("Sending mDNS to " OSMO_SOCKADDR_STR_FMT ": %s\n", OSMO_SOCKADDR_STR_FMT_ARGS(&cmdline_opts.mdns_addr),
|
||||
osmo_mslookup_result_name_c(ctx, &q, &r));
|
||||
|
||||
rc = 1;
|
||||
sock = osmo_mdns_sock_init(ctx, cmdline_opts.mdns_addr.ip, cmdline_opts.mdns_addr.port,
|
||||
cb_doing_nothing, NULL, 0);
|
||||
if (!sock) {
|
||||
print_error("unable to open mDNS socket\n");
|
||||
goto exit_cleanup;
|
||||
}
|
||||
|
||||
struct msgb *msg = osmo_mdns_result_encode(ctx, 0, &q, &r, cmdline_opts.mdns_domain_suffix);
|
||||
if (!msg) {
|
||||
print_error("unable to encode mDNS response\n");
|
||||
goto exit_cleanup_sock;
|
||||
}
|
||||
|
||||
if (osmo_mdns_sock_send(sock, msg)) {
|
||||
print_error("unable to send mDNS message\n");
|
||||
goto exit_cleanup_sock;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
exit_cleanup_sock:
|
||||
osmo_mdns_sock_cleanup(sock);
|
||||
exit_cleanup:
|
||||
talloc_free(ctx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct {
|
||||
void *ctx;
|
||||
unsigned int requests_handled;
|
||||
struct osmo_fd socket_ofd;
|
||||
struct osmo_mslookup_client *mslookup_client;
|
||||
struct llist_head queries;
|
||||
struct llist_head socket_clients;
|
||||
enum result_format format;
|
||||
} globals = {
|
||||
.queries = LLIST_HEAD_INIT(globals.queries),
|
||||
.socket_clients = LLIST_HEAD_INIT(globals.socket_clients),
|
||||
};
|
||||
|
||||
typedef void (*formatter_t)(char *buf, size_t buflen, const char *query_str, const struct osmo_mslookup_result *r);
|
||||
|
||||
void formatter_csv(char *buf, size_t buflen, const char *query_str, const struct osmo_mslookup_result *r)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf=buf, .len=buflen };
|
||||
OSMO_STRBUF_PRINTF(sb, "%s", query_str);
|
||||
|
||||
if (!r)
|
||||
OSMO_STRBUF_PRINTF(sb, "\tERROR\t\t\t\t\t\t");
|
||||
else {
|
||||
OSMO_STRBUF_PRINTF(sb, "\t%s", osmo_mslookup_result_code_name(r->rc));
|
||||
OSMO_STRBUF_PRINTF(sb, "\t%s", r->last ? "last" : "not-last");
|
||||
OSMO_STRBUF_PRINTF(sb, "\t%u", r->age);
|
||||
switch (r->rc) {
|
||||
case OSMO_MSLOOKUP_RC_RESULT:
|
||||
if (osmo_sockaddr_str_is_nonzero(&r->host_v4))
|
||||
OSMO_STRBUF_PRINTF(sb, "\t%s\t%u", r->host_v4.ip, r->host_v4.port);
|
||||
else
|
||||
OSMO_STRBUF_PRINTF(sb, "\t\t");
|
||||
if (osmo_sockaddr_str_is_nonzero(&r->host_v6))
|
||||
OSMO_STRBUF_PRINTF(sb, "\t%s\t%u", r->host_v6.ip, r->host_v6.port);
|
||||
else
|
||||
OSMO_STRBUF_PRINTF(sb, "\t\t");
|
||||
break;
|
||||
default:
|
||||
OSMO_STRBUF_PRINTF(sb, "\t\t\t\t\t");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void formatter_json(char *buf, size_t buflen, const char *query_str, const struct osmo_mslookup_result *r)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf=buf, .len=buflen };
|
||||
OSMO_STRBUF_PRINTF(sb, "{\"query\": \"%s\"", query_str);
|
||||
|
||||
if (!r)
|
||||
OSMO_STRBUF_PRINTF(sb, ", \"result\": \"ERROR\"");
|
||||
else {
|
||||
OSMO_STRBUF_PRINTF(sb, ", \"result\": \"%s\"", osmo_mslookup_result_code_name(r->rc));
|
||||
OSMO_STRBUF_PRINTF(sb, ", \"last\": %s", r->last ? "true" : "false");
|
||||
OSMO_STRBUF_PRINTF(sb, ", \"age\": %u", r->age);
|
||||
if (r->rc == OSMO_MSLOOKUP_RC_RESULT) {
|
||||
if (osmo_sockaddr_str_is_nonzero(&r->host_v4))
|
||||
OSMO_STRBUF_PRINTF(sb, ", \"v4\": [\"%s\", \"%u\"]", r->host_v4.ip, r->host_v4.port);
|
||||
if (osmo_sockaddr_str_is_nonzero(&r->host_v6))
|
||||
OSMO_STRBUF_PRINTF(sb, ", \"v6\": [\"%s\", \"%u\"]", r->host_v6.ip, r->host_v6.port);
|
||||
}
|
||||
}
|
||||
OSMO_STRBUF_PRINTF(sb, "}");
|
||||
}
|
||||
|
||||
formatter_t formatters[] = {
|
||||
[FORMAT_CSV] = formatter_csv,
|
||||
[FORMAT_JSON] = formatter_json,
|
||||
};
|
||||
|
||||
void respond_str_stdout(const char *str) {
|
||||
fprintf(stdout, "%s\n", str);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void start_query_str(const char *query_str);
|
||||
void start_query_strs(char **query_strs, size_t query_strs_len);
|
||||
|
||||
struct socket_client {
|
||||
struct llist_head entry;
|
||||
struct osmo_fd ofd;
|
||||
char query_str[1024];
|
||||
};
|
||||
|
||||
static void socket_client_close(struct socket_client *c)
|
||||
{
|
||||
struct osmo_fd *ofd = &c->ofd;
|
||||
|
||||
close(ofd->fd);
|
||||
ofd->fd = -1;
|
||||
osmo_fd_unregister(ofd);
|
||||
|
||||
llist_del(&c->entry);
|
||||
talloc_free(c);
|
||||
}
|
||||
|
||||
void socket_client_respond_result(struct socket_client *c, const char *response)
|
||||
{
|
||||
write(c->ofd.fd, response, strlen(response));
|
||||
}
|
||||
|
||||
static int socket_read_cb(struct osmo_fd *ofd)
|
||||
{
|
||||
struct socket_client *c = ofd->data;
|
||||
int rc;
|
||||
char rxbuf[1024];
|
||||
char *query_with_timeout;
|
||||
char *query_str;
|
||||
char *at;
|
||||
|
||||
rc = recv(ofd->fd, rxbuf, sizeof(rxbuf), 0);
|
||||
if (rc == 0)
|
||||
goto close;
|
||||
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
goto close;
|
||||
}
|
||||
|
||||
if (rc >= sizeof(c->query_str))
|
||||
goto close;
|
||||
|
||||
rxbuf[rc] = '\0';
|
||||
query_with_timeout = strtok(rxbuf, "\r\n");
|
||||
if (!query_with_timeout) {
|
||||
print_error("ERROR: failed to read line from socket\n");
|
||||
goto close;
|
||||
}
|
||||
|
||||
at = strchr(query_with_timeout, '@');
|
||||
query_str = at ? at + 1 : query_with_timeout;
|
||||
|
||||
if (c->query_str[0]) {
|
||||
print_error("ERROR: Only one query per client connect is allowed;"
|
||||
" received '%s' and '%s' on the same connection\n",
|
||||
c->query_str, query_str);
|
||||
formatters[globals.format](g_buf, sizeof(g_buf), query_str, NULL);
|
||||
socket_client_respond_result(c, g_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
OSMO_STRLCPY_ARRAY(c->query_str, query_str);
|
||||
start_query_str(query_with_timeout);
|
||||
printf("query: %s\n", query_with_timeout);
|
||||
return rc;
|
||||
|
||||
close:
|
||||
socket_client_close(c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int socket_cb(struct osmo_fd *ofd, unsigned int flags)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (flags & OSMO_FD_READ)
|
||||
rc = socket_read_cb(ofd);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int socket_accept(struct osmo_fd *ofd, unsigned int flags)
|
||||
{
|
||||
struct socket_client *c;
|
||||
struct sockaddr_un un_addr;
|
||||
socklen_t len;
|
||||
int rc;
|
||||
|
||||
len = sizeof(un_addr);
|
||||
rc = accept(ofd->fd, (struct sockaddr*)&un_addr, &len);
|
||||
if (rc < 0) {
|
||||
print_error("Failed to accept a new connection\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
c = talloc_zero(globals.ctx, struct socket_client);
|
||||
OSMO_ASSERT(c);
|
||||
c->ofd.fd = rc;
|
||||
c->ofd.when = OSMO_FD_READ;
|
||||
c->ofd.cb = socket_cb;
|
||||
c->ofd.data = c;
|
||||
|
||||
if (osmo_fd_register(&c->ofd) != 0) {
|
||||
print_error("Failed to register new connection fd\n");
|
||||
close(c->ofd.fd);
|
||||
c->ofd.fd = -1;
|
||||
talloc_free(c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
llist_add(&c->entry, &globals.socket_clients);
|
||||
|
||||
if (globals.format == FORMAT_CSV && cmdline_opts.csv_headers)
|
||||
write(c->ofd.fd, CSV_HEADERS, strlen(CSV_HEADERS));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_init(const char *sock_path)
|
||||
{
|
||||
struct osmo_fd *ofd = &globals.socket_ofd;
|
||||
int rc;
|
||||
|
||||
ofd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, sock_path, OSMO_SOCK_F_BIND);
|
||||
if (ofd->fd < 0) {
|
||||
print_error("Could not create unix socket: %s: %s\n", sock_path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ofd->when = OSMO_FD_READ;
|
||||
ofd->cb = socket_accept;
|
||||
|
||||
rc = osmo_fd_register(ofd);
|
||||
if (rc < 0) {
|
||||
print_error("Could not register listen fd: %d\n", rc);
|
||||
close(ofd->fd);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void socket_close()
|
||||
{
|
||||
struct socket_client *c, *n;
|
||||
llist_for_each_entry_safe(c, n, &globals.socket_clients, entry)
|
||||
socket_client_close(c);
|
||||
if (osmo_fd_is_registered(&globals.socket_ofd)) {
|
||||
close(globals.socket_ofd.fd);
|
||||
globals.socket_ofd.fd = -1;
|
||||
osmo_fd_unregister(&globals.socket_ofd);
|
||||
}
|
||||
}
|
||||
|
||||
struct query {
|
||||
struct llist_head entry;
|
||||
|
||||
char query_str[128];
|
||||
struct osmo_mslookup_query query;
|
||||
uint32_t handle;
|
||||
};
|
||||
|
||||
void respond_result(const char *query_str, const struct osmo_mslookup_result *r)
|
||||
{
|
||||
struct socket_client *c, *n;
|
||||
formatters[globals.format](g_buf, sizeof(g_buf), query_str, r);
|
||||
respond_str_stdout(g_buf);
|
||||
|
||||
llist_for_each_entry_safe(c, n, &globals.socket_clients, entry) {
|
||||
if (!strcmp(query_str, c->query_str)) {
|
||||
socket_client_respond_result(c, g_buf);
|
||||
if (!r || r->last)
|
||||
socket_client_close(c);
|
||||
}
|
||||
}
|
||||
if (!r || r->last)
|
||||
globals.requests_handled++;
|
||||
}
|
||||
|
||||
void respond_err(const char *query_str)
|
||||
{
|
||||
respond_result(query_str, NULL);
|
||||
}
|
||||
|
||||
struct query *query_by_handle(uint32_t request_handle)
|
||||
{
|
||||
struct query *q;
|
||||
llist_for_each_entry(q, &globals.queries, entry) {
|
||||
if (request_handle == q->handle)
|
||||
return q;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mslookup_result_cb(struct osmo_mslookup_client *client,
|
||||
uint32_t request_handle,
|
||||
const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_result *result)
|
||||
{
|
||||
struct query *q = query_by_handle(request_handle);
|
||||
if (!q)
|
||||
return;
|
||||
respond_result(q->query_str, result);
|
||||
if (result->last) {
|
||||
llist_del(&q->entry);
|
||||
talloc_free(q);
|
||||
}
|
||||
}
|
||||
|
||||
void start_query_str(const char *query_str)
|
||||
{
|
||||
struct query *q;
|
||||
const char *domain_str = query_str;
|
||||
char *at;
|
||||
struct osmo_mslookup_query_handling h = {
|
||||
.min_wait_milliseconds = cmdline_opts.min_delay,
|
||||
.result_timeout_milliseconds = cmdline_opts.timeout,
|
||||
.result_cb = mslookup_result_cb,
|
||||
};
|
||||
|
||||
at = strchr(query_str, '@');
|
||||
if (at) {
|
||||
int rc;
|
||||
char timeouts[16];
|
||||
char *dash;
|
||||
char *timeout;
|
||||
|
||||
domain_str = at + 1;
|
||||
|
||||
h.min_wait_milliseconds = h.result_timeout_milliseconds = 0;
|
||||
|
||||
if (osmo_print_n(timeouts, sizeof(timeouts), query_str, at - query_str) >= sizeof(timeouts)) {
|
||||
print_error("ERROR: timeouts part too long in query string\n");
|
||||
respond_err(domain_str);
|
||||
return;
|
||||
}
|
||||
|
||||
dash = strchr(timeouts, '-');
|
||||
if (dash) {
|
||||
char min_delay[16];
|
||||
osmo_print_n(min_delay, sizeof(min_delay), timeouts, dash - timeouts);
|
||||
h.min_wait_milliseconds = parse_int(0, UINT32_MAX, min_delay, &rc);
|
||||
if (rc) {
|
||||
print_error("ERROR: invalid min-delay number: %s\n", min_delay);
|
||||
respond_err(domain_str);
|
||||
return;
|
||||
}
|
||||
timeout = dash + 1;
|
||||
} else {
|
||||
timeout = timeouts;
|
||||
}
|
||||
if (*timeout) {
|
||||
h.result_timeout_milliseconds = parse_int(0, UINT32_MAX, timeout, &rc);
|
||||
if (rc) {
|
||||
print_error("ERROR: invalid timeout number: %s\n", timeout);
|
||||
respond_err(domain_str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen(domain_str) >= sizeof(q->query_str)) {
|
||||
print_error("ERROR: query string is too long: '%s'\n", domain_str);
|
||||
respond_err(domain_str);
|
||||
return;
|
||||
}
|
||||
|
||||
q = talloc_zero(globals.ctx, struct query);
|
||||
OSMO_ASSERT(q);
|
||||
OSMO_STRLCPY_ARRAY(q->query_str, domain_str);
|
||||
|
||||
if (osmo_mslookup_query_init_from_domain_str(&q->query, q->query_str)) {
|
||||
print_error("ERROR: cannot parse query string: '%s'\n", domain_str);
|
||||
respond_err(domain_str);
|
||||
talloc_free(q);
|
||||
return;
|
||||
}
|
||||
|
||||
q->handle = osmo_mslookup_client_request(globals.mslookup_client, &q->query, &h);
|
||||
if (!q->handle) {
|
||||
print_error("ERROR: cannot send query: '%s'\n", domain_str);
|
||||
respond_err(domain_str);
|
||||
talloc_free(q);
|
||||
return;
|
||||
}
|
||||
|
||||
llist_add(&q->entry, &globals.queries);
|
||||
}
|
||||
|
||||
void start_query_strs(char **query_strs, size_t query_strs_len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < query_strs_len; i++)
|
||||
start_query_str(query_strs[i]);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc = EXIT_FAILURE;
|
||||
globals.ctx = talloc_named_const(NULL, 0, "osmo-mslookup-client");
|
||||
|
||||
osmo_init_logging2(globals.ctx, NULL);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
|
||||
log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
|
||||
log_set_print_level(osmo_stderr_target, 1);
|
||||
log_set_print_category(osmo_stderr_target, 1);
|
||||
log_set_print_category_hex(osmo_stderr_target, 0);
|
||||
log_set_print_extended_timestamp(osmo_stderr_target, 1);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
|
||||
while (1) {
|
||||
int c;
|
||||
long long int val;
|
||||
char *endptr;
|
||||
int option_index = 0;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{ "format", 1, 0, 'f' },
|
||||
{ "no-csv-headers", 0, 0, 'H' },
|
||||
{ "daemon", 0, 0, 'd' },
|
||||
{ "mdns-ip", 1, 0, 'm' },
|
||||
{ "mdns-port", 1, 0, 'M' },
|
||||
{ "mdns-domain-suffix", 1, 0, 'D' },
|
||||
{ "timeout", 1, 0, 'T' },
|
||||
{ "min-delay", 1, 0, 't' },
|
||||
{ "socket", 1, 0, 's' },
|
||||
{ "send", 0, 0, 'S' },
|
||||
{ "quiet", 0, 0, 'q' },
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{}
|
||||
};
|
||||
|
||||
#define PARSE_INT(TARGET, MINVAL, MAXVAL) do { \
|
||||
int _rc; \
|
||||
TARGET = parse_int(MINVAL, MAXVAL, optarg, &_rc); \
|
||||
if (_rc) { \
|
||||
print_error("Invalid " #TARGET ": %s\n", optarg); \
|
||||
goto program_exit; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
c = getopt_long(argc, argv, "f:Hdm:M:D:t:T:s:SqhV", long_options, &option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'f':
|
||||
cmdline_opts.format_str = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
cmdline_opts.csv_headers = false;
|
||||
break;
|
||||
case 'd':
|
||||
cmdline_opts.daemon = true;
|
||||
break;
|
||||
case 'm':
|
||||
if (osmo_sockaddr_str_from_str(&cmdline_opts.mdns_addr, optarg, cmdline_opts.mdns_addr.port)
|
||||
|| !osmo_sockaddr_str_is_nonzero(&cmdline_opts.mdns_addr)) {
|
||||
print_error("Invalid mDNS IP address: %s\n", optarg);
|
||||
goto program_exit;
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
PARSE_INT(cmdline_opts.mdns_addr.port, 1, 65535);
|
||||
break;
|
||||
case 'D':
|
||||
cmdline_opts.mdns_domain_suffix = optarg;
|
||||
break;
|
||||
case 't':
|
||||
PARSE_INT(cmdline_opts.min_delay, 0, UINT32_MAX);
|
||||
break;
|
||||
case 'T':
|
||||
PARSE_INT(cmdline_opts.timeout, 0, UINT32_MAX);
|
||||
break;
|
||||
case 's':
|
||||
cmdline_opts.socket_path = optarg;
|
||||
break;
|
||||
case 'S':
|
||||
cmdline_opts.send = true;
|
||||
break;
|
||||
case 'q':
|
||||
cmdline_opts.quiet = true;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
print_help();
|
||||
rc = 0;
|
||||
goto program_exit;
|
||||
case 'V':
|
||||
print_version();
|
||||
rc = 0;
|
||||
goto program_exit;
|
||||
|
||||
default:
|
||||
/* catch unknown options *as well as* missing arguments. */
|
||||
print_error("Error in command line options. Exiting.\n");
|
||||
goto program_exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmdline_opts.send) {
|
||||
if (cmdline_opts.daemon || cmdline_opts.format_str || cmdline_opts.socket_path) {
|
||||
print_error("--send option cannot have any listening related args.");
|
||||
}
|
||||
rc = do_send(argc - optind, argv + optind);
|
||||
goto program_exit;
|
||||
}
|
||||
|
||||
if (!cmdline_opts.daemon && !(argc - optind)) {
|
||||
print_help();
|
||||
goto program_exit;
|
||||
}
|
||||
|
||||
if (cmdline_opts.daemon && !cmdline_opts.timeout) {
|
||||
print_error("In daemon mode, --timeout must not be zero.\n");
|
||||
goto program_exit;
|
||||
}
|
||||
|
||||
if (cmdline_opts.quiet)
|
||||
log_target_destroy(osmo_stderr_target);
|
||||
|
||||
if (cmdline_opts.format_str) {
|
||||
if (osmo_str_startswith("json", cmdline_opts.format_str))
|
||||
globals.format = FORMAT_JSON;
|
||||
else if (osmo_str_startswith("csv", cmdline_opts.format_str))
|
||||
globals.format = FORMAT_CSV;
|
||||
else {
|
||||
print_error("Invalid format: %s\n", cmdline_opts.format_str);
|
||||
goto program_exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (globals.format == FORMAT_CSV && cmdline_opts.csv_headers)
|
||||
respond_str_stdout(CSV_HEADERS);
|
||||
|
||||
globals.mslookup_client = osmo_mslookup_client_new(globals.ctx);
|
||||
if (!globals.mslookup_client
|
||||
|| !osmo_mslookup_client_add_mdns(globals.mslookup_client,
|
||||
cmdline_opts.mdns_addr.ip, cmdline_opts.mdns_addr.port,
|
||||
-1, cmdline_opts.mdns_domain_suffix)) {
|
||||
print_error("Failed to start mDNS client\n");
|
||||
goto program_exit;
|
||||
}
|
||||
|
||||
if (cmdline_opts.socket_path) {
|
||||
if (socket_init(cmdline_opts.socket_path))
|
||||
goto program_exit;
|
||||
}
|
||||
|
||||
start_query_strs(&argv[optind], argc - optind);
|
||||
|
||||
while (1) {
|
||||
osmo_select_main_ctx(0);
|
||||
|
||||
if (!cmdline_opts.daemon
|
||||
&& globals.requests_handled
|
||||
&& llist_empty(&globals.queries))
|
||||
break;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
program_exit:
|
||||
osmo_mslookup_client_free(globals.mslookup_client);
|
||||
socket_close();
|
||||
log_fini();
|
||||
talloc_free(globals.ctx);
|
||||
return rc;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user