mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr.git
synced 2025-11-02 05:03:31 +00:00
Compare commits
37 Commits
fairwaves/
...
36c3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f8f1b9777 | ||
|
|
a5ed663dea | ||
|
|
15031855bf | ||
|
|
83b909e4f3 | ||
|
|
fde8f64d1d | ||
|
|
d8e8a8425a | ||
|
|
c1d56fd2e9 | ||
|
|
6ac66ac3ad | ||
|
|
769a7f46f4 | ||
|
|
5bc457e19a | ||
|
|
e3c788d9a8 | ||
|
|
e6751dd207 | ||
|
|
e8b7b8634b | ||
|
|
ce172efc0b | ||
|
|
2f1049827b | ||
|
|
92d1c4a0de | ||
|
|
fc4af602ba | ||
|
|
011e781da7 | ||
|
|
cb88c34aca | ||
|
|
e18e3a42f1 | ||
|
|
710e0c9486 | ||
|
|
f6d457e7ef | ||
|
|
fa287c579c | ||
|
|
e78241ae07 | ||
|
|
62d916f3cd | ||
|
|
3ad481afbf | ||
|
|
6aa871db71 | ||
|
|
16a16e6aa4 | ||
|
|
fac52fbd6b | ||
|
|
f0c02ad9c7 | ||
|
|
43c36f99dd | ||
|
|
008ce4bd43 | ||
|
|
f13a8bc4f9 | ||
|
|
50bf7b775b | ||
|
|
cb508554df | ||
|
|
06455eac9c | ||
|
|
2bdcc8eec9 |
@@ -17,8 +17,7 @@ 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@
|
||||
|
||||
|
||||
22
configure.ac
22
configure.ac
@@ -25,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
|
||||
@@ -39,10 +34,10 @@ PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.3.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(LIBOSMOABIS, libosmoabis >= 0.6.0)
|
||||
|
||||
PKG_CHECK_MODULES(SQLITE3, sqlite3)
|
||||
@@ -112,15 +107,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(
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SPDX-License-Identifier: MIT
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
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):
|
||||
Based on esme.py from RCCN:
|
||||
https://github.com/Rhizomatica/rccn/blob/master/rccn/esme.py
|
||||
Copyright 2017 keith <keith@rhizomatica.org>
|
||||
|
||||
@@ -39,7 +39,13 @@ import time
|
||||
|
||||
def can_handle_pdu(pdu):
|
||||
if not isinstance(pdu, smpplib.command.DeliverSM):
|
||||
logging.info('PDU is not a DeliverSM, ignoring')
|
||||
logging.info('PDU is not a DeliverSM. Is OsmoMSC configured properly?')
|
||||
return False
|
||||
|
||||
# Multipart SMS etc. not handled here (see RCCN's esme.py)
|
||||
if pdu.esm_class & smpplib.consts.SMPP_GSMFEAT_UDHI:
|
||||
logging.info("UDH (User Data Header) handling not implemented in this"
|
||||
" example, dropping message.")
|
||||
return False
|
||||
|
||||
if int(pdu.dest_addr_ton) == smpplib.consts.SMPP_TON_INTL:
|
||||
@@ -63,8 +69,7 @@ def query_mslookup(service_type, id, id_type='msisdn'):
|
||||
return json.loads(result_line)
|
||||
|
||||
|
||||
def tx_sms(dst_host, dst_port, source, destination, registered_delivery,
|
||||
unicode_text):
|
||||
def tx_sms(dst_host, dst_port, source, destination, 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)
|
||||
@@ -79,7 +84,7 @@ def tx_sms(dst_host, dst_port, source, destination, registered_delivery,
|
||||
dest_addr_npi=smpplib.consts.SMPP_NPI_ISDN,
|
||||
destination_addr=destination.decode(),
|
||||
short_message=unicode_text,
|
||||
registered_delivery=registered_delivery,
|
||||
registered_delivery=False,
|
||||
)
|
||||
|
||||
smpp_client.unbind()
|
||||
@@ -108,8 +113,7 @@ def rx_deliver_sm(pdu):
|
||||
|
||||
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)
|
||||
pdu.destination_addr, pdu.short_message)
|
||||
|
||||
return smpplib.consts.SMPP_ESME_ROK
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SPDX-License-Identifier: MIT
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
Copyright 2019 sysmocom s.f.m.c GmbH <info@sysmocom.de>
|
||||
|
||||
This is a freeswitch dialplan implementation, see:
|
||||
|
||||
@@ -49,12 +49,7 @@ 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 distcheck || cat-testlogs.sh
|
||||
|
||||
@@ -5,7 +5,7 @@ Documentation=https://osmocom.org/projects/osmo-hlr/wiki/OsmoHLR
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/osmo-hlr -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db
|
||||
ExecStart=/usr/bin/osmo-hlr -U -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
|
||||
48
debian/changelog
vendored
48
debian/changelog
vendored
@@ -1,51 +1,3 @@
|
||||
osmo-hlr (1.2.0-fw.1) unstable; urgency=medium
|
||||
|
||||
* relocated to master:a450a8595
|
||||
|
||||
-- Kirill Zakharenko <kirill.zakharenko@fairwaves.co> Fri, 1 May 2020 18:23:29 +0300
|
||||
|
||||
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 ]
|
||||
|
||||
@@ -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
|
||||
@@ -24,3 +24,10 @@ hlr
|
||||
bind ip 127.0.0.1
|
||||
ussd route prefix *#100# internal own-msisdn
|
||||
ussd route prefix *#101# internal own-imsi
|
||||
ussd route prefix *#102# internal get-ran
|
||||
ussd route prefix *#200# internal gsm-off
|
||||
ussd route prefix *#201# internal gsm-on
|
||||
ussd route prefix *#300# internal umts-off
|
||||
ussd route prefix *#301# internal umts-on
|
||||
ussd route prefix *#400# internal lte-off
|
||||
ussd route prefix *#401# internal lte-on
|
||||
|
||||
@@ -38,7 +38,7 @@ There are two fundamentally distinct subscriber lookups provided by the mslookup
|
||||
----
|
||||
digraph G {
|
||||
rankdir=LR
|
||||
|
||||
|
||||
subgraph cluster_village_b {
|
||||
label="Village B"
|
||||
ms_bob [label="Bob\n(from village B)",shape=box]
|
||||
@@ -113,7 +113,7 @@ msc {
|
||||
----
|
||||
digraph G {
|
||||
rankdir=LR
|
||||
|
||||
|
||||
subgraph cluster_village_b {
|
||||
label="Village B"
|
||||
|
||||
@@ -214,12 +214,6 @@ port, but beware that the IP address must be from a multicast range, see <<ietf-
|
||||
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
|
||||
@@ -230,25 +224,23 @@ requests matching locally attached subscribers.
|
||||
service sip.voice at 10.9.8.7 5060
|
||||
service smpp.sms at 10.9.8.7 2775
|
||||
|
||||
In this example:
|
||||
In this example, "10.9.8.7 5060" would be the IP address and port on which the local site's PBX is bound to receive SIP
|
||||
Invite requests; "10.9.8.7 2775" would be the local site's OsmoMSC SMPP bind address and port.
|
||||
|
||||
- "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).
|
||||
Obviously, these IP addresses must be routable back to this site from all other sites. If, for example, the PBX is
|
||||
configured to bind on "0.0.0.0", it won't work to enter the same as service address -- remote sites cannot route to
|
||||
0.0.0.0. Instead, the mslookup service requires a public IP address of a local interface. For the same reasons, never
|
||||
add link-local addresses like 127.0.0.1 as mslookup services.
|
||||
|
||||
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
|
||||
msc 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
|
||||
msc msc-901-70-0
|
||||
service sip.voice at 10.9.8.7 5060
|
||||
service smpp.sms at 10.9.8.7 2775
|
||||
|
||||
@@ -332,7 +324,7 @@ The mslookup ID types are fixed, while service names can be chosen arbitrarily.
|
||||
|===
|
||||
|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)
|
||||
|sip.voice | SIP | SIP PBX or 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
|
||||
|===
|
||||
@@ -420,7 +412,6 @@ dialplan implementation. An example dialplan implementation for FreeSWITCH that
|
||||
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=".*">
|
||||
@@ -434,7 +425,6 @@ To integrate it with your FREESWITCH setup, add a new `extension` block to your
|
||||
|
||||
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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
SUBDIRS = osmocom
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
osmocom/gsupclient/cni_peer_id.h \
|
||||
osmocom/gsupclient/gsup_peer_id.h \
|
||||
osmocom/gsupclient/gsup_client.h \
|
||||
osmocom/gsupclient/gsup_req.h \
|
||||
osmocom/mslookup/mdns.h \
|
||||
|
||||
@@ -32,35 +32,33 @@ struct osmo_ipa_name {
|
||||
uint8_t val[128];
|
||||
};
|
||||
|
||||
bool osmo_ipa_name_is_empty(const struct osmo_ipa_name *ipa_name);
|
||||
bool osmo_ipa_name_is_empty(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 */
|
||||
enum osmo_gsup_peer_id_type {
|
||||
OSMO_GSUP_PEER_ID_EMPTY=0,
|
||||
OSMO_GSUP_PEER_ID_IPA_NAME,
|
||||
/* OSMO_GSUP_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); }
|
||||
extern const struct value_string osmo_gsup_peer_id_type_names[];
|
||||
static inline const char *osmo_gsup_peer_id_type_name(enum osmo_gsup_peer_id_type val)
|
||||
{ return get_value_string(osmo_gsup_peer_id_type_names, val); }
|
||||
|
||||
struct osmo_cni_peer_id {
|
||||
enum osmo_cni_peer_id_type type;
|
||||
struct osmo_gsup_peer_id {
|
||||
enum osmo_gsup_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,
|
||||
bool osmo_gsup_peer_id_is_empty(struct osmo_gsup_peer_id *gsup_peer_id);
|
||||
int osmo_gsup_peer_id_set(struct osmo_gsup_peer_id *gsup_peer_id, enum osmo_gsup_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,
|
||||
int osmo_gsup_peer_id_set_str(struct osmo_gsup_peer_id *gsup_peer_id, enum osmo_gsup_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);
|
||||
int osmo_gsup_peer_id_cmp(const struct osmo_gsup_peer_id *a, const struct osmo_gsup_peer_id *b);
|
||||
const char *osmo_gsup_peer_id_to_str(const struct osmo_gsup_peer_id *gsup_peer_id);
|
||||
@@ -19,14 +19,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_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) ? osmo_gsup_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)
|
||||
@@ -56,11 +56,11 @@ struct osmo_gsup_req {
|
||||
/* 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;
|
||||
struct osmo_gsup_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;
|
||||
struct osmo_gsup_peer_id via_proxy;
|
||||
|
||||
/* Identify this request by number, for logging. */
|
||||
unsigned int nr;
|
||||
@@ -82,28 +82,24 @@ struct osmo_gsup_req {
|
||||
struct msgb *msg;
|
||||
};
|
||||
|
||||
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id *from_peer, struct msgb *msg,
|
||||
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_gsup_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. */
|
||||
/*! Call _osmo_gsup_req_respond() to convey the sender's source file and line in the logs. */
|
||||
#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. */
|
||||
/*! Call _osmo_gsup_req_respond_msgt() to convey the sender's source file and line in the logs. */
|
||||
#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.
|
||||
*/
|
||||
/*! Log an error message, and call _osmo_gsup_req_respond() to convey the sender's source file and line in the logs. */
|
||||
#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); \
|
||||
|
||||
@@ -16,5 +16,6 @@ noinst_HEADERS = \
|
||||
proxy.h \
|
||||
rand.h \
|
||||
remote_hlr.h \
|
||||
sms_over_gsup.h \
|
||||
timestamp.h \
|
||||
$(NULL)
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
#include <stdbool.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_peer_id.h>
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
struct hlr;
|
||||
|
||||
@@ -33,6 +35,11 @@ enum stmt_idx {
|
||||
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,
|
||||
DB_STMT_UPD_RAT_FLAG,
|
||||
DB_STMT_RAT_BY_ID,
|
||||
_NUM_DB_STMT
|
||||
};
|
||||
|
||||
@@ -100,11 +107,18 @@ struct hlr_subscriber {
|
||||
bool ms_purged_ps;
|
||||
time_t last_lu_seen;
|
||||
time_t last_lu_seen_ps;
|
||||
char last_lu_rat_cs[128];
|
||||
char last_lu_rat_ps[128];
|
||||
/* talloc'd IPA unit name */
|
||||
struct osmo_ipa_name vlr_via_proxy;
|
||||
struct osmo_ipa_name sgsn_via_proxy;
|
||||
bool rat_types[OSMO_RAT_COUNT];
|
||||
};
|
||||
|
||||
static const struct hlr_subscriber hlr_subscriber_empty = {
|
||||
.rat_types = { true, true, true },
|
||||
};
|
||||
|
||||
/* A format string for use with strptime(3). This format string is
|
||||
* used to parse the last_lu_seen column stored in the HLR database.
|
||||
* See https://sqlite.org/lang_datefunc.html, function datetime(). */
|
||||
@@ -158,11 +172,19 @@ int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_s
|
||||
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 osmo_ipa_name *via_proxy,
|
||||
const enum osmo_rat_type rat_types[], size_t rat_types_len);
|
||||
|
||||
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_gsup_peer_id *vlr, unsigned int *ind);
|
||||
int db_ind_del(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr);
|
||||
|
||||
int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed);
|
||||
int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr);
|
||||
int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed);
|
||||
|
||||
/*! 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*.
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#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_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_req.h>
|
||||
|
||||
#define LOG_DGSM(imsi, level, fmt, args...) \
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#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_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_req.h>
|
||||
|
||||
#ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN
|
||||
@@ -74,5 +74,5 @@ int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup,
|
||||
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,
|
||||
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_gsup_peer_id *to_peer,
|
||||
struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup);
|
||||
|
||||
@@ -56,6 +56,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;
|
||||
@@ -111,6 +112,14 @@ struct hlr {
|
||||
} mdns;
|
||||
} client;
|
||||
} mslookup;
|
||||
|
||||
struct {
|
||||
/* FIXME actually use branch fixeria/sms for SMSC routing. completely unimplemented */
|
||||
struct osmo_gsup_peer_id smsc;
|
||||
|
||||
/* If no SMSC is present / responsible, try punching the SMS through directly when this is true. */
|
||||
bool try_direct_delivery;
|
||||
} sms_over_gsup;
|
||||
};
|
||||
|
||||
extern struct hlr *g_hlr;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_peer_id.h>
|
||||
#include <osmocom/mslookup/mslookup.h>
|
||||
|
||||
struct osmo_mslookup_query;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#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/gsupclient/gsup_peer_id.h>
|
||||
#include <osmocom/hlr/timestamp.h>
|
||||
|
||||
struct osmo_gsup_req;
|
||||
|
||||
8
include/osmocom/hlr/sms_over_gsup.h
Normal file
8
include/osmocom/hlr/sms_over_gsup.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/gsupclient/gsup_req.h>
|
||||
|
||||
#define OSMO_MSLOOKUP_SERVICE_SMS_GSUP "gsup.sms"
|
||||
|
||||
bool sms_over_gsup_check_handle_msg(struct osmo_gsup_req *req);
|
||||
26
sql/hlr.sql
26
sql/hlr.sql
@@ -45,6 +45,12 @@ CREATE TABLE subscriber (
|
||||
last_lu_seen TIMESTAMP default NULL,
|
||||
last_lu_seen_ps TIMESTAMP default NULL,
|
||||
|
||||
-- Last Radio Access Type list as sent during Location Updating Request.
|
||||
-- This is usually just one RAT name, but can be a comma separated list of strings
|
||||
-- of all the RAT types sent during Location Updating Request.
|
||||
last_lu_rat_cs TEXT default NULL,
|
||||
last_lu_rat_ps TEXT 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,8 +85,26 @@ 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)
|
||||
);
|
||||
|
||||
-- Optional: add subscriber entries to allow or disallow specific RATs (2G or 3G or ...).
|
||||
-- If a subscriber has no entry, that means that all RATs are allowed (backwards compat).
|
||||
CREATE TABLE subscriber_rat (
|
||||
subscriber_id INTEGER, -- subscriber.id
|
||||
rat TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu','EUTRAN-SGs')) NOT NULL, -- Radio Access Technology, see enum ran_type
|
||||
allowed BOOLEAN CHECK(allowed in (0, 1)) NOT NULL DEFAULT 0,
|
||||
UNIQUE (subscriber_id, rat)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
|
||||
CREATE UNIQUE INDEX idx_subscr_rat_flag ON subscriber_rat (subscriber_id, rat);
|
||||
|
||||
-- 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 = 5;
|
||||
PRAGMA user_version = 8;
|
||||
|
||||
8
sql/upgrade_v2_to_v3.sql
Normal file
8
sql/upgrade_v2_to_v3.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
CREATE TABLE subscriber_rat (
|
||||
subscriber_id INTEGER, -- subscriber.id
|
||||
rat TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu')) NOT NULL, -- Radio Access Technology, see enum ran_type
|
||||
allowed BOOLEAN NOT NULL DEFAULT 0,
|
||||
);
|
||||
|
||||
PRAGMA user_version = 3;
|
||||
@@ -61,6 +61,7 @@ osmo_hlr_SOURCES = \
|
||||
mslookup_server.c \
|
||||
mslookup_server_mdns.c \
|
||||
dgsm_vty.c \
|
||||
sms_over_gsup.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_hlr_LDADD = \
|
||||
@@ -82,7 +83,7 @@ osmo_hlr_db_tool_SOURCES = \
|
||||
logging.c \
|
||||
rand_urandom.c \
|
||||
dbd_decode_binary.c \
|
||||
$(srcdir)/gsupclient/cni_peer_id.c \
|
||||
$(srcdir)/gsupclient/gsup_peer_id.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_hlr_db_tool_LDADD = \
|
||||
|
||||
100
src/db.c
100
src/db.c
@@ -22,13 +22,15 @@
|
||||
#include <stdbool.h>
|
||||
#include <sqlite3.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/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 5
|
||||
#define CURRENT_SCHEMA_VERSION 8
|
||||
|
||||
#define SEL_COLUMNS \
|
||||
"id," \
|
||||
@@ -47,6 +49,8 @@
|
||||
"ms_purged_ps," \
|
||||
"last_lu_seen," \
|
||||
"last_lu_seen_ps," \
|
||||
"last_lu_rat_cs," \
|
||||
"last_lu_rat_ps," \
|
||||
"vlr_via_proxy," \
|
||||
"sgsn_via_proxy"
|
||||
|
||||
@@ -81,10 +85,23 @@ static const char *stmt_sql[] = {
|
||||
"INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)"
|
||||
" 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_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch'),"
|
||||
" last_lu_rat_cs = $rat"
|
||||
" WHERE id = $subscriber_id",
|
||||
[DB_STMT_SET_LAST_LU_SEEN_PS] = "UPDATE subscriber SET last_lu_seen_ps = datetime($val, 'unixepoch'),"
|
||||
" last_lu_rat_ps = $rat"
|
||||
" 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",
|
||||
[DB_STMT_UPD_RAT_FLAG] = "INSERT OR REPLACE INTO subscriber_rat (subscriber_id, rat, allowed)"
|
||||
" VALUES ($subscriber_id, $rat, $allowed)",
|
||||
[DB_STMT_RAT_BY_ID] =
|
||||
"SELECT rat, allowed"
|
||||
" FROM subscriber_rat"
|
||||
" WHERE subscriber_id = $subscriber_id",
|
||||
};
|
||||
|
||||
static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
|
||||
@@ -330,7 +347,7 @@ static int db_upgrade_v3(struct db_context *dbc)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* A newer SQLite version would allow simply 'ATLER TABLE subscriber RENAME COLUMN hlr_number TO msc_number'.
|
||||
/* A newer SQLite version would allow simply 'ALTER TABLE subscriber RENAME COLUMN hlr_number TO msc_number'.
|
||||
* This is a really expensive workaround for that in order to cover earlier SQLite versions as well:
|
||||
* Create a new table with the new column name and copy the data over (https://www.sqlite.org/faq.html#q11).
|
||||
*/
|
||||
@@ -479,6 +496,70 @@ static int db_upgrade_v5(struct db_context *dbc)
|
||||
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;
|
||||
}
|
||||
|
||||
static int db_upgrade_v7(struct db_context *dbc)
|
||||
{
|
||||
int rc;
|
||||
const char *statements[] = {
|
||||
"-- Optional: add subscriber entries to allow or disallow specific RATs (2G or 3G or ...).\n"
|
||||
"-- If a subscriber has no entry, that means that all RATs are allowed (backwards compat).\n"
|
||||
"CREATE TABLE subscriber_rat (\n"
|
||||
" subscriber_id INTEGER, -- subscriber.id\n"
|
||||
" rat TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu')) NOT NULL, -- Radio Access Technology, see enum ran_type\n"
|
||||
" allowed BOOLEAN CHECK(allowed in (0, 1)) NOT NULL DEFAULT 0,\n"
|
||||
" UNIQUE (subscriber_id, rat)\n"
|
||||
")",
|
||||
"CREATE UNIQUE INDEX idx_subscr_rat_flag ON subscriber_rat (subscriber_id, rat)",
|
||||
"PRAGMA user_version = 7",
|
||||
};
|
||||
|
||||
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 7\n");
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int db_upgrade_v8(struct db_context *dbc)
|
||||
{
|
||||
int rc;
|
||||
const char *statements[] = {
|
||||
"ALTER TABLE subscriber ADD COLUMN last_lu_rat_cs TEXT default NULL",
|
||||
"ALTER TABLE subscriber ADD COLUMN last_lu_rat_ps TEXT default NULL",
|
||||
"PRAGMA user_version = 8",
|
||||
};
|
||||
|
||||
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 7\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,
|
||||
@@ -486,6 +567,9 @@ static db_upgrade_func_t db_upgrade_path[] = {
|
||||
db_upgrade_v3,
|
||||
db_upgrade_v4,
|
||||
db_upgrade_v5,
|
||||
db_upgrade_v6,
|
||||
db_upgrade_v7,
|
||||
db_upgrade_v8,
|
||||
};
|
||||
|
||||
static int db_get_user_version(struct db_context *dbc)
|
||||
@@ -574,11 +658,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) {
|
||||
@@ -616,9 +698,9 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
|
||||
if (version < CURRENT_SCHEMA_VERSION) {
|
||||
LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version);
|
||||
if (!allow_upgrade) {
|
||||
LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database to schema version %d; "
|
||||
LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database from schema version %d to %d; "
|
||||
"use the --db-upgrade option to allow HLR database upgrades\n",
|
||||
CURRENT_SCHEMA_VERSION);
|
||||
version, CURRENT_SCHEMA_VERSION);
|
||||
}
|
||||
} else
|
||||
LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version);
|
||||
|
||||
252
src/db_hlr.c
252
src/db_hlr.c
@@ -37,7 +37,7 @@
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_peer_id.h>
|
||||
|
||||
#define LOGHLR(imsi, level, fmt, args ...) LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)
|
||||
|
||||
@@ -483,7 +483,7 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
|
||||
if (!subscr)
|
||||
goto out;
|
||||
|
||||
*subscr = (struct hlr_subscriber){};
|
||||
*subscr = hlr_subscriber_empty;
|
||||
|
||||
/* obtain the various columns */
|
||||
subscr->id = sqlite3_column_int64(stmt, 0);
|
||||
@@ -505,12 +505,17 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
|
||||
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);
|
||||
copy_sqlite3_text_to_buf(subscr->last_lu_rat_cs, stmt, 16);
|
||||
copy_sqlite3_text_to_buf(subscr->last_lu_rat_ps, stmt, 17);
|
||||
copy_sqlite3_text_to_ipa_name(&subscr->vlr_via_proxy, stmt, 18);
|
||||
copy_sqlite3_text_to_ipa_name(&subscr->sgsn_via_proxy, stmt, 19);
|
||||
|
||||
out:
|
||||
db_remove_reset(stmt);
|
||||
|
||||
if (ret == 0)
|
||||
db_subscr_get_rat_types(dbc, subscr);
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
*err = NULL;
|
||||
@@ -737,11 +742,14 @@ out:
|
||||
*/
|
||||
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 osmo_ipa_name *via_proxy,
|
||||
const enum osmo_rat_type rat_types[], size_t rat_types_len)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int rc, ret = 0;
|
||||
struct timespec localtime;
|
||||
char rat_types_str[128] = "";
|
||||
int i;
|
||||
|
||||
stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
|
||||
: DB_STMT_UPD_VLR_BY_ID];
|
||||
@@ -803,6 +811,21 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < rat_types_len; i++) {
|
||||
char *pos = rat_types_str + strnlen(rat_types_str, sizeof(rat_types_str));
|
||||
int len = sizeof(rat_types_str) - (pos - rat_types_str);
|
||||
rc = snprintf(pos, len, "%s%s", pos == rat_types_str ? "" : ",", osmo_rat_type_name(rat_types[i]));
|
||||
if (rc > len) {
|
||||
osmo_strlcpy(rat_types_str + sizeof(rat_types_str) - 4, "...", 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!db_bind_text(stmt, "$rat", rat_types_str)) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_DONE) {
|
||||
LOGP(DAUC, LOGL_ERROR,
|
||||
@@ -884,3 +907,222 @@ 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_gsup_peer_id *vlr,
|
||||
unsigned int *ind, bool del)
|
||||
{
|
||||
const char *vlr_name = NULL;
|
||||
int rc;
|
||||
|
||||
switch (vlr->type) {
|
||||
case OSMO_GSUP_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_gsup_peer_id type: %s\n",
|
||||
osmo_gsup_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_gsup_peer_id *vlr, unsigned int *ind)
|
||||
{
|
||||
return _db_ind(dbc, vlr, ind, false);
|
||||
}
|
||||
|
||||
int db_ind_del(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr)
|
||||
{
|
||||
return _db_ind(dbc, vlr, NULL, true);
|
||||
}
|
||||
|
||||
int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed)
|
||||
{
|
||||
int rc;
|
||||
int ret = 0;
|
||||
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_RAT_FLAG];
|
||||
|
||||
if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
|
||||
return -EIO;
|
||||
|
||||
OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
|
||||
if (!db_bind_text(stmt, "$rat", osmo_rat_type_name(rat)))
|
||||
return -EIO;
|
||||
|
||||
if (!db_bind_int(stmt, "$allowed", allowed ? 1 : 0))
|
||||
return -EIO;
|
||||
|
||||
/* execute the statement */
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_DONE) {
|
||||
LOGP(DDB, LOGL_ERROR, "%s %s: SQL error: %s\n",
|
||||
allowed ? "enable" : "disable", osmo_rat_type_name(rat),
|
||||
sqlite3_errmsg(dbc->db));
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* verify execution result */
|
||||
rc = sqlite3_changes(dbc->db);
|
||||
if (!rc) {
|
||||
LOGP(DDB, LOGL_ERROR, "Cannot %s %s: no such subscriber: ID=%" PRIu64 "\n",
|
||||
allowed ? "enable" : "disable", osmo_rat_type_name(rat),
|
||||
subscr_id);
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
} else if (rc != 1) {
|
||||
LOGP(DDB, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
|
||||
allowed ? "enable" : "disable", osmo_rat_type_name(rat),
|
||||
rc);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
out:
|
||||
db_remove_reset(stmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr)
|
||||
{
|
||||
int rc;
|
||||
int ret = 0;
|
||||
int i;
|
||||
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_RAT_BY_ID];
|
||||
|
||||
if (!db_bind_int64(stmt, "$subscriber_id", subscr->id))
|
||||
return -EIO;
|
||||
|
||||
for (i = 0; i < OSMO_RAT_COUNT; i++)
|
||||
subscr->rat_types[i] = true;
|
||||
|
||||
/* execute the statement */
|
||||
while (1) {
|
||||
enum osmo_rat_type rat;
|
||||
bool allowed;
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
if (rc == SQLITE_DONE)
|
||||
break;
|
||||
if (rc != SQLITE_ROW)
|
||||
return -rc;
|
||||
|
||||
rc = get_string_value(osmo_rat_type_names, (const char*)sqlite3_column_text(stmt, 0));
|
||||
if (rc == -EINVAL) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (rc <= 0 || rc >= OSMO_RAT_COUNT) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rat = rc;
|
||||
|
||||
allowed = sqlite3_column_int(stmt, 1);
|
||||
|
||||
subscr->rat_types[rat] = allowed;
|
||||
LOGP(DAUC, LOGL_DEBUG, "db: imsi='%s' %s %s\n",
|
||||
subscr->imsi, osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden");
|
||||
}
|
||||
|
||||
out:
|
||||
db_remove_reset(stmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed)
|
||||
{
|
||||
int rc;
|
||||
OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
|
||||
|
||||
db_subscr_get_rat_types(hlr->dbc, subscr);
|
||||
|
||||
if (subscr->rat_types[rat] == allowed) {
|
||||
LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",
|
||||
allowed ? "enable" : "disable", osmo_rat_type_name(rat));
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
rc = db_subscr_set_rat_type_flag(hlr->dbc, subscr->id, rat, allowed);
|
||||
if (rc)
|
||||
return rc > 0? -rc : rc;
|
||||
|
||||
/* FIXME: If we're disabling, send message to VLR to detach subscriber */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#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/gsupclient/gsup_peer_id.h>
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#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 <osmocom/gsupclient/gsup_peer_id.h>
|
||||
|
||||
struct cmd_node mslookup_node = {
|
||||
MSLOOKUP_NODE,
|
||||
@@ -119,7 +119,7 @@ DEFUN(cfg_mslookup_mdns_domain_suffix,
|
||||
|
||||
DEFUN(cfg_mslookup_no_mdns,
|
||||
cfg_mslookup_no_mdns_cmd,
|
||||
"no mdns bind",
|
||||
"no mdns",
|
||||
NO_STR "Disable both server and client for mDNS mslookup\n")
|
||||
{
|
||||
g_hlr->mslookup.server.mdns.enable = false;
|
||||
@@ -178,9 +178,9 @@ DEFUN(cfg_mslookup_server_mdns_domain_suffix,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mslookup_server_no_mdns_bind,
|
||||
cfg_mslookup_server_no_mdns_bind_cmd,
|
||||
"no mdns bind",
|
||||
DEFUN(cfg_mslookup_server_no_mdns,
|
||||
cfg_mslookup_server_no_mdns_cmd,
|
||||
"no mdns",
|
||||
NO_STR "Disable server for mDNS mslookup (do not answer remote requests)\n")
|
||||
{
|
||||
g_hlr->mslookup.server.mdns.enable = false;
|
||||
@@ -196,9 +196,8 @@ 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;
|
||||
@@ -366,8 +365,8 @@ DEFUN(cfg_mslookup_client_timeout,
|
||||
vty_out(vty, "%% 'exit' this node to apply changes%s", VTY_NEWLINE)
|
||||
|
||||
|
||||
DEFUN(cfg_mslookup_client_mdns_bind,
|
||||
cfg_mslookup_client_mdns_bind_cmd,
|
||||
DEFUN(cfg_mslookup_client_mdns,
|
||||
cfg_mslookup_client_mdns_cmd,
|
||||
"mdns bind [IP] [<1-65535>]",
|
||||
MDNS_STR
|
||||
"Enable mDNS client, and configure multicast address to send mDNS mslookup requests to\n"
|
||||
@@ -388,9 +387,9 @@ DEFUN(cfg_mslookup_client_mdns_domain_suffix,
|
||||
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;
|
||||
@@ -467,7 +466,7 @@ int config_write_mslookup(struct vty *vty)
|
||||
|
||||
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",
|
||||
vty_out(vty, " mdns to %s %u%s",
|
||||
g_hlr->mslookup.client.mdns.query_addr.ip,
|
||||
g_hlr->mslookup.client.mdns.query_addr.port,
|
||||
VTY_NEWLINE);
|
||||
@@ -535,7 +534,7 @@ DEFUN(do_mslookup_show_services,
|
||||
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);
|
||||
vty_out(vty, "msc %s%s", osmo_ipa_name_to_str(&msc->name), VTY_NEWLINE);
|
||||
config_write_msc_services(vty, " ", msc);
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
@@ -555,7 +554,7 @@ void dgsm_vty_init(void)
|
||||
install_node(&mslookup_server_node, NULL);
|
||||
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);
|
||||
@@ -570,9 +569,9 @@ void dgsm_vty_init(void)
|
||||
install_element(MSLOOKUP_NODE, &cfg_mslookup_no_client_cmd);
|
||||
install_node(&mslookup_client_node, NULL);
|
||||
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_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_no_mdns_cmd);
|
||||
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_gateway_proxy_cmd);
|
||||
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_gateway_proxy_cmd);
|
||||
|
||||
|
||||
@@ -65,13 +65,13 @@ int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg)
|
||||
static void gsup_server_send_req_response(struct osmo_gsup_req *req, struct osmo_gsup_message *response)
|
||||
{
|
||||
struct osmo_gsup_server *server = req->cb_data;
|
||||
struct osmo_cni_peer_id *routing;
|
||||
struct osmo_gsup_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)) {
|
||||
&& !osmo_gsup_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. */
|
||||
@@ -80,12 +80,12 @@ static void gsup_server_send_req_response(struct osmo_gsup_req *req, struct osmo
|
||||
routing = &req->source_name;
|
||||
}
|
||||
switch (routing->type) {
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
case OSMO_GSUP_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));
|
||||
osmo_gsup_peer_id_type_name(routing->type));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -111,22 +111,22 @@ static void gsup_server_send_req_response(struct osmo_gsup_req *req, struct osmo
|
||||
struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb *msg)
|
||||
{
|
||||
struct osmo_gsup_req *req;
|
||||
struct osmo_cni_peer_id cpi = {
|
||||
.type = OSMO_CNI_PEER_ID_IPA_NAME,
|
||||
struct osmo_gsup_peer_id gpi = {
|
||||
.type = OSMO_GSUP_PEER_ID_IPA_NAME,
|
||||
.ipa_name = conn->peer_name,
|
||||
};
|
||||
|
||||
req = osmo_gsup_req_new(conn->server, &cpi, msg, gsup_server_send_req_response, conn->server, NULL);
|
||||
req = osmo_gsup_req_new(conn->server, &gpi, msg, gsup_server_send_req_response, conn->server, NULL);
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
if (!osmo_cni_peer_id_is_empty(&req->via_proxy)) {
|
||||
if (!osmo_gsup_peer_id_is_empty(&req->via_proxy)) {
|
||||
switch (req->via_proxy.type) {
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
case OSMO_GSUP_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_peer_id_type_name(req->source_name.type));
|
||||
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
|
||||
return NULL;
|
||||
}
|
||||
@@ -137,7 +137,7 @@ struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb
|
||||
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_gsup_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;
|
||||
@@ -459,6 +459,7 @@ 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 omso_gsup_message
|
||||
@@ -475,46 +476,46 @@ 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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_cni_peer_id *to_peer,
|
||||
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_gsup_peer_id *to_peer,
|
||||
struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup)
|
||||
{
|
||||
int rc;
|
||||
@@ -523,22 +524,22 @@ int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struc
|
||||
* 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) {
|
||||
if (req->source_name.type != OSMO_GSUP_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));
|
||||
osmo_gsup_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) {
|
||||
if (to_peer->type != OSMO_GSUP_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));
|
||||
osmo_gsup_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));
|
||||
LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to %s\n", osmo_gsup_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;
|
||||
|
||||
@@ -9,7 +9,7 @@ AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/incl
|
||||
lib_LTLIBRARIES = libosmo-gsup-client.la
|
||||
|
||||
libosmo_gsup_client_la_SOURCES = \
|
||||
cni_peer_id.c \
|
||||
gsup_peer_id.c \
|
||||
gsup_client.c \
|
||||
gsup_req.c \
|
||||
$(NULL)
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_peer_id.h>
|
||||
|
||||
bool osmo_ipa_name_is_empty(const struct osmo_ipa_name *ipa_name)
|
||||
bool osmo_ipa_name_is_empty(struct osmo_ipa_name *ipa_name)
|
||||
{
|
||||
return (!ipa_name) || (!ipa_name->len);
|
||||
}
|
||||
@@ -90,61 +90,55 @@ int osmo_ipa_name_cmp(const struct osmo_ipa_name *a, const struct osmo_ipa_name
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name)
|
||||
{
|
||||
size_t len = ipa_name->len;
|
||||
if (!len)
|
||||
return talloc_strdup(ctx, "");
|
||||
return "";
|
||||
if (ipa_name->val[len-1] == '\0')
|
||||
len--;
|
||||
return osmo_escape_str_c(ctx, (char*)ipa_name->val, len);
|
||||
return osmo_escape_str_c(OTC_SELECT, (char*)ipa_name->val, len);
|
||||
}
|
||||
|
||||
bool osmo_cni_peer_id_is_empty(const struct osmo_cni_peer_id *cni_peer_id)
|
||||
bool osmo_gsup_peer_id_is_empty(struct osmo_gsup_peer_id *gsup_peer_id)
|
||||
{
|
||||
if (!cni_peer_id)
|
||||
if (!gsup_peer_id)
|
||||
return true;
|
||||
switch (cni_peer_id->type) {
|
||||
case OSMO_CNI_PEER_ID_EMPTY:
|
||||
switch (gsup_peer_id->type) {
|
||||
case OSMO_GSUP_PEER_ID_EMPTY:
|
||||
return true;
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
return osmo_ipa_name_is_empty(&cni_peer_id->ipa_name);
|
||||
case OSMO_GSUP_PEER_ID_IPA_NAME:
|
||||
return osmo_ipa_name_is_empty(&gsup_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,
|
||||
int osmo_gsup_peer_id_set(struct osmo_gsup_peer_id *gsup_peer_id, enum osmo_gsup_peer_id_type type,
|
||||
const uint8_t *val, size_t len)
|
||||
{
|
||||
cni_peer_id->type = type;
|
||||
gsup_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);
|
||||
case OSMO_GSUP_PEER_ID_IPA_NAME:
|
||||
return osmo_ipa_name_set(&gsup_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,
|
||||
int osmo_gsup_peer_id_set_str(struct osmo_gsup_peer_id *gsup_peer_id, enum osmo_gsup_peer_id_type type,
|
||||
const char *str_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
*cni_peer_id = (struct osmo_cni_peer_id){};
|
||||
*gsup_peer_id = (struct osmo_gsup_peer_id){};
|
||||
|
||||
switch (type) {
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
cni_peer_id->type = OSMO_CNI_PEER_ID_IPA_NAME;
|
||||
case OSMO_GSUP_PEER_ID_IPA_NAME:
|
||||
gsup_peer_id->type = OSMO_GSUP_PEER_ID_IPA_NAME;
|
||||
va_start(ap, str_fmt);
|
||||
rc = osmo_ipa_name_set_str_va(&cni_peer_id->ipa_name, str_fmt, ap);
|
||||
rc = osmo_ipa_name_set_str_va(&gsup_peer_id->ipa_name, str_fmt, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
default:
|
||||
@@ -152,36 +146,30 @@ int osmo_cni_peer_id_set_str(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni
|
||||
}
|
||||
}
|
||||
|
||||
int osmo_cni_peer_id_cmp(const struct osmo_cni_peer_id *a, const struct osmo_cni_peer_id *b)
|
||||
int osmo_gsup_peer_id_cmp(const struct osmo_gsup_peer_id *a, const struct osmo_gsup_peer_id *b)
|
||||
{
|
||||
if (a->type != b->type)
|
||||
return OSMO_CMP(a->type, b->type);
|
||||
switch (a->type) {
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
case OSMO_GSUP_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" },
|
||||
const struct value_string osmo_gsup_peer_id_type_names[] = {
|
||||
{ OSMO_GSUP_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)
|
||||
const char *osmo_gsup_peer_id_to_str(const struct osmo_gsup_peer_id *gpi)
|
||||
{
|
||||
switch (cpi->type) {
|
||||
case OSMO_CNI_PEER_ID_IPA_NAME:
|
||||
return osmo_ipa_name_to_str_c(ctx, &cpi->ipa_name);
|
||||
switch (gpi->type) {
|
||||
case OSMO_GSUP_PEER_ID_IPA_NAME:
|
||||
return osmo_ipa_name_to_str(&gpi->ipa_name);
|
||||
default:
|
||||
return talloc_strdup(ctx, osmo_cni_peer_id_type_name(cpi->type));
|
||||
return osmo_gsup_peer_id_type_name(gpi->type);
|
||||
}
|
||||
}
|
||||
@@ -25,81 +25,26 @@
|
||||
#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.
|
||||
* When this function returns, the original sender is found in req->source_name. If this is not the immediate peer name,
|
||||
* 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).
|
||||
*
|
||||
* 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] msg The GSUP message buffer.
|
||||
* \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().
|
||||
* osmo_gsup_req_respond*() functions.
|
||||
* \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.
|
||||
* \return a newly allocated osmo_gsup_req, or NULL on error.
|
||||
*/
|
||||
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id *from_peer, struct msgb *msg,
|
||||
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_gsup_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)
|
||||
{
|
||||
@@ -109,7 +54,7 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
|
||||
|
||||
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));
|
||||
osmo_gsup_peer_id_to_str(from_peer));
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
@@ -125,7 +70,7 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
|
||||
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);
|
||||
LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: cannot decode (rc=%d)\n", osmo_gsup_peer_id_to_str(from_peer), rc);
|
||||
osmo_gsup_req_free(req);
|
||||
return NULL;
|
||||
}
|
||||
@@ -133,18 +78,18 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
|
||||
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,
|
||||
if (osmo_gsup_peer_id_set(&req->source_name, OSMO_GSUP_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_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))
|
||||
if (osmo_gsup_peer_id_cmp(&req->source_name, from_peer))
|
||||
req->via_proxy = *from_peer;
|
||||
}
|
||||
|
||||
@@ -159,8 +104,6 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
|
||||
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");
|
||||
@@ -171,25 +114,6 @@ void osmo_gsup_req_free(struct osmo_gsup_req *req)
|
||||
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)
|
||||
{
|
||||
@@ -220,18 +144,6 @@ exit_cleanup:
|
||||
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)
|
||||
{
|
||||
@@ -242,17 +154,6 @@ int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_messag
|
||||
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)
|
||||
{
|
||||
|
||||
37
src/hlr.c
37
src/hlr.c
@@ -38,7 +38,7 @@
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/mslookup/mslookup_client.h>
|
||||
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_peer_id.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/ctrl.h>
|
||||
@@ -51,6 +51,7 @@
|
||||
#include <osmocom/hlr/dgsm.h>
|
||||
#include <osmocom/hlr/proxy.h>
|
||||
#include <osmocom/hlr/lu_fsm.h>
|
||||
#include <osmocom/hlr/sms_over_gsup.h>
|
||||
#include <osmocom/mslookup/mdns.h>
|
||||
|
||||
struct hlr *g_hlr;
|
||||
@@ -280,13 +281,13 @@ 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(unsigned int auc_3g_ind, struct osmo_gsup_req *req)
|
||||
static int rx_send_auth_info(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);
|
||||
@@ -294,13 +295,18 @@ static int rx_send_auth_info(unsigned int auc_3g_ind, struct osmo_gsup_req *req)
|
||||
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_gsup_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,
|
||||
ARRAY_SIZE(gsup_out.auth_vectors),
|
||||
req->gsup.rand, req->gsup.auts, separation_bit);
|
||||
|
||||
if (rc <= 0) {
|
||||
@@ -314,7 +320,7 @@ static int rx_send_auth_info(unsigned int auc_3g_ind, struct osmo_gsup_req *req)
|
||||
" Returning slightly inaccurate cause 'IMSI Unknown' via GSUP");
|
||||
return rc;
|
||||
case -ENOENT:
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown");
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_ROAMING_NOTALLOWED, "IMSI unknown");
|
||||
return rc;
|
||||
default:
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "failure to look up IMSI in db");
|
||||
@@ -366,7 +372,7 @@ static int rx_purge_ms_req(struct osmo_gsup_req *req)
|
||||
if (rc == 0)
|
||||
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_PURGE_MS_RESULT, true);
|
||||
else if (rc == -ENOENT)
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown");
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_ROAMING_NOTALLOWED, "IMSI unknown");
|
||||
else
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "db error");
|
||||
return rc;
|
||||
@@ -448,8 +454,8 @@ static int read_cb_forward(struct osmo_gsup_req *req)
|
||||
struct osmo_ipa_name 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;
|
||||
}
|
||||
@@ -503,6 +509,10 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
|
||||
}
|
||||
}
|
||||
|
||||
/* SMS over GSUP */
|
||||
if (sms_over_gsup_check_handle_msg(req))
|
||||
return 0;
|
||||
|
||||
/* 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. */
|
||||
@@ -516,7 +526,7 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
|
||||
switch (req->gsup.message_type) {
|
||||
/* requests sent to us */
|
||||
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
|
||||
rx_send_auth_info(conn->auc_3g_ind, req);
|
||||
rx_send_auth_info(req);
|
||||
break;
|
||||
case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
|
||||
rx_upd_loc_req(conn, req);
|
||||
@@ -707,6 +717,7 @@ 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);
|
||||
@@ -717,6 +728,8 @@ int main(int argc, char **argv)
|
||||
/* Init default (call independent) SS session guard timeout value */
|
||||
g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT;
|
||||
|
||||
g_hlr->sms_over_gsup.try_direct_delivery = true;
|
||||
|
||||
rc = osmo_init_logging2(hlr_ctx, &hlr_log_info);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error initializing logging\n");
|
||||
|
||||
253
src/hlr_ussd.c
253
src/hlr_ussd.c
@@ -252,7 +252,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;
|
||||
}
|
||||
|
||||
@@ -279,19 +278,20 @@ static int ss_gsup_send_to_ms(struct ss_session *ss, struct osmo_gsup_server *gs
|
||||
}
|
||||
|
||||
static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_msg_type,
|
||||
bool final, struct msgb *ss_msg)
|
||||
bool final, struct msgb *ss_msg)
|
||||
|
||||
{
|
||||
struct osmo_gsup_message resp = {0};
|
||||
struct osmo_gsup_message resp = {
|
||||
.message_type = gsup_msg_type,
|
||||
.session_id = ss->session_id,
|
||||
};
|
||||
int rc;
|
||||
|
||||
resp.message_type = gsup_msg_type;
|
||||
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;
|
||||
resp.session_id = ss->session_id;
|
||||
if (ss_msg) {
|
||||
resp.ss_info = msgb_data(ss_msg);
|
||||
resp.ss_info_len = msgb_length(ss_msg);
|
||||
@@ -373,6 +373,206 @@ static int handle_ussd_own_imsi(struct ss_session *ss,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_ussd_get_ran(struct ss_session *ss,
|
||||
const struct osmo_gsup_message *gsup,
|
||||
const struct ss_request *req)
|
||||
{
|
||||
struct hlr_subscriber subscr;
|
||||
char response[512];
|
||||
int rc;
|
||||
const char *rat;
|
||||
|
||||
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
if (!*subscr.last_lu_rat_cs)
|
||||
rat = "nothing, you don't exist";
|
||||
else if (!strcmp(subscr.last_lu_rat_cs, "GERAN-A"))
|
||||
rat = "2G";
|
||||
else if (!strcmp(subscr.last_lu_rat_cs, "UTRAN-Iu"))
|
||||
rat = "3G";
|
||||
else if (!strcmp(subscr.last_lu_rat_cs, "EUTRAN-SGs"))
|
||||
rat = "4G";
|
||||
else
|
||||
rat = subscr.last_lu_rat_cs;
|
||||
|
||||
snprintf(response, sizeof(response),
|
||||
"Now on %s. Available:%s%s%s.",
|
||||
rat,
|
||||
subscr.rat_types[OSMO_RAT_GERAN_A]? " 2G" : "",
|
||||
subscr.rat_types[OSMO_RAT_UTRAN_IU]? " 3G" : "",
|
||||
subscr.rat_types[OSMO_RAT_EUTRAN_SGS]? " 4G" : "");
|
||||
|
||||
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, response);
|
||||
break;
|
||||
case -ENOENT:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
|
||||
break;
|
||||
case -EIO:
|
||||
default:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int handle_ussd_gsm_on(struct ss_session *ss,
|
||||
const struct osmo_gsup_message *gsup,
|
||||
const struct ss_request *req)
|
||||
{
|
||||
struct hlr_subscriber subscr;
|
||||
int rc;
|
||||
|
||||
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_GERAN_A, true);
|
||||
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
|
||||
"Enabled GERAN-A (2G)");
|
||||
break;
|
||||
case -ENOENT:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
|
||||
break;
|
||||
case -EIO:
|
||||
default:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int handle_ussd_gsm_off(struct ss_session *ss,
|
||||
const struct osmo_gsup_message *gsup,
|
||||
const struct ss_request *req)
|
||||
{
|
||||
struct hlr_subscriber subscr;
|
||||
int rc;
|
||||
|
||||
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_GERAN_A, false);
|
||||
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
|
||||
"Disabled GERAN-A (2G)");
|
||||
break;
|
||||
case -ENOENT:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
|
||||
break;
|
||||
case -EIO:
|
||||
default:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int handle_ussd_umts_on(struct ss_session *ss,
|
||||
const struct osmo_gsup_message *gsup,
|
||||
const struct ss_request *req)
|
||||
{
|
||||
struct hlr_subscriber subscr;
|
||||
int rc;
|
||||
|
||||
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_UTRAN_IU, true);
|
||||
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
|
||||
"Enabled UTRAN-Iu (3G)");
|
||||
break;
|
||||
case -ENOENT:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
|
||||
break;
|
||||
case -EIO:
|
||||
default:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int handle_ussd_umts_off(struct ss_session *ss,
|
||||
const struct osmo_gsup_message *gsup,
|
||||
const struct ss_request *req)
|
||||
{
|
||||
struct hlr_subscriber subscr;
|
||||
int rc;
|
||||
|
||||
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_UTRAN_IU, false);
|
||||
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
|
||||
"Disabled UTRAN-Iu (3G)");
|
||||
break;
|
||||
case -ENOENT:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
|
||||
break;
|
||||
case -EIO:
|
||||
default:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int handle_ussd_lte_on(struct ss_session *ss,
|
||||
const struct osmo_gsup_message *gsup,
|
||||
const struct ss_request *req)
|
||||
{
|
||||
struct hlr_subscriber subscr;
|
||||
int rc;
|
||||
|
||||
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_EUTRAN_SGS, true);
|
||||
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
|
||||
"Enabled EUTRAN-SGs (4G)");
|
||||
break;
|
||||
case -ENOENT:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
|
||||
break;
|
||||
case -EIO:
|
||||
default:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int handle_ussd_lte_off(struct ss_session *ss,
|
||||
const struct osmo_gsup_message *gsup,
|
||||
const struct ss_request *req)
|
||||
{
|
||||
struct hlr_subscriber subscr;
|
||||
int rc;
|
||||
|
||||
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_EUTRAN_SGS, false);
|
||||
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
|
||||
"Disabled EUTRAN-SGs (4G)");
|
||||
break;
|
||||
case -ENOENT:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
|
||||
break;
|
||||
case -EIO:
|
||||
default:
|
||||
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static const struct hlr_iuse hlr_iuses[] = {
|
||||
{
|
||||
@@ -383,6 +583,34 @@ static const struct hlr_iuse hlr_iuses[] = {
|
||||
.name = "own-imsi",
|
||||
.handle_ussd = handle_ussd_own_imsi,
|
||||
},
|
||||
{
|
||||
.name = "get-ran",
|
||||
.handle_ussd = handle_ussd_get_ran,
|
||||
},
|
||||
{
|
||||
.name = "gsm-on",
|
||||
.handle_ussd = handle_ussd_gsm_on,
|
||||
},
|
||||
{
|
||||
.name = "gsm-off",
|
||||
.handle_ussd = handle_ussd_gsm_off,
|
||||
},
|
||||
{
|
||||
.name = "umts-on",
|
||||
.handle_ussd = handle_ussd_umts_on,
|
||||
},
|
||||
{
|
||||
.name = "umts-off",
|
||||
.handle_ussd = handle_ussd_umts_off,
|
||||
},
|
||||
{
|
||||
.name = "lte-on",
|
||||
.handle_ussd = handle_ussd_lte_on,
|
||||
},
|
||||
{
|
||||
.name = "lte-off",
|
||||
.handle_ussd = handle_ussd_lte_off,
|
||||
},
|
||||
};
|
||||
|
||||
const struct hlr_iuse *iuse_find(const char *name)
|
||||
@@ -416,22 +644,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 osmo_gsup_peer_id *peer_name)
|
||||
{
|
||||
if (peer_name->type != OSMO_CNI_PEER_ID_IPA_NAME)
|
||||
if (peer_name->type != OSMO_GSUP_PEER_ID_IPA_NAME)
|
||||
return false;
|
||||
if (peer_name->ipa_name.len <= 5)
|
||||
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 osmo_gsup_peer_id *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);
|
||||
OSMO_ASSERT(peer_name->type == OSMO_GSUP_PEER_ID_IPA_NAME);
|
||||
|
||||
return euse_find(g_hlr, (const char*)(peer_name->ipa_name.val)+5);
|
||||
}
|
||||
@@ -498,6 +726,7 @@ static int handle_ussd(struct ss_session *ss, bool is_euse_originated, const str
|
||||
ss->u.iuse->handle_ussd(ss, gsup, req);
|
||||
/* Release session immediately */
|
||||
ss_session_free(ss);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,10 +750,10 @@ 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) {
|
||||
if (gsup_req->source_name.type != OSMO_GSUP_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_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;
|
||||
}
|
||||
@@ -566,7 +795,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 */
|
||||
OSMO_ASSERT(gsup_req->source_name.type == OSMO_GSUP_PEER_ID_IPA_NAME); /* checked above */
|
||||
ss->vlr_name = gsup_req->source_name.ipa_name;
|
||||
} else {
|
||||
ss->initial_req_from_euse = gsup_req;
|
||||
|
||||
@@ -174,10 +174,17 @@ DEFUN(cfg_hlr_gsup_ipa_name,
|
||||
#define UROUTE_STR "Routing Configuration\n"
|
||||
#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
|
||||
|
||||
#define INT_CHOICE "(own-msisdn|own-imsi)"
|
||||
#define INT_CHOICE "(own-msisdn|own-imsi|get-ran|gsm-on|gsm-off|umts-on|umts-off|lte-on|lte-off)"
|
||||
#define INT_STR "Internal USSD Handler\n" \
|
||||
"Respond with subscribers' own MSISDN\n" \
|
||||
"Respond with subscribers' own IMSI\n"
|
||||
"Respond with subscribers' own IMSI\n" \
|
||||
"Respond with available RAN types\n" \
|
||||
"Enable GSM service\n" \
|
||||
"Disable GSM service\n" \
|
||||
"Enable UMTS service\n" \
|
||||
"Disable UMTS service\n" \
|
||||
"Enable LTE service\n" \
|
||||
"Disable LTE service\n"
|
||||
|
||||
#define EXT_STR "External USSD Handler\n" \
|
||||
"Name of External USSD Handler (IPA CCM ID)\n"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
@@ -38,13 +39,17 @@ struct vty;
|
||||
|
||||
static char *get_datestr(const time_t *t, char *buf, size_t bufsize)
|
||||
{
|
||||
struct tm tm;
|
||||
gmtime_r(t, &tm);
|
||||
strftime(buf, bufsize, "%FT%T+00:00", &tm);
|
||||
struct tm *tm;
|
||||
|
||||
tm = gmtime(t);
|
||||
if (!tm)
|
||||
return "UNKNOWN";
|
||||
|
||||
strftime(buf, bufsize, "%FT%T+00:00", tm);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen)
|
||||
static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen, const char *last_lu_rat)
|
||||
{
|
||||
uint32_t age;
|
||||
char datebuf[32];
|
||||
@@ -52,7 +57,7 @@ static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t
|
||||
return;
|
||||
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);
|
||||
vty_out(vty, " (invalid timestamp)");
|
||||
else {
|
||||
vty_out(vty, " (");
|
||||
#define UNIT_AGO(UNITNAME, UNITVAL) \
|
||||
@@ -64,14 +69,18 @@ static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t
|
||||
UNIT_AGO("h", 60*60);
|
||||
UNIT_AGO("m", 60);
|
||||
UNIT_AGO("s", 1);
|
||||
vty_out(vty, " ago)%s", VTY_NEWLINE);
|
||||
vty_out(vty, " ago)");
|
||||
#undef UNIT_AGO
|
||||
}
|
||||
if (last_lu_rat && *last_lu_rat != '\0')
|
||||
vty_out(vty, " on %s", last_lu_rat);
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
|
||||
{
|
||||
int rc;
|
||||
int i;
|
||||
struct osmo_sub_auth_data aud2g;
|
||||
struct osmo_sub_auth_data aud3g;
|
||||
|
||||
@@ -108,8 +117,12 @@ 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);
|
||||
dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps);
|
||||
dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, subscr->last_lu_rat_cs);
|
||||
dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps, subscr->last_lu_rat_ps);
|
||||
for (i = OSMO_RAT_UNKNOWN + 1; i < ARRAY_SIZE(subscr->rat_types); i++) {
|
||||
vty_out(vty, " %s: %s%s", osmo_rat_type_name(i), subscr->rat_types[i] ? "allowed" : "forbidden",
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
if (!*subscr->imsi)
|
||||
return;
|
||||
@@ -626,6 +639,48 @@ DEFUN(subscriber_nam,
|
||||
}
|
||||
|
||||
|
||||
DEFUN(subscriber_rat,
|
||||
subscriber_rat_cmd,
|
||||
SUBSCR_UPDATE "rat (geran-a|utran-iu|eutran-sgs) (allowed|forbidden)",
|
||||
SUBSCR_UPDATE_HELP
|
||||
"Allow or forbid specific Radio Access Types\n"
|
||||
"Set access to GERAN-A\n"
|
||||
"Set access to UTRAN-Iu\n"
|
||||
"Set access to EUTRAN-SGs\n"
|
||||
"Allow access\n"
|
||||
"Forbid access\n")
|
||||
{
|
||||
struct hlr_subscriber subscr;
|
||||
const char *id_type = argv[0];
|
||||
const char *id = argv[1];
|
||||
const char *rat_str = argv[2];
|
||||
const char *allowed_forbidden = argv[3];
|
||||
enum osmo_rat_type rat = OSMO_RAT_UNKNOWN;
|
||||
bool allowed;
|
||||
int rc;
|
||||
|
||||
if (strcmp(rat_str, "geran-a") == 0)
|
||||
rat = OSMO_RAT_GERAN_A;
|
||||
else if (strcmp(rat_str, "utran-iu") == 0)
|
||||
rat = OSMO_RAT_UTRAN_IU;
|
||||
else if (strcmp(rat_str, "eutran-sgs") == 0)
|
||||
rat = OSMO_RAT_EUTRAN_SGS;
|
||||
|
||||
allowed = (strcmp(allowed_forbidden, "allowed") == 0);
|
||||
|
||||
if (get_subscr_by_argv(vty, id_type, id, &subscr))
|
||||
return CMD_WARNING;
|
||||
|
||||
rc = hlr_subscr_rat_flag(g_hlr, &subscr, rat, allowed);
|
||||
|
||||
if (rc && rc != -ENOEXEC) {
|
||||
vty_out(vty, "%% Error: cannot set %s to %s%s",
|
||||
osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
void hlr_vty_subscriber_init(void)
|
||||
{
|
||||
install_element_ve(&subscriber_show_cmd);
|
||||
@@ -639,4 +694,5 @@ void hlr_vty_subscriber_init(void)
|
||||
install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
|
||||
install_element(ENABLE_NODE, &subscriber_imei_cmd);
|
||||
install_element(ENABLE_NODE, &subscriber_nam_cmd);
|
||||
install_element(ENABLE_NODE, &subscriber_rat_cmd);
|
||||
}
|
||||
|
||||
57
src/lu_fsm.c
57
src/lu_fsm.c
@@ -26,7 +26,7 @@
|
||||
#include <osmocom/gsm/apn.h>
|
||||
#include <osmocom/gsm/gsm48_ie.h>
|
||||
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_req.h>
|
||||
#include <osmocom/hlr/logging.h>
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
@@ -52,11 +52,11 @@ struct lu {
|
||||
bool is_ps;
|
||||
|
||||
/* VLR requesting the LU. */
|
||||
struct osmo_cni_peer_id vlr_name;
|
||||
struct osmo_gsup_peer_id 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 osmo_gsup_peer_id via_proxy;
|
||||
};
|
||||
LLIST_HEAD(g_all_lu);
|
||||
|
||||
@@ -108,6 +108,8 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct lu *lu;
|
||||
bool any_rat_allowed;
|
||||
int i;
|
||||
|
||||
OSMO_ASSERT(update_location_req);
|
||||
OSMO_ASSERT(update_location_req->gsup.message_type == OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST);
|
||||
@@ -130,13 +132,13 @@ 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 (osmo_gsup_peer_id_is_empty(&lu->vlr_name)) {
|
||||
lu_failure(lu, GMM_CAUSE_NET_FAIL, "LU without a VLR");
|
||||
return;
|
||||
}
|
||||
|
||||
if (db_subscr_get_by_imsi(g_hlr->dbc, update_location_req->gsup.imsi, &lu->subscr) < 0) {
|
||||
lu_failure(lu, GMM_CAUSE_IMSI_UNKNOWN, "Subscriber does not exist");
|
||||
lu_failure(lu, GMM_CAUSE_ROAMING_NOTALLOWED, "Subscriber does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -151,10 +153,32 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if any available RAT type is allowed. See 3GPP TS 29.010 3.2 'Routeing area updating' and 3.8 'Location
|
||||
* update' for the "No Suitable cells in location area" error code. */
|
||||
any_rat_allowed = false;
|
||||
for (i = 0; i < update_location_req->gsup.supported_rat_types_len; i++) {
|
||||
enum osmo_rat_type rat = update_location_req->gsup.supported_rat_types[i];
|
||||
if (rat <= 0 || rat >= OSMO_RAT_COUNT) {
|
||||
lu_failure(lu, GMM_CAUSE_COND_IE_ERR, "Invalid RAT type in GSUP request: %s",
|
||||
osmo_rat_type_name(rat));
|
||||
return;
|
||||
}
|
||||
if (lu->subscr.rat_types[rat]) {
|
||||
any_rat_allowed = true;
|
||||
LOG_LU(lu, LOGL_DEBUG, "subscriber allowed on %s\n", osmo_rat_type_name(rat));
|
||||
} else {
|
||||
LOG_LU(lu, LOGL_DEBUG, "subscriber not allowed on %s\n", osmo_rat_type_name(rat));
|
||||
}
|
||||
}
|
||||
if (!any_rat_allowed && update_location_req->gsup.supported_rat_types_len > 0) {
|
||||
lu_failure(lu, GMM_CAUSE_NO_SUIT_CELL_IN_LA, "subscriber not allowed on any available RAT type");
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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 +187,31 @@ 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 (!osmo_gsup_peer_id_is_empty(&lu->via_proxy)) {
|
||||
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));
|
||||
osmo_gsup_peer_id_to_str(&lu->vlr_name),
|
||||
osmo_gsup_peer_id_to_str(&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));
|
||||
osmo_gsup_peer_id_to_str(&lu->vlr_name));
|
||||
}
|
||||
|
||||
if (osmo_cni_peer_id_is_empty(&lu->vlr_name)
|
||||
|| (lu->vlr_name.type != OSMO_CNI_PEER_ID_IPA_NAME)) {
|
||||
if (osmo_gsup_peer_id_is_empty(&lu->vlr_name)
|
||||
|| (lu->vlr_name.type != OSMO_GSUP_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));
|
||||
osmo_gsup_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)) {
|
||||
if (!osmo_gsup_peer_id_is_empty(&lu->via_proxy) && (lu->via_proxy.type != OSMO_GSUP_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));
|
||||
osmo_gsup_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)) {
|
||||
osmo_gsup_peer_id_is_empty(&lu->via_proxy)? NULL : &lu->via_proxy.ipa_name,
|
||||
update_location_req->gsup.supported_rat_types, update_location_req->gsup.supported_rat_types_len)) {
|
||||
lu_failure(lu, GMM_CAUSE_NET_FAIL, "Cannot update %s in the database",
|
||||
lu->is_ps ? "SGSN number" : "VLR number");
|
||||
return;
|
||||
|
||||
@@ -213,9 +213,9 @@ struct osmo_mdns_record *osmo_mdns_record_txt_keyval_encode(void *ctx, const cha
|
||||
|
||||
va_start(ap, value_fmt);
|
||||
value = talloc_vasprintf(ctx, value_fmt, ap);
|
||||
va_end(ap);
|
||||
if (!value)
|
||||
return NULL;
|
||||
va_end(ap);
|
||||
r = _osmo_mdns_record_txt_encode(ctx, key, value);
|
||||
talloc_free(value);
|
||||
return r;
|
||||
|
||||
@@ -83,12 +83,7 @@ char *osmo_mdns_rfc_qname_decode(void *ctx, const char *qname, size_t qname_max_
|
||||
return NULL;
|
||||
|
||||
while (*qname) {
|
||||
size_t len;
|
||||
|
||||
if (i >= qname_max_len)
|
||||
return NULL;
|
||||
|
||||
len = *qname;
|
||||
size_t len = *qname;
|
||||
next_label = qname + len + 1;
|
||||
|
||||
if (next_label >= qname_end || i + len > OSMO_MDNS_RFC_MAX_NAME_LEN)
|
||||
|
||||
@@ -84,7 +84,7 @@ struct osmo_mdns_sock *osmo_mdns_sock_init(void *ctx, const char *ip, unsigned i
|
||||
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;
|
||||
goto error;
|
||||
}
|
||||
memcpy(&multicast_req.imr_multiaddr, &((struct sockaddr_in*)(ret->ai->ai_addr))->sin_addr,
|
||||
sizeof(multicast_req.imr_multiaddr));
|
||||
@@ -92,7 +92,7 @@ struct osmo_mdns_sock *osmo_mdns_sock_init(void *ctx, const char *ip, unsigned i
|
||||
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;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Always allow binding the same IP and port twice. This is needed in OsmoHLR (where the code becomes cleaner by
|
||||
@@ -102,22 +102,20 @@ struct osmo_mdns_sock *osmo_mdns_sock_init(void *ctx, const char *ip, unsigned i
|
||||
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;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
goto error;
|
||||
}
|
||||
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;
|
||||
goto error;
|
||||
|
||||
return ret;
|
||||
error_sock:
|
||||
close(sock);
|
||||
error:
|
||||
if (ret->ai)
|
||||
freeaddrinfo(ret->ai);
|
||||
|
||||
@@ -205,9 +205,9 @@ size_t osmo_mslookup_result_to_str_buf(char *buf, size_t buflen,
|
||||
if (result && result->rc == OSMO_MSLOOKUP_RC_NONE)
|
||||
result = NULL;
|
||||
if (result) {
|
||||
if (result->rc != OSMO_MSLOOKUP_RC_RESULT) {
|
||||
if (result->rc != OSMO_MSLOOKUP_RC_RESULT)
|
||||
OSMO_STRBUF_PRINTF(sb, " %s", osmo_mslookup_result_code_name(result->rc));
|
||||
} else {
|
||||
if (result->rc == OSMO_MSLOOKUP_RC_RESULT) {
|
||||
if (result->host_v4.ip[0]) {
|
||||
OSMO_STRBUF_PRINTF(sb, " -> ipv4: " OSMO_SOCKADDR_STR_FMT,
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&result->host_v4));
|
||||
@@ -260,8 +260,6 @@ static int token(char *dest, size_t dest_size, const char *start, const char *en
|
||||
|
||||
/*! 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)
|
||||
|
||||
@@ -146,7 +146,7 @@ void osmo_mslookup_client_rx_result(struct osmo_mslookup_client *client, uint32_
|
||||
if (!req) {
|
||||
LOGP(DMSLOOKUP, LOGL_ERROR,
|
||||
"Internal error: Got mslookup result for a request that does not exist (handle %u)\n",
|
||||
request_handle);
|
||||
req->request_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,6 @@ static void mdns_method_request(struct osmo_mslookup_client_method *method, cons
|
||||
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 */
|
||||
|
||||
@@ -315,18 +315,17 @@ int do_send(int argc, char ** argv)
|
||||
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;
|
||||
goto exit_cleanup;
|
||||
}
|
||||
|
||||
if (osmo_mdns_sock_send(sock, msg)) {
|
||||
print_error("unable to send mDNS message\n");
|
||||
goto exit_cleanup_sock;
|
||||
goto exit_cleanup;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
exit_cleanup_sock:
|
||||
osmo_mdns_sock_cleanup(sock);
|
||||
exit_cleanup:
|
||||
osmo_mdns_sock_cleanup(sock);
|
||||
talloc_free(ctx);
|
||||
return rc;
|
||||
}
|
||||
@@ -456,11 +455,6 @@ static int socket_read_cb(struct osmo_fd *ofd)
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -261,7 +261,6 @@ static bool subscriber_has_done_lu_here_hlr(const struct osmo_mslookup_query *qu
|
||||
if (!subscr->vlr_number[0]) {
|
||||
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: not attached (vlr_number unset)\n",
|
||||
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (subscr->vlr_via_proxy.len) {
|
||||
|
||||
@@ -148,7 +148,7 @@ void mslookup_server_mdns_config_apply()
|
||||
g_hlr->mslookup.server.mdns.domain_suffix);
|
||||
if (!g_hlr->mslookup.server.mdns.running)
|
||||
LOGP(DMSLOOKUP, 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));
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.server.mdns.running->bind_addr));
|
||||
else
|
||||
LOGP(DMSLOOKUP, LOGL_NOTICE, "Started mslookup mDNS server, receiving mDNS requests at multicast "
|
||||
OSMO_SOCKADDR_STR_FMT "\n",
|
||||
|
||||
16
src/proxy.c
16
src/proxy.c
@@ -80,7 +80,7 @@ static void proxy_deferred_gsup_req_add(struct proxy *proxy, struct osmo_gsup_re
|
||||
static void proxy_pending_req_remote_hlr_connect_result(struct osmo_gsup_req *req, struct remote_hlr *remote_hlr)
|
||||
{
|
||||
if (!remote_hlr || !remote_hlr_is_up(remote_hlr)) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "Proxy: Failed to connect to home HLR");
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_ROAMING_NOTALLOWED, "Proxy: Failed to connect to home HLR");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -263,10 +263,10 @@ static int proxy_acknowledge_gsup_to_remote_hlr(struct proxy *proxy, const struc
|
||||
bool cs;
|
||||
int rc;
|
||||
|
||||
if (req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) {
|
||||
if (req->source_name.type != OSMO_GSUP_PEER_ID_IPA_NAME) {
|
||||
LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_ERROR,
|
||||
"Unsupported GSUP peer id type: %s\n",
|
||||
osmo_cni_peer_id_type_name(req->source_name.type));
|
||||
osmo_gsup_peer_id_type_name(req->source_name.type));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ static int proxy_acknowledge_gsup_to_remote_hlr(struct proxy *proxy, const struc
|
||||
"%s: preliminary VLR name for%s%s to %s\n",
|
||||
rc ? "failed to update" : "updated",
|
||||
cs ? " CS" : "", ps ? " PS" : "",
|
||||
osmo_cni_peer_id_to_str(&req->source_name));
|
||||
osmo_gsup_peer_id_to_str(&req->source_name));
|
||||
break;
|
||||
/* TODO: delete proxy entry in case of a Purge Request? */
|
||||
default:
|
||||
@@ -483,13 +483,13 @@ int proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_s
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!osmo_cni_peer_id_is_empty(&req->via_proxy)) {
|
||||
if (!osmo_gsup_peer_id_is_empty(&req->via_proxy)) {
|
||||
LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_INFO, "VLR->HLR: forwarding from %s via proxy %s\n",
|
||||
osmo_cni_peer_id_to_str(&req->source_name),
|
||||
osmo_cni_peer_id_to_str(&req->via_proxy));
|
||||
osmo_gsup_peer_id_to_str(&req->source_name),
|
||||
osmo_gsup_peer_id_to_str(&req->via_proxy));
|
||||
} else {
|
||||
LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_INFO, "VLR->HLR: forwarding from %s\n",
|
||||
osmo_cni_peer_id_to_str(&req->source_name));
|
||||
osmo_gsup_peer_id_to_str(&req->source_name));
|
||||
}
|
||||
|
||||
/* We could always store in the defer queue and empty the queue if the connection is already up.
|
||||
|
||||
@@ -233,9 +233,9 @@ void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct
|
||||
else
|
||||
forward = req->gsup;
|
||||
|
||||
if (req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) {
|
||||
if (req->source_name.type != OSMO_GSUP_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));
|
||||
osmo_gsup_peer_id_type_name(req->source_name.type));
|
||||
return;
|
||||
}
|
||||
forward.source_name = req->source_name.ipa_name.val;
|
||||
|
||||
423
src/sms_over_gsup.c
Normal file
423
src/sms_over_gsup.c
Normal file
@@ -0,0 +1,423 @@
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/gsm48_ie.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
|
||||
#include <osmocom/gsm/gsm0411_utils.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_11.h>
|
||||
|
||||
#include <osmocom/mslookup/mslookup_client.h>
|
||||
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/remote_hlr.h>
|
||||
#include <osmocom/hlr/mslookup_server.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include <osmocom/hlr/sms_over_gsup.h>
|
||||
|
||||
static int sms_extract_destination_msisdn(char *msisdn, size_t msisdn_size, struct osmo_gsup_req *req)
|
||||
{
|
||||
int rc;
|
||||
if (req->gsup.sm_rp_da_type == OSMO_GSUP_SMS_SM_RP_ODA_MSISDN
|
||||
&& req->gsup.sm_rp_da_len > 0) {
|
||||
LOG_GSUP_REQ(req, LOGL_INFO, "Extracting destination MSISDN from GSUP SM_RP_DA IE: %s\n",
|
||||
osmo_hexdump(req->gsup.sm_rp_da, req->gsup.sm_rp_da_len));
|
||||
rc = gsm48_decode_bcd_number2(msisdn, msisdn_size, req->gsup.sm_rp_da, req->gsup.sm_rp_da_len, 0);
|
||||
if (!rc)
|
||||
LOG_GSUP_REQ(req, LOGL_INFO, "success -> %s\n", msisdn);
|
||||
else
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "fail %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* The DA was not an MSISDN -- get from inside the SMS PDU */
|
||||
if (req->gsup.sm_rp_ui_len > 3) {
|
||||
const uint8_t *da = req->gsup.sm_rp_ui + 2;
|
||||
uint8_t da_len = *da;
|
||||
uint8_t da_len_bytes;
|
||||
uint8_t address_lv[12] = {};
|
||||
|
||||
da_len_bytes = 2 + da_len/2 + da_len%2;
|
||||
|
||||
if (da_len_bytes < 4 || da_len_bytes > 12
|
||||
|| da_len_bytes > req->gsup.sm_rp_ui_len - 2) {
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "Invalid da_len_bytes %u\n", da_len_bytes);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(address_lv, da, da_len_bytes);
|
||||
address_lv[0] = da_len_bytes - 1;
|
||||
|
||||
LOG_GSUP_REQ(req, LOGL_INFO, "Extracting destination MSISDN from SMS PDU DA: %s\n",
|
||||
osmo_hexdump(address_lv, da_len_bytes));
|
||||
rc = gsm48_decode_bcd_number2(msisdn, msisdn_size, address_lv, da_len_bytes, 1);
|
||||
if (!rc)
|
||||
LOG_GSUP_REQ(req, LOGL_INFO, "success -> %s\n", msisdn);
|
||||
else
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "fail %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "fail: no SM_RP_DA nor SMS PDU (sm_rp_ui_len > 3)\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int sms_extract_sender_msisdn(char *msisdn, size_t msisdn_size, struct osmo_gsup_req *req)
|
||||
{
|
||||
int rc;
|
||||
if (req->gsup.sm_rp_oa_type == OSMO_GSUP_SMS_SM_RP_ODA_MSISDN
|
||||
&& req->gsup.sm_rp_oa_len > 0) {
|
||||
LOG_GSUP_REQ(req, LOGL_INFO, "Extracting sender MSISDN from GSUP SM_RP_OA IE: %s\n",
|
||||
osmo_hexdump(req->gsup.sm_rp_oa, req->gsup.sm_rp_oa_len));
|
||||
rc = gsm48_decode_bcd_number2(msisdn, msisdn_size, req->gsup.sm_rp_oa, req->gsup.sm_rp_oa_len, 0);
|
||||
if (!rc)
|
||||
LOG_GSUP_REQ(req, LOGL_INFO, "success -> %s\n", msisdn);
|
||||
else
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "fail %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
LOG_GSUP_REQ(req, LOGL_ERROR, "fail: no MSISDN obtained from SM_RP_OA\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static struct msgb *sms_mo_pdu_to_mt_pdu(const uint8_t *mo_pdu, size_t mo_pdu_len, const char *sender_msisdn)
|
||||
{
|
||||
/* Hacky shortened copy-paste of osmo-msc's gsm340_rx_tpdu() */
|
||||
|
||||
uint8_t protocol_id;
|
||||
uint8_t data_coding_scheme;
|
||||
uint8_t user_data_len;
|
||||
uint8_t user_data_octet_len;
|
||||
const uint8_t *user_data;
|
||||
uint8_t status_rep_req;
|
||||
uint8_t ud_hdr_ind;
|
||||
|
||||
{
|
||||
const uint8_t *smsp = mo_pdu;
|
||||
enum sms_alphabet sms_alphabet;
|
||||
uint8_t sms_vpf;
|
||||
uint8_t da_len_bytes;
|
||||
|
||||
sms_vpf = (*smsp & 0x18) >> 3;
|
||||
status_rep_req = (*smsp & 0x20) >> 5;
|
||||
ud_hdr_ind = (*smsp & 0x40);
|
||||
|
||||
smsp += 2;
|
||||
|
||||
/* length in bytes of the destination address */
|
||||
da_len_bytes = 2 + *smsp/2 + *smsp%2;
|
||||
if (da_len_bytes < 4 || da_len_bytes > 12)
|
||||
return NULL;
|
||||
smsp += da_len_bytes;
|
||||
|
||||
protocol_id = *smsp++;
|
||||
data_coding_scheme = *smsp++;
|
||||
|
||||
sms_alphabet = gsm338_get_sms_alphabet(data_coding_scheme);
|
||||
if (sms_alphabet == 0xffffffff)
|
||||
return NULL;
|
||||
|
||||
switch (sms_vpf) {
|
||||
case GSM340_TP_VPF_RELATIVE:
|
||||
smsp++;
|
||||
break;
|
||||
case GSM340_TP_VPF_ABSOLUTE:
|
||||
case GSM340_TP_VPF_ENHANCED:
|
||||
/* the additional functionality indicator... */
|
||||
if (sms_vpf == GSM340_TP_VPF_ENHANCED && *smsp & (1<<7))
|
||||
smsp++;
|
||||
smsp += 7;
|
||||
break;
|
||||
case GSM340_TP_VPF_NONE:
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* As per 3GPP TS 03.40, section 9.2.3.16, TP-User-Data-Length (TP-UDL)
|
||||
* may indicate either the number of septets, or the number of octets,
|
||||
* depending on Data Coding Scheme. We store TP-UDL value as-is,
|
||||
* so this should be kept in mind to avoid buffer overruns. */
|
||||
user_data_len = *smsp++;
|
||||
user_data = smsp;
|
||||
if (user_data_len > 0) {
|
||||
if (sms_alphabet == DCS_7BIT_DEFAULT) {
|
||||
/* TP-UDL is indicated in septets (up to 160) */
|
||||
if (user_data_len > GSM340_UDL_SPT_MAX) {
|
||||
user_data_len = GSM340_UDL_SPT_MAX;
|
||||
}
|
||||
user_data_octet_len = gsm_get_octet_len(user_data_len);
|
||||
} else {
|
||||
/* TP-UDL is indicated in octets (up to 140) */
|
||||
if (user_data_len > GSM340_UDL_OCT_MAX) {
|
||||
user_data_len = GSM340_UDL_OCT_MAX;
|
||||
}
|
||||
user_data_octet_len = user_data_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
/* The following is a hacky copy pasted and shortened version of osmo-msc's gsm340_gen_sms_deliver_tpdu() */
|
||||
struct msgb *msg = gsm411_msgb_alloc();
|
||||
uint8_t *smsp;
|
||||
uint8_t oa[12]; /* max len per 03.40 */
|
||||
int oa_len;
|
||||
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
/* generate first octet with masked bits */
|
||||
smsp = msgb_put(msg, 1);
|
||||
/* TP-MTI (message type indicator) */
|
||||
*smsp = GSM340_SMS_DELIVER_SC2MS;
|
||||
/* TP-MMS (more messages to send) */
|
||||
if (0 /* FIXME */)
|
||||
*smsp |= 0x04;
|
||||
/* TP-SRI(deliver)/SRR(submit) */
|
||||
if (status_rep_req)
|
||||
*smsp |= 0x20;
|
||||
/* TP-UDHI (indicating TP-UD contains a header) */
|
||||
if (ud_hdr_ind)
|
||||
*smsp |= 0x40;
|
||||
|
||||
/* generate originator address */
|
||||
oa_len = gsm340_gen_oa(oa, sizeof(oa), 0, 0, sender_msisdn);
|
||||
if (oa_len < 0) {
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
smsp = msgb_put(msg, oa_len);
|
||||
memcpy(smsp, oa, oa_len);
|
||||
|
||||
/* generate TP-PID */
|
||||
smsp = msgb_put(msg, 1);
|
||||
*smsp = protocol_id;
|
||||
|
||||
/* generate TP-DCS */
|
||||
smsp = msgb_put(msg, 1);
|
||||
*smsp = data_coding_scheme;
|
||||
|
||||
/* generate TP-SCTS */
|
||||
smsp = msgb_put(msg, 7);
|
||||
gsm340_gen_scts(smsp, time(NULL));
|
||||
|
||||
/* generate TP-UDL */
|
||||
smsp = msgb_put(msg, 1);
|
||||
*smsp = user_data_len;
|
||||
smsp = msgb_put(msg, user_data_octet_len);
|
||||
memcpy(smsp, user_data, user_data_octet_len);
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void sms_recipient_up_cb(const struct osmo_sockaddr_str *addr, struct remote_hlr *remote_hlr, void *data)
|
||||
{
|
||||
struct osmo_gsup_req *req = data;
|
||||
struct osmo_gsup_message modified_gsup = req->gsup;
|
||||
// struct msgb *mt_pdu = NULL;
|
||||
if (!remote_hlr) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_MSC_TEMP_NOTREACH,
|
||||
"Failed to connect to SMS recipient: " OSMO_SOCKADDR_STR_FMT,
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(addr));
|
||||
return;
|
||||
}
|
||||
/* We must not send out another MO request, to make sure we don't send the request in an infinite loop. */
|
||||
#if 0
|
||||
if (req->gsup.message_type == OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST) {
|
||||
char sender_msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
|
||||
if (sms_extract_sender_msisdn(sender_msisdn, sizeof(sender_msisdn), req)) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "Cannot find sender MSISDN");
|
||||
return;
|
||||
}
|
||||
mt_pdu = sms_mo_pdu_to_mt_pdu(req->gsup.sm_rp_ui, req->gsup.sm_rp_ui_len, sender_msisdn);
|
||||
if (!mt_pdu) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
|
||||
"Cannot translate PDU to a DELIVER PDU");
|
||||
return;
|
||||
}
|
||||
modified_gsup.message_type = OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST;
|
||||
modified_gsup.sm_rp_ui = mt_pdu->data;
|
||||
modified_gsup.sm_rp_ui_len = mt_pdu->len;
|
||||
}
|
||||
#endif
|
||||
LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to remote HLR " OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
|
||||
remote_hlr_gsup_forward_to_remote_hlr(remote_hlr, req, &modified_gsup);
|
||||
}
|
||||
|
||||
static void sms_over_gsup_mt(struct osmo_gsup_req *req)
|
||||
{
|
||||
/* Find a locally connected MSC that knows this MSISDN. */
|
||||
uint32_t lu_age;
|
||||
struct osmo_gsup_peer_id local_msc_id;
|
||||
struct osmo_mslookup_query query = {
|
||||
.service = OSMO_MSLOOKUP_SERVICE_SMS_GSUP,
|
||||
.id = {
|
||||
.type = OSMO_MSLOOKUP_ID_MSISDN,
|
||||
},
|
||||
};
|
||||
struct osmo_gsup_message modified_gsup = req->gsup;
|
||||
struct msgb *mt_pdu = NULL;
|
||||
|
||||
if (sms_extract_destination_msisdn(query.id.msisdn, sizeof(query.id.msisdn), req)) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "invalid MSISDN");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_GSUP_REQ(req, LOGL_NOTICE, "SMS to MSISDN: %s\n", query.id.msisdn);
|
||||
|
||||
/* If a local attach is found, write the subscriber's IMSI to the modified_gsup buffer */
|
||||
if (!subscriber_has_done_lu_here(&query, &lu_age, &local_msc_id.ipa_name,
|
||||
modified_gsup.imsi, sizeof(modified_gsup.imsi))) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_MSC_TEMP_NOTREACH,
|
||||
"SMS recipient not reachable: %s\n",
|
||||
osmo_mslookup_result_name_c(OTC_SELECT, &query, NULL));
|
||||
return;
|
||||
}
|
||||
local_msc_id.type = OSMO_GSUP_PEER_ID_IPA_NAME;
|
||||
/* A local MSC indeed has an active subscription for the recipient. Deliver there. */
|
||||
|
||||
if (modified_gsup.message_type == OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST) {
|
||||
/* This is a direct local delivery, and sms_over_gsup_mo_directly_to_mt() just passed the MO request
|
||||
* altough here we are on the MT side. We must not send out another MO request, to make sure we don't
|
||||
* send the request in an infinite loop.
|
||||
* Also patch in the recipient's IMSI.
|
||||
*/
|
||||
char sender_msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
|
||||
if (sms_extract_sender_msisdn(sender_msisdn, sizeof(sender_msisdn), req)) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "Cannot find sender MSISDN");
|
||||
return;
|
||||
}
|
||||
mt_pdu = sms_mo_pdu_to_mt_pdu(req->gsup.sm_rp_ui, req->gsup.sm_rp_ui_len, sender_msisdn);
|
||||
if (!mt_pdu) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
|
||||
"Cannot translate PDU to a DELIVER PDU");
|
||||
return;
|
||||
}
|
||||
modified_gsup.message_type = OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST;
|
||||
modified_gsup.sm_rp_ui = mt_pdu->data;
|
||||
modified_gsup.sm_rp_ui_len = mt_pdu->len;
|
||||
}
|
||||
|
||||
osmo_gsup_forward_to_local_peer(g_hlr->gs, &local_msc_id, req, &modified_gsup);
|
||||
|
||||
if (mt_pdu)
|
||||
msgb_free(mt_pdu);
|
||||
}
|
||||
|
||||
static void resolve_sms_recipient_cb(struct osmo_mslookup_client *client,
|
||||
uint32_t request_handle,
|
||||
const struct osmo_mslookup_query *query,
|
||||
const struct osmo_mslookup_result *result)
|
||||
{
|
||||
struct osmo_gsup_req *req = query->priv;
|
||||
const struct osmo_sockaddr_str *remote_hlr_addr = NULL;
|
||||
const struct mslookup_service_host *local_gsup;
|
||||
|
||||
if (result->rc == OSMO_MSLOOKUP_RC_RESULT) {
|
||||
if (osmo_sockaddr_str_is_nonzero(&result->host_v4))
|
||||
remote_hlr_addr = &result->host_v4;
|
||||
else if (osmo_sockaddr_str_is_nonzero(&result->host_v6))
|
||||
remote_hlr_addr = &result->host_v6;
|
||||
}
|
||||
|
||||
if (!remote_hlr_addr) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_MSC_TEMP_NOTREACH,
|
||||
"Failed to resolve SMS recipient: %s\n",
|
||||
osmo_mslookup_result_name_c(OTC_SELECT, query, result));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Possibly, this HLR here has responded to itself via mslookup. Don't make a GSUP connection to ourselves,
|
||||
* instead go directly to the MT path. */
|
||||
local_gsup = mslookup_server_get_local_gsup_addr();
|
||||
LOG_GSUP_REQ(req, LOGL_NOTICE, "local_gsup " OSMO_SOCKADDR_STR_FMT " " OSMO_SOCKADDR_STR_FMT
|
||||
" remote_hlr_addr " OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&local_gsup->host_v4),
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&local_gsup->host_v6),
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(remote_hlr_addr));
|
||||
if (local_gsup
|
||||
&& (!osmo_sockaddr_str_cmp(&local_gsup->host_v4, remote_hlr_addr)
|
||||
|| !osmo_sockaddr_str_cmp(&local_gsup->host_v6, remote_hlr_addr))) {
|
||||
sms_over_gsup_mt(req);
|
||||
return;
|
||||
}
|
||||
|
||||
remote_hlr_get_or_connect(remote_hlr_addr, true, sms_recipient_up_cb, req);
|
||||
}
|
||||
|
||||
static void sms_over_gsup_mo_directly_to_mt(struct osmo_gsup_req *req)
|
||||
{
|
||||
/* Figure out the location of the SMS recipient by mslookup */
|
||||
if (osmo_mslookup_client_active(g_hlr->mslookup.client.client)) {
|
||||
/* D-GSM is active. Kick off an mslookup for the current location of the MSISDN. */
|
||||
uint32_t request_handle;
|
||||
struct osmo_mslookup_query_handling handling = {
|
||||
.min_wait_milliseconds = g_hlr->mslookup.client.result_timeout_milliseconds,
|
||||
.result_cb = resolve_sms_recipient_cb,
|
||||
};
|
||||
struct osmo_mslookup_query query = {
|
||||
.id = {
|
||||
.type = OSMO_MSLOOKUP_ID_MSISDN,
|
||||
},
|
||||
.priv = req,
|
||||
};
|
||||
if (sms_extract_destination_msisdn(query.id.msisdn, sizeof(query.id.msisdn), req)
|
||||
|| !osmo_msisdn_str_valid(query.id.msisdn)) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "invalid MSISDN");
|
||||
return;
|
||||
}
|
||||
OSMO_STRLCPY_ARRAY(query.service, OSMO_MSLOOKUP_SERVICE_SMS_GSUP);
|
||||
|
||||
request_handle = osmo_mslookup_client_request(g_hlr->mslookup.client.client, &query, &handling);
|
||||
if (request_handle) {
|
||||
/* Querying succeeded. Wait for resolve_sms_recipient_cb() to be called. */
|
||||
return;
|
||||
}
|
||||
/* Querying failed. Try whether delivering to a locally connected MSC works out. */
|
||||
LOG_DGSM(req->gsup.imsi, LOGL_ERROR,
|
||||
"Error dispatching mslookup query for SMS: %s -- trying local delivery\n",
|
||||
osmo_mslookup_result_name_c(OTC_SELECT, &query, NULL));
|
||||
}
|
||||
|
||||
/* Attempt direct delivery */
|
||||
sms_over_gsup_mt(req);
|
||||
}
|
||||
|
||||
static void sms_over_gsup_mo(struct osmo_gsup_req *req)
|
||||
{
|
||||
if (!osmo_gsup_peer_id_is_empty(&g_hlr->sms_over_gsup.smsc)) {
|
||||
/* Forward to SMSC */
|
||||
/* FIXME actually use branch fixeria/sms for this */
|
||||
osmo_gsup_forward_to_local_peer(g_hlr->gs, &g_hlr->sms_over_gsup.smsc, req, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_hlr->sms_over_gsup.try_direct_delivery) {
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_PROTO_ERR_UNSPEC,
|
||||
"cannot deliver SMS over GSUP: No SMSC (and direct delivery disabled)");
|
||||
return;
|
||||
}
|
||||
|
||||
sms_over_gsup_mo_directly_to_mt(req);
|
||||
}
|
||||
|
||||
bool sms_over_gsup_check_handle_msg(struct osmo_gsup_req *req)
|
||||
{
|
||||
switch (req->gsup.message_type) {
|
||||
case OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST:
|
||||
sms_over_gsup_mo(req);
|
||||
return true;
|
||||
|
||||
case OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST:
|
||||
sms_over_gsup_mt(req);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ db_test_LDADD = \
|
||||
$(top_builddir)/src/db_auc.o \
|
||||
$(top_builddir)/src/db_hlr.o \
|
||||
$(top_builddir)/src/db.o \
|
||||
$(top_builddir)/src/cni_peer_id.o \
|
||||
$(top_builddir)/src/gsup_peer_id.o \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/gsupclient/cni_peer_id.h>
|
||||
#include <osmocom/gsupclient/gsup_peer_id.h>
|
||||
#include <osmocom/hlr/db.h>
|
||||
#include <osmocom/hlr/logging.h>
|
||||
|
||||
@@ -173,6 +173,8 @@ void dump_subscr(struct hlr_subscriber *subscr)
|
||||
Pfo(lmsi, "0x%x", subscr);
|
||||
Pb(true, ms_purged_cs);
|
||||
Pb(true, ms_purged_ps);
|
||||
Ps(last_lu_rat_cs);
|
||||
Ps(last_lu_rat_ps);
|
||||
fprintf(stderr, "}\n");
|
||||
#undef Ps
|
||||
#undef Pd
|
||||
@@ -243,7 +245,7 @@ static int db_subscr_lu_str(struct db_context *dbc, int64_t subscr_id,
|
||||
{
|
||||
struct osmo_ipa_name vlr_nr;
|
||||
osmo_ipa_name_set_str(&vlr_nr, vlr_or_sgsn_number);
|
||||
return db_subscr_lu(dbc, subscr_id, &vlr_nr, is_ps, NULL);
|
||||
return db_subscr_lu(dbc, subscr_id, &vlr_nr, is_ps, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void test_subscr_create_update_sel_delete()
|
||||
@@ -918,6 +920,50 @@ static void test_subscr_sqn()
|
||||
comment_end();
|
||||
}
|
||||
|
||||
static void test_ind()
|
||||
{
|
||||
comment_start();
|
||||
|
||||
#define ASSERT_IND(VLR, IND) do { \
|
||||
unsigned int ind; \
|
||||
struct osmo_gsup_peer_id vlr; \
|
||||
OSMO_ASSERT(!osmo_gsup_peer_id_set_str(&vlr, OSMO_GSUP_PEER_ID_IPA_NAME, VLR)); \
|
||||
ASSERT_RC(db_ind(dbc, &vlr, &ind), 0); \
|
||||
fprintf(stderr, "%s ind = %u\n\n", osmo_quote_str((char*)vlr.ipa_name.val, vlr.ipa_name.len), ind); \
|
||||
if (ind != (IND)) \
|
||||
fprintf(stderr, " ERROR: expected " #IND "\n"); \
|
||||
} while (0)
|
||||
#define IND_DEL(VLR) do { \
|
||||
struct osmo_gsup_peer_id vlr; \
|
||||
OSMO_ASSERT(!osmo_gsup_peer_id_set_str(&vlr, OSMO_GSUP_PEER_ID_IPA_NAME, VLR)); \
|
||||
ASSERT_RC(db_ind_del(dbc, &vlr), 0); \
|
||||
fprintf(stderr, "%s ind deleted\n\n", osmo_quote_str((char*)vlr.ipa_name.val, vlr.ipa_name.len)); \
|
||||
} while (0)
|
||||
|
||||
ASSERT_IND("msc-23", 1);
|
||||
ASSERT_IND("sgsn-11", 2);
|
||||
ASSERT_IND("msc-42", 3);
|
||||
ASSERT_IND("sgsn-22", 4);
|
||||
ASSERT_IND("msc-0x17", 5);
|
||||
ASSERT_IND("sgsn-0xaa", 6);
|
||||
ASSERT_IND("msc-42", 3);
|
||||
ASSERT_IND("sgsn-22", 4);
|
||||
ASSERT_IND("msc-0x17", 5);
|
||||
ASSERT_IND("sgsn-0xaa", 6);
|
||||
ASSERT_IND("sgsn-0xbb", 7);
|
||||
ASSERT_IND("msc-0x2a", 8);
|
||||
ASSERT_IND("msc-42", 3);
|
||||
ASSERT_IND("sgsn-22", 4);
|
||||
ASSERT_IND("msc-23", 1);
|
||||
ASSERT_IND("sgsn-11", 2);
|
||||
|
||||
IND_DEL("msc-0x17"); /* dropped IND == 5 */
|
||||
ASSERT_IND("msc-0x2a", 8); /* known CS remains where it is */
|
||||
ASSERT_IND("any-unknown", 9); /* new VLR takes a new IND from the end */
|
||||
|
||||
comment_end();
|
||||
}
|
||||
|
||||
static struct {
|
||||
bool verbose;
|
||||
} cmdline_opts = {
|
||||
@@ -998,6 +1044,7 @@ int main(int argc, char **argv)
|
||||
test_subscr_aud();
|
||||
test_subscr_aud_invalid_len();
|
||||
test_subscr_sqn();
|
||||
test_ind();
|
||||
|
||||
printf("Done\n");
|
||||
db_close(dbc);
|
||||
|
||||
@@ -1613,3 +1613,83 @@ db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT
|
||||
|
||||
===== test_subscr_sqn: SUCCESS
|
||||
|
||||
|
||||
===== test_ind
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-23\0" ind = 1
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-11\0" ind = 2
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-42\0" ind = 3
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-22\0" ind = 4
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-0x17\0" ind = 5
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-0xaa\0" ind = 6
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-42\0" ind = 3
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-22\0" ind = 4
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-0x17\0" ind = 5
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-0xaa\0" ind = 6
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-0xbb\0" ind = 7
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-0x2a\0" ind = 8
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-42\0" ind = 3
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-22\0" ind = 4
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-23\0" ind = 1
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"sgsn-11\0" ind = 2
|
||||
|
||||
db_ind_del(dbc, &vlr) --> 0
|
||||
|
||||
"msc-0x17\0" ind deleted
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"msc-0x2a\0" ind = 8
|
||||
|
||||
db_ind(dbc, &vlr, &ind) --> 0
|
||||
|
||||
"any-unknown\0" ind = 9
|
||||
|
||||
===== test_ind: SUCCESS
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ OsmoHLR# subscriber imsi 123456789012345 create
|
||||
ID: 1
|
||||
IMSI: 123456789012345
|
||||
MSISDN: none
|
||||
GERAN-A: allowed
|
||||
UTRAN-Iu: allowed
|
||||
EUTRAN-SGs: allowed
|
||||
OsmoHLR# subscriber imsi 123456789012345 update msisdn 098765432109876
|
||||
% Updated subscriber IMSI='123456789012345' to MSISDN='098765432109876'
|
||||
OsmoHLR# subscriber imsi 123456789012345 update aud2g comp128v1 ki BeefedCafeFaceAcedAddedDecadeFee
|
||||
@@ -13,11 +16,17 @@ OsmoHLR# subscriber imsi 111111111 create
|
||||
ID: 2
|
||||
IMSI: 111111111
|
||||
MSISDN: none
|
||||
GERAN-A: allowed
|
||||
UTRAN-Iu: allowed
|
||||
EUTRAN-SGs: allowed
|
||||
OsmoHLR# subscriber imsi 222222222 create
|
||||
% Created subscriber 222222222
|
||||
ID: 3
|
||||
IMSI: 222222222
|
||||
MSISDN: none
|
||||
GERAN-A: allowed
|
||||
UTRAN-Iu: allowed
|
||||
EUTRAN-SGs: allowed
|
||||
OsmoHLR# subscriber imsi 222222222 update msisdn 22222
|
||||
% Updated subscriber IMSI='222222222' to MSISDN='22222'
|
||||
OsmoHLR# subscriber imsi 333333 create
|
||||
@@ -25,6 +34,9 @@ OsmoHLR# subscriber imsi 333333 create
|
||||
ID: 4
|
||||
IMSI: 333333
|
||||
MSISDN: none
|
||||
GERAN-A: allowed
|
||||
UTRAN-Iu: allowed
|
||||
EUTRAN-SGs: allowed
|
||||
OsmoHLR# subscriber imsi 333333 update msisdn 3
|
||||
% Updated subscriber IMSI='333333' to MSISDN='3'
|
||||
OsmoHLR# subscriber imsi 333333 update aud2g comp128v2 ki 33333333333333333333333333333333
|
||||
@@ -33,6 +45,9 @@ OsmoHLR# subscriber imsi 444444444444444 create
|
||||
ID: 5
|
||||
IMSI: 444444444444444
|
||||
MSISDN: none
|
||||
GERAN-A: allowed
|
||||
UTRAN-Iu: allowed
|
||||
EUTRAN-SGs: allowed
|
||||
OsmoHLR# subscriber imsi 444444444444444 update msisdn 4444
|
||||
% Updated subscriber IMSI='444444444444444' to MSISDN='4444'
|
||||
OsmoHLR# subscriber imsi 444444444444444 update aud3g milenage k 44444444444444444444444444444444 op 44444444444444444444444444444444
|
||||
@@ -41,6 +56,9 @@ OsmoHLR# subscriber imsi 5555555 create
|
||||
ID: 6
|
||||
IMSI: 5555555
|
||||
MSISDN: none
|
||||
GERAN-A: allowed
|
||||
UTRAN-Iu: allowed
|
||||
EUTRAN-SGs: allowed
|
||||
OsmoHLR# subscriber imsi 5555555 update msisdn 55555555555555
|
||||
% Updated subscriber IMSI='5555555' to MSISDN='55555555555555'
|
||||
OsmoHLR# subscriber imsi 5555555 update aud2g xor ki 55555555555555555555555555555555
|
||||
|
||||
@@ -85,6 +85,9 @@ DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 2
|
||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 3
|
||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 4
|
||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 5
|
||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 6
|
||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 7
|
||||
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 8
|
||||
DMAIN Cmdline option --db-check: Database was opened successfully, quitting.
|
||||
|
||||
Resulting db:
|
||||
@@ -117,6 +120,13 @@ algo_id_3g|ind_bitlen|k|op|opc|sqn|subscriber_id
|
||||
5|5|44444444444444444444444444444444|44444444444444444444444444444444||0|5
|
||||
5|5|55555555555555555555555555555555||55555555555555555555555555555555|0|6
|
||||
|
||||
Table: ind
|
||||
name|type|notnull|dflt_value|pk
|
||||
ind|INTEGER|0||1
|
||||
vlr|TEXT|1||0
|
||||
|
||||
Table ind contents:
|
||||
|
||||
Table: subscriber
|
||||
name|type|notnull|dflt_value|pk
|
||||
ggsn_number|VARCHAR(15)|0||0
|
||||
@@ -125,6 +135,8 @@ id|INTEGER|0||1
|
||||
imei|VARCHAR(14)|0||0
|
||||
imeisv|VARCHAR|0||0
|
||||
imsi|VARCHAR(15)|1||0
|
||||
last_lu_rat_cs|TEXT|0|NULL|0
|
||||
last_lu_rat_ps|TEXT|0|NULL|0
|
||||
last_lu_seen|TIMESTAMP|0|NULL|0
|
||||
last_lu_seen_ps|TIMESTAMP|0|NULL|0
|
||||
lmsi|INTEGER|0||0
|
||||
@@ -144,13 +156,13 @@ vlr_number|VARCHAR(15)|0||0
|
||||
vlr_via_proxy|VARCHAR|0||0
|
||||
|
||||
Table subscriber contents:
|
||||
ggsn_number|gmlc_number|id|imei|imeisv|imsi|last_lu_seen|last_lu_seen_ps|lmsi|ms_purged_cs|ms_purged_ps|msc_number|msisdn|nam_cs|nam_ps|periodic_lu_tmr|periodic_rau_tau_tmr|sgsn_address|sgsn_number|sgsn_via_proxy|smsc_number|vlr_number|vlr_via_proxy
|
||||
||1|||123456789012345||||0|0||098765432109876|1|1|||||||MSC-1|
|
||||
||2|||111111111||||1|0|||1|1||||||||
|
||||
||3|||222222222||||0|1||22222|1|1||||||||
|
||||
||4|||333333||||0|0||3|0|1||||||||
|
||||
||5|||444444444444444||||0|0||4444|1|0||||||||
|
||||
||6|||5555555||||0|0||55555555555555|0|0||||||||
|
||||
ggsn_number|gmlc_number|id|imei|imeisv|imsi|last_lu_rat_cs|last_lu_rat_ps|last_lu_seen|last_lu_seen_ps|lmsi|ms_purged_cs|ms_purged_ps|msc_number|msisdn|nam_cs|nam_ps|periodic_lu_tmr|periodic_rau_tau_tmr|sgsn_address|sgsn_number|sgsn_via_proxy|smsc_number|vlr_number|vlr_via_proxy
|
||||
||1|||123456789012345||||||0|0||098765432109876|1|1|||||||MSC-1|
|
||||
||2|||111111111||||||1|0|||1|1||||||||
|
||||
||3|||222222222||||||0|1||22222|1|1||||||||
|
||||
||4|||333333||||||0|0||3|0|1||||||||
|
||||
||5|||444444444444444||||||0|0||4444|1|0||||||||
|
||||
||6|||5555555||||||0|0||55555555555555|0|0||||||||
|
||||
|
||||
Table: subscriber_apn
|
||||
name|type|notnull|dflt_value|pk
|
||||
@@ -166,10 +178,18 @@ subscriber_id|INTEGER|0||0
|
||||
|
||||
Table subscriber_multi_msisdn contents:
|
||||
|
||||
Table: subscriber_rat
|
||||
name|type|notnull|dflt_value|pk
|
||||
allowed|BOOLEAN|1|0|0
|
||||
rat|TEXT|1||0
|
||||
subscriber_id|INTEGER|0||0
|
||||
|
||||
Table subscriber_rat contents:
|
||||
|
||||
Verify that osmo-hlr can open it:
|
||||
osmo-hlr --database $db --db-check --config-file $srcdir/osmo-hlr.cfg
|
||||
rc = 0
|
||||
DMAIN hlr starting
|
||||
DDB using database: <PATH>test.db
|
||||
DDB Database <PATH>test.db' has HLR DB schema version 5
|
||||
DDB Database <PATH>test.db' has HLR DB schema version 8
|
||||
DMAIN Cmdline option --db-check: Database was opened successfully, quitting.
|
||||
|
||||
@@ -32,7 +32,7 @@ gsup_server_test_LDADD = \
|
||||
$(top_srcdir)/src/gsup_server.c \
|
||||
$(top_srcdir)/src/gsup_router.c \
|
||||
$(top_srcdir)/src/gsup_send.c \
|
||||
$(top_srcdir)/src/gsupclient/cni_peer_id.c \
|
||||
$(top_srcdir)/src/gsupclient/gsup_peer_id.c \
|
||||
$(top_srcdir)/src/gsupclient/gsup_req.c \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
|
||||
@@ -24,6 +24,7 @@ EXTRA_DIST = \
|
||||
|
||||
check_PROGRAMS = \
|
||||
mdns_test \
|
||||
mslookup_client_mdns_test \
|
||||
mslookup_client_test \
|
||||
mslookup_test \
|
||||
$(NULL)
|
||||
@@ -44,6 +45,14 @@ mslookup_client_test_LDADD = \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
mslookup_client_mdns_test_SOURCES = \
|
||||
mslookup_client_mdns_test.c \
|
||||
$(NULL)
|
||||
mslookup_client_mdns_test_LDADD = \
|
||||
$(top_builddir)/src/mslookup/libosmo-mslookup.la \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
mdns_test_SOURCES = \
|
||||
mdns_test.c \
|
||||
$(NULL)
|
||||
@@ -52,18 +61,6 @@ mdns_test_LDADD = \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
if ENABLE_MSLOOKUP_CLIENT_MDNS_TEST
|
||||
check_PROGRAMS += mslookup_client_mdns_test
|
||||
|
||||
mslookup_client_mdns_test_SOURCES = \
|
||||
mslookup_client_mdns_test.c \
|
||||
$(NULL)
|
||||
mslookup_client_mdns_test_LDADD = \
|
||||
$(top_builddir)/src/mslookup/libosmo-mslookup.la \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
.PHONY: update_exp
|
||||
update_exp:
|
||||
for i in $(check_PROGRAMS); do \
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@@ -190,45 +189,11 @@ static void test_server_client()
|
||||
client_stop();
|
||||
}
|
||||
|
||||
bool is_multicast_enabled()
|
||||
{
|
||||
bool ret = true;
|
||||
struct addrinfo *ai;
|
||||
int sock;
|
||||
struct addrinfo hints = {0};
|
||||
struct ip_mreq multicast_req = {0};
|
||||
in_addr_t iface = INADDR_ANY;
|
||||
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = (AI_PASSIVE | AI_NUMERICHOST);
|
||||
assert(getaddrinfo("239.192.23.42", "4266", &hints, &ai) == 0);
|
||||
|
||||
sock = socket(ai->ai_family, ai->ai_socktype, 0);
|
||||
assert(sock != -1);
|
||||
assert(setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char*)&iface, sizeof(iface)) != -1);
|
||||
|
||||
memcpy(&multicast_req.imr_multiaddr, &((struct sockaddr_in*)(ai->ai_addr))->sin_addr,
|
||||
sizeof(multicast_req.imr_multiaddr));
|
||||
multicast_req.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multicast_req, sizeof(multicast_req)) == -1)
|
||||
ret = false;
|
||||
|
||||
freeaddrinfo(ai);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run all tests
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
if (!is_multicast_enabled()) {
|
||||
fprintf(stderr, "ERROR: multicast is disabled! (OS#4361)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
talloc_enable_null_tracking();
|
||||
ctx = talloc_named_const(NULL, 0, "main");
|
||||
osmo_init_logging2(ctx, NULL);
|
||||
|
||||
@@ -116,7 +116,7 @@ OsmoHLR(config-mslookup)# list
|
||||
...
|
||||
mdns bind [IP] [<1-65535>]
|
||||
mdns domain-suffix DOMAIN_SUFFIX
|
||||
no mdns bind
|
||||
no mdns
|
||||
server
|
||||
no server
|
||||
client
|
||||
@@ -155,11 +155,11 @@ OsmoHLR(config-mslookup-server)# list
|
||||
...
|
||||
mdns bind [IP] [<1-65535>]
|
||||
mdns domain-suffix DOMAIN_SUFFIX
|
||||
no mdns bind
|
||||
no mdns
|
||||
service NAME at IP <1-65535>
|
||||
no service NAME
|
||||
no service NAME at IP <1-65535>
|
||||
msc ipa-name .IPA_NAME
|
||||
msc .UNIT_NAME
|
||||
|
||||
OsmoHLR(config-mslookup-server)# mdns ?
|
||||
bind Configure where the mDNS server listens for mslookup requests
|
||||
@@ -196,11 +196,9 @@ OsmoHLR(config-mslookup-server)# no service foo at 1.2.3.4 ?
|
||||
OsmoHLR(config-mslookup-server)# msc?
|
||||
msc Configure services for individual local MSCs
|
||||
OsmoHLR(config-mslookup-server)# msc ?
|
||||
ipa-name Identify locally connected MSC by IPA Unit Name
|
||||
OsmoHLR(config-mslookup-server)# msc ipa-name ?
|
||||
IPA_NAME IPA Unit Name of the local MSC to configure
|
||||
UNIT_NAME IPA Unit Name of the local MSC to configure
|
||||
|
||||
OsmoHLR(config-mslookup-server)# msc ipa-name MSC-1
|
||||
OsmoHLR(config-mslookup-server)# msc MSC-1
|
||||
OsmoHLR(config-mslookup-server-msc)# ?
|
||||
...
|
||||
service Configure addresses of local services, as sent in replies to remote mslookup requests.
|
||||
@@ -248,7 +246,7 @@ OsmoHLR(config-mslookup-client)# list
|
||||
timeout <1-100000>
|
||||
mdns bind [IP] [<1-65535>]
|
||||
mdns domain-suffix DOMAIN_SUFFIX
|
||||
no mdns bind
|
||||
no mdns
|
||||
gateway-proxy IP [<1-65535>]
|
||||
no gateway-proxy
|
||||
|
||||
@@ -307,13 +305,13 @@ OsmoHLR(config-mslookup-server)# service qwert at qwert 1234
|
||||
OsmoHLR(config-mslookup-server)# service foo.bar at 123.45.67.89 1011
|
||||
OsmoHLR(config-mslookup-server)# service baz.bar at 121.31.41.5 1617
|
||||
OsmoHLR(config-mslookup-server)# service baz.bar at a:b:c::d 1819
|
||||
OsmoHLR(config-mslookup-server)# msc ipa-name msc-901-70-23
|
||||
OsmoHLR(config-mslookup-server)# msc msc-901-70-23
|
||||
OsmoHLR(config-mslookup-server-msc)# service foo.bar at 76.54.32.10 1234
|
||||
OsmoHLR(config-mslookup-server-msc)# service baz.bar at 12.11.10.98 7654
|
||||
OsmoHLR(config-mslookup-server-msc)# service baz.bar at 999:999:999::999 9999
|
||||
OsmoHLR(config-mslookup-server-msc)# service baz.bar at dd:cc:bb::a 3210
|
||||
OsmoHLR(config-mslookup-server-msc)# exit
|
||||
OsmoHLR(config-mslookup-server)# msc ipa-name msc-901-70-42
|
||||
OsmoHLR(config-mslookup-server)# msc msc-901-70-42
|
||||
OsmoHLR(config-mslookup-server-msc)# service foo.bar at 1.1.1.1 1111
|
||||
OsmoHLR(config-mslookup-server-msc)# service baz.bar at 2.2.2.2 2222
|
||||
OsmoHLR(config-mslookup-server-msc)# service baz.bar at 2222:2222:2222::2 2222
|
||||
@@ -322,12 +320,12 @@ Local GSUP HLR address returned in mslookup responses for local IMSIs: 127.0.0.1
|
||||
service foo.bar at 123.45.67.89 1011
|
||||
service baz.bar at 121.31.41.5 1617
|
||||
service baz.bar at a:b:c::d 1819
|
||||
msc ipa-name MSC-1
|
||||
msc ipa-name msc-901-70-23
|
||||
msc MSC-1
|
||||
msc msc-901-70-23
|
||||
service foo.bar at 76.54.32.10 1234
|
||||
service baz.bar at 12.11.10.98 7654
|
||||
service baz.bar at dd:cc:bb::a 3210
|
||||
msc ipa-name msc-901-70-42
|
||||
msc msc-901-70-42
|
||||
service foo.bar at 1.1.1.1 1111
|
||||
service baz.bar at 2.2.2.2 2222
|
||||
service baz.bar at 2222:2222:2222::2 2222
|
||||
@@ -351,14 +349,14 @@ mslookup
|
||||
service baz.bar at 2222:2222:2222::2 2222
|
||||
client
|
||||
gateway-proxy 1.2.3.4 4222
|
||||
mdns bind 239.192.23.42 4266
|
||||
mdns to 239.192.23.42 4266
|
||||
...
|
||||
|
||||
OsmoHLR(config-mslookup-server-msc)# no service baz.bar
|
||||
OsmoHLR(config-mslookup-server-msc)# no service asdf
|
||||
% mslookup server: cannot remove service 'asdf'
|
||||
OsmoHLR(config-mslookup-server-msc)# exit
|
||||
OsmoHLR(config-mslookup-server)# msc ipa-name msc-901-70-23
|
||||
OsmoHLR(config-mslookup-server)# msc msc-901-70-23
|
||||
OsmoHLR(config-mslookup-server-msc)# no service baz.bar at dd:cc:bb::a 3210
|
||||
% mslookup server: cannot remove service 'baz.bar' to dd:cc:bb::a 3210
|
||||
OsmoHLR(config-mslookup-server-msc)# no service asdf at asdf asdf
|
||||
@@ -381,11 +379,11 @@ OsmoHLR(config-mslookup-client)# do show mslookup services
|
||||
Local GSUP HLR address returned in mslookup responses for local IMSIs: 127.0.0.1:4222
|
||||
service foo.bar at 123.45.67.89 1011
|
||||
service baz.bar at 121.31.41.5 1617
|
||||
msc ipa-name MSC-1
|
||||
msc ipa-name msc-901-70-23
|
||||
msc MSC-1
|
||||
msc msc-901-70-23
|
||||
service foo.bar at 76.54.32.10 1234
|
||||
service baz.bar at 12.11.10.98 7654
|
||||
msc ipa-name msc-901-70-42
|
||||
msc msc-901-70-42
|
||||
service foo.bar at 1.1.1.1 1111
|
||||
|
||||
OsmoHLR(config-mslookup-client)# show running-config
|
||||
@@ -402,7 +400,7 @@ mslookup
|
||||
msc msc-901-70-42
|
||||
service foo.bar at 1.1.1.1 1111
|
||||
client
|
||||
mdns bind 239.192.23.42 4266
|
||||
mdns to 239.192.23.42 4266
|
||||
...
|
||||
|
||||
OsmoHLR(config-mslookup-client)# exit
|
||||
@@ -413,11 +411,11 @@ Local GSUP HLR address returned in mslookup responses for local IMSIs: 23.42.17.
|
||||
service foo.bar at 123.45.67.89 1011
|
||||
service baz.bar at 121.31.41.5 1617
|
||||
service gsup.hlr at 23.42.17.11 4223
|
||||
msc ipa-name MSC-1
|
||||
msc ipa-name msc-901-70-23
|
||||
msc MSC-1
|
||||
msc msc-901-70-23
|
||||
service foo.bar at 76.54.32.10 1234
|
||||
service baz.bar at 12.11.10.98 7654
|
||||
msc ipa-name msc-901-70-42
|
||||
msc msc-901-70-42
|
||||
service foo.bar at 1.1.1.1 1111
|
||||
|
||||
OsmoHLR(config-mslookup-server)# show running-config
|
||||
@@ -435,5 +433,5 @@ mslookup
|
||||
msc msc-901-70-42
|
||||
service foo.bar at 1.1.1.1 1111
|
||||
client
|
||||
mdns bind 239.192.23.42 4266
|
||||
mdns to 239.192.23.42 4266
|
||||
...
|
||||
|
||||
@@ -65,9 +65,7 @@ cat $abs_srcdir/mslookup/mslookup_client_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/mslookup/mslookup_client_test], [0], [ignore], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
# AT_SKIP_IF: disable without --enable-mslookup-client-mdns-test (OS#4385)
|
||||
AT_SETUP([mslookup_client_mdns])
|
||||
AT_SKIP_IF([! test -e $abs_top_builddir/tests/mslookup/mslookup_client_mdns_test ])
|
||||
AT_KEYWORDS([mslookup_client_mdns])
|
||||
cat $abs_srcdir/mslookup/mslookup_client_mdns_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/mslookup/mslookup_client_mdns_test], [0], [ignore], [experr])
|
||||
|
||||
Reference in New Issue
Block a user