Compare commits

..

20 Commits

Author SHA1 Message Date
Neels Hofmeyr
1971b6731a dgsm Proxy Cache design WIP
Change-Id: Ifa322e84fadd3b04943c8c7024c0e2de4935bed0
2020-04-28 16:36:18 +02:00
Neels Hofmeyr
b3c1726fb9 NOT FOR MERGE proxy cache design doc ladders
Change-Id: Ie0093bdb7ddf268d1a5fdf09b201cbd3a347225f
2020-04-28 16:36:15 +02:00
Neels Hofmeyr
81ac78dfac esme_dgsm.py: add --always-fail option for debugging SMPP
Change-Id: Ibacf2676cae40712c89b57ced34085311d9a416d
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
dd73ccfc99 db v6: determine 3G AUC IND from VLR name
Each VLR requesting auth tuples should use a distinct IND pool for 3G auth.  So
far we tied the IND to the GSUP peer connection; MSC and SGSN were always
distinct GSUP peers, they ended up using distinct INDs.

However, we have implemented a GSUP proxy, so that, in a distributed setup, a
remotely roaming subscriber has only one direct GSUP peer proxying for both
remote MSC and SGSN. That means as soon as a subscriber roams to a different
site, we would use the GSUP proxy name to determine the IND instead of the
separate MSC and SGSN. The site's MSC and SGSN appear as the same client, get
the same IND bucket, waste SQNs rapidly and cause auth tuple generation load.

So instead of using the local client as IND, persistently keep a list of VLR
names and assign a different IND to each. Use the gsup_req->source_name as
indicator, which reflects the actual remote VLR's name (remote MSC or SGSN).

Persist the site <-> IND assignments in the database.

Add an IND test to db_test.c

There was an earlier patch version that separated the IND pools by cn_domain,
but it turned out to add complex semantics, while only solving one aspect of
the "adjacent VLR" problem. We need a solution not only for CS vs PS, but also
for 2,3G vs 4G, and for sites that are physically adjacent to each other. This
patch version does not offer any automatic solution for that -- as soon as more
than 2^IND_bitlen (usually 32) VLRs show up, it is the responsibility of the
admin to ensure the 'ind' table in the hlr.db does not have unfortunate IND
assignments. So far no VTY commands exist for that, they may be added in the
future.

Related: OS#4319
Change-Id: I6f0a6bbef3a27507605c3b4a0e1a89bdfd468374
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
40d3a96f9d auc3g: officially wrap IND around IND_bitlen space
To determine distinct IND pools for each connected VLR, we need to pick ever
increasing values for any new peer showing up. Each subscriber's individual
IND_bitlen is then required to modulo the least significant N of bits that fit
in its IND_bitlen to effectively round-robin in the available IND pool space.
So far we did that but issued a warning message. This is actually exactly what
we want and it doesn't need to be treated like it weren't so.

Change-Id: I716d8a8a249235c8093d7a6a78b3535d893d867e
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
1e635ab550 vty: show subscriber: show lu d,h,m,s ago, not just seconds
Change-Id: I0fe34e0f065160ef959b2b7b4dd040f3f2985f43
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
43d5cfc13b vty: show subscriber: change format of 'last LU seen'
So far, the time string format comes from ctime_r, and we manually add "UTC" to it.

The ctime_r format is wildly chaotic IMHO, mixing weekday, day-of-month and
hour and year in very unsorted ways.

Adding "UTC" to it is non-standard.

Instead use an ISO-8601 standardized time string via strftime().

Change-Id: I6731968f05050399f4dd43b241290186e0c59e1a
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
61917ed51d drop error log for when a subscriber does not exist
Checking for existence of a subscriber and seeing that there is none is not
inherently an error. However, osmo-hlr currently logs on all occasions:

  DAUC ERROR Cannot read subscriber from db: MSISDN='1001': No such subscriber

This spams the ERROR log level. Particularly when a D-GSM setup does subscriber
existence checks for every incoming mslookup request, that potentially creates
constant ERROR logging.

The "No such subscriber" part comes from db_sel(), which might also return an
sqlite3_errmsg(). We still want those sqlite3_errmsg()es in the ERROR log.

Hence print an ERROR log only if db_sel() returns an rc != -ENOENT.

Change-Id: I5044e9b4519b948edc4e451cef0f7830d315619b
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
43de5efb3d adoc: add D-GSM chapter to osmohlr-usermanual
Change-Id: I392b5523870c2ef3267179160028d26f3f761b77
2020-04-28 14:53:55 +02:00
Oliver Smith
c992d85068 hlr_vty_subscr: prettier output for last LU seen
Extend the "last LU seen on ..." line with the amount of seconds that
passed since now, or "(invalid timestamp)".

Patch split from Id7fc50567211a0870ac0524f6dee94d4513781ba, because it
depends on timestamp_age which was just added in
Ife4a61d71926d08f310a1aeed9d9f1974f64178b.

Change-Id: I24f9e86c1aa0b1576290094e024562f41b988f37
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
d29b2f236f gsup_server: send routing error back to the correct peer
If a peer attempts to add a route to an ipa-name that we already have in the
routing system, don't send the routing error to the peer that already has the
name, but to the peer that attempts to re-use it and would cause the collision.

This is fixing a situation where for example a locally attached MSC has name
'MSC-1', and a remote site is proxying GSUP here for a remote MSC that also has
the name 'MSC-1'. Send the routing error back to the proxy, not local 'MSC-1'.

Change-Id: Icafaedc11b5925149d338bdcb987ae985a7323d6
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
647c106d1c D-GSM 3/n: implement roaming by mslookup in osmo-hlr
Add mslookup client to find remote home HLRs of unknown IMSIs, and
proxy/forward GSUP for those to the right remote HLR instances.

Add remote_hlr.c to manage one GSUP client per remote HLR GSUP address.

Add proxy.c to keep state about remotely handled IMSIs (remote GSUP address,
MSISDN, and probably more in future patches).  The mslookup_server that
determines whether a given MSISDN is attached locally now also needs to look in
the proxy record: it is always the osmo-hlr immediately peering for the MSC
that should respond to mslookup service address queries like SIP and SMPP.
(Only gsup.hlr service is always answered by the home HLR.)

Add dgsm.c to set up an mdns mslookup client, ask for IMSI homes, and to decide
which GSUP is handled locally and which needs to go to a remote HLR.

Add full VTY config and VTY tests.

For a detailed overview of the D-GSM and mslookup related files, please see the
elaborate comment at the top of mslookup.c (already added in an earlier patch).

Change-Id: I2fe453553c90e6ee527ed13a13089900efd488aa
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
a75d1c2c94 D-GSM 2/n: implement mDNS method of mslookup server
Implement the mslookup server's mDNS responder, to actually service remote
mslookup requests:
- VTY mslookup/server config with service names,
- the mslookup_mdns_server listening for mslookup requests,

For a detailed overview of the D-GSM and mslookup related files, please see the
elaborate comment at the top of mslookup.c (already added in an earlier patch).

Change-Id: I5cae6459090588b4dd292be90a5e8903432669d2
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
32cc07ad06 D-GSM 1/n: add mslookup server in osmo-hlr
Implement the mslookup server to service remote mslookup requests.

This patch merely adds the logic to answer incoming mslookup requests, an
actual method to receive requests (mDNS) follows in a subsequent patch.

- API to configure service names and addresses for the local site (per MSC).
- determine whether a subscriber is on a local MSC
  (checking the local proxy will be added in subsequent patch that adds proxy
  capability).
- VTY config follows in a subsequent patch.

For a detailed overview of the D-GSM and mslookup related files, please see the
elaborate comment at the top of mslookup.c (already added in an earlier patch).

Change-Id: Ife4a61d71926d08f310a1aeed9d9f1974f64178b
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
bf8b614c0e test_nodes.vty: remove cruft
This stuff is not testing osmo-hlr specific nodes, remove.

Change-Id: Ia11a209778b78ab02424e2abf3f9004fe97cf570
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
bcb5dca1c9 enlarge the GSUP message headroom
Make room for (more) arbitrary IPA headers, like longer IPA names as configured
by the user.

Change-Id: I7d86f2dadcae29fe1550ea2c9773394ab31a837b
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
f6c8f04c79 db v5: prep for D-GSM: add vlr_via_proxy and sgsn_via_proxy
D-GSM will store in the HLR DB whether a locally connected MSC has attached the
subscriber (last_lu_seen[_ps]), or whether the attach happened via a GSUP proxy
from a different site.

Add columns for this separately in this patch.

Change-Id: I98c7b3870559ede84adf56e4bf111f53c7487745
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
82112ea835 gsup client: add up_down_cb(), add osmo_gsup_client_create3()
For the GSUP clients in upcoming D-GSM enabled osmo-hlr, it will be necessary
to trigger an event as soon as a GSUP client connection becomes ready for
communication. Add the osmo_gsup_client->up_down_cb.

Add osmo_gsup_client_create3() pass the up_down_cb in the arguments. Also add
a cb data argument.

We need the callbacks and data pointer in the osmo_gsup_client_create()
function right before startup, because this function immediately starts up the
connection. Who knows whether callbacks might trigger right away.

Because there are so many arguments, and to prevent the need for ever new
versions of this function, pass the arguments as an extendable struct.

Change-Id: I6f181e42b678465bc9945f192559dc57d2083c6d
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
0d28d85168 2/2: fixup: add osmo_gsup_peer_id with type enum and union
During code review it was requested to insert an ability to handle different
kinds of peer id, in order to be able to add a Global Title in the future.

Add this, but only in the publicly visible API. For osmo-hlr internal code, I
intend to push implementing this into the future, when a different peer
identification actually gets introduced.

This way we don't need to implement it now in all osmo-hlr code paths (save
time now), but still make all API users aware that this type may be extended in
the future.

Change-Id: Ide9dcdca283ab989240cfc6e53e9211862a199c5
2020-04-28 14:53:55 +02:00
Neels Hofmeyr
b2553ebb4a 1/2: refactor: add and use lu_fsm, osmo_gsup_req, osmo_ipa_name
These are seemingly orthogonal changes in one patch, because they are in fact
sufficiently intertwined that we are not willing to spend the time to separate
them. They are also refactoring changes, unlikely to make sense on their own.

** lu_fsm:

Attempting to make luop.c keep state about incoming GSUP requests made me find
shortcomings in several places:
- since it predates osmo_fsm, it is a state machine that does not strictly
  enforce the order of state transitions or the right sequence of incoming
  events.
- several places OSMO_ASSERT() on data received from the network.
- modifies the subscriber state before a LU is accepted.
- dead code about canceling a subscriber in a previous VLR. That would be a
  good thing to actually do, which should also be trivial now that we record
  vlr_name and sgsn_name, but I decided to remove the dead code for now.

To both step up the LU game *and* make it easier for me to integrate
osmo_gsup_req handling, I decided to create a lu_fsm, drawing from my, by now,
ample experience of writing osmo_fsms.

** osmo_gsup_req:

Prepare for D-GSM, where osmo-hlr will do proxy routing for remote HLRs /
communicate with remote MSCs via a proxy:

a) It is important that a response that osmo-hlr generates and that is sent
back to a requesting MSC contains all IEs that are needed to route it back to
the requester. Particularly source_name must become destination_name in the
response to be able to even reach the requesting MSC. Other fields are also
necessary to match, which were so far taken care of in individual numerous code
paths.

b) For some operations, the response to a GSUP request is generated
asynchronously (like Update Location Request -> Response, or taking the
response from an EUSE, or the upcoming proxying to a remote HLR). To be able to
feed a request message's information back into the response, we must thus keep
the request data around. Since struct osmo_gsup_message references a lot of
external data, usually with pointers directly into the received msgb, it is not
so trivial to pass GSUP message data around asynchronously, on its own.

osmo_gsup_req is the combined solution for both a and b: it keeps all data for
a GSUP message by taking ownership of the incoming msgb, and it provides an
explicit API "forcing" callers to respond with osmo_gsup_req_respond(), so that
all code paths trivially are definitely responding with the correct IEs set to
match the request's routing (by using osmo_gsup_make_response() recently added
to libosmocore).

Adjust all osmo-hlr code paths to use *only* osmo_gsup_req to respond to
incoming requests received on the GSUP server (above LU code being one of
them).

In fact, the same should be done on the client side. Hence osmo_gsup_req is
implemented in a server/client agnostic way, and is placed in
libosmo-gsupclient. As soon as we see routing errors in complex GSUP setups,
using osmo_gsup_req in the related GSUP client is likely to resolve those
problems without much thinking required beyond making all code paths use it.

libosmo-gsupclient is hence added to osmo-hlr binary's own library
dependencies. It would have been added by the D-GSM proxy routing anyway, we
are just doing it a little sooner.

** gsup_peer_id.c / osmo_ipa_name:

We so far handle an IPA unit name as pointer + size, or as just pointer with
implicit talloc size. To ease working with GSUP peer identification data, I
require:

- a non-allocated storage of an IPA Name. It brings the drawback of being
  size limited, but our current implementation is anyway only able to handle
  MSC and SGSN names of 31 characters (see struct hlr_subscriber).
- a single-argument handle for IPA Name,
- easy to use utility functions like osmo_ipa_name_to_str(), osmo_ipa_name_cmp(), and copying
  by simple assignment, a = b.

Hence this patch adds a osmo_ipa_name in gsup_peer_id.h and gsup_peer_id.c. Heavily
used in LU and osmo_gsup_req.

Depends: libosmocore Id9692880079ea0f219f52d81b1923a76fc640566
Change-Id: I3a8dff3d4a1cbe10d6ab08257a0138d6b2a082d9
2020-04-28 14:53:53 +02:00
89 changed files with 3930 additions and 1516 deletions

3
.gitignore vendored
View File

@@ -26,7 +26,6 @@ m4
*.m4
missing
.deps
*~
*.pc
.libs
@@ -68,5 +67,3 @@ doc/manuals/generated/
doc/manuals/osmomsc-usermanual.xml
doc/manuals/common
doc/manuals/build
contrib/osmo-hlr.spec

View File

@@ -1,9 +1,9 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2
SUBDIRS = \
doc \
src \
include \
doc \
sql \
contrib \
tests \
@@ -11,8 +11,6 @@ SUBDIRS = \
EXTRA_DIST = \
.version \
contrib/osmo-hlr.spec.in \
debian \
$(NULL)
AM_DISTCHECK_CONFIGURE_FLAGS = \

View File

@@ -1,66 +0,0 @@
osmo-hlr - Osmocom HLR Implementation
=====================================
This repository contains a C-language implementation of a GSM Home
Location Register (HLR). It is part of the
[Osmocom](https://osmocom.org/) Open Source Mobile Communications
project.
Warning: While the HLR logical functionality is implemented, OsmoHLR
does not use the ETSI/3GPP TCAP/MAP protocol stack. Instead, a much
simpler custom protocol (GSUP) is used. This means, OsmoHLR is of
no use outside the context of an Osmocom core network. You can use
it with OsmoMSC, OsmoSGSN etc. - but not with third party components.
Homepage
--------
The official homepage of the project is
https://osmocom.org/projects/osmo-hlr/wiki
GIT Repository
--------------
You can clone from the official osmo-hlr.git repository using
git clone git://git.osmocom.org/osmo-hlr.git
git clone https://git.osmocom.org/osmo-hlr.git
There is a cgit interface at https://git.osmocom.org/osmo-hlr/
Documentation
-------------
User Manuals and VTY reference manuals are [optionally] built in PDF form
as part of the build process.
Pre-rendered PDF version of the current "master" can be found at
[User Manual](https://ftp.osmocom.org/docs/latest/osmohlr-usermanual.pdf)
as well as the VTY reference manuals
* [VTY Reference Manual for osmo-hlr](https://ftp.osmocom.org/docs/latest/osmohlr-vty-reference.pdf)
Mailing List
------------
Discussions related to osmo-hlr are happening on the
openbsc@lists.osmocom.org mailing list, please see
https://lists.osmocom.org/mailman/listinfo/openbsc for subscription
options and the list archive.
Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
Contributing
------------
Our coding standards are described at
https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards
We us a gerrit based patch submission/review process for managing
contributions. Please see
https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for
more details
The current patch queue for osmo-hlr can be seen at
https://gerrit.osmocom.org/#/q/project:osmo-hlr+status:open

View File

@@ -1,9 +0,0 @@
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# In short:
# LIBVERSION=c:r:a
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line

View File

@@ -12,8 +12,6 @@ AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.9])
AC_CONFIG_TESTDIR(tests)
CFLAGS="$CFLAGS -std=gnu11"
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -27,7 +25,7 @@ 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
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'])])
@@ -41,11 +39,11 @@ PKG_PROG_PKG_CONFIG([0.20])
PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.1.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.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(LIBOSMOABIS, libosmoabis >= 0.6.0)
PKG_CHECK_MODULES(SQLITE3, sqlite3)
@@ -188,6 +186,7 @@ AC_OUTPUT(
Makefile
doc/Makefile
doc/examples/Makefile
doc/sequence_charts/Makefile
src/Makefile
src/gsupclient/Makefile
src/mslookup/Makefile
@@ -202,10 +201,10 @@ AC_OUTPUT(
contrib/Makefile
contrib/systemd/Makefile
contrib/dgsm/Makefile
contrib/osmo-hlr.spec
tests/Makefile
tests/auc/Makefile
tests/auc/gen_ts_55_205_test_sets/Makefile
tests/gsup_server/Makefile
tests/gsup/Makefile
tests/db/Makefile
tests/db_upgrade/Makefile

View File

@@ -2,13 +2,3 @@ SUBDIRS = \
systemd \
dgsm \
$(NULL)
EXTRA_DIST = osmo-hlr-post-upgrade.sh
install-data-hook:
install -Dm755 $(srcdir)/osmo-hlr-post-upgrade.sh \
-t $(DESTDIR)$(datadir)/osmocom/
uninstall-hook:
@$(PRE_UNINSTALL)
$(RM) $(DESTDIR)$(datadir)/osmocom/osmo-hlr-post-upgrade.sh

View File

@@ -35,6 +35,7 @@ osmo-build-dep.sh libosmo-abis
# Additional configure options and depends
CONFIG=""
if [ "$WITH_MANUALS" = "1" ]; then
osmo-build-dep.sh osmo-gsm-manuals
CONFIG="--enable-manuals"
fi
@@ -56,11 +57,11 @@ autoreconf --install --force
$CONFIG
$MAKE $PARALLEL_MAKE
$MAKE check || cat-testlogs.sh
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck || cat-testlogs.sh
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck || cat-testlogs.sh
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish
fi
$MAKE $PARALLEL_MAKE maintainer-clean
$MAKE maintainer-clean
osmo-clean-workspace.sh

View File

@@ -1,95 +0,0 @@
#!/bin/sh -e
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright 2021 sysmocom s.f.m.c GmbH <info@sysmocom.de>
#
# Packagers are supposed to call this script in post-upgrade, so it can safely
# upgrade the database scheme if required.
DB="/var/lib/osmocom/hlr.db"
IS_ACTIVE=0
msg() {
echo "osmo-hlr-post-upgrade: $@"
}
err() {
msg "ERROR: $@"
}
open_db() {
# Attempt to open the database with osmo-hlr-db-tool, it will fail if
# upgrading the schema is required
osmo-hlr-db-tool -s -l "$DB" create
}
check_upgrade_required() {
if ! [ -e "$DB" ]; then
msg "nothing to do (no existing database)"
exit 0
fi
if open_db 2>/dev/null; then
msg "nothing to do (database version is up to date)"
exit 0
fi
msg "database upgrade is required"
}
stop_service() {
if systemctl is-active -q osmo-hlr; then
IS_ACTIVE=1
msg "stopping osmo-hlr service"
systemctl stop osmo-hlr
# Verify that it stopped
for i in $(seq 1 100); do
if ! systemctl is-active -q osmo-hlr; then
return
fi
sleep 0.1
done
err "failed to stop osmo-hlr service"
exit 1
else
msg "osmo-hlr service is not running"
fi
}
create_backup() {
backup="$DB.$(date +%Y%m%d%H%M%S).bak"
msg "creating backup: $backup"
if [ -e "$backup" ]; then
err "backup already exists: $backup"
exit 1
fi
cp "$DB" "$backup"
}
upgrade() {
msg "performing database upgrade"
osmo-hlr-db-tool -s -U -l "$DB" create
if ! open_db 2>/dev/null; then
err "failed to open the database after upgrade"
err "osmo-hlr-db-tool output:"
open_db
# exit because of "set -e"
fi
msg "database upgrade successful"
}
start_service() {
if [ "$IS_ACTIVE" = "1" ]; then
msg "starting osmo-hlr service"
systemctl start osmo-hlr
fi
}
check_upgrade_required
stop_service
create_backup
upgrade
start_service

View File

@@ -1,195 +0,0 @@
#
# spec file for package osmo-hlr
#
# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
# Copyright (c) 2016, Martin Hauke <mardnh@gmx.de>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
Name: osmo-hlr
Version: @VERSION@
Release: 0
Summary: Osmocom Home Location Register for GSUP protocol towards OsmoSGSN and OsmoCSCN
License: AGPL-3.0-or-later AND GPL-2.0-or-later
Group: Productivity/Telephony/Servers
URL: https://osmocom.org/projects/osmo-hlr
Source: %{name}-%{version}.tar.xz
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: libtool
BuildRequires: pkgconfig >= 0.20
BuildRequires: python3
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: pkgconfig(libosmoabis) >= 1.1.0
BuildRequires: pkgconfig(libosmocore) >= 1.5.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.5.0
BuildRequires: pkgconfig(libosmogsm) >= 1.5.0
BuildRequires: pkgconfig(libosmovty) >= 1.5.0
BuildRequires: pkgconfig(sqlite3)
BuildRequires: pkgconfig(talloc) >= 2.0.1
# only needed for populate_hlr_db.pl
Requires: libdbi-drivers-dbd-sqlite3
%{?systemd_requires}
%description
The GSUP HLR is a stand-alone HLR (Home Location Register) for SIM
and USIM based subscribers which exposes the GSUP protocol towards
its users. OsmoSGSN supports this protocol.
osmo-gsup-hlr is still very simplistic. It is a single-threaded
architecture and uses only sqlite3 tables as back-end. It is suitable
for installations of the scale that OsmoNITB was able to handle. It
also lacks various features like fine-grained control of subscribed
services (like supplementary services).
%package -n libosmo-gsup-client0
Summary: Osmocom GSUP (General Subscriber Update Protocol) client library
License: GPL-2.0-or-later
Group: System/Libraries
%description -n libosmo-gsup-client0
This is a shared library that can be used to implement client programs for
the GSUP protocol. The typical GSUP server is OsmoHLR, with OsmoMSC, OsmoSGSN
and External USSD Entities (EUSEs) using this library to implement clients.
%package -n libosmo-gsup-client-devel
Summary: Development files for the Osmocom GSUP client library
License: GPL-2.0-or-later
Group: Development/Libraries/C and C++
Requires: libosmo-gsup-client0 = %{version}
%description -n libosmo-gsup-client-devel
This is a shared library that can be used to implement client programs for
the GSUP protocol. The typical GSUP server is OsmoHLR, with OsmoMSC, OsmoSGSN
and External USSD Entities (EUSEs) using this library to implement clients.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmo-gsup-client.
%package -n libosmo-mslookup0
Summary: Osmocom MS lookup library
License: GPL-2.0-or-later
Group: System/Libraries
%description -n libosmo-mslookup0
This shared library contains routines for looking up mobile subscribers.
%package -n libosmo-mslookup-devel
Summary: Development files for the Osmocom MS lookup library
License: GPL-2.0-or-later
Group: Development/Libraries/C and C++
Requires: libosmo-mslookup0 = %{version}
%description -n libosmo-mslookup-devel
This shared library contains routines for looking up mobile subscribers.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmo-mslookup.
%package -n osmo-mslookup-client
Summary: Standalone program using libosmo-mslookup
License: GPL-2.0-or-later
Group: Development/Libraries/C and C++
%description -n osmo-mslookup-client
Standalone program using libosmo-mslookup to easily integrate with programs
that want to connect services (SIP, SMS,...) to the current location of a
subscriber.
%prep
%setup -q
%build
echo "%{version}" >.tarball-version
autoreconf -fi
%configure \
--docdir="%{_docdir}/%{name}" \
--with-systemdsystemunitdir=%{_unitdir} \
--enable-shared \
--disable-static
make V=1 %{?_smp_mflags}
%install
%make_install
install -d "%{buildroot}/%{_localstatedir}/lib/osmocom"
find %{buildroot} -type f -name "*.la" -delete -print
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%if 0%{?suse_version}
%preun
%service_del_preun %{name}.service
%postun
%service_del_postun %{name}.service
%pre
%service_add_pre %{name}.service
%endif
%post
%if 0%{?suse_version}
%service_add_post %{name}.service
%endif
/usr/share/osmocom/osmo-hlr-post-upgrade.sh
%post -n libosmo-gsup-client0 -p /sbin/ldconfig
%postun -n libosmo-gsup-client0 -p /sbin/ldconfig
%post -n libosmo-mslookup0 -p /sbin/ldconfig
%postun -n libosmo-mslookup0 -p /sbin/ldconfig
%files
%license COPYING
%dir %{_docdir}/%{name}
%dir %{_docdir}/%{name}/examples
%{_docdir}/%{name}/examples/osmo-hlr.cfg
%{_docdir}/%{name}/examples/osmo-hlr-dgsm.cfg
%dir %{_docdir}/%{name}/sql
%{_docdir}/%{name}/sql/hlr.sql
%{_docdir}/%{name}/sql//hlr_data.sql
%dir %{_sysconfdir}/osmocom
%dir %{_localstatedir}/lib/osmocom
%{_bindir}/osmo-hlr
%{_bindir}/osmo-hlr-db-tool
%dir %{_sysconfdir}/osmocom
%config %{_sysconfdir}/osmocom/osmo-hlr.cfg
%{_unitdir}/osmo-hlr.service
%dir %{_datadir}/osmocom
%{_datadir}/osmocom/osmo-hlr-post-upgrade.sh
%files -n libosmo-gsup-client0
%{_libdir}/libosmo-gsup-client.so.0*
%files -n libosmo-gsup-client-devel
%{_bindir}/osmo-euse-demo
%dir %{_includedir}/osmocom
%dir %{_includedir}/osmocom/gsupclient
%{_includedir}/osmocom/gsupclient/*.h
%{_libdir}/libosmo-gsup-client.so
%{_libdir}/pkgconfig/libosmo-gsup-client.pc
%files -n libosmo-mslookup0
%{_libdir}/libosmo-mslookup.so.0*
%files -n libosmo-mslookup-devel
%dir %{_includedir}/osmocom
%dir %{_includedir}/osmocom/mslookup
%{_includedir}/osmocom/mslookup/*.h
%{_libdir}/libosmo-mslookup.so
%{_libdir}/pkgconfig/libosmo-mslookup.pc
%files -n osmo-mslookup-client
%{_bindir}/osmo-mslookup-client
%changelog

121
debian/changelog vendored
View File

@@ -1,124 +1,3 @@
osmo-hlr (1.4.0) unstable; urgency=medium
[ Keith ]
* Correct configuration written from vty
* vty: enable show subscribers filtered by IMEI
[ Harald Welte ]
* add README.md file as customary for cgit, github, gitlab, etc.
[ Oliver Smith ]
* Add post-upgrade script for automatic db upgrade
* debian/control: remove dh-systemd build-depend
[ Pau Espin Pedrol ]
* db: Avoid use uninitialized rc if running 0 statements
[ Neels Hofmeyr ]
* db v6: determine 3G AUC IND from VLR name
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 14:56:41 +0100
osmo-hlr (1.3.0) unstable; urgency=medium
[ Alexander Couzens ]
* hlr: respect the num_auth_vectors requested
* hlr: remove unused internal USSD list
[ Oliver Smith ]
* add libosmo-mslookup abstract client
* add mDNS lookup method to libosmo-mslookup
* Makefile.am: fix pkgconfig_DATA
* add mDNS lookup method to libosmo-mslookup (#2)
* contrib/dgsm/ add example esme and dialplan
* mslookup_client.c: fix dereferencing null pointer
* mdns_msg.c: always call va_end
* mslookup_client_mdns.c: fix dereferencing null
* osmo-mslookup-client.c: fix dereferencing null
* osmo-mslookup-client: fix dereferencing null
* mdns_sock.c: fix resource leak of sock
* mdns_rfc.c: fix possible access of uninit. mem
* mslookup_client_mdns_test: disable by default
* mslookup_client_mdns_test: no automatic skip
* Cosmetic: mention OS#4491 in location cancel code
* hlr_vty_subscr: prettier output for last LU seen
* contrib: import RPM spec
* contrib: integrate RPM spec
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
* contrib/jenkins: don't build osmo-gsm-manuals
* configure.ac: set -std=gnu11
[ Neels Hofmeyr ]
* add osmo-mslookup-client program
* add osmo-mslookup-client program (#2)
* fix missing braces in LOGP_GSUP_FWD
* gsup_client.c: fix deprecation for client create func
* 1/2: refactor: add and use lu_fsm, osmo_gsup_req, osmo_ipa_name
* 2/2: wrap ipa_name in osmo_cni_peer_id with type enum and union
* gsup client: add up_down_cb(), add osmo_gsup_client_create3()
* db v5: prep for D-GSM: add vlr_via_proxy and sgsn_via_proxy
* enlarge the GSUP message headroom
* test_nodes.vty: remove cruft
* D-GSM 1/n: add mslookup server in osmo-hlr
* D-GSM 2/n: implement mDNS method of mslookup server
* D-GSM 3/n: implement roaming by mslookup in osmo-hlr
* gsup_server: send routing error back to the correct peer
* adoc: add D-GSM chapter to osmohlr-usermanual
* drop error log for when a subscriber does not exist
* vty: show subscriber: change format of 'last LU seen'
* vty: show subscriber: show lu d,h,m,s ago, not just seconds
* auc3g: officially wrap IND around IND_bitlen space
* make osmo_cni_peer_id_cmp() NULL safe
* osmo_gsup_req_new(): require from_peer != NULL
* gsup_server.c: properly handle negative rc from osmo_gsup_conn_ccm_get()
* osmo_mslookup_server_mdns_rx(): handle read() rc == 0
* hlr_subscr_nam(): fix condition to fix nam=false notifications
* esme_dgsm.py: add --always-fail option for debugging SMPP
* osmo-mslookup-client: fix segfault for respond_error() caller
* manual: describe subscriber import by SQL
[ Harald Welte ]
* Revert "add osmo-mslookup-client program"
* Revert "add mDNS lookup method to libosmo-mslookup"
* Use OSMO_FD_* instead of deprecated BSC_FD_*
* support the XOR algorithm for UMTS AKA
* auc_test.c: Add some comments on what the test cases actually do
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
* manuals: generate vty reference xml at build time
[ Vadim Yanitskiy ]
* db: fix possible SQLite3 allocated memory leak in db_open()
* gsup_server: fix typo: s/omso_gsup_message/osmo_gsup_message/
* debian/control: change maintainer to the Osmocom team / mailing list
* cosmetic: fix spelling in logging message: existAnt -> existEnt
* doc/manuals: fix s/There/The/ in 'USSD Configuration'
* doc/manuals: re-organize description of internal USSD handlers
* USSD: fix handle_ussd(): do not free() unconditionally
* USSD: add special 'idle' handler to IUSE for testing
[ Eric ]
* configure.ac: fix libtool issue with clang and sanitizer
[ Philipp Maier ]
* doc: do not use loglevel info for log category ss
[ Pau Espin Pedrol ]
* configure.ac: Fix trailing whitespace
* doc: Update VTY reference xml file
* Support setting rt-prio and cpu-affinity mask through VTY
* Set TCP NODELAY sockopt to GSUP cli and srv connections
* contrib/jenkins: Enable parallel make in make distcheck
* .gitignore: Ignore new autofoo tmp files
* tests: Replace deprecated API log_set_print_filename
[ Keith ]
* osmo-hlr-db-tool: Make import from osmo-nitb less "lossy"
* Correct vty inline help for show subscriber
* Add vty command to show summary of all or filtered subscribers
* Fix Coverity Warnings
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 18:13:53 +0100
osmo-hlr (1.2.0) unstable; urgency=medium
[ Ruben Undheim ]

11
debian/control vendored
View File

@@ -1,18 +1,19 @@
Source: osmo-hlr
Section: net
Priority: optional
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Maintainer: Max Suraev <msuraev@sysmocom.de>
Build-Depends: debhelper (>= 9),
pkg-config,
dh-autoreconf,
dh-systemd (>= 1.5),
autotools-dev,
python3-minimal,
libosmocore-dev (>= 1.5.0),
libosmo-abis-dev (>= 1.1.0),
libosmo-netif-dev (>= 1.1.0),
libosmocore-dev,
libosmo-abis-dev,
libosmo-netif-dev,
libsqlite3-dev,
sqlite3,
osmo-gsm-manuals-dev (>= 1.1.0)
osmo-gsm-manuals-dev
Standards-Version: 3.9.6
Vcs-Browser: http://cgit.osmocom.org/osmo-hlr
Vcs-Git: git://git.osmocom.org/osmo-hlr

View File

@@ -5,5 +5,4 @@
/usr/share/doc/osmo-hlr/sql/hlr.sql
/usr/share/doc/osmo-hlr/sql/hlr_data.sql
/usr/share/doc/osmo-hlr/examples/osmo-hlr.cfg
/usr/share/osmocom/osmo-hlr-post-upgrade.sh
/var/lib/osmocom

5
debian/postinst vendored
View File

@@ -1,5 +0,0 @@
#!/bin/sh -e
# Debian's postinst script is called on both installation and upgrade. Call the
# post-upgrade script in both cases, it won't do anything if there is nothing
# to do.
/usr/share/osmocom/osmo-hlr-post-upgrade.sh

View File

@@ -1,4 +1,5 @@
SUBDIRS = \
examples \
manuals \
sequence_charts \
$(NULL)

View File

@@ -12,7 +12,7 @@ log stderr
logging level main notice
logging level db notice
logging level auc notice
logging level ss notice
logging level ss info
logging level linp error
!
line vty

View File

@@ -4,22 +4,21 @@ EXTRA_DIST = example_subscriber_add_update_delete.vty \
osmohlr-usermanual.adoc \
osmohlr-usermanual-docinfo.xml \
osmohlr-vty-reference.xml \
chapters/proxy_cache_attach.msc \
chapters/proxy_cache_more_tuples.msc \
chapters/proxy_cache_periodic_lu.msc \
chapters/proxy_cache_tuple_cache_dry.msc \
chapters/proxy_cache_umts_aka_resync.msc \
regen_doc.sh \
chapters \
vty
if BUILD_MANUALS
ASCIIDOC = osmohlr-usermanual.adoc
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc $(srcdir)/*.vty $(srcdir)/*.ctrl
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc $(srcdir)/*.vty $(srcdir)/*.ctrl $(srcdir)/chapters/*.msc
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
VTY_REFERENCE = osmohlr-vty-reference.xml
BUILT_REFERENCE_XML = $(builddir)/vty/hlr_vty_reference.xml
$(builddir)/vty/hlr_vty_reference.xml: $(top_builddir)/src/osmo-hlr
mkdir -p $(builddir)/vty
$(top_builddir)/src/osmo-hlr --vty-ref-xml > $@
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
OSMO_REPOSITORY = osmo-hlr

View File

@@ -0,0 +1,20 @@
all: \
proxy_cache_attach.msc \
proxy_cache_more_tuples.msc \
proxy_cache_tuple_cache_dry.msc \
proxy_cache_periodic_lu.msc \
proxy_cache_umts_aka_resync.msc \
$(NULL)
png: \
proxy_cache.png \
proxy_cache_unused.png \
lu_attach_and_periodic.png \
$(NULL)
%.png: %.msc
mscgen -T png -o $@ $<
%.msc: %.ladder
@which ladder_to_msc.py || (echo 'PLEASE POINT YOUR $$PATH AT libosmocore/contrib/ladder_to_msc.py' && false)
ladder_to_msc.py -i $< -o $@

View File

@@ -0,0 +1,333 @@
== Distributed GSM / GSUP Proxy Cache: Remedy Temporary Link Failure to Home HLR
The aim of the Proxy Cache is to still provide service to roaming subscribers even if the GSUP link to the home HLR is
temporarily down or unresponsive.
If a subscriber from a remote site is currently roaming at this local site, and the link to the subscriber's home HLR
has succeeded before, the GSUP proxy cache can try to bridge the time of temporary link failure to that home HLR.
Tasks to take over from an unreachable home HLR:
- Cache and send auth tuples on Send Auth Info Request.
- Acknowledge periodic Location Updating.
- ...?
=== Design Considerations
==== Authentication
The most critical role of the home HLR is providing the Authentication and Key Agreement (AKA) tuples. If the home HLR
is not reachable, the lack of fresh authentication challenges would normally cause the subscriber to be rejected. To
avoid that, a proxying HLR needs to be able to provide AKA tuples on behalf of the home HLR.
In short, the strategy of the D-GSM proxy cache is:
- Try to keep a certain number of unused full UMTS AKA tuples in the proxy cache at all times.
- When the MSC requests more tuples, dispense some from the cache, and fill it back up later on, as soon as a good link
is available.
- When the tuple cache in the proxy HLR runs dry, 3G RAN becomes unusable. But 2G RAN may fall back to GSM AKA, if the
proxy HLR configuration permits it: resend previously used GSM AKA auth tuples to the MSC, omitting UMTS AKA items
from the Send Auth Info Result, to force the MSC to send a GSM AKA challenge on 2G.
The remaining part of this section provides detailed reasoning for this strategy.
The aim is to attach a subscriber without immediate access to the authentication key data.
Completely switching off authentication would be an option on GERAN (2G), but this would mean complete lack of
encryption on the air interface, and is not recommended. On 3G and later, authentication is always mandatory.
The key data is known only to the USIM and the home HLR. The HLR generates distinct authentication tuples, each
containing a cryptographic challenge (RAND, AUTN) and its expected response (SRES, XRES). The MSC consumes one tuple
per authentication: it sends the challenge to the subscriber, and compares the response received.
The proxy could generate fresh tuples if the cryptographic key data (Ki,K,OP/OPC) from the home HLR was shared with the
proxy HLR. Distributed GSM does not do this, because:
- The key data is cryptographically very valuable. If it were leaked, any and all authentication challenges would be
fully compromised.
- In D-GSM, each home site shall retain exclusive authority over the user data. It should not be necessary to share the
secret keys with any remote site.
So, how about resending already used auth tuples to the MSC when no fresh ones are available? Resending identical
authentication challenges makes the system vulnerable to relatively trivial replay-attacks, but this may be an
acceptable fallback in situations of failing links, if it means being able to provide reliable roaming.
But, even if a proxying HLR is willing to compromise cryptographic security to improve service, this can only work with
GSM AKA:
- In GSM AKA (so-called 2G auth), tuples may be re-used any amount of times without a strict need to generate more
authentication challenges. The SIM will merely calculate the (same) SRES response again, and authentication will
succeed. It is bad security to do so, but it is a choice the core network is free to make.
- UMTS AKA (Milenage or so-called 3G auth, but also used on 2G GERAN) adds mutual authentication, i.e. the core network
must prove that it is authentic. Specifically to thwart replay-attacks that would spoof a core network, UMTS AKA
contains an ongoing sequence number (SQN) that is woven into the authentication challenge. An SQN may skip forward by
a certain number of counts, but it can never move backwards. If a USIM detects a stale SQN, it will request an
authentication re-synchronisation (by passing AUTS in an Authentication Failure message), after which a freshly
generated UMTS AKA challenge is strictly required -- not possible with an unresponsive home HLR.
Post-R99 (1999) 2G GERAN networks are capable of UMTS AKA, so, not only 3G, but also the vast majority of 2G networks
today use UMTS AKA -- and so does Osmocom, typically. Hence it is desirable to fully support UMTS AKA in D-GSM.
[options="header"]
|===
| RAN | authentication is... 2+| available AKA types
| GERAN (2G) | optional | GSM AKA | UMTS AKA
| UTRAN (3G) | mandatory | - | UMTS AKA
|===
UMTS AKA will not allow re-sending previously used authentication tuples. But a UMTS capable SIM will fall back to GSM
AKA if the network sent only a GSM AKA challenge. If the proxy HLR sends only GSM AKA tuples, then the MSC will request
GSM authentication, and re-sending old tuples is again possible. However, a USIM will only fall back to GSM AKA if the
phone is attaching on a 2G network. For 3G RAN and later, UMTS AKA is mandatory. So, as soon as a site uses 3G or newer
RAN technology, there is simply no way to resend previously used authentication tuples.
The only way to have unused UMTS AKA tuples in the proxy HLR is to already have them stored from an earlier time. The
idea is to request more auth tuples in advance whenever the link is available, and cache them in the proxy. When the MSC
uses up some tuples from the proxy HLR, the proxy cache can fill up again in its own time, by requesting more tuples
from the home HLR at a time of good link. Then, the next time the subscriber needs immediate action, it does not matter
whether the home HLR is directly reachable or not.
In an aside, since OsmoMSC already caches a number of authentication tuples, one option would be to implement this in
OsmoMSC, and not in the proxy HLR: the MSC could request new tuples long before its tuple cache runs dry. However, the
OsmoMSC VLR state is volatile, and a power cycle of the system would lose the tuple cache; if the home HLR is
unreachable at the same time of the power cycle, roaming service would be interrupted. The proxy cache in the HLR is
persistent, so roaming can continue immediately after a power cycle, even if the home HLR link is down.
==== Location Updating
Any attached subscriber periodically repeats a Location Updating procedure, typically every 15 minutes. If a home HLR is
unreachable at the time of the periodic Location Updating, a roaming subscriber would assume that it is detached from
the network, even though the local site it is roaming at is still fully operational.
The aim of D-GSM is to keep subscribers attached even if the remote home HLR is temporarily unreachable. The simplest
way to achieve that is by directly responding with a Update Location Result to the MSC.
In addition to accepting an Update Location, a proxy HLR should also start an Insert Subscriber Data procedure, as a
home HLR would do. For a periodic Location Updating, the MSC should already know all of the information that an Insert
Subscriber Data would convey (i.e. the MSISDN), and there would be no strict need to resend this data. But if a
subscriber quickly detaches and re-attaches (e.g. the device rebooted), the MSC has discarded the subscriber info from
the VLR, and hence the proxy HLR should also always perform an Insert Subscriber Data. (On the GSUP wire, a periodic LU
is indistinguishable from an IMSI-Attach LU.)
Furthermore, the longer the proxy HLR's cache keeps a roaming subscriber's data after an IMSI Detach, the longer it is
possible for the subscriber to immediately re-attach despite the home HLR being temporarily unreachable.
If a subscriber has carried out a GSUP Update Location with the proxy HLR while the home HLR was unreachable, it is not
strictly necessary to repeat that Update Location message to the home HLR later. The home HLR does keep a timestamped
record of an Update Location from a proxy HLR if seen, but that has no visible effect on serving the subscriber:
- If the home HLR still thinks that the subscriber is currently attached at the home site, it will respond to mslookup
requests. But the actual site the subscriber is roaming at will have a younger age, and its mslookup responses will
win.
- If the home HLR has no record of the subscriber being attached recently, or has a record of being attached at another
remote site, it does not respond to mslookup requests for that subscriber. If it records the new proxy LU, it still
does not respond to mslookup requests since the subscriber is attached remotely, i.e. there is no difference.
It is thinkable to always handle an Update Location in the proxy HLR, and never even attempt to involve the home HLR in
case the proxy cache already has data for a given subscriber, but then the proxy HLR would never notice a changed MSISDN
or authorization status for this subscriber. It is best practice to involve the home HLR whenever possible.
==== IMSI Detach
If a GSUP client reports a detaching IMSI when the home HLR is not reachable, simply respond with an ack.
It is not required to signal the home HLR with a detach once the link is back up. A home HLR anyway flags a remotely
roaming subscriber as attached-at-a-proxy, and there is literally no difference between telling a home HLR about a
detach or not.
(TODO: is there even a GSUP message that a VLR should send on IMSI Detach? see OS#4374)
[[proxy_cache_umts_aka_resync]]
==== UMTS AKA Resync
When the SQN between USIM and AUC (subscriber and home HLR) have diverged, the Send Authentication Info Request from the
MSC contains an AUTS IE. This means that a resynchronization between USIM and AUC (the home HLR) is necessary. All of
the UMTS AKA tuples in the proxy cache are now unusable, and the home HLR must respond with fresh tuples after doing a
resync. This also means that either the home HLR must be reachable immediately, or GSM AKA fallback must be allowed for
the subscriber to remain in roaming service.
In short:
- A UMTS AKA resync is handled similarly to the attaching of a so far unknown subscriber.
- With the exception that previous GSM AKA tuples may be available to try a fallback to re-using older tuples.
Needless to say that avoiding the need for UMTS AKA resynchronization is an important aspect of D-GSM's resilience
against unreliable links.
In UMTS AKA, there is not one single SQN, but there are a number SQN slots, called IND slots or IND buckets. The IND
bitlen configured on the USIM determines the amount of slots available. The IND bitlen is usually 5, i.e. 2^5^ = 32
slots. Monotonously rising SQN are only strictly enforced within each slot, so that each site should maintain a
different IND slot. OsmoHLR determines distinct IND slots based on the IPA unit name. As soon as more than 16 sites
(with an MSC and SGSN each) are maintained, IND slots may be shared between distinct sites, and administrative care
should be taken to choose wisely which sites share the same slots: those that least share a common user group.
On 2G RAN, it may be possible to fall back to GSM AKA after a UMTS AKA resync request.
TODO: test this
Either way, the AUTS that was received from the MSC definitely needs to find its way to the home HLR, and, ideally, the
immediately returned auth tuples from the home HLR should be used to attach the subscriber.
=== CS and PS
Each subscriber may have multiple HLR subscriptions from distinct CN Domain VLRs at any time: Circuit Switched (MSC) and
Packet Switched (SGSN) attach separately and perform Update Location Requests that are completely orthogonal, as far as
the HLR is concerned.
Particularly the UMTS AKA tuples, which use distinct IND slots per VLR, need to be cached separately per CN Domain.
Hence it is not enough to maintain one cache per subscriber. A separate auth tuple cache and Mobility Management state
has to be kept for each VLR that is requesting roaming service for a given subscriber.
=== Intercepting GSUP Conversations
Taking over GSUP conversations in the proxy HLR is not as trivial as it may sound. Here are potential problems and how
to fix them.
[[proxy_cache_gsup_mm_messages]]
==== Which GSUP Conversations to Intercept
For the purpose of providing highly available roaming despite unreliable links to the home HLR, it suffices to intercept
Mobility Management (MM) related GSUP messages, only:
- Send Auth Info Request / Result
- Update Location Request / Result
- Insert Subscriber Data Request / Result
- PurgeMS Request / Result (?)
An interesting feature would be to also intercept specific USSD requests, like returning the own MSISDN or IMSI more
reliably, or handling services that only make sense when served by the local site. At the time of writing, this is seen
as a future extension of D-GSM and not considered for implementation.
==== Determining Whether a Home HLR is Responsive
Normally, all GSUP messages are merely routed via the proxy HLR and are handled by the home HLR. The idea is that the
proxy HLR jumps in and saves a GSUP conversation when the home HLR is not answering properly.
The simplest method to decide whether a home HLR is currently connected would be to look at the GSUP client state.
However, a local flag that indicates an established GSUP connection does not necessarily mean a reliable link.
There are keep-alive messages on the GSUP/IPA link, and a lost connection should reflect in the client state, so that a
lost GSUP link definitely indicates an unresponsive home HLR. But for various reasons (e.g. packet loss), the link might
look intact, but still a given GSUP message fails to get a response from the home HLR.
A more resilient method to decide whether a home HLR is responsive is to keep track of every MM related GSUP
conversation for each subscriber, and to jump in and take over the GSUP conversation as soon as the response is taking
too long to arrive. However, choosing an inadequate timeout period would either mean responding after the MSC has
already timed out (too slow), or completely cutting off all responses from a high-latency home HLR (too fast).
Also, if the proxy HLR has already responded to the MSC, but a slow home HLR's response arrives shortly after,
forwarding this late message to the MSC on top of the earlier response to the same request would confuse the GSUP
conversation.
So, the proxy HLR just jumping into the GSUP conversation when a specific delay has passed is fairly complex and error
prone. A better idea is to always intercept MM related GSUP conversations:
[[proxy_cache_gsup_conversations]]
==== Solution: Distinct GSUP Conversations
A solution that avoids all of the above problems is to *always* take over *all* MM related conversations (see
<<proxy_cache_gsup_mm_messages>>), as soon as the proxy has sufficient data to service them by itself; at the same time,
the proxy HLR should also relay the same requests to the home HLR, and acknowledge its responses, after the fact.
If the proxy cache already has a complete record of a subscriber, the proxy HLR can always directly accept an Update
Location Request, including an Insert Subscriber Data. A prompt response ensures that the MSC does not timeout its GSUP
request, and reduces waiting time for the subscriber.
To ensure that the proxy HLR's data on the subscriber doesn't become stale and diverge from the home HLR, the proxy
asynchronously also forwards an Update Location Request to the home HLR. In most normal cases, there will be no
surprises, and the home HLR will continue with an Insert Subscriber Data Request containing already known data, and an
Update Location Result accepting the LU.
If the home HLR does not respond, the proxy HLR ignores that fact -- the home HLR is not reachable, and the aim is to
continue to service the subscriber for the time being.
But, should the home HLR's Insert Subscriber Data Request send different data than the proxy cache sees on record, the
proxy HLR can trigger another Insert Subscriber Data Request to the MSC, to correct the stale data sent before.
Similarly, if the home HLR rejects the Update Location Request completely, the proxy HLR can tell the MSC to detach the
subscriber with a Cancel Location Request message, as soon as it notices the rejection.
Note that a UMTS AKA resynchronization request invalidates the entire auth tuple cache and needs to either be sent to
the home HLR immediately, if available, or the AUTS from the USIM must later reach the home HLR to obtain fresh UMTS AKA
tuples for the cache. See <<proxy_cache_umts_aka_resync>>.
=== Message Sequences
==== Normal Roaming Attach
On first attaching via a proxy HLR, when there is no proxy state for the subscriber yet, the home HLR must be reachable.
The normal procedure takes place without modification, except that he proxy HLR keeps a copy of the first auth tuples it
forwards from the home HLR back to the MSC (marked as used) (1). This is to have auth tuples available for resending
already used tuples in a fallback to GSM AKA, in case this is enabled in the proxy HLR config.
After the Location Updating has completed successfully, the proxy HLR fills up its auth tuple cache by additional Send
Auth Info Requests (2). As soon as unused auth tuples become available, the proxy HLR can discard already used tuples
from (1).
.Normal attaching of a subscriber that is roaming here
["mscgen"]
----
include::proxy_cache_attach.msc[]
----
==== MSC Requests More Auth Tuples
As soon as the MSC has run out of fresh auth tuples, it will ask the HLR proxy for more. Without proxy caching, this
request would be directly forwarded to the home HLR. Instead, the proxy HLR finds unused auth tuples in the cache and
directly sends those (3). Even if there is a reliable link, the home HLR is not contacted at this point.
Directly after completing the Send Auth Info Result, the proxy HLR finds that less tuples than requested by the D-GSM
configuration are cached, and asks the home HLR for more tuples, to fill up the cache (4). If there currently is no
reliable link, this will fail, and the proxy HLR will retry periodically (5) / upon GSUP reconnect.
.When the MSC has used up all of its auth tuples, but the proxy HLR still has unused auth tuples in the cache
["mscgen"]
----
include::proxy_cache_more_tuples.msc[]
----
==== Running Out of Auth Tuples
When all fresh tuples from the proxy HLR have been used up, and the home HLR remains unreachable, the proxy HLR normally
fails and rejects the subscriber (default configuration).
If explicitly enabled in the configuration, the proxy HLR will attempt to fall back to GSM AKA and resend already spent
tuples, deliberately omitting UMTS AKA parts (6).
Note that an attempt to refill the tuple cache in the proxy HLR always happens asynchronously. If there are no tuples,
that means the link to the home HLR is currently broken, and there is no point in trying to contact it now. Tuples will
be obtained as soon as the link is established again.
.When the MSC has used up all of its auth tuples and the proxy HLR tuple cache is dry
["mscgen"]
----
include::proxy_cache_tuple_cache_dry.msc[]
----
==== Periodic Location Updating
Each subscriber performs periodic Location Updating to ensure that it is not implicitly detached from the network. When
the proxy HLR already has a proxy cache for this subscriber, all information to complete the periodic Location Updating
is already known in the proxy HLR. If the link to the home HLR is unresponsive, the proxy HLR mimicks the Insert
Subscriber Data Request that the home HLR would normally send, using the cached MSISDN, and then sends the Update
Location Result. The subscriber remains attached without a responsive link to the home HLR being required.
.Periodic Location Updating when the MSC still has unused auth tuples
["mscgen"]
----
include::proxy_cache_periodic_lu.msc[]
----
==== UMTS AKA Resync
The AUTS from a UMTS AKA resync needs to reach the home HLR sooner or later, and a resync renders all UMTS AKA tuples in
the cache stale.
.Cached tuples become unusable from a UMTS AKA resynchronisation request from the USIM.
["mscgen"]
----
include::proxy_cache_umts_aka_resync.msc[]
----

View File

@@ -0,0 +1,39 @@
{hscale=2}
ms = MS,BSS
msc = MSC
hlr = HLR proxy
home = Home HLR
ms -> msc Location Updating Request (IMSI Attach)
msc -> hlr Send Auth Info Request
hlr <> hlr No proxy cache data available for this subscriber
hlr () home mslookup finds the home HLR
hlr -> home Send Auth Info Request
hlr <- home Send Auth Info Result
with 5 auth tuples
hlr () . (1) Keep a copy of the auth tuples
msc <- hlr Send Auth Info Result
msc () . MSC stores 5 auth tuples,
uses the first one now,
and keeps the rest for later requests
ms () msc Authentication
msc -> hlr Update Location Request
hlr -> home Update Location Request
hlr <- home Insert Subscriber Data Request
(with subscriber's MSISDN)
hlr () . proxy HLR caches the MSISDN
msc <- hlr Insert Subscriber Data Request
msc -> hlr Insert Subscriber Data Result
hlr -> home Insert Subscriber Data Result
hlr <- home Update Location Result
msc <- hlr Update Location Result
ms <- msc Location Updating Accept
hlr <> . After successful Update Location, check the cache
hlr () . (2) Ask for more auth tuples to cache
(amount of tuples configurable)
hlr -> home Send Auth Info Request
hlr <- home Send Auth Info Result
hlr () . store 5 more tuples
hlr -> home Send Auth Info Request
hlr <- home Send Auth Info Result
hlr () . store yet 5 more tuples

View File

@@ -0,0 +1,33 @@
msc {
hscale="2";
ms[label="MS,BSS"],__msc[label="MSC"],hlr[label="HLR proxy"],home[label="Home HLR"];
ms => __msc [label="Location Updating Request (IMSI Attach)"];
__msc => hlr [label="Send Auth Info Request"];
hlr abox hlr [label="No proxy cache data available for this subscriber"];
hlr rbox home [label="mslookup finds the home HLR"];
hlr => home [label="Send Auth Info Request"];
hlr <= home [label="Send Auth Info Result\nwith 5 auth tuples"];
hlr rbox hlr [label="(1) Keep a copy of the auth tuples"];
__msc <= hlr [label="Send Auth Info Result"];
__msc rbox __msc [label="MSC stores 5 auth tuples,\nuses the first one now,\nand keeps the rest for later requests"];
ms rbox __msc [label="Authentication"];
__msc => hlr [label="Update Location Request"];
hlr => home [label="Update Location Request"];
hlr <= home [label="Insert Subscriber Data Request\n(with subscriber's MSISDN)"];
hlr rbox hlr [label="proxy HLR caches the MSISDN"];
__msc <= hlr [label="Insert Subscriber Data Request"];
__msc => hlr [label="Insert Subscriber Data Result"];
hlr => home [label="Insert Subscriber Data Result"];
hlr <= home [label="Update Location Result"];
__msc <= hlr [label="Update Location Result"];
ms <= __msc [label="Location Updating Accept"];
hlr abox hlr [label="After successful Update Location, check the cache"];
hlr rbox hlr [label="(2) Ask for more auth tuples to cache\n(amount of tuples configurable)"];
hlr => home [label="Send Auth Info Request"];
hlr <= home [label="Send Auth Info Result"];
hlr rbox hlr [label="store 5 more tuples"];
hlr => home [label="Send Auth Info Request"];
hlr <= home [label="Send Auth Info Result"];
hlr rbox hlr [label="store yet 5 more tuples"];
}

View File

@@ -0,0 +1,31 @@
{hscale=2}
ms = MS,BSS
msc = MSC
hlr = HLR proxy
home = Home HLR
ms -> msc CM Service Request / Paging Response
msc -> hlr Send Auth Info Request
hlr () . Use already set up proxy path
hlr <> . there still are unsent auth tuples
in the cache
hlr () . (3) Send cached, fresh tuples
msc <- hlr Send Auth Info Result
containing auth tuples
from the proxy cache
ms () msc Authentication
ms () msc Continue the CM Service / Paging action
hlr <> . Note that there are no/few unused tuples in the cache, fill up again
hlr () . (4) Ask for more auth tuples to cache
hlr -> home Send Auth Info Request
--- If the home HLR link is not working
hlr <> . no link
or
response timeout
hlr () . (5) Set up a timer to retry SAI
(a few minutes?)
hlr <> . Timer triggers
hlr -> home Send Auth Info Request
--- If the home HLR link is functional
hlr <- home Send Auth Info Result
hlr () . store 5 more tuples

View File

@@ -0,0 +1,24 @@
msc {
hscale="2";
ms[label="MS,BSS"],__msc[label="MSC"],hlr[label="HLR proxy"],home[label="Home HLR"];
ms => __msc [label="CM Service Request / Paging Response"];
__msc => hlr [label="Send Auth Info Request"];
hlr rbox hlr [label="Use already set up proxy path"];
hlr abox hlr [label="there still are unsent auth tuples\nin the cache"];
hlr rbox hlr [label="(3) Send cached, fresh tuples"];
__msc <= hlr [label="Send Auth Info Result\ncontaining auth tuples\nfrom the proxy cache"];
ms rbox __msc [label="Authentication"];
ms rbox __msc [label="Continue the CM Service / Paging action"];
hlr abox hlr [label="Note that there are no/few unused tuples in the cache, fill up again"];
hlr rbox hlr [label="(4) Ask for more auth tuples to cache"];
hlr => home [label="Send Auth Info Request"];
--- [label="If the home HLR link is not working"];
hlr abox hlr [label="no link\nor\nresponse timeout"];
hlr rbox hlr [label="(5) Set up a timer to retry SAI\n(a few minutes?)"];
hlr abox hlr [label="Timer triggers"];
hlr => home [label="Send Auth Info Request"];
--- [label="If the home HLR link is functional"];
hlr <= home [label="Send Auth Info Result"];
hlr rbox hlr [label="store 5 more tuples"];
}

View File

@@ -0,0 +1,47 @@
{hscale=2}
ms = MS,BSS
msc = MSC
hlr = HLR proxy
home = Home HLR
ms -> msc Location Updating Request (Periodic)
ms () msc Authentication,
using the next of 5 auth tuples the MSC has stored
msc -> hlr Update Location Request
hlr () . Use already set up proxy path
hlr <> . (8) proxy cache already has all information to answer
msc <- hlr Insert Subscriber Data Request
msc -> hlr Insert Subscriber Data Result
msc <- hlr Update Location Result
ms <- msc Location Updating Accept
hlr () . (9) Verify Update Location with home HLR
|||
--- if the home HLR has no changes and accepts
hlr -> home Update Location Request
hlr <- home Insert Subscriber Data Request
hlr -> home Insert Subscriber Data Result
hlr <> . Notice identical MSISDN
hlr <- home Update Location Result
|||
--- if the home HLR is unreachable
hlr -> home Update Location Request
hlr <> . no link
or
response timeout
hlr () . Don't care, carry on
|||
--- if the home HLR has a modified MSISDN, and accepts
hlr -> home Update Location Request
hlr <- home Insert Subscriber Data Request
hlr -> home Insert Subscriber Data Result
hlr <> . Notice changed MSISDN
msc <- hlr Insert Subscriber Data Request
msc -> hlr Insert Subscriber Data Result
hlr <- home Update Location Result
|||
--- if the home HLR rejects
hlr -> home Update Location Request
hlr <- home Update Location Error
msc <- hlr Cancel Location Request
msc -> hlr Cancel Location Result
hlr () . Clear subscriber cache

View File

@@ -0,0 +1,43 @@
msc {
hscale="2";
ms[label="MS,BSS"],__msc[label="MSC"],hlr[label="HLR proxy"],home[label="Home HLR"];
ms => __msc [label="Location Updating Request (Periodic)"];
ms rbox __msc [label="Authentication,\nusing the next of 5 auth tuples the MSC has stored"];
__msc => hlr [label="Update Location Request"];
hlr rbox hlr [label="Use already set up proxy path"];
hlr abox hlr [label="(8) proxy cache already has all information to answer"];
__msc <= hlr [label="Insert Subscriber Data Request"];
__msc => hlr [label="Insert Subscriber Data Result"];
__msc <= hlr [label="Update Location Result"];
ms <= __msc [label="Location Updating Accept"];
hlr rbox hlr [label="(9) Verify Update Location with home HLR"];
|||;
--- [label="if the home HLR has no changes and accepts"];
hlr => home [label="Update Location Request"];
hlr <= home [label="Insert Subscriber Data Request"];
hlr => home [label="Insert Subscriber Data Result"];
hlr abox hlr [label="Notice identical MSISDN"];
hlr <= home [label="Update Location Result"];
|||;
--- [label="if the home HLR is unreachable"];
hlr => home [label="Update Location Request"];
hlr abox hlr [label="no link\nor\nresponse timeout"];
hlr rbox hlr [label="Don't care, carry on"];
|||;
--- [label="if the home HLR has a modified MSISDN, and accepts"];
hlr => home [label="Update Location Request"];
hlr <= home [label="Insert Subscriber Data Request"];
hlr => home [label="Insert Subscriber Data Result"];
hlr abox hlr [label="Notice changed MSISDN"];
__msc <= hlr [label="Insert Subscriber Data Request"];
__msc => hlr [label="Insert Subscriber Data Result"];
hlr <= home [label="Update Location Result"];
|||;
--- [label="if the home HLR rejects"];
hlr => home [label="Update Location Request"];
hlr <= home [label="Update Location Error"];
__msc <= hlr [label="Cancel Location Request"];
__msc => hlr [label="Cancel Location Result"];
hlr rbox hlr [label="Clear subscriber cache"];
}

View File

@@ -0,0 +1,21 @@
{hscale=2}
ms = MS,BSS
msc = MSC
hlr = HLR proxy
home = Home HLR
ms -> msc CM Service Request / Paging Response
msc -> hlr Send Auth Info Request
hlr () . Use already set up proxy path
hlr <> . All cached auth tuples have been sent to the MSC before
--- If no GSM AKA fallback is allowed
msc <- hlr Send Auth Info Error
ms () msc Detach
--- If GSM AKA fallback is allowed
hlr () . (6) Resend only GSM AKA tuples, already sent earlier
msc <- hlr Send Auth Info Result
containing GSM AKA auth tuples
from the proxy cache
ms () msc 2G: Authentication
3G: Detach
ms () msc Continue the CM Service / Paging action

View File

@@ -0,0 +1,17 @@
msc {
hscale="2";
ms[label="MS,BSS"],__msc[label="MSC"],hlr[label="HLR proxy"],home[label="Home HLR"];
ms => __msc [label="CM Service Request / Paging Response"];
__msc => hlr [label="Send Auth Info Request"];
hlr rbox hlr [label="Use already set up proxy path"];
hlr abox hlr [label="All cached auth tuples have been sent to the MSC before"];
--- [label="If no GSM AKA fallback is allowed"];
__msc <= hlr [label="Send Auth Info Error"];
ms rbox __msc [label="Detach"];
--- [label="If GSM AKA fallback is allowed"];
hlr rbox hlr [label="(6) Resend only GSM AKA tuples, already sent earlier"];
__msc <= hlr [label="Send Auth Info Result\ncontaining GSM AKA auth tuples\nfrom the proxy cache"];
ms rbox __msc [label="2G: Authentication\n3G: Detach"];
ms rbox __msc [label="Continue the CM Service / Paging action"];
}

View File

@@ -0,0 +1,55 @@
{hscale=2}
ms = MS,BSS
msc = MSC
hlr = HLR proxy
home = Home HLR
ms -> msc CM Service Request / Paging Response
msc -> hlr Send Auth Info Request
hlr <> . there still are unsent auth tuples
in the cache
hlr () . Send cached, fresh tuples
msc <- hlr Send Auth Info Result
containing auth tuples
from the proxy cache
ms <- msc Authentication Request
ms -> msc USIM requests UMTS AKA resync
Auth Failure with AUTS
msc -> hlr Send Auth Info Request
containing AUTS IE
hlr () . Mark all UMTS AKA tuples as stale
--- If the home HLR responds in time
hlr -> home Send Auth Info Request
hlr <- home Send Auth Info Result
with 5 auth tuples
hlr () . clear tuple cache, store new tuples
msc <- hlr Send Auth Info Result
ms () msc Authentication
hlr () . fill up the tuple cache as necessary
hlr -> home Send Auth Info Request
hlr <- home Send Auth Info Result
...
--- If the home HLR is unreachable
hlr -> home Send Auth Info Request
hlr <> . no link
or
response timeout
hlr () . Store the AUTS received earlier,
and set up a timer to retry SAI
(a few minutes?)
--- If no GSM AKA fallback is allowed
msc <- hlr Send Auth Info Error
ms () msc Detach
--- If GSM AKA fallback is allowed
hlr () . Resend only GSM AKA tuples, already sent earlier
msc <- hlr Send Auth Info Result
containing GSM AKA auth tuples
from the proxy cache
ms () msc 2G: Authentication
3G: Detach
---
hlr <> . AUTS timer triggers
hlr -> home Send Auth Info Request
hlr <- home Send Auth Info Result
hlr () . clear tuple cache, store new tuples
hlr () . continue to fill up the cache as necessary

View File

@@ -0,0 +1,41 @@
msc {
hscale="2";
ms[label="MS,BSS"],__msc[label="MSC"],hlr[label="HLR proxy"],home[label="Home HLR"];
ms => __msc [label="CM Service Request / Paging Response"];
__msc => hlr [label="Send Auth Info Request"];
hlr abox hlr [label="there still are unsent auth tuples\nin the cache"];
hlr rbox hlr [label="Send cached, fresh tuples"];
__msc <= hlr [label="Send Auth Info Result\ncontaining auth tuples\nfrom the proxy cache"];
ms <= __msc [label="Authentication Request"];
ms => __msc [label="USIM requests UMTS AKA resync\nAuth Failure with AUTS"];
__msc => hlr [label="Send Auth Info Request\ncontaining AUTS IE"];
hlr rbox hlr [label="Mark all UMTS AKA tuples as stale"];
--- [label="If the home HLR responds in time"];
hlr => home [label="Send Auth Info Request"];
hlr <= home [label="Send Auth Info Result\nwith 5 auth tuples"];
hlr rbox hlr [label="clear tuple cache, store new tuples"];
__msc <= hlr [label="Send Auth Info Result"];
ms rbox __msc [label="Authentication"];
hlr rbox hlr [label="fill up the tuple cache as necessary"];
hlr => home [label="Send Auth Info Request"];
hlr <= home [label="Send Auth Info Result"];
...;
--- [label="If the home HLR is unreachable"];
hlr => home [label="Send Auth Info Request"];
hlr abox hlr [label="no link\nor\nresponse timeout"];
hlr rbox hlr [label="Store the AUTS received earlier,\nand set up a timer to retry SAI\n(a few minutes?)"];
--- [label="If no GSM AKA fallback is allowed"];
__msc <= hlr [label="Send Auth Info Error"];
ms rbox __msc [label="Detach"];
--- [label="If GSM AKA fallback is allowed"];
hlr rbox hlr [label="Resend only GSM AKA tuples, already sent earlier"];
__msc <= hlr [label="Send Auth Info Result\ncontaining GSM AKA auth tuples\nfrom the proxy cache"];
ms rbox __msc [label="2G: Authentication\n3G: Detach"];
---;
hlr abox hlr [label="AUTS timer triggers"];
hlr => home [label="Send Auth Info Request"];
hlr <= home [label="Send Auth Info Result"];
hlr rbox hlr [label="clear tuple cache, store new tuples"];
hlr rbox hlr [label="continue to fill up the cache as necessary"];
}

View File

@@ -54,7 +54,7 @@ this database file will be created in the current working directory.
Alternatively, you may use the `osmo-hlr-db-tool`, which is installed along
with `osmo-hlr`, to bootstrap an empty database, or to migrate subscriber data
from an old 'OsmoNITB' database. See <<db_import_nitb>>.
from an old 'OsmoNITB' database. See `osmo-hlr-db-tool --help`.
=== Multiple instances

View File

@@ -127,83 +127,3 @@ OsmoHLR# subscriber imei 35761300444848 show
----
<1> Randomly generated 5 digit MSISDN
<2> Disabled CS and PS NAM prevent the subscriber from accessing the network
=== Import Subscriber Data
==== Scripted Import
WARNING: It is not generally a good idea to depend on the HLR database's internal table structure, but in the lack of an
automated import procedure, this example is provided as an ad-hoc method to aid automated subscriber import. This is not
guaranteed to remain valid.
NOTE: We may add CSV and other import methods to the `osmo-hlr-db-tool`, but so far that is not implemented. Contact the
community if you are interested in such a feature being implemented.
NOTE: `sqlite3` is available from your distribution packages or `sqlite.org`.
Currently, probably the easiest way to automatically import subscribers to OsmoHLR is to write out a text file with SQL
commands per subscriber, and feed that to `sqlite3`, as described below.
A difficulty is to always choose subscriber IDs that are not yet in use. For an initial import, the subscriber ID may be
incremented per subscriber record. If adding more subscribers to an existing database, it is necessary to choose
subscriber IDs that are not yet in use. Get the highest ID in use with:
----
sqlite3 hlr.db 'select max(id) from subscriber'
----
A full SQL example of adding a single subscriber with id 23, IMSI 001010123456789, MSISDN 1234, Ki for COMP128v1, and K
and OPC for Milenage:
----
INSERT subscriber (id, imsi, msisdn) VALUES (23, '001010123456789', '1234');
INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki)
VALUES(23, 1, '0123456789abcdef0123456789abcdef');
INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc)
VALUES(23, 5, '0123456789abcdef0123456789abcdef',NULL,'0123456789abcdef0123456789abcdef');
----
Table entries to `auc_2g` and/or `auc_3g` may be omitted if no such key material is required.
UMTS Milenage auth (on both 2G and 3G RAN) is configured by the `auc_3g` table. `algo_id_3g` must currently always be 5
(MILENAGE).
The algorithm IDs for `algo_id_2g` and `algo_id_3g` are:
.Algorithm IDs in OsmoHLR's database
[options="header",width="50%",cols="40%,60%"]
|===
|`algo_id_2g` / `algo_id_3g` | Authentication Algorithm
| 1 | COMP128v1
| 2 | COMP128v2
| 3 | COMP128v3
| 4 | XOR
| 5 | MILENAGE
|===
Create an empty HLR database with
----
osmo-hlr-db-tool -l hlr.db create
----
Repeat above SQL commands per subscriber, incrementing the subscriber ID for each block, then feed the SQL commands for
the subscribers to be imported to the `sqlite3` command line tool:
----
sqlite3 hlr.db < subscribers.sql
----
[[db_import_nitb]]
==== Import OsmoNITB database
To upgrade from old OsmoNITB to OsmoHLR, use `osmo-hlr-db-tool`:
----
osmo-hlr-db-tool -l hlr.db import-nitb-db nitb.db
----
Be aware that the import is lossy, only the IMSI, MSISDN, nam_cs/ps and 2G auth data are set.

View File

@@ -50,29 +50,15 @@ prefix route to the named EUSE. All USSD short codes starting with *123 will be
routed to the named EUSE.
`ussd route prefix *#100# internal own-msisdn` installs a prefix route
to the named internal USSD handler. The above command will restore
to the named internal USSD handler. There above command will restore
the old behavior, in which *#100# will return a text message containing
the subscribers own phone number. More information on internal USSD
handlers can be found in <<iuse_handlers>>.
the subscribers own phone number. There is one other handler called
`own-imsi` which will return the IMSI instead of the MSISDN.
`ussd default-route external foobar-00-00-00-00-00-00` installs a
default route to the named EUSE. This means that all USSD codes for
which no more specific route exists will be routed to the named EUSE.
[[iuse_handlers]]
=== Built-in USSD handlers
OsmoHLR has an Internal USSD Entity (IUSE) that allows to handle some
USSD requests internally. It features a set of simple handlers, which
can be assigned to one or more USSD request prefixes:
* `own-msisdn` returns subscriber's MSISDN (if assigned);
* `own-imsi` returns subscriber's IMSI;
* `test-idle` keeps the session idle until the MS terminates it, or
the guard timer expires (may be useful for testing).
Additional handlers can be added on request.
=== Example EUSE program
We have provided an example EUSE developed in C language using existing

View File

@@ -26,9 +26,9 @@ include::./common/chapters/control_if.adoc[]
include::{srcdir}/chapters/dgsm.adoc[]
include::./common/chapters/gsup.adoc[]
include::{srcdir}/chapters/proxy_cache.adoc[]
include::./common/chapters/vty_cpu_sched.adoc[]
include::./common/chapters/gsup.adoc[]
include::./common/chapters/port_numbers.adoc[]
@@ -37,3 +37,4 @@ include::./common/chapters/bibliography.adoc[]
include::./common/chapters/glossary.adoc[]
include::./common/chapters/gfdl.adoc[]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
all:
echo "built only on manual invocation, needs mscgen and dot (graphviz) programs: invoke 'make charts'"
charts: msc dot
EXTRA_DIST = \
proxy_cache.dot \
proxy_cache__mm_fsm.dot \
proxy_cache__to_home_hlr_fsm.dot \
$(NULL)
CLEANFILES = \
proxy_cache.png \
proxy_cache__mm_fsm.png \
proxy_cache__to_home_hlr_fsm.png \
$(NULL)
msc: \
$(NULL)
dot: \
$(builddir)/proxy_cache.png \
$(builddir)/proxy_cache__mm_fsm.png \
$(builddir)/proxy_cache__to_home_hlr_fsm.png \
$(NULL)
$(builddir)/%.png: %.msc
mscgen -T png -o $@ $<
$(builddir)/%.msc: $(srcdir)/%.ladder
@which ladder_to_msc.py || (echo 'PLEASE POINT YOUR $$PATH AT libosmocore/contrib/ladder_to_msc.py' && false)
ladder_to_msc.py -i $< -o $@
$(builddir)/%.png: $(srcdir)/%.dot
dot -Tpng $< > $@
.PHONY: poll
poll:
while true; do $(MAKE) msc dot; sleep 1; done

View File

@@ -0,0 +1,19 @@
digraph G {
rankdir=LR
labelloc=t;
msc [label="MS/BSS/MSC"]
subgraph cluster_proxy {
label="HLR Proxy"
proxy_mm [label="Proxy Mobility Management FSM",shape=box3d]
proxy_home [label="Proxy to Home HLR FSM",shape=box3d]
proxy [label="GSUP Proxy"]
proxy_mm -> proxy_home [constraint=false,dir=both,label="(FSM events)"]
}
hlr [label="Home HLR"]
msc -> proxy_mm [dir=both,label="MM related GSUP\n (immediate response\n if possible)"]
proxy_home -> hlr [dir=both,label="MM related GSUP\n (delayed)"]
msc -> proxy -> hlr [dir=both,label="non-MM GSUP",style=dashed]
}

View File

@@ -0,0 +1,78 @@
digraph G {
rankdir=TB
labelloc=t; label="HLR Proxy MM FSM"
top,top2,top3[shape=invtriangle,label="(1)"]
top -> READY
new [label="proxy_cache_subscr_new()\n/ proxy_cache_subscr_from_db()",shape=box]
READY [style=bold]
HIBERNATE [shape=note,label="Hibernate\n (keep in DB)"]
CLEAR [shape=box,label="Clear DB entry\n (discard completely)"]
WAIT_AUTH_TUPLES [style=bold]
WAIT_SUBSCR_DATA [style=bold]
WAIT_GSUP_ISD_RESULT [style=bold]
home_fsm [label="Proxy to Home HLR FSM",shape=box3d]
{rank=same;READY,home_fsm}
new -> {READY,home_fsm}
READY -> {event_lu_req,event_auth_info_req} [arrowhead=none]
event_auth_info_req [shape=rarrow,label="Rx GSUP\nSend Auth Info Request\nfrom MSC"]
event_auth_info_req -> junction_send_auth_info_req
junction_send_auth_info_req [shape=diamond,label="Unused\nauth tuples\navailable?"]
junction_send_auth_info_req -> action_send_auth_info [label="yes"]
junction_send_auth_info_req -> emit_need_tuples [label="no"]
emit_need_tuples [shape=lpromoter,label="emit\n HOME_EV_CHECK_TUPLES\n to Home FSM"]
emit_need_tuples->WAIT_AUTH_TUPLES
WAIT_AUTH_TUPLES -> rx_ev_rx_auth_tuples [arrowhead=none]
rx_ev_rx_auth_tuples [shape=rpromoter,label="receive\n MM_EV_RX_AUTH_TUPLES"]
rx_ev_rx_auth_tuples -> action_send_auth_info
action_send_auth_info [shape=larrow,label="Tx GSUP\nSend Auth Info Result\nwith fresh auth tuples\n to MSC"]
action_send_auth_info -> emit_check_tuples
emit_check_tuples [shape=lpromoter,label="emit\n HOME_EV_CHECK_TUPLES\n to Home FSM"]
emit_check_tuples -> top2
WAIT_AUTH_TUPLES -> junction_check_auth_fallback [label="Timeout",style=dashed]
junction_check_auth_fallback -> action_do_auth_fallback [label="yes",style=dashed]
action_do_auth_fallback [shape=larrow,label="Tx GSUP\nSend Auth Info Result\nwith recycled auth tuple\n(GSM AKA only)"]
junction_check_auth_fallback [shape=diamond,label="Re-usable\nauth tuples\navailable?"]
junction_check_auth_fallback -> action_fail_auth [label="no",style=dashed]
action_fail_auth [shape=larrow,label="Tx GSUP\nSend Auth Info Error\npending re-connection to\nthe home HLR"]
{action_do_auth_fallback,action_fail_auth} -> top2 [style=dashed]
event_lu_req [shape=rarrow,label="Rx GSUP\nUpdate Location Request\nfrom MSC"]
event_lu_req -> emit_lu_req
emit_lu_req [shape=lpromoter,label="emit\n HOME_EV_CONFIRM_LU"];
emit_lu_req -> junction_check_subscriber_data
junction_check_subscriber_data [shape=diamond,label="Subscriber\nData\nknown?"]
junction_check_subscriber_data -> WAIT_SUBSCR_DATA [label=no]
WAIT_SUBSCR_DATA -> rx_ev_subscr_data [arrowhead=none]
rx_ev_subscr_data [shape=rpromoter,label="receive\n MM_EV_RX_SUBSCR_DATA"];
rx_ev_subscr_data -> action_subscr_data_req
junction_check_subscriber_data -> action_subscr_data_req [label="yes"]
action_subscr_data_req [shape=larrow,label="Tx GSUP\n Insert Subscriber Data\n Request to MSC"]
action_subscr_data_req -> WAIT_GSUP_ISD_RESULT
WAIT_GSUP_ISD_RESULT -> tx_gsup_isd_res [arrowhead=none]
tx_gsup_isd_res [shape=rarrow,label="Rx GSUP\n Insert Subscriber Data Result\nfrom MSC"]
tx_gsup_isd_res -> top3
{WAIT_GSUP_ISD_RESULT,WAIT_SUBSCR_DATA} -> action_lu_reject [label="Timeout",style=dashed]
action_lu_reject [shape=larrow,label="Tx GSUP\nUpdate Location Error\nto MSC\npending reconnect of home HLR"]
action_lu_reject -> top3 [style=dashed]
READY -> HIBERNATE [label="Timeout"]
READY -> rx_ev_subscr_invalid [arrowhead=none]
rx_ev_subscr_invalid[shape=rpromoter,label="receive\n MM_EV_SUBSCR_INVALID"]
rx_ev_subscr_invalid -> tx_purge_req
tx_purge_req [shape=larrow,label="Tx GSUP\nPurge MS Request"]
tx_purge_req -> note_purge [style=dotted]
note_purge [shape=note,label="Don't care about\nPurge MS Result"]
tx_purge_req -> CLEAR
{CLEAR,HIBERNATE} -> TERM
TERM[shape=octagon][style=bold]
}

View File

@@ -0,0 +1,97 @@
digraph G {
rankdir=TB
labelloc=t; label="HLR Proxy to Home HLR FSM"
top,to_top1,to_top2,to_top3,to_top4,to_top5[shape=invtriangle,label="(A)"]
top->junction_resolve_home_hlr
at_clear,to_clear1,to_clear2 [shape=invtriangle,label="(X)"]
mm_fsm [shape=box3d,label="Proxy MM FSM"]
mm_fsm -> junction_resolve_home_hlr [style=invisible,arrowhead=none]
WAIT_HOME_HLR_RESOLVED [style=bold]
WAIT_UPDATE_LOCATION_RESULT [style=bold]
WAIT_SEND_AUTH_INFO_RESULT [style=bold]
IDLE [style=bold]
CLEAR [style=bold]
new [label="proxy_cache_subscr_new()\n/ proxy_cache_subscr_from_db()",shape=box]
{
rank=same;
junction_resolve_home_hlr [shape=diamond,label="Home HLR\n known?"]
junction_confirm_home_hlr [shape=diamond,label="mslookup\n for home HLR\n very old?"]
junction_update_location [shape=diamond,label="Did MM FSM emit another\n HOME_EV_CONFIRM_LU?"]
junction_auth_info [shape=diamond,label="Auth tuple\ncache full of\nfresh tuples?"]
}
new -> {junction_resolve_home_hlr, mm_fsm}
junction_resolve_home_hlr -> junction_confirm_home_hlr [label="known"]
junction_resolve_home_hlr -> action_mslookup [label="wat"]
action_mslookup [shape=larrow,label="start mslookup"]
action_mslookup -> WAIT_HOME_HLR_RESOLVED
WAIT_HOME_HLR_RESOLVED -> rx_mslookup_res [arrowhead=none]
rx_mslookup_res [shape=rarrow,label="home HLR\n resolved"]
rx_mslookup_res -> to_top1
WAIT_HOME_HLR_RESOLVED -> CLEAR [style=dashed,label=Timeout]
junction_update_location -> action_lu_req [label="need data\n/\nneed to confirm LU"]
action_lu_req [shape=larrow,label="Tx GSUP Update Location Req\nto home HLR"]
action_lu_req -> WAIT_UPDATE_LOCATION_RESULT
WAIT_UPDATE_LOCATION_RESULT->rx_isd [arrowhead=none]
rx_isd [shape=rarrow,label="Rx GSUP\n Insert Subscriber\n Data Request\nfrom home HLR"]
rx_isd -> emit_rx_subscr_data
emit_rx_subscr_data [shape=lpromoter,label="emit\n MM_EV_RX_SUBSCR_DATA"]
emit_rx_subscr_data->action_tx_isd_res
action_tx_isd_res -> WAIT_UPDATE_LOCATION_RESULT
action_tx_isd_res [shape=larrow,label="Tx GSUP Insert Subscriber Data Result\nto home HLR"]
WAIT_UPDATE_LOCATION_RESULT -> rx_lu_res [arrowhead=none]
rx_lu_res [shape=rarrow,label="Rx GSUP Update\n Location Result\nfrom home HLR"]
rx_lu_res -> to_top2
junction_update_location -> junction_auth_info [label="data known,\nLU confirmed"]
WAIT_UPDATE_LOCATION_RESULT->junction_lu_failed [label="Timeout "]
junction_lu_failed [shape=diamond,label="has home HLR\never confirmed\na LU before?"];
junction_lu_failed -> to_clear2 [label="never\nconfirmed",style=dashed]
junction_lu_failed -> note_lu_failed [label="has\nconfirmed\nbefore"]
note_lu_failed [shape=note,label="Home HLR is\ntemporarily unreachable,\n just carry on."]
note_lu_failed -> to_top3
junction_confirm_home_hlr -> action_mslookup_confirm [label="very old"]
junction_confirm_home_hlr -> junction_update_location [label="fair enough"]
action_mslookup_confirm [shape=larrow,label="start mslookup"]
note_mslookup_confirm [shape=note,label="Result evaluated in IDLE state.\nIf no mslookup result comes\n back, the home HLR is\ntemporarily unreachable:\ncontinue anyway.\nThis is sanity checking for\na *modified* home HLR"]
action_mslookup_confirm -> note_mslookup_confirm [arrowhead=none,style=dotted]
action_mslookup_confirm -> junction_update_location
junction_auth_info -> action_sai_req [label="need more tuples"]
junction_auth_info -> set_idle_timeout [label="cache is fresh"]
action_sai_req [shape=larrow,label="Tx GSUP\n Send Auth Info Request\n to home HLR"]
action_sai_req -> WAIT_SEND_AUTH_INFO_RESULT
WAIT_SEND_AUTH_INFO_RESULT->rx_sai_res[arrowhead=none]
rx_sai_res[shape=rarrow,label="Rx GSUP\n Send Auth Info\n Result\nfrom home HLR"]
rx_sai_res -> emit_rx_tuples
emit_rx_tuples [shape=lpromoter,label="emit\n MM_EV_RX_AUTH_TUPLES"]
emit_rx_tuples -> to_top4
WAIT_SEND_AUTH_INFO_RESULT->set_idle_timeout[label="Timeout",style=dashed]
set_idle_timeout [shape=diamond,label="Select IDLE\n timeout:\nmin(\nretry tuples,\n confirm mslookup)"]
set_idle_timeout -> IDLE
IDLE -> rx_mslookup_confirm_res [arrowhead=none]
rx_mslookup_confirm_res [shape=rarrow,label="Rx mslookup result"]
rx_mslookup_confirm_res -> junction_compare_home_hlr
junction_compare_home_hlr [shape=diamond,label="mslookup result\n matches home HLR\n on record?"]
junction_compare_home_hlr -> set_idle_timeout [label="matches"]
junction_compare_home_hlr -> to_clear1 [label="mismatch",style=dashed]
IDLE -> to_top5 [label="Timeout"]
IDLE -> {rx_ev_check_tuples,rx_ev_confirm_lu} [arrowhead=none]
rx_ev_check_tuples [shape=rpromoter,label="receive\n HOME_EV_CHECK_TUPLES"]
rx_ev_confirm_lu [shape=rpromoter,label="receive\n HOME_EV_CONFIRM_LU"]
{rx_ev_check_tuples,rx_ev_confirm_lu} -> to_top5
at_clear -> CLEAR
CLEAR -> emit_subscr_invalid -> TERM
emit_subscr_invalid [shape=lpromoter,label="emit\n MM_EV_SUBSCR_INVALID"]
TERM[shape=octagon][style=bold]
}

View File

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

View File

@@ -32,35 +32,35 @@ 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);
const char *osmo_gsup_peer_id_to_str_c(void *ctx, const struct osmo_gsup_peer_id *gsup_peer_id);

View File

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

View File

@@ -14,6 +14,8 @@ noinst_HEADERS = \
mslookup_server.h \
mslookup_server_mdns.h \
proxy.h \
proxy_mm.h \
proxy_db.h \
rand.h \
remote_hlr.h \
timestamp.h \

View File

@@ -3,19 +3,12 @@
#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>
struct hlr;
enum stmt_idx {
DB_STMT_SEL_ALL,
DB_STMT_SEL_ALL_ORDER_LAST_SEEN,
DB_STMT_SEL_FILTER_MSISDN,
DB_STMT_SEL_FILTER_IMSI,
DB_STMT_SEL_FILTER_IMEI,
DB_STMT_SEL_FILTER_CS,
DB_STMT_SEL_FILTER_PS,
DB_STMT_SEL_BY_IMSI,
DB_STMT_SEL_BY_MSISDN,
DB_STMT_SEL_BY_ID,
@@ -159,9 +152,6 @@ int db_subscr_update_imei_by_imsi(struct db_context *dbc, const char* imsi, cons
int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi);
int db_subscr_exists_by_msisdn(struct db_context *dbc, const char *msisdn);
int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,
void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,
int *count, const char **err);
int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
struct hlr_subscriber *subscr);
int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
@@ -177,8 +167,8 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
bool purge_val, bool is_ps);
int db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr, unsigned int *ind);
int db_ind_del(struct db_context *dbc, const struct osmo_cni_peer_id *vlr);
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);
/*! Call sqlite3_column_text() and copy result to a char[].
* \param[out] buf A char[] used as sizeof() arg(!) and osmo_strlcpy() target.

View File

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

View File

@@ -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
@@ -42,6 +42,8 @@ struct osmo_gsup_conn {
//struct oap_state oap_state;
struct tlv_parsed ccm;
unsigned int auc_3g_ind; /*!< IND index used for UMTS AKA SQN */
/* Set when Location Update is received: */
bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */
bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */
@@ -72,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);

View File

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

View File

@@ -20,9 +20,12 @@
#pragma once
#include <time.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/gsupclient/cni_peer_id.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gsupclient/gsup_peer_id.h>
#include <osmocom/hlr/timestamp.h>
struct osmo_gsup_req;
@@ -65,6 +68,8 @@ struct proxy_subscr {
char msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
struct osmo_sockaddr_str remote_hlr_addr;
struct proxy_subscr_domain_state cs, ps;
struct osmo_fsm_inst *mm_fi;
struct osmo_fsm_inst *to_home_fi;
};
void proxy_init(struct osmo_gsup_server *gsup_server_to_vlr);

View File

@@ -0,0 +1,37 @@
/* Copyright 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
*
* Author: Neels Hofmeyr <neels@hofmeyr.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* If a subscriber from a remote site has successfully attached at this local site, and the link to the subscriber's
* home HLR has succeeded, this will try to bridge the time of temporary link failure to that home HLR.
* Tasks to take over from the unreachable home HLR:
* - Resend known auth tuples on OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST.
* - ...?
*
*
*/
/* Data stored per subscriber */
struct proxy_broken_link_cache {
struct osmo_auth_vector auth_vectors[OSMO_GSUP_MAX_NUM_AUTH_INFO];
size_t num_auth_vectors;
timestamp_t last_update;
};

View File

@@ -0,0 +1,54 @@
#pragma once
#include <osmocom/core/linuxlist.h>
#include <osmocom/hlr/proxy.h>
enum proxy_mm_fsm_event {
PROXY_MM_EV_SUBSCR_INVALID,
PROXY_MM_EV_RX_GSUP_LU,
PROXY_MM_EV_RX_GSUP_SAI,
PROXY_MM_EV_RX_SUBSCR_DATA,
PROXY_MM_EV_RX_GSUP_ISD_RESULT,
PROXY_MM_EV_RX_AUTH_TUPLES,
};
enum proxy_to_home_fsm_event {
PROXY_TO_HOME_EV_HOME_HLR_RESOLVED,
PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ,
PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT,
PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT,
PROXY_TO_HOME_EV_CHECK_TUPLES,
PROXY_TO_HOME_EV_CONFIRM_LU,
};
extern struct llist_head proxy_mm_list;
struct proxy_mm_auth_cache {
struct llist_head entry;
uint64_t db_id;
struct osmo_auth_vector auth_vectors[OSMO_GSUP_MAX_NUM_AUTH_INFO];
size_t num_auth_vectors;
unsigned int sent_to_vlr_count;
};
struct proxy_mm {
struct llist_head entry;
struct osmo_gsup_peer_id vlr_name;
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
bool is_ps;
struct osmo_fsm_inst *mm_fi;
struct osmo_fsm_inst *to_home_fi;
struct llist_head auth_cache;
};
struct proxy_mm *proxy_mm_alloc(const struct osmo_gsup_peer_id *vlr_name,
bool is_ps,
const char *imsi);
void proxy_mm_add_auth_vectors(struct proxy_mm *proxy_mm,
const struct osmo_auth_vector *auth_vectors, size_t num_auth_vectors);
struct proxy_mm_auth_cache *proxy_mm_get_auth_vectors(struct proxy_mm *proxy_mm);
void proxy_mm_use_auth_vectors(struct proxy_mm *proxy_mm, struct proxy_mm_auth_cache *ac);
void proxy_mm_discard_auth_vectors(struct proxy_mm *proxy_mm, struct proxy_mm_auth_cache *ac);
bool proxy_mm_subscriber_data_known(const struct proxy_mm *proxy_mm);

View File

@@ -87,6 +87,27 @@ CREATE TABLE ind (
UNIQUE (vlr)
);
CREATE TABLE proxy_auth_cache {
id INTEGER PRIMARY KEY,
vlr TEXT NOT NULL,
imsi TEXT NOT NULL,
sent_count INTEGER
};
CREATE TABLE proxy_auth_cache_vector {
-- belongs to this proxy_auth_cache entry
proxy_auth_cache_id INTEGER,
rand VARCHAR(32),
autn VARCHAR(32),
ck VARCHAR(32),
ik VARCHAR(32),
res VARCHAR(32),
kc[8] VARCHAR(16),
sres[4] VARCHAR(8),
-- enum osmo_sub_auth_type bitmask
auth_types INTEGER
};
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
-- Set HLR database schema version number

View File

@@ -54,6 +54,9 @@ osmo_hlr_SOURCES = \
gsup_send.c \
hlr_ussd.c \
proxy.c \
proxy_mm.c \
proxy_to_home.c \
proxy_db.c \
dgsm.c \
remote_hlr.c \
lu_fsm.c \
@@ -82,7 +85,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 = \

View File

@@ -51,14 +51,6 @@
"sgsn_via_proxy"
static const char *stmt_sql[] = {
[DB_STMT_SEL_ALL] = "SELECT " SEL_COLUMNS " FROM subscriber;",
[DB_STMT_SEL_ALL_ORDER_LAST_SEEN] = "SELECT " SEL_COLUMNS " FROM subscriber "
"WHERE last_lu_seen IS NOT NULL ORDER BY last_lu_seen;",
[DB_STMT_SEL_FILTER_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn LIKE $search ORDER BY msisdn",
[DB_STMT_SEL_FILTER_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi LIKE $search ORDER BY imsi",
[DB_STMT_SEL_FILTER_IMEI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imei LIKE $search ORDER BY imei",
[DB_STMT_SEL_FILTER_CS] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE nam_cs = $search ORDER BY last_lu_seen",
[DB_STMT_SEL_FILTER_PS] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE nam_ps = $search ORDER BY last_lu_seen",
[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
[DB_STMT_SEL_BY_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn = ?",
[DB_STMT_SEL_BY_ID] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE id = ?",
@@ -237,7 +229,7 @@ void db_close(struct db_context *dbc)
static int db_run_statements(struct db_context *dbc, const char **statements, size_t statements_count)
{
int rc = 0;
int rc;
int i;
for (i = 0; i < statements_count; i++) {
const char *stmt_str = statements[i];

View File

@@ -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)
@@ -264,11 +264,11 @@ int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
switch (aud->algo) {
case OSMO_AUTH_ALG_NONE:
case OSMO_AUTH_ALG_MILENAGE:
case OSMO_AUTH_ALG_XOR:
break;
case OSMO_AUTH_ALG_COMP128v1:
case OSMO_AUTH_ALG_COMP128v2:
case OSMO_AUTH_ALG_COMP128v3:
case OSMO_AUTH_ALG_XOR:
LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
" auth algo not suited for 3G: %s\n",
osmo_auth_alg_name(aud->algo));
@@ -625,94 +625,6 @@ int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
return rc;
}
/*! Retrieve subscriber data from the HLR database.
* \param[in,out] dbc database context.
* \param[in] filter_type ASCII string of identifier type to search.
* \param[in] filter ASCII string to search.
* \param[in] get_cb pointer to call back function for data.
* \param[in,out] data pointer to pass to callback function.
* \param[in,out] count counter for number of matched subscribers.
* \param[in,our] err
* \returns 0 on success, -ENOENT if no subscriber was found, -EIO on
* database error.
*/
int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,
void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,
int *count, const char **err)
{
sqlite3_stmt *stmt;
char search[256];
int rc;
struct hlr_subscriber subscr;
bool show_ls = false;
if (!filter_type) {
stmt = dbc->stmt[DB_STMT_SEL_ALL];
} else if (strcmp(filter_type, "imei") == 0) {
stmt = dbc->stmt[DB_STMT_SEL_FILTER_IMEI];
} else if (strcmp(filter_type, "imsi") == 0) {
stmt = dbc->stmt[DB_STMT_SEL_FILTER_IMSI];
} else if (strcmp(filter_type, "msisdn") == 0) {
stmt = dbc->stmt[DB_STMT_SEL_FILTER_MSISDN];
} else if (strcmp(filter_type, "cs") == 0) {
stmt = dbc->stmt[DB_STMT_SEL_FILTER_CS];
} else if (strcmp(filter_type, "ps") == 0) {
stmt = dbc->stmt[DB_STMT_SEL_FILTER_PS];
} else if (strcmp(filter_type, "last_lu_seen") == 0) {
show_ls = true;
stmt = dbc->stmt[DB_STMT_SEL_ALL_ORDER_LAST_SEEN];
} else {
return -EIO;
}
if (filter_type && filter && strcmp(filter_type, "last_lu_seen") != 0) {
if (strcmp(filter, "on") == 0) {
sprintf(search, "%s", "1");
} else if (strcmp(filter, "off") == 0) {
sprintf(search, "%s", "0");
} else {
sprintf(search, "%%%s%%", filter);
}
if (!db_bind_text(stmt, "$search", search)) {
*err = sqlite3_errmsg(dbc->db);
return -EIO;
}
}
rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE) {
db_remove_reset(stmt);
*err = "No matching subscriber(s)";
return -ENOENT;
}
while (rc == SQLITE_ROW) {
subscr = (struct hlr_subscriber){
.id = sqlite3_column_int64(stmt, 0),};
copy_sqlite3_text_to_buf(subscr.imsi, stmt, 1);
copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
copy_sqlite3_text_to_buf(subscr.imei, stmt, 3);
subscr.nam_cs = sqlite3_column_int(stmt, 9);
subscr.nam_ps = sqlite3_column_int(stmt, 10);
if (show_ls)
parse_last_lu_seen(&subscr.last_lu_seen, (const char *)sqlite3_column_text(stmt, 14),
subscr.imsi, "CS");
get_cb(&subscr, data);
rc = sqlite3_step(stmt);
(*count)++;
}
db_remove_reset(stmt);
if (rc != SQLITE_DONE) {
*err = sqlite3_errmsg(dbc->db);
LOGP(DAUC, LOGL_ERROR, "Cannot read subscribers from db:: %s\n", *err);
return rc;
}
*err = NULL;
return 0;
}
/*! Retrieve subscriber data from the HLR database.
* \param[in,out] dbc database context.
* \param[in] id ID of the subscriber in the HLR db.
@@ -1027,14 +939,14 @@ out:
return ret;
}
int _db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr,
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_CNI_PEER_ID_IPA_NAME:
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));
@@ -1043,8 +955,8 @@ int _db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr,
vlr_name = (const char*)vlr->ipa_name.val;
break;
default:
LOGP(DDB, LOGL_ERROR, "Unsupported osmo_cni_peer_id type: %s\n",
osmo_cni_peer_id_type_name(vlr->type));
LOGP(DDB, LOGL_ERROR, "Unsupported osmo_gsup_peer_id type: %s\n",
osmo_gsup_peer_id_type_name(vlr->type));
return -ENOTSUP;
}
@@ -1066,12 +978,12 @@ int _db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr,
return _db_ind_get(dbc, vlr_name, ind);
}
int db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr, unsigned int *ind)
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_cni_peer_id *vlr)
int db_ind_del(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr)
{
return _db_ind(dbc, vlr, NULL, true);
}

View File

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

View File

@@ -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,
@@ -442,7 +442,7 @@ int config_write_mslookup(struct vty *vty)
msc = mslookup_server_msc_get(&mslookup_server_msc_wildcard, false);
if (msc)
config_write_msc_services(vty, " ", msc);
config_write_msc_services(vty, " ", msc);
llist_for_each_entry(msc, &g_hlr->mslookup.server.local_site_services, entry) {
if (!osmo_ipa_name_cmp(&mslookup_server_msc_wildcard, &msc->name))

View File

@@ -18,8 +18,6 @@
*/
#include <errno.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
@@ -67,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. */
@@ -82,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;
}
@@ -113,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;
}
@@ -139,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;
@@ -276,7 +274,7 @@ static int osmo_gsup_server_ccm_cb(struct ipa_server_conn *conn,
{
struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data;
uint8_t *addr = NULL;
int addr_len;
size_t addr_len;
LOGP(DLGSUP, LOGL_INFO, "CCM Callback\n");
@@ -317,17 +315,41 @@ static int osmo_gsup_server_closed_cb(struct ipa_server_conn *conn)
return 0;
}
static void update_fd_settings(int fd)
/* Add conn to the clients list in a way that conn->auc_3g_ind takes the lowest
* unused integer and the list of clients remains sorted by auc_3g_ind.
* Keep this function non-static to allow linking in a unit test. */
void osmo_gsup_server_add_conn(struct llist_head *clients,
struct osmo_gsup_conn *conn)
{
int ret;
int val;
struct osmo_gsup_conn *c;
struct osmo_gsup_conn *prev_conn;
/*TODO: Set keepalive settings here. See OS#4312 */
c = llist_first_entry_or_null(clients, struct osmo_gsup_conn, list);
val = 1;
ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
if (ret < 0)
LOGP(DLGSUP, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
/* Is the first index, 0, unused? */
if (!c || c->auc_3g_ind > 0) {
conn->auc_3g_ind = 0;
llist_add(&conn->list, clients);
return;
}
/* Look for a gap later on */
prev_conn = NULL;
llist_for_each_entry(c, clients, list) {
/* skip first item, we know it has auc_3g_ind == 0. */
if (!prev_conn) {
prev_conn = c;
continue;
}
if (c->auc_3g_ind > prev_conn->auc_3g_ind + 1)
break;
prev_conn = c;
}
OSMO_ASSERT(prev_conn);
conn->auc_3g_ind = prev_conn->auc_3g_ind + 1;
llist_add(&conn->list, &prev_conn->list);
}
/* a client has connected to the server socket and we have accept()ed it */
@@ -349,11 +371,10 @@ static int osmo_gsup_server_accept_cb(struct ipa_server_link *link, int fd)
/* link data structure with server structure */
conn->server = gsups;
llist_add_tail(&conn->list, &gsups->clients);
osmo_gsup_server_add_conn(&gsups->clients, conn);
LOGP(DLGSUP, LOGL_INFO, "New GSUP client %s:%d\n", conn->conn->addr, conn->conn->port);
update_fd_settings(fd);
LOGP(DLGSUP, LOGL_INFO, "New GSUP client %s:%d (IND=%u)\n",
conn->conn->addr, conn->conn->port, conn->auc_3g_ind);
/* request the identity of the client */
rc = ipa_ccm_send_id_req(fd);
@@ -440,7 +461,7 @@ int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
/**
* Populate a gsup message structure with an Insert Subscriber Data Message.
* All required memory buffers for data pointed to by pointers in struct osmo_gsup_message
* All required memory buffers for data pointed to by pointers in struct omso_gsup_message
* must be allocated by the caller and should have the same lifetime as the gsup parameter.
*
* \param[out] gsup The gsup message to populate.
@@ -493,7 +514,7 @@ int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup,
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;
@@ -502,22 +523,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;

View File

@@ -1,7 +1,7 @@
# This is _NOT_ the library release version, it's an API version.
# Please read chapter "Library interface versions" of the libtool documentation
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
LIBVERSION=1:0:1
LIBVERSION=0:0:0
AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include \
$(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOABIS_CFLAGS)
@@ -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)

View File

@@ -31,8 +31,6 @@
#include <errno.h>
#include <string.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
static void start_test_procedure(struct osmo_gsup_client *gsupc);
@@ -131,19 +129,6 @@ static void gsup_client_oap_register(struct osmo_gsup_client *gsupc)
client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
}
static void update_fd_settings(int fd)
{
int ret;
int val;
/*TODO: Set keepalive settings here. See OS#4312 */
val = 1;
ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
if (ret < 0)
LOGP(DLGSUP, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
}
static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
{
struct osmo_gsup_client *gsupc = link->data;
@@ -154,7 +139,6 @@ static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
gsupc->is_connected = up;
if (up) {
update_fd_settings(link->ofd->fd);
start_test_procedure(gsupc);
if (gsupc->oap_state.state == OSMO_OAP_INITIALIZED)

View File

@@ -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);
}
@@ -107,44 +107,44 @@ const char *osmo_ipa_name_to_str_c(void *ctx, const struct osmo_ipa_name *ipa_na
return osmo_escape_str_c(ctx, (char*)ipa_name->val, len);
}
bool osmo_cni_peer_id_is_empty(const struct osmo_cni_peer_id *cni_peer_id)
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,42 +152,36 @@ 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 == b)
return 0;
if (!a)
return -1;
if (!b)
return 1;
if (a->type != b->type)
return OSMO_CMP(a->type, b->type);
switch (a->type) {
case OSMO_CNI_PEER_ID_IPA_NAME:
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)
/* Call osmo_gsup_peer_id_to_str_c with OTC_SELECT */
const char *osmo_gsup_peer_id_to_str(const struct osmo_gsup_peer_id *gpi)
{
return osmo_cni_peer_id_to_str_c(OTC_SELECT, cpi);
return osmo_gsup_peer_id_to_str_c(OTC_SELECT, gpi);
}
/* 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_c(void *ctx, 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_c(ctx, &gpi->ipa_name);
default:
return talloc_strdup(ctx, osmo_cni_peer_id_type_name(cpi->type));
return talloc_strdup(ctx, osmo_gsup_peer_id_type_name(gpi->type));
}
}

View File

@@ -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)
{
@@ -107,15 +52,9 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
struct osmo_gsup_req *req;
int rc;
if (!from_peer) {
LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from NULL peer is not allowed\n");
msgb_free(msg);
return NULL;
}
if (!msgb_l2(msg) || !msgb_l2len(msg)) {
LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: missing or empty L2 data\n",
osmo_cni_peer_id_to_str(from_peer));
osmo_gsup_peer_id_to_str(from_peer));
msgb_free(msg);
return NULL;
}
@@ -127,10 +66,11 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
req->msg = msg;
req->send_response_cb = send_response_cb;
req->cb_data = cb_data;
req->source_name = *from_peer;
if (from_peer)
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;
}
@@ -138,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;
}
@@ -164,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");
@@ -176,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)
{
@@ -225,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)
{
@@ -247,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)
{

View File

@@ -31,7 +31,6 @@
#include <osmocom/vty/command.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/ports.h>
#include <osmocom/vty/cpu_sched_vty.h>
#include <osmocom/ctrl/control_vty.h>
#include <osmocom/gsm/apn.h>
#include <osmocom/gsm/gsm48_ie.h>
@@ -39,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>
@@ -269,9 +268,9 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val,
if (nam_val)
return 0;
if (subscr->vlr_number[0] && !osmo_ipa_name_set_str(&vlr_name, subscr->vlr_number))
if (subscr->vlr_number && osmo_ipa_name_set_str(&vlr_name, subscr->vlr_number))
osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &vlr_name, &gsup_del_data);
if (subscr->sgsn_number[0] && !osmo_ipa_name_set_str(&vlr_name, subscr->sgsn_number))
if (subscr->sgsn_number && osmo_ipa_name_set_str(&vlr_name, subscr->sgsn_number))
osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &vlr_name, &gsup_del_data);
return 0;
}
@@ -304,7 +303,7 @@ static int rx_send_auth_info(struct osmo_gsup_req *req)
LOG_GSUP_REQ(req, LOGL_ERROR,
"Unable to determine 3G auth IND for source %s (rc=%d),"
" generating tuples with IND = 0\n",
osmo_cni_peer_id_to_str(&req->source_name), rc);
osmo_gsup_peer_id_to_str(&req->source_name), rc);
auc_3g_ind = 0;
}
@@ -586,10 +585,6 @@ static void print_help()
printf(" -U --db-upgrade Allow HLR database schema upgrades.\n");
printf(" -C --db-check Quit after opening (and upgrading) the database.\n");
printf(" -V --version Print the version of OsmoHLR.\n");
printf("\nVTY reference generation:\n");
printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
}
static struct {
@@ -605,37 +600,10 @@ static struct {
.db_upgrade = false,
};
static void handle_long_options(const char *prog_name, const int long_option)
{
static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
switch (long_option) {
case 1:
vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
if (vty_ref_mode < 0) {
fprintf(stderr, "%s: Unknown VTY reference generation "
"mode '%s'\n", prog_name, optarg);
exit(2);
}
break;
case 2:
fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
exit(0);
default:
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
exit(2);
}
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static int long_option = 0;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"config-file", 1, 0, 'c'},
@@ -648,8 +616,6 @@ static void handle_options(int argc, char **argv)
{"db-upgrade", 0, 0, 'U' },
{"db-check", 0, 0, 'C' },
{"version", 0, 0, 'V' },
{"vty-ref-mode", 1, &long_option, 1},
{"vty-ref-xml", 0, &long_option, 2},
{0, 0, 0, 0}
};
@@ -659,9 +625,6 @@ static void handle_options(int argc, char **argv)
break;
switch (c) {
case 0:
handle_long_options(argv[0], long_option);
break;
case 'h':
print_usage();
print_help();
@@ -775,10 +738,9 @@ int main(int argc, char **argv)
osmo_stats_init(hlr_ctx);
vty_init(&vty_info);
ctrl_vty_init(hlr_ctx);
handle_options(argc, argv);
hlr_vty_init();
dgsm_vty_init();
osmo_cpu_sched_vty_init(hlr_ctx);
handle_options(argc, argv);
rc = vty_read_config_file(cmdline_opts.config_file, NULL);
if (rc < 0) {

View File

@@ -25,7 +25,6 @@
#include <getopt.h>
#include <inttypes.h>
#include <string.h>
#include <errno.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/application.h>
@@ -71,9 +70,8 @@ static void print_help()
printf(" (All commands imply this if none exists yet.)\n");
printf("\n");
printf(" import-nitb-db <nitb.db> Add OsmoNITB db's subscribers to OsmoHLR db.\n");
printf(" Be aware that the import is somewhat lossy, only the IMSI,\n");
printf(" MSISDN, IMEI, nam_cs/ps, 2G auth data and last seen LU are set.\n");
printf(" The most recently associated IMEI from the Equipment table is used.\n");
printf(" Be aware that the import is lossy, only the\n");
printf(" IMSI, MSISDN, nam_cs/ps and 2G auth data are set.\n");
}
static void print_version(int print_copyright)
@@ -214,15 +212,9 @@ enum nitb_stmt {
static const char *nitb_stmt_sql[] = {
[NITB_SELECT_SUBSCR] =
"SELECT s.imsi, s.id, s.extension, s.authorized,"
" SUBSTR(e.imei,0,15), STRFTIME('%s', s.expire_lu)"
" FROM Subscriber s LEFT JOIN"
" (SELECT imei, subscriber_id, MAX(Equipment.updated) AS updated"
" FROM Equipment,EquipmentWatch"
" WHERE Equipment.id = EquipmentWatch.equipment_id"
" GROUP BY EquipmentWatch.subscriber_id) e"
" ON e.subscriber_id = s.id"
" ORDER by s.id",
"SELECT imsi, id, extension, authorized"
" FROM Subscriber"
" ORDER BY id",
[NITB_SELECT_AUTH_KEYS] =
"SELECT algorithm_id, a3a8_ki from authkeys"
" WHERE subscriber_id = $subscr_id",
@@ -230,65 +222,8 @@ static const char *nitb_stmt_sql[] = {
sqlite3_stmt *nitb_stmt[ARRAY_SIZE(nitb_stmt_sql)] = {};
enum hlr_db_stmt {
HLR_DB_STMT_SET_IMPLICIT_LU_BY_IMSI,
};
static const char *hlr_db_stmt_sql[] = {
[HLR_DB_STMT_SET_IMPLICIT_LU_BY_IMSI] =
"UPDATE subscriber SET last_lu_seen = datetime($last_lu, 'unixepoch') WHERE imsi = $imsi",
};
sqlite3_stmt *hlr_db_stmt[ARRAY_SIZE(hlr_db_stmt_sql)] = {};
size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out);
/*! Set a subscriber's LU timestamp in the HLR database.
* In normal operations there is never any need to explicitly
* update the value of last_lu_seen, so this function can live here.
*
* \param[in,out] dbc database context.
* \param[in] imsi ASCII string of IMSI digits
* \param[in] imei ASCII string of identifier digits, or NULL to remove the IMEI.
* \returns 0 on success, -ENOENT when the given subscriber does not exist,
* -EIO on database errors.
*/
int db_subscr_update_lu_by_imsi(struct db_context *dbc, const char* imsi, const int last_lu)
{
int rc, ret = 0;
sqlite3_stmt *stmt = hlr_db_stmt[HLR_DB_STMT_SET_IMPLICIT_LU_BY_IMSI];
if (!db_bind_text(stmt, "$imsi", imsi))
return -EIO;
if (last_lu && !db_bind_int(stmt, "$last_lu", last_lu))
return -EIO;
/* execute the statement */
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
LOGP(DAUC, LOGL_ERROR, "Update last_lu_seen for subscriber IMSI='%s': SQL Error: %s\n", imsi,
sqlite3_errmsg(dbc->db));
ret = -EIO;
goto out;
}
/* verify execution result */
rc = sqlite3_changes(dbc->db);
if (!rc) {
LOGP(DAUC, LOGL_ERROR, "Cannot update last_lu_seen for subscriber IMSI='%s': no such subscriber\n", imsi);
ret = -ENOENT;
} else if (rc != 1) {
LOGP(DAUC, LOGL_ERROR, "Update last_lu_seen for subscriber IMSI='%s': SQL modified %d rows (expected 1)\n",
imsi, rc);
ret = -EIO;
}
out:
db_remove_reset(stmt);
return ret;
}
void import_nitb_subscr_aud(sqlite3 *nitb_db, const char *imsi, int64_t nitb_id, int64_t hlr_id)
{
int rc;
@@ -362,7 +297,6 @@ void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
int64_t imsi;
char imsi_str[32];
bool authorized;
int last_lu_int;
imsi = sqlite3_column_int64(stmt, 0);
@@ -381,18 +315,8 @@ void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
nitb_id = sqlite3_column_int64(stmt, 1);
copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
authorized = sqlite3_column_int(stmt, 3) ? true : false;
copy_sqlite3_text_to_buf(subscr.imei, stmt, 4);
/* Default periodic LU was 30 mins and the expire_lu
* was twice that + 1 min
*/
last_lu_int = sqlite3_column_int(stmt, 5) - 3660;
db_subscr_update_msisdn_by_imsi(dbc, imsi_str, subscr.msisdn);
/* In case the subscriber was somehow never seen, invent an IMEI */
if (strlen(subscr.imei) == 14)
db_subscr_update_imei_by_imsi(dbc, imsi_str, subscr.imei);
db_subscr_update_lu_by_imsi(dbc, imsi_str, last_lu_int);
db_subscr_nam(dbc, imsi_str, authorized, true);
db_subscr_nam(dbc, imsi_str, authorized, false);
@@ -437,17 +361,6 @@ int import_nitb_db(void)
}
}
for (i = 0; i < ARRAY_SIZE(hlr_db_stmt_sql); i++) {
sql = hlr_db_stmt_sql[i];
rc = sqlite3_prepare_v2(g_hlr_db_tool_ctx->dbc->db, hlr_db_stmt_sql[i], -1,
&hlr_db_stmt[i], NULL);
if (rc != SQLITE_OK) {
LOGP(DDB, LOGL_ERROR, "OsmoHLR DB: Unable to prepare SQL statement '%s'\n", sql);
ret = -1;
goto out_free;
}
}
stmt = nitb_stmt[NITB_SELECT_SUBSCR];
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
@@ -474,7 +387,6 @@ int main(int argc, char **argv)
{
int rc;
int (*main_action)(void);
int i;
main_action = NULL;
g_hlr_db_tool_ctx = talloc_zero(NULL, struct hlr_db_tool_ctx);
@@ -518,11 +430,6 @@ int main(int argc, char **argv)
if (main_action)
rc = (*main_action)();
/* db_close will only finalize statments in g_hlr_db_tool_ctx->dbc->stmt
* it is ok to call finalize on NULL */
for (i = 0; i < ARRAY_SIZE(hlr_db_stmt); i++) {
sqlite3_finalize(hlr_db_stmt[i]);
}
db_close(g_hlr_db_tool_ctx->dbc);
log_fini();
exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);

View File

@@ -279,20 +279,19 @@ 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,
struct msgb *ss_msg)
bool final, struct msgb *ss_msg)
{
struct osmo_gsup_message resp;
struct osmo_gsup_message resp = {0};
int rc;
resp = (struct osmo_gsup_message) {
.message_type = gsup_msg_type,
.session_id = ss->session_id,
.session_state = ss->state,
};
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);
@@ -312,8 +311,7 @@ static int ss_tx_reject(struct ss_session *ss, int invoke_id, uint8_t problem_ta
LOGPSS(ss, LOGL_NOTICE, "Tx Reject(%u, 0x%02x, 0x%02x)\n", invoke_id,
problem_tag, problem_code);
OSMO_ASSERT(msg);
ss->state = OSMO_GSUP_SESSION_STATE_END;
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, msg);
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
}
#endif
@@ -322,16 +320,15 @@ static int ss_tx_to_ms_error(struct ss_session *ss, uint8_t invoke_id, uint8_t e
struct msgb *msg = gsm0480_gen_return_error(invoke_id, error_code);
LOGPSS(ss, LOGL_NOTICE, "Tx ReturnError(%u, 0x%02x)\n", invoke_id, error_code);
OSMO_ASSERT(msg);
ss->state = OSMO_GSUP_SESSION_STATE_END;
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, msg);
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
}
static int ss_tx_to_ms_ussd_7bit(struct ss_session *ss, uint8_t invoke_id, const char *text)
static int ss_tx_to_ms_ussd_7bit(struct ss_session *ss, bool final, uint8_t invoke_id, const char *text)
{
struct msgb *msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text);
LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text);
OSMO_ASSERT(msg);
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, msg);
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, final, msg);
}
/***********************************************************************
@@ -347,8 +344,6 @@ static int handle_ussd_own_msisdn(struct ss_session *ss,
char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
int rc;
ss->state = OSMO_GSUP_SESSION_STATE_END;
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
switch (rc) {
case 0:
@@ -356,7 +351,7 @@ static int handle_ussd_own_msisdn(struct ss_session *ss,
snprintf(buf, sizeof(buf), "You have no MSISDN!");
else
snprintf(buf, sizeof(buf), "Your extension is %s", subscr.msisdn);
ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, buf);
break;
case -ENOENT:
ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
@@ -374,21 +369,7 @@ static int handle_ussd_own_imsi(struct ss_session *ss,
{
char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
snprintf(buf, sizeof(buf), "Your IMSI is %s", ss->imsi);
ss->state = OSMO_GSUP_SESSION_STATE_END;
ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
return 0;
}
/* This handler just keeps the session idle unless the guard timer expires. */
static int handle_ussd_test_idle(struct ss_session *ss,
const struct osmo_gsup_message *gsup,
const struct ss_request *req)
{
char buf[GSM0480_USSD_7BIT_STRING_LEN + 1];
snprintf(buf, sizeof(buf), "Keeping your session idle, it will expire "
"at most in %u seconds.", g_hlr->ncss_guard_timeout);
ss->state = OSMO_GSUP_SESSION_STATE_CONTINUE;
ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, buf);
return 0;
}
@@ -402,10 +383,6 @@ static const struct hlr_iuse hlr_iuses[] = {
.name = "own-imsi",
.handle_ussd = handle_ussd_own_imsi,
},
{
.name = "test-idle",
.handle_ussd = handle_ussd_test_idle,
},
};
const struct hlr_iuse *iuse_find(const char *name)
@@ -439,22 +416,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);
}
@@ -519,9 +496,8 @@ static int handle_ussd(struct ss_session *ss, bool is_euse_originated, const str
} else {
/* Handle internally */
ss->u.iuse->handle_ussd(ss, gsup, req);
/* Release session if the handler has changed its state to END */
if (ss->state == OSMO_GSUP_SESSION_STATE_END)
ss_session_free(ss);
/* Release session immediately */
ss_session_free(ss);
}
}
@@ -545,10 +521,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;
}
@@ -590,7 +566,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;

View File

@@ -102,8 +102,6 @@ static int config_write_hlr_gsup(struct vty *vty)
vty_out(vty, " gsup%s", VTY_NEWLINE);
if (g_hlr->gsup_bind_addr)
vty_out(vty, " bind ip %s%s", g_hlr->gsup_bind_addr, VTY_NEWLINE);
if (g_hlr->gsup_unit_name.serno)
vty_out(vty, " ipa-name %s%s", g_hlr->gsup_unit_name.serno, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -116,8 +114,8 @@ static void show_one_conn(struct vty *vty, const struct osmo_gsup_conn *conn)
rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &name, IPAC_IDTAG_SERNR);
OSMO_ASSERT(rc);
vty_out(vty, " '%s' from %s:%5u, CS=%u, PS=%u%s",
name, isc->addr, isc->port, conn->supports_cs, conn->supports_ps,
vty_out(vty, " '%s' from %s:%5u, CS=%u, PS=%u, 3G_IND=%u%s",
name, isc->addr, isc->port, conn->supports_cs, conn->supports_ps, conn->auc_3g_ind,
VTY_NEWLINE);
}
@@ -176,11 +174,10 @@ 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|test-idle)"
#define INT_CHOICE "(own-msisdn|own-imsi)"
#define INT_STR "Internal USSD Handler\n" \
"Respond with subscribers' own MSISDN\n" \
"Respond with subscribers' own IMSI\n" \
"Keep the session idle (useful for testing)\n"
"Respond with subscribers' own IMSI\n"
#define EXT_STR "External USSD Handler\n" \
"Name of External USSD Handler (IPA CCM ID)\n"
@@ -308,7 +305,7 @@ DEFUN(cfg_no_euse, cfg_no_euse_cmd,
{
struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
if (!euse) {
vty_out(vty, "%% Cannot remove non-existent EUSE %s%s", argv[0], VTY_NEWLINE);
vty_out(vty, "%% Cannot remove non-existant EUSE %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
if (g_hlr->euse_default == euse) {

View File

@@ -44,14 +44,13 @@ static char *get_datestr(const time_t *t, char *buf, size_t bufsize)
return buf;
}
static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen, bool only_age)
static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen)
{
uint32_t age;
char datebuf[32];
if (!last_lu_seen)
return;
if (!only_age)
vty_out(vty, " last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));
vty_out(vty, " last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));
if (!timestamp_age(&last_lu_seen, &age))
vty_out(vty, " (invalid timestamp)%s", VTY_NEWLINE);
else {
@@ -65,10 +64,7 @@ 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);
if (!only_age)
vty_out(vty, " ago)%s", VTY_NEWLINE);
else
vty_out(vty, " ago)");
vty_out(vty, " ago)%s", VTY_NEWLINE);
#undef UNIT_AGO
}
}
@@ -112,8 +108,8 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
vty_out(vty, " PS disabled%s", VTY_NEWLINE);
if (subscr->ms_purged_ps)
vty_out(vty, " PS purged%s", VTY_NEWLINE);
dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, false);
dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps, false);
dump_last_lu_seen(vty, "CS", subscr->last_lu_seen);
dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps);
if (!*subscr->imsi)
return;
@@ -163,28 +159,6 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
}
}
static void subscr_dump_summary_vty(struct hlr_subscriber *subscr, void *data)
{
struct vty *vty = data;
vty_out(vty, "%-5"PRIu64" %-12s %-16s", subscr->id,
*subscr->msisdn ? subscr->msisdn : "none",
*subscr->imsi ? subscr->imsi : "none");
if (*subscr->imei) {
char checksum = osmo_luhn(subscr->imei, 14);
if (checksum == -EINVAL)
vty_out(vty, " %-14s (INVALID LENGTH!)", subscr->imei);
else
vty_out(vty, " %-14s%c", subscr->imei, checksum);
} else {
vty_out(vty," ------------- ");
}
vty_out(vty, " %-2s%-2s ", subscr->nam_cs ? "CS" : "", subscr->nam_ps ? "PS" : "");
if (subscr->last_lu_seen)
dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, true);
vty_out_newline(vty);
}
static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
{
char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
@@ -212,52 +186,10 @@ static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id,
return rc;
}
static void dump_summary_table_vty(struct vty *vty, bool header, bool show_ls)
{
const char *texts = "ID MSISDN IMSI IMEI NAM";
const char *lines = "----- ------------ ---------------- ---------------- -----";
const char *ls_text = " LAST SEEN";
const char *ls_line = " ------------";
if (header) {
if (!show_ls)
vty_out(vty, "%s%s%s%s", texts, VTY_NEWLINE, lines, VTY_NEWLINE);
else
vty_out(vty, "%s%s%s%s%s%s", texts, ls_text, VTY_NEWLINE, lines, ls_line, VTY_NEWLINE);
} else {
if (!show_ls)
vty_out(vty, "%s%s%s%s", lines, VTY_NEWLINE, texts, VTY_NEWLINE);
else
vty_out(vty, "%s%s%s%s%s%s", lines, ls_line, VTY_NEWLINE, texts, ls_text, VTY_NEWLINE);
}
}
static int get_subscrs(struct vty *vty, const char *filter_type, const char *filter)
{
int rc = -1;
int count = 0;
const char *err;
bool show_ls = (filter_type && strcmp(filter_type, "last_lu_seen") == 0);
dump_summary_table_vty(vty, true, show_ls);
rc = db_subscrs_get(g_hlr->dbc, filter_type, filter, subscr_dump_summary_vty, vty, &count, &err);
if (count > 40) {
dump_summary_table_vty(vty, false, show_ls);
}
if (count > 0)
vty_out(vty, " Subscribers Shown: %d%s", count, VTY_NEWLINE);
if (rc)
vty_out(vty, "%% %s%s", err, VTY_NEWLINE);
return rc;
}
#define SUBSCR_CMD "subscriber "
#define SUBSCR_CMD_HELP "Subscriber management commands\n"
#define SUBSCR_SHOW_HELP "Show subscriber information\n"
#define SUBSCRS_SHOW_HELP "Show all subscribers (with filter possibility)\n"
#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
#define SUBSCR_FILTER "(imei|imsi|msisdn) FILTER"
#define SUBSCR_ID_HELP \
"Identify subscriber by IMSI\n" \
"Identify subscriber by MSISDN (phone number)\n" \
@@ -275,7 +207,7 @@ static int get_subscrs(struct vty *vty, const char *filter_type, const char *fil
DEFUN(subscriber_show,
subscriber_show_cmd,
SUBSCR "show",
SUBSCR_HELP SUBSCR_SHOW_HELP)
SUBSCR_HELP "Show subscriber information\n")
{
struct hlr_subscriber subscr;
const char *id_type = argv[0];
@@ -290,50 +222,7 @@ DEFUN(subscriber_show,
ALIAS(subscriber_show, show_subscriber_cmd,
"show " SUBSCR_CMD SUBSCR_ID,
SHOW_STR SUBSCR_SHOW_HELP SUBSCR_ID_HELP);
DEFUN(show_subscriber_all,
show_subscriber_all_cmd,
"show subscribers all",
SHOW_STR SUBSCRS_SHOW_HELP "Show summary of all subscribers\n")
{
if (get_subscrs(vty, NULL, NULL))
return CMD_WARNING;
return CMD_SUCCESS;
}
DEFUN(show_subscriber_filtered,
show_subscriber_filtered_cmd,
"show subscribers " SUBSCR_FILTER,
SHOW_STR SUBSCRS_SHOW_HELP
"Filter Subscribers by IMEI\n" "Filter Subscribers by IMSI\n" "Filter Subscribers by MSISDN\n"
"String to match in imei, imsi or msisdn\n")
{
const char *filter_type = argv[0];
const char *filter = argv[1];
if (get_subscrs(vty, filter_type, filter))
return CMD_WARNING;
return CMD_SUCCESS;
}
ALIAS(show_subscriber_filtered, show_subscriber_filtered_cmd2,
"show subscribers (cs|ps) (on|off)",
SHOW_STR SUBSCR_SHOW_HELP
"Filter Subscribers by CS Network Access Mode\n" "Filter Subscribers by PS Network Access Mode\n"
"Authorised\n" "Not Authorised\n");
DEFUN(show_subscriber_order_last_seen, show_subscriber_order_last_seen_cmd,
"show subscribers last-seen",
SHOW_STR SUBSCR_SHOW_HELP "Show Subscribers Ordered by Last Seen Time\n")
{
if (get_subscrs(vty, "last_lu_seen", NULL))
return CMD_WARNING;
return CMD_SUCCESS;
}
SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
DEFUN(subscriber_create,
subscriber_create_cmd,
@@ -662,55 +551,6 @@ DEFUN(subscriber_aud3g,
return CMD_SUCCESS;
}
DEFUN(subscriber_aud3g_xor,
subscriber_aud3g_xor_cmd,
SUBSCR_UPDATE "aud3g xor k K"
" [ind-bitlen] [<0-28>]",
SUBSCR_UPDATE_HELP
"Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
"Use XOR algorithm\n"
"Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
"Set IND bit length\n" "IND bit length value (default: 5)\n")
{
struct hlr_subscriber subscr;
int minlen = 0;
int maxlen = 0;
int rc;
const char *id_type = argv[0];
const char *id = argv[1];
const char *k = argv[2];
int ind_bitlen = argc > 4? atoi(argv[4]) : 5;
struct sub_auth_data_str aud3g = {
.type = OSMO_AUTH_TYPE_UMTS,
.u.umts = {
.k = k,
.opc_is_op = 0,
.opc = "00000000000000000000000000000000",
.ind_bitlen = ind_bitlen,
},
};
if (!auth_algo_parse("xor", &aud3g.algo, &minlen, &maxlen)) {
vty_out(vty, "%% Unknown auth algorithm: '%s'%s", "xor", VTY_NEWLINE);
return CMD_WARNING;
}
if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
return CMD_WARNING;
if (get_subscr_by_argv(vty, id_type, id, &subscr))
return CMD_WARNING;
rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
if (rc) {
vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
subscr.imsi, VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(subscriber_imei,
subscriber_imei_cmd,
SUBSCR_UPDATE "imei (none|IMEI)",
@@ -788,10 +628,6 @@ DEFUN(subscriber_nam,
void hlr_vty_subscriber_init(void)
{
install_element_ve(&show_subscriber_all_cmd);
install_element_ve(&show_subscriber_filtered_cmd);
install_element_ve(&show_subscriber_filtered_cmd2);
install_element_ve(&show_subscriber_order_last_seen_cmd);
install_element_ve(&subscriber_show_cmd);
install_element_ve(&show_subscriber_cmd);
install_element(ENABLE_NODE, &subscriber_create_cmd);
@@ -801,7 +637,6 @@ void hlr_vty_subscriber_init(void)
install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
install_element(ENABLE_NODE, &subscriber_aud3g_xor_cmd);
install_element(ENABLE_NODE, &subscriber_imei_cmd);
install_element(ENABLE_NODE, &subscriber_nam_cmd);
}

View File

@@ -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);
@@ -130,7 +130,7 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
osmo_fsm_inst_update_id_f_sanitize(fi, '_', "%s:IMSI-%s", lu->is_ps ? "PS" : "CS", update_location_req->gsup.imsi);
if (osmo_cni_peer_id_is_empty(&lu->vlr_name)) {
if (osmo_gsup_peer_id_is_empty(&lu->vlr_name)) {
lu_failure(lu, GMM_CAUSE_NET_FAIL, "LU without a VLR");
return;
}
@@ -163,30 +163,30 @@ 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)) {
lu_failure(lu, GMM_CAUSE_NET_FAIL, "Cannot update %s in the database",
lu->is_ps ? "SGSN number" : "VLR number");
return;

View File

@@ -487,7 +487,7 @@ static int socket_cb(struct osmo_fd *ofd, unsigned int flags)
{
int rc = 0;
if (flags & OSMO_FD_READ)
if (flags & BSC_FD_READ)
rc = socket_read_cb(ofd);
if (rc < 0)
return rc;
@@ -512,7 +512,7 @@ int socket_accept(struct osmo_fd *ofd, unsigned int flags)
c = talloc_zero(globals.ctx, struct socket_client);
OSMO_ASSERT(c);
c->ofd.fd = rc;
c->ofd.when = OSMO_FD_READ;
c->ofd.when = BSC_FD_READ;
c->ofd.cb = socket_cb;
c->ofd.data = c;
@@ -543,7 +543,7 @@ int socket_init(const char *sock_path)
return -1;
}
ofd->when = OSMO_FD_READ;
ofd->when = BSC_FD_READ;
ofd->cb = socket_accept;
rc = osmo_fd_register(ofd);
@@ -584,11 +584,11 @@ void respond_result(const char *query_str, const struct osmo_mslookup_result *r)
llist_for_each_entry_safe(c, n, &globals.socket_clients, entry) {
if (!strcmp(query_str, c->query_str)) {
socket_client_respond_result(c, g_buf);
if (!r || r->last)
if (r->last)
socket_client_close(c);
}
}
if (!r || r->last)
if (r->last)
globals.requests_handled++;
}

View File

@@ -71,7 +71,7 @@ static int osmo_mslookup_server_mdns_rx(struct osmo_fd *osmo_fd, unsigned int wh
/* Parse the message and print it */
n = read(osmo_fd->fd, buffer, sizeof(buffer));
if (n <= 0)
if (n < 0)
return n;
ctx = talloc_named_const(server, 0, __func__);

View File

@@ -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:
@@ -403,6 +403,18 @@ static int proxy_acknowledge_gsup_from_remote_hlr(struct proxy *proxy, const str
);
break;
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
/* Remember the auth tuples: if the remote HLR becomes unreachable for an intermediate period, we can
* still re-use this auth information a number of times and keep the subscriber attached (on this
* roaming/proxy HLR). */
#if 0
for (i = 0; i < gsup->num_auth_vectors; i++)
proxy_subscr_new.auth_vectors[i] = gsup->auth_vectors[i];
proxy_subscr_new.num_auth_vectors = gsup->num_auth_vectors;
rc = proxy_subscr_create_or_update(proxy, &proxy_subscr_new);
#endif
break;
default:
break;
}
@@ -483,13 +495,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.

417
src/proxy_mm.c Normal file
View File

@@ -0,0 +1,417 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/tdef.h>
#include <osmocom/gsm/apn.h>
#include <osmocom/hlr/hlr.h>
#include <osmocom/hlr/proxy_mm.h>
#include <osmocom/hlr/proxy_db.h>
enum proxy_mm_fsm_state {
PROXY_MM_ST_READY,
PROXY_MM_ST_WAIT_SUBSCR_DATA,
PROXY_MM_ST_WAIT_GSUP_ISD_RESULT,
PROXY_MM_ST_WAIT_AUTH_TUPLES,
};
static const struct value_string proxy_mm_fsm_event_names[] = {
OSMO_VALUE_STRING(PROXY_MM_EV_SUBSCR_INVALID),
OSMO_VALUE_STRING(PROXY_MM_EV_RX_GSUP_LU),
OSMO_VALUE_STRING(PROXY_MM_EV_RX_GSUP_SAI),
OSMO_VALUE_STRING(PROXY_MM_EV_RX_SUBSCR_DATA),
OSMO_VALUE_STRING(PROXY_MM_EV_RX_GSUP_ISD_RESULT),
OSMO_VALUE_STRING(PROXY_MM_EV_RX_AUTH_TUPLES),
{}
};
static struct osmo_fsm proxy_mm_fsm;
static struct osmo_fsm proxy_to_home_fsm;
struct osmo_tdef proxy_mm_tdefs[] = {
// FIXME
{ .T=-1, .default_val=5, .desc="proxy_mm ready timeout" },
{ .T=-2, .default_val=5, .desc="proxy_mm wait_subscr_data timeout" },
{ .T=-3, .default_val=5, .desc="proxy_mm wait_gsup_isd_result timeout" },
{ .T=-4, .default_val=5, .desc="proxy_mm wait_auth_tuples timeout" },
{}
};
static const struct osmo_tdef_state_timeout proxy_mm_fsm_timeouts[32] = {
// FIXME
[PROXY_MM_ST_READY] = { .T=-1 },
[PROXY_MM_ST_WAIT_SUBSCR_DATA] = { .T=-2 },
[PROXY_MM_ST_WAIT_GSUP_ISD_RESULT] = { .T=-3 },
[PROXY_MM_ST_WAIT_AUTH_TUPLES] = { .T=-4 },
};
#define proxy_mm_fsm_state_chg(state) \
osmo_tdef_fsm_inst_state_chg(mm_fi, state, \
proxy_mm_fsm_timeouts, \
proxy_mm_tdefs, \
5)
LLIST_HEAD(proxy_mm_list);
struct proxy_mm *proxy_mm_alloc(const struct osmo_gsup_peer_id *vlr_name,
bool is_ps,
const char *imsi)
{
struct proxy_mm *proxy_mm;
struct osmo_fsm_inst *mm_fi = osmo_fsm_inst_alloc(&proxy_mm_fsm, g_hlr, NULL, LOGL_DEBUG, imsi);
OSMO_ASSERT(mm_fi);
proxy_mm = talloc(mm_fi, struct proxy_mm);
OSMO_ASSERT(proxy_mm);
mm_fi->priv = proxy_mm;
*proxy_mm = (struct proxy_mm){
.mm_fi = mm_fi,
.is_ps = is_ps,
};
OSMO_STRLCPY_ARRAY(proxy_mm->imsi, imsi);
INIT_LLIST_HEAD(&proxy_mm->auth_cache);
llist_add(&proxy_mm->entry, &proxy_mm_list);
proxy_mm->to_home_fi = osmo_fsm_inst_alloc_child(&proxy_to_home_fsm, mm_fi, PROXY_MM_EV_SUBSCR_INVALID);
proxy_mm->to_home_fi->priv = proxy_mm;
/* Do a state change to activate timeout */
proxy_mm_fsm_state_chg(PROXY_MM_ST_READY);
return proxy_mm;
}
void proxy_mm_add_auth_vectors(struct proxy_mm *proxy_mm,
const struct osmo_auth_vector *auth_vectors, size_t num_auth_vectors)
{
struct proxy_mm_auth_cache *ac = talloc_zero(proxy_mm, struct proxy_mm_auth_cache);
int i;
OSMO_ASSERT(ac);
ac->num_auth_vectors = num_auth_vectors;
for (i = 0; i < num_auth_vectors; i++)
ac->auth_vectors[i] = auth_vectors[i];
if (proxy_db_add_auth_vectors(&proxy_mm->vlr_name, ac)) {
talloc_free(ac);
return;
}
llist_add(&ac->entry, &proxy_mm->auth_cache);
}
struct proxy_mm_auth_cache *proxy_mm_get_auth_vectors(struct proxy_mm *proxy_mm)
{
struct proxy_mm_auth_cache *i;
struct proxy_mm_auth_cache *ac = NULL;
llist_for_each_entry(i, &proxy_mm->auth_cache, entry) {
if (!ac || i->sent_to_vlr_count < ac->sent_to_vlr_count) {
ac = i;
}
}
/* ac now points to (one of) the least used auth cache entries (or NULL if none). */
return ac;
}
void proxy_mm_discard_auth_vectors(struct proxy_mm *proxy_mm, struct proxy_mm_auth_cache *ac)
{
proxy_db_drop_auth_vectors(ac->db_id);
llist_del(&ac->entry);
talloc_free(ac);
}
/* Mark given auth cache entries as sent to the VLR and clean up if necessary. */
void proxy_mm_use_auth_vectors(struct proxy_mm *proxy_mm, struct proxy_mm_auth_cache *ac)
{
struct proxy_mm_auth_cache *i, *i_next;
bool found_fresh_ac = false;
/* The aim is to keep at least one set of already used auth tuples in the cache. If there are still fresh ones,
* all used auth vectors can be discarded. If there are no fresh ones left, keep only this last set. */
llist_for_each_entry_safe(i, i_next, &proxy_mm->auth_cache, entry) {
if (i == ac)
continue;
if (i->sent_to_vlr_count) {
/* An auth entry other than this freshly used one, which has been used before.
* No need to keep it. */
proxy_mm_discard_auth_vectors(proxy_mm, i);
continue;
}
if (!i->sent_to_vlr_count)
found_fresh_ac = true;
}
if (found_fresh_ac) {
/* There are still other, fresh auth vectors. */
proxy_mm_discard_auth_vectors(proxy_mm, ac);
} else {
/* else, only this ac remains in the list */
ac->sent_to_vlr_count++;
proxy_db_auth_vectors_update_sent_count(ac);
}
}
static void proxy_mm_ready_action(struct osmo_fsm_inst *mm_fi, uint32_t event, void *data)
{
struct proxy *proxy = g_hlr->gs->proxy;
struct proxy_mm *proxy_mm = mm_fi->priv;
switch (event) {
case PROXY_MM_EV_SUBSCR_INVALID:
/* Home HLR has responded and rejected a Location Updating, or no home HLR could be found. The
* subscriber is invalid, remove it from the cache. */
proxy_subscr_del(proxy, proxy_mm->imsi);
osmo_fsm_inst_term(mm_fi, OSMO_FSM_TERM_REGULAR, NULL);
break;
case PROXY_MM_EV_RX_GSUP_LU:
/* The MSC asks for a LU. If we don't know details about this subscriber, then we'll have to wait for the
* home HLR to respond. If we already know details about the subscriber, we respond immediately (with
* Insert Subscriber Data and accept the LU), but also ask the home HLR to confirm the LU later. */
osmo_fsm_inst_dispatch(proxy_mm->to_home_fi, PROXY_TO_HOME_EV_CONFIRM_LU, NULL);
if (proxy_mm_subscriber_data_known(proxy_mm))
proxy_mm_fsm_state_chg(PROXY_MM_ST_WAIT_GSUP_ISD_RESULT);
else
proxy_mm_fsm_state_chg(PROXY_MM_ST_WAIT_SUBSCR_DATA);
break;
case PROXY_MM_EV_RX_GSUP_SAI:
// FIXME
break;
default:
OSMO_ASSERT(false);
}
}
static int proxy_mm_ready_timeout(struct osmo_fsm_inst *mm_fi)
{
/* Return 1 to terminate FSM instance, 0 to keep running */
return 1;
}
void proxy_mm_wait_subscr_data_onenter(struct osmo_fsm_inst *mm_fi, uint32_t prev_state)
{
//struct proxy_mm *proxy_mm = mm_fi->priv;
// FIXME
}
static void proxy_mm_wait_subscr_data_action(struct osmo_fsm_inst *mm_fi, uint32_t event, void *data)
{
//struct proxy_mm *proxy_mm = mm_fi->priv;
switch (event) {
case PROXY_MM_EV_RX_SUBSCR_DATA:
// FIXME
break;
default:
OSMO_ASSERT(false);
}
}
static int proxy_mm_wait_subscr_data_timeout(struct osmo_fsm_inst *mm_fi)
{
/* Return 1 to terminate FSM instance, 0 to keep running */
return 1;
}
static void proxy_mm_lu_error(struct osmo_fsm_inst *mm_fi)
{
osmo_gsup_req_respond_err(req, GMM_CAUSE_ROAMING_NOTALLOWED,
"LU does not accept GSUP rx");
}
void proxy_mm_wait_gsup_isd_result_onenter(struct osmo_fsm_inst *mm_fi, uint32_t prev_state)
{
struct proxy_mm *proxy_mm = mm_fi->priv;
struct proxy_subscr proxy_subscr;
struct osmo_gsup_message isd_req;
uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
uint8_t apn[APN_MAXLEN];
isd_req.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST;
if (proxy_subscr_get_by_imsi(&proxy_subscr, g_hlr->gs->proxy, proxy_mm->imsi)) {
LOGPFSML(mm_fi, LOGL_ERROR,
"Proxy: trying to send cached Subscriber Data, but there is no proxy entry\n");
proxy_mm_lu_error(mm_fi);
return;
}
if (proxy_subscr.msisdn[0] == '\0') {
LOGPFSML(mm_fi, LOGL_ERROR,
"Proxy: trying to send cached Subscriber Data, but subscriber has no MSISDN in proxy cache\n");
proxy_mm_lu_error(mm_fi);
return;
}
if (osmo_gsup_create_insert_subscriber_data_msg(&isd_req, proxy_mm->imsi,
proxy_subscr->msisdn, msisdn_enc, sizeof(msisdn_enc),
apn, sizeof(apn),
proxy_mm->is_ps? OSMO_GSUP_CN_DOMAIN_PS : OSMO_GSUP_CN_DOMAIN_CS)) {
LOGPFSML(mm_fi, LOGL_ERROR, "Proxy: failed to send cached Subscriber Data\n");
proxy_mm_lu_error(mm_fi);
return;
}
}
static void proxy_mm_wait_gsup_isd_result_action(struct osmo_fsm_inst *mm_fi, uint32_t event, void *data)
{
//struct proxy_mm *proxy_mm = mm_fi->priv;
switch (event) {
case PROXY_MM_EV_RX_GSUP_ISD_RESULT:
// FIXME
break;
default:
OSMO_ASSERT(false);
}
}
static int proxy_mm_wait_gsup_isd_result_timeout(struct osmo_fsm_inst *mm_fi)
{
/* Return 1 to terminate FSM instance, 0 to keep running */
return 1;
}
void proxy_mm_wait_auth_tuples_onenter(struct osmo_fsm_inst *mm_fi, uint32_t prev_state)
{
//struct proxy_mm *proxy_mm = mm_fi->priv;
// FIXME
}
static void proxy_mm_wait_auth_tuples_action(struct osmo_fsm_inst *mm_fi, uint32_t event, void *data)
{
//struct proxy_mm *proxy_mm = mm_fi->priv;
switch (event) {
case PROXY_MM_EV_RX_AUTH_TUPLES:
// FIXME
break;
default:
OSMO_ASSERT(false);
}
}
static int proxy_mm_wait_auth_tuples_timeout(struct osmo_fsm_inst *mm_fi)
{
/* Return 1 to terminate FSM instance, 0 to keep running */
return 1;
}
#define S(x) (1 << (x))
static const struct osmo_fsm_state proxy_mm_fsm_states[] = {
[PROXY_MM_ST_READY] = {
.name = "ready",
.in_event_mask = 0
| S(PROXY_MM_EV_SUBSCR_INVALID)
| S(PROXY_MM_EV_RX_GSUP_LU)
| S(PROXY_MM_EV_RX_GSUP_SAI)
,
.out_state_mask = 0
| S(PROXY_MM_ST_READY)
| S(PROXY_MM_ST_WAIT_SUBSCR_DATA)
| S(PROXY_MM_ST_WAIT_GSUP_ISD_RESULT)
| S(PROXY_MM_ST_WAIT_AUTH_TUPLES)
,
.action = proxy_mm_ready_action,
},
[PROXY_MM_ST_WAIT_SUBSCR_DATA] = {
.name = "wait_subscr_data",
.in_event_mask = 0
| S(PROXY_MM_EV_RX_SUBSCR_DATA)
,
.out_state_mask = 0
| S(PROXY_MM_ST_WAIT_GSUP_ISD_RESULT)
| S(PROXY_MM_ST_READY)
,
.onenter = proxy_mm_wait_subscr_data_onenter,
.action = proxy_mm_wait_subscr_data_action,
},
[PROXY_MM_ST_WAIT_GSUP_ISD_RESULT] = {
.name = "wait_gsup_isd_result",
.in_event_mask = 0
| S(PROXY_MM_EV_RX_GSUP_ISD_RESULT)
,
.out_state_mask = 0
| S(PROXY_MM_ST_READY)
,
.onenter = proxy_mm_wait_gsup_isd_result_onenter,
.action = proxy_mm_wait_gsup_isd_result_action,
},
[PROXY_MM_ST_WAIT_AUTH_TUPLES] = {
.name = "wait_auth_tuples",
.in_event_mask = 0
| S(PROXY_MM_EV_RX_AUTH_TUPLES)
,
.out_state_mask = 0
| S(PROXY_MM_ST_READY)
,
.onenter = proxy_mm_wait_auth_tuples_onenter,
.action = proxy_mm_wait_auth_tuples_action,
},
};
static int proxy_mm_fsm_timer_cb(struct osmo_fsm_inst *mm_fi)
{
//struct proxy_mm *proxy_mm = mm_fi->priv;
switch (mm_fi->state) {
case PROXY_MM_ST_READY:
return proxy_mm_ready_timeout(mm_fi);
case PROXY_MM_ST_WAIT_SUBSCR_DATA:
return proxy_mm_wait_subscr_data_timeout(mm_fi);
case PROXY_MM_ST_WAIT_GSUP_ISD_RESULT:
return proxy_mm_wait_gsup_isd_result_timeout(mm_fi);
case PROXY_MM_ST_WAIT_AUTH_TUPLES:
return proxy_mm_wait_auth_tuples_timeout(mm_fi);
default:
/* Return 1 to terminate FSM instance, 0 to keep running */
return 1;
}
}
void proxy_mm_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct proxy_mm *proxy_mm = fi->priv;
llist_del(&proxy_mm->entry);
}
static struct osmo_fsm proxy_mm_fsm = {
.name = "proxy_mm",
.states = proxy_mm_fsm_states,
.num_states = ARRAY_SIZE(proxy_mm_fsm_states),
.log_subsys = DLGLOBAL, // FIXME
.event_names = proxy_mm_fsm_event_names,
.timer_cb = proxy_mm_fsm_timer_cb,
.cleanup = proxy_mm_fsm_cleanup,
};
static __attribute__((constructor)) void proxy_mm_fsm_register(void)
{
OSMO_ASSERT(osmo_fsm_register(&proxy_mm_fsm) == 0);
}
bool proxy_mm_subscriber_data_known(const struct proxy_mm *proxy_mm)
{
struct proxy_subscr proxy_subscr;
if (proxy_subscr_get_by_imsi(&proxy_subscr, g_hlr->gs->proxy, proxy_mm->imsi))
return false;
return proxy_subscr.msisdn[0] != '\0';
}

439
src/proxy_to_home.c Normal file
View File

@@ -0,0 +1,439 @@
#include <osmocom/hlr/proxy_mm.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/tdef.h>
enum proxy_to_home_fsm_state {
PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED,
PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT,
PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT,
PROXY_TO_HOME_ST_IDLE,
PROXY_TO_HOME_ST_CLEAR,
};
static const struct value_string proxy_to_home_fsm_event_names[] = {
OSMO_VALUE_STRING(PROXY_TO_HOME_EV_HOME_HLR_RESOLVED),
OSMO_VALUE_STRING(PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ),
OSMO_VALUE_STRING(PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT),
OSMO_VALUE_STRING(PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT),
OSMO_VALUE_STRING(PROXY_TO_HOME_EV_CHECK_TUPLES),
OSMO_VALUE_STRING(PROXY_TO_HOME_EV_CONFIRM_LU),
{}
};
static struct osmo_fsm proxy_to_home_fsm;
struct osmo_tdef proxy_to_home_tdefs[] = {
// FIXME
{ .T=-1, .default_val=5, .desc="proxy_to_home wait_home_hlr_resolved timeout" },
{ .T=-2, .default_val=5, .desc="proxy_to_home wait_update_location_result timeout" },
{ .T=-3, .default_val=5, .desc="proxy_to_home wait_send_auth_info_result timeout" },
{ .T=-4, .default_val=5, .desc="proxy_to_home idle timeout" },
{ .T=-5, .default_val=5, .desc="proxy_to_home clear timeout" },
{}
};
#if 0
static const struct osmo_tdef_state_timeout proxy_to_home_fsm_timeouts[32] = {
// FIXME
[PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED] = { .T=-1 },
[PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT] = { .T=-2 },
[PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT] = { .T=-3 },
[PROXY_TO_HOME_ST_IDLE] = { .T=-4 },
[PROXY_TO_HOME_ST_CLEAR] = { .T=-5 },
};
#endif
#define proxy_to_home_fsm_state_chg(state) \
osmo_tdef_fsm_inst_state_chg(fi, state, \
proxy_to_home_fsm_timeouts, \
proxy_to_home_tdefs, \
5)
void proxy_to_home_wait_home_hlr_resolved_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
//struct proxy_mm *proxy_mm = fi->priv;
// FIXME
}
static void proxy_to_home_wait_home_hlr_resolved_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
//struct proxy_mm *proxy_mm = fi->priv;
switch (event) {
case PROXY_TO_HOME_EV_HOME_HLR_RESOLVED:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT:
// FIXME
break;
case PROXY_TO_HOME_EV_CHECK_TUPLES:
// FIXME
break;
case PROXY_TO_HOME_EV_CONFIRM_LU:
// FIXME
break;
default:
OSMO_ASSERT(false);
}
}
static int proxy_to_home_wait_home_hlr_resolved_timeout(struct osmo_fsm_inst *fi)
{
/* Return 1 to terminate FSM instance, 0 to keep running */
return 1;
}
void proxy_to_home_wait_update_location_result_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
//struct proxy_mm *proxy_mm = fi->priv;
// FIXME
}
static void proxy_to_home_wait_update_location_result_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
//struct proxy_mm *proxy_mm = fi->priv;
switch (event) {
case PROXY_TO_HOME_EV_HOME_HLR_RESOLVED:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT:
// FIXME
break;
case PROXY_TO_HOME_EV_CHECK_TUPLES:
// FIXME
break;
case PROXY_TO_HOME_EV_CONFIRM_LU:
// FIXME
break;
default:
OSMO_ASSERT(false);
}
}
static int proxy_to_home_wait_update_location_result_timeout(struct osmo_fsm_inst *fi)
{
/* Return 1 to terminate FSM instance, 0 to keep running */
return 1;
}
void proxy_to_home_wait_send_auth_info_result_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
//struct proxy_mm *proxy_mm = fi->priv;
// FIXME
}
static void proxy_to_home_wait_send_auth_info_result_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
//struct proxy_mm *proxy_mm = fi->priv;
switch (event) {
case PROXY_TO_HOME_EV_HOME_HLR_RESOLVED:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT:
// FIXME
break;
case PROXY_TO_HOME_EV_CHECK_TUPLES:
// FIXME
break;
case PROXY_TO_HOME_EV_CONFIRM_LU:
// FIXME
break;
default:
OSMO_ASSERT(false);
}
}
static int proxy_to_home_wait_send_auth_info_result_timeout(struct osmo_fsm_inst *fi)
{
/* Return 1 to terminate FSM instance, 0 to keep running */
return 1;
}
void proxy_to_home_idle_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
//struct proxy_mm *proxy_mm = fi->priv;
// FIXME
}
static void proxy_to_home_idle_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
//struct proxy_mm *proxy_mm = fi->priv;
switch (event) {
case PROXY_TO_HOME_EV_HOME_HLR_RESOLVED:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT:
// FIXME
break;
case PROXY_TO_HOME_EV_CHECK_TUPLES:
// FIXME
break;
case PROXY_TO_HOME_EV_CONFIRM_LU:
// FIXME
break;
default:
OSMO_ASSERT(false);
}
}
static int proxy_to_home_idle_timeout(struct osmo_fsm_inst *fi)
{
/* Return 1 to terminate FSM instance, 0 to keep running */
return 1;
}
void proxy_to_home_clear_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
//struct proxy_mm *proxy_mm = fi->priv;
// FIXME
}
static void proxy_to_home_clear_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
//struct proxy_mm *proxy_mm = fi->priv;
switch (event) {
case PROXY_TO_HOME_EV_HOME_HLR_RESOLVED:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT:
// FIXME
break;
case PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT:
// FIXME
break;
case PROXY_TO_HOME_EV_CHECK_TUPLES:
// FIXME
break;
case PROXY_TO_HOME_EV_CONFIRM_LU:
// FIXME
break;
default:
OSMO_ASSERT(false);
}
}
static int proxy_to_home_clear_timeout(struct osmo_fsm_inst *fi)
{
/* Return 1 to terminate FSM instance, 0 to keep running */
return 1;
}
#define S(x) (1 << (x))
static const struct osmo_fsm_state proxy_to_home_fsm_states[] = {
[PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED] = {
.name = "wait_home_hlr_resolved",
.in_event_mask = 0
| S(PROXY_TO_HOME_EV_HOME_HLR_RESOLVED)
| S(PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ)
| S(PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT)
| S(PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT)
| S(PROXY_TO_HOME_EV_CHECK_TUPLES)
| S(PROXY_TO_HOME_EV_CONFIRM_LU)
,
.out_state_mask = 0
| S(PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED)
| S(PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT)
| S(PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT)
| S(PROXY_TO_HOME_ST_IDLE)
| S(PROXY_TO_HOME_ST_CLEAR)
,
.onenter = proxy_to_home_wait_home_hlr_resolved_onenter,
.action = proxy_to_home_wait_home_hlr_resolved_action,
},
[PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT] = {
.name = "wait_update_location_result",
.in_event_mask = 0
| S(PROXY_TO_HOME_EV_HOME_HLR_RESOLVED)
| S(PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ)
| S(PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT)
| S(PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT)
| S(PROXY_TO_HOME_EV_CHECK_TUPLES)
| S(PROXY_TO_HOME_EV_CONFIRM_LU)
,
.out_state_mask = 0
| S(PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED)
| S(PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT)
| S(PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT)
| S(PROXY_TO_HOME_ST_IDLE)
| S(PROXY_TO_HOME_ST_CLEAR)
,
.onenter = proxy_to_home_wait_update_location_result_onenter,
.action = proxy_to_home_wait_update_location_result_action,
},
[PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT] = {
.name = "wait_send_auth_info_result",
.in_event_mask = 0
| S(PROXY_TO_HOME_EV_HOME_HLR_RESOLVED)
| S(PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ)
| S(PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT)
| S(PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT)
| S(PROXY_TO_HOME_EV_CHECK_TUPLES)
| S(PROXY_TO_HOME_EV_CONFIRM_LU)
,
.out_state_mask = 0
| S(PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED)
| S(PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT)
| S(PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT)
| S(PROXY_TO_HOME_ST_IDLE)
| S(PROXY_TO_HOME_ST_CLEAR)
,
.onenter = proxy_to_home_wait_send_auth_info_result_onenter,
.action = proxy_to_home_wait_send_auth_info_result_action,
},
[PROXY_TO_HOME_ST_IDLE] = {
.name = "idle",
.in_event_mask = 0
| S(PROXY_TO_HOME_EV_HOME_HLR_RESOLVED)
| S(PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ)
| S(PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT)
| S(PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT)
| S(PROXY_TO_HOME_EV_CHECK_TUPLES)
| S(PROXY_TO_HOME_EV_CONFIRM_LU)
,
.out_state_mask = 0
| S(PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED)
| S(PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT)
| S(PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT)
| S(PROXY_TO_HOME_ST_IDLE)
| S(PROXY_TO_HOME_ST_CLEAR)
,
.onenter = proxy_to_home_idle_onenter,
.action = proxy_to_home_idle_action,
},
[PROXY_TO_HOME_ST_CLEAR] = {
.name = "clear",
.in_event_mask = 0
| S(PROXY_TO_HOME_EV_HOME_HLR_RESOLVED)
| S(PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ)
| S(PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT)
| S(PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT)
| S(PROXY_TO_HOME_EV_CHECK_TUPLES)
| S(PROXY_TO_HOME_EV_CONFIRM_LU)
,
.out_state_mask = 0
| S(PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED)
| S(PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT)
| S(PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT)
| S(PROXY_TO_HOME_ST_IDLE)
| S(PROXY_TO_HOME_ST_CLEAR)
,
.onenter = proxy_to_home_clear_onenter,
.action = proxy_to_home_clear_action,
},
};
static int proxy_to_home_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
//struct proxy_mm *proxy_mm = fi->priv;
switch (fi->state) {
case PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED:
return proxy_to_home_wait_home_hlr_resolved_timeout(fi);
case PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT:
return proxy_to_home_wait_update_location_result_timeout(fi);
case PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT:
return proxy_to_home_wait_send_auth_info_result_timeout(fi);
case PROXY_TO_HOME_ST_IDLE:
return proxy_to_home_idle_timeout(fi);
case PROXY_TO_HOME_ST_CLEAR:
return proxy_to_home_clear_timeout(fi);
default:
/* Return 1 to terminate FSM instance, 0 to keep running */
return 1;
}
}
void proxy_to_home_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
//struct proxy_mm *proxy_mm = fi->priv;
// FIXME
}
static struct osmo_fsm proxy_to_home_fsm = {
.name = "proxy_to_home",
.states = proxy_to_home_fsm_states,
.num_states = ARRAY_SIZE(proxy_to_home_fsm_states),
.log_subsys = DLGLOBAL, // FIXME
.event_names = proxy_to_home_fsm_event_names,
.timer_cb = proxy_to_home_fsm_timer_cb,
.cleanup = proxy_to_home_fsm_cleanup,
};
static __attribute__((constructor)) void proxy_to_home_fsm_register(void)
{
OSMO_ASSERT(osmo_fsm_register(&proxy_to_home_fsm) == 0);
}

View File

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

View File

@@ -1,5 +1,6 @@
SUBDIRS = \
auc \
gsup_server \
db \
gsup \
db_upgrade \

View File

@@ -113,7 +113,6 @@ int rand_get(uint8_t *rand, unsigned int len)
return len;
}
/* Subscriber with 2G-only (COMP128v1) authentication data */
static void test_gen_vectors_2g_only(void)
{
struct osmo_sub_auth_data aud2g;
@@ -175,8 +174,6 @@ static void test_gen_vectors_2g_only(void)
comment_end();
}
/* Subscriber with separate 2G (COMP128v1) and 3G (MILENAGE) authentication data,
* reflects the default configuration of sysmoUSIM-SJS1 */
static void test_gen_vectors_2g_plus_3g(void)
{
struct osmo_sub_auth_data aud2g;
@@ -287,9 +284,6 @@ void _test_gen_vectors_3g_only__expect_vecs(struct osmo_auth_vector vecs[3])
);
}
/* Subscriber with only 3G (MILENAGE) authentication data,
* reflects the default configuration of sysmoISIM-SJA2. Resulting
* tuples are suitable for both 2G and 3G authentication */
static void test_gen_vectors_3g_only(void)
{
struct osmo_sub_auth_data aud2g;
@@ -460,55 +454,6 @@ static void test_gen_vectors_3g_only(void)
comment_end();
}
/* Subscriber with only 3G (XOR) authentication data,
* reflects the default configuration of sysmoTSIM-SJAx as well
* as many "Test USIM" cards. Resulting tuples are suitable for both
* 2G and 3G authentication */
static void test_gen_vectors_3g_xor(void)
{
struct osmo_sub_auth_data aud2g;
struct osmo_sub_auth_data aud3g;
struct osmo_auth_vector vec;
int rc;
comment_start();
aud2g = (struct osmo_sub_auth_data){ 0 };
aud3g = (struct osmo_sub_auth_data){
.type = OSMO_AUTH_TYPE_UMTS,
.algo = OSMO_AUTH_ALG_XOR,
.u.umts.sqn = 0,
};
osmo_hexparse("000102030405060708090a0b0c0d0e0f",
aud3g.u.umts.k, sizeof(aud3g.u.umts.k));
osmo_hexparse("00000000000000000000000000000000",
aud3g.u.umts.opc, sizeof(aud3g.u.umts.opc));
next_rand("b5039c57e4a75051551d1a390a71ce48", true);
vec = (struct osmo_auth_vector){ {0} };
VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64);
rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL);
VERBOSE_ASSERT(rc, == 1, "%d");
VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64);
VEC_IS(&vec,
" rand: b5039c57e4a75051551d1a390a71ce48\n"
" autn: 54e0a256565d0000b5029e54e0a25656\n"
" ck: 029e54e0a256565d141032067cc047b5\n"
" ik: 9e54e0a256565d141032067cc047b502\n"
" res: b5029e54e0a256565d141032067cc047\n"
" res_len: 10\n"
" kc: 98e880384887f9fe\n"
" sres: 0ec81877\n"
" auth_types: 03000000\n"
);
comment_end();
}
/* Test a variety of invalid authentication data combinations */
void test_gen_vectors_bad_args()
{
struct osmo_auth_vector vec;
@@ -668,20 +613,15 @@ int main(int argc, char **argv)
void *tall_ctx = talloc_named_const(NULL, 1, "auc_test");
osmo_init_logging2(tall_ctx, &hlr_log_info);
log_set_print_filename2(osmo_stderr_target,
cmdline_opts.verbose ?
LOG_FILENAME_BASENAME :
LOG_FILENAME_NONE);
log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
test_gen_vectors_2g_only();
test_gen_vectors_2g_plus_3g();
test_gen_vectors_3g_only();
test_gen_vectors_3g_xor();
test_gen_vectors_bad_args();
printf("Done\n");

View File

@@ -217,29 +217,6 @@ DAUC vector [2]: auth_types = 0x3
===== test_gen_vectors_3g_only: SUCCESS
===== test_gen_vectors_3g_xor
aud3g.u.umts.sqn == 0
DAUC Computing 1 auth vector: 3G only (2G derived from 3G keys)
DAUC 3G: k = 000102030405060708090a0b0c0d0e0f
DAUC 3G: opc = 00000000000000000000000000000000
DAUC 3G: for sqn ind 0, previous sqn was 0
DAUC vector [0]: rand = b5039c57e4a75051551d1a390a71ce48
DAUC vector [0]: sqn = 0
DAUC vector [0]: autn = 54e0a256565d0000b5029e54e0a25656
DAUC vector [0]: ck = 029e54e0a256565d141032067cc047b5
DAUC vector [0]: ik = 9e54e0a256565d141032067cc047b502
DAUC vector [0]: res = b5029e54e0a256565d141032067cc047
DAUC vector [0]: res_len = 16
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 98e880384887f9fe
DAUC vector [0]: sres = 0ec81877
DAUC vector [0]: auth_types = 0x3
rc == 1
aud3g.u.umts.sqn == 0
vector matches expectations
===== test_gen_vectors_3g_xor: SUCCESS
===== test_gen_vectors_bad_args
- no auth data (a)

View File

@@ -106,10 +106,9 @@ int main()
void *tall_ctx = talloc_named_const(NULL, 1, "test");
msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &hlr_log_info);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");

View File

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

View File

@@ -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>
@@ -924,16 +924,16 @@ static void test_ind()
#define ASSERT_IND(VLR, IND) do { \
unsigned int ind; \
struct osmo_cni_peer_id vlr; \
OSMO_ASSERT(!osmo_cni_peer_id_set_str(&vlr, OSMO_CNI_PEER_ID_IPA_NAME, VLR)); \
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_cni_peer_id vlr; \
OSMO_ASSERT(!osmo_cni_peer_id_set_str(&vlr, OSMO_CNI_PEER_ID_IPA_NAME, VLR)); \
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)
@@ -1024,13 +1024,9 @@ int main(int argc, char **argv)
handle_options(argc, argv);
osmo_init_logging2(ctx, &hlr_log_info);
log_set_print_filename2(osmo_stderr_target,
cmdline_opts.verbose ?
LOG_FILENAME_BASENAME :
LOG_FILENAME_NONE);
log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");

View File

@@ -101,7 +101,7 @@ int main(int argc, char **argv)
{
ctx = talloc_named_const(NULL, 0, "gsup_test");
osmo_init_logging2(ctx, &info);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);

View File

@@ -0,0 +1,44 @@
AM_CPPFLAGS = \
$(all_includes) \
$(NULL)
AM_CFLAGS = \
-Wall \
-ggdb3 \
-I$(top_srcdir)/include \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
-no-install \
$(NULL)
EXTRA_DIST = \
gsup_server_test.ok \
gsup_server_test.err \
$(NULL)
noinst_PROGRAMS = \
gsup_server_test \
$(NULL)
gsup_server_test_SOURCES = \
gsup_server_test.c \
$(NULL)
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/gsup_peer_id.c \
$(top_srcdir)/src/gsupclient/gsup_req.c \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(NULL)
.PHONY: update_exp
update_exp:
$(builddir)/gsup_server_test >"$(srcdir)/gsup_server_test.ok" 2>"$(srcdir)/gsup_server_test.err"

View File

@@ -0,0 +1,145 @@
/* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <osmocom/core/utils.h>
#include <osmocom/hlr/gsup_server.h>
#define comment_start() printf("\n===== %s\n", __func__)
#define comment_end() printf("===== %s: SUCCESS\n\n", __func__)
#define btw(fmt, args...) printf("\n" fmt "\n", ## args)
#define VERBOSE_ASSERT(val, expect_op, fmt) \
do { \
printf(#val " == " fmt "\n", (val)); \
OSMO_ASSERT((val) expect_op); \
} while (0)
void osmo_gsup_server_add_conn(struct llist_head *clients,
struct osmo_gsup_conn *conn);
static void test_add_conn(void)
{
struct llist_head _list;
struct llist_head *clients = &_list;
struct osmo_gsup_conn conn_inst[23] = {};
struct osmo_gsup_conn *conn;
unsigned int i;
comment_start();
INIT_LLIST_HEAD(clients);
btw("Add 10 items");
for (i = 0; i < 10; i++) {
osmo_gsup_server_add_conn(clients, &conn_inst[i]);
printf("conn_inst[%u].auc_3g_ind == %u\n", i, conn_inst[i].auc_3g_ind);
OSMO_ASSERT(clients->next == &conn_inst[0].list);
}
btw("Expecting a list of 0..9");
i = 0;
llist_for_each_entry(conn, clients, list) {
printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
OSMO_ASSERT(conn->auc_3g_ind == i);
OSMO_ASSERT(conn == &conn_inst[i]);
i++;
}
btw("Punch two holes in the sequence in arbitrary order,"
" a larger one from 2..4 and a single one at 7.");
llist_del(&conn_inst[4].list);
llist_del(&conn_inst[2].list);
llist_del(&conn_inst[3].list);
llist_del(&conn_inst[7].list);
btw("Expecting a list of 0,1, 5,6, 8,9");
i = 0;
llist_for_each_entry(conn, clients, list) {
printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
i++;
}
btw("Add conns, expecting them to take the open slots");
osmo_gsup_server_add_conn(clients, &conn_inst[12]);
VERBOSE_ASSERT(conn_inst[12].auc_3g_ind, == 2, "%u");
osmo_gsup_server_add_conn(clients, &conn_inst[13]);
VERBOSE_ASSERT(conn_inst[13].auc_3g_ind, == 3, "%u");
osmo_gsup_server_add_conn(clients, &conn_inst[14]);
VERBOSE_ASSERT(conn_inst[14].auc_3g_ind, == 4, "%u");
osmo_gsup_server_add_conn(clients, &conn_inst[17]);
VERBOSE_ASSERT(conn_inst[17].auc_3g_ind, == 7, "%u");
osmo_gsup_server_add_conn(clients, &conn_inst[18]);
VERBOSE_ASSERT(conn_inst[18].auc_3g_ind, == 10, "%u");
btw("Expecting a list of 0..10");
i = 0;
llist_for_each_entry(conn, clients, list) {
printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
OSMO_ASSERT(conn->auc_3g_ind == i);
i++;
}
btw("Does it also work for the first item?");
llist_del(&conn_inst[0].list);
btw("Expecting a list of 1..10");
i = 0;
llist_for_each_entry(conn, clients, list) {
printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
OSMO_ASSERT(conn->auc_3g_ind == i + 1);
i++;
}
btw("Add another conn, should take auc_3g_ind == 0");
osmo_gsup_server_add_conn(clients, &conn_inst[20]);
VERBOSE_ASSERT(conn_inst[20].auc_3g_ind, == 0, "%u");
btw("Expecting a list of 0..10");
i = 0;
llist_for_each_entry(conn, clients, list) {
printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
OSMO_ASSERT(conn->auc_3g_ind == i);
i++;
}
btw("If a client reconnects, it will (likely) get the same auc_3g_ind");
VERBOSE_ASSERT(conn_inst[5].auc_3g_ind, == 5, "%u");
llist_del(&conn_inst[5].list);
conn_inst[5].auc_3g_ind = 423;
osmo_gsup_server_add_conn(clients, &conn_inst[5]);
VERBOSE_ASSERT(conn_inst[5].auc_3g_ind, == 5, "%u");
comment_end();
}
int main(int argc, char **argv)
{
printf("test_gsup_server.c\n");
test_add_conn();
printf("Done\n");
return 0;
}

View File

View File

@@ -0,0 +1,94 @@
test_gsup_server.c
===== test_add_conn
Add 10 items
conn_inst[0].auc_3g_ind == 0
conn_inst[1].auc_3g_ind == 1
conn_inst[2].auc_3g_ind == 2
conn_inst[3].auc_3g_ind == 3
conn_inst[4].auc_3g_ind == 4
conn_inst[5].auc_3g_ind == 5
conn_inst[6].auc_3g_ind == 6
conn_inst[7].auc_3g_ind == 7
conn_inst[8].auc_3g_ind == 8
conn_inst[9].auc_3g_ind == 9
Expecting a list of 0..9
conn[0].auc_3g_ind == 0
conn[1].auc_3g_ind == 1
conn[2].auc_3g_ind == 2
conn[3].auc_3g_ind == 3
conn[4].auc_3g_ind == 4
conn[5].auc_3g_ind == 5
conn[6].auc_3g_ind == 6
conn[7].auc_3g_ind == 7
conn[8].auc_3g_ind == 8
conn[9].auc_3g_ind == 9
Punch two holes in the sequence in arbitrary order, a larger one from 2..4 and a single one at 7.
Expecting a list of 0,1, 5,6, 8,9
conn[0].auc_3g_ind == 0
conn[1].auc_3g_ind == 1
conn[2].auc_3g_ind == 5
conn[3].auc_3g_ind == 6
conn[4].auc_3g_ind == 8
conn[5].auc_3g_ind == 9
Add conns, expecting them to take the open slots
conn_inst[12].auc_3g_ind == 2
conn_inst[13].auc_3g_ind == 3
conn_inst[14].auc_3g_ind == 4
conn_inst[17].auc_3g_ind == 7
conn_inst[18].auc_3g_ind == 10
Expecting a list of 0..10
conn[0].auc_3g_ind == 0
conn[1].auc_3g_ind == 1
conn[2].auc_3g_ind == 2
conn[3].auc_3g_ind == 3
conn[4].auc_3g_ind == 4
conn[5].auc_3g_ind == 5
conn[6].auc_3g_ind == 6
conn[7].auc_3g_ind == 7
conn[8].auc_3g_ind == 8
conn[9].auc_3g_ind == 9
conn[10].auc_3g_ind == 10
Does it also work for the first item?
Expecting a list of 1..10
conn[0].auc_3g_ind == 1
conn[1].auc_3g_ind == 2
conn[2].auc_3g_ind == 3
conn[3].auc_3g_ind == 4
conn[4].auc_3g_ind == 5
conn[5].auc_3g_ind == 6
conn[6].auc_3g_ind == 7
conn[7].auc_3g_ind == 8
conn[8].auc_3g_ind == 9
conn[9].auc_3g_ind == 10
Add another conn, should take auc_3g_ind == 0
conn_inst[20].auc_3g_ind == 0
Expecting a list of 0..10
conn[0].auc_3g_ind == 0
conn[1].auc_3g_ind == 1
conn[2].auc_3g_ind == 2
conn[3].auc_3g_ind == 3
conn[4].auc_3g_ind == 4
conn[5].auc_3g_ind == 5
conn[6].auc_3g_ind == 6
conn[7].auc_3g_ind == 7
conn[8].auc_3g_ind == 8
conn[9].auc_3g_ind == 9
conn[10].auc_3g_ind == 10
If a client reconnects, it will (likely) get the same auc_3g_ind
conn_inst[5].auc_3g_ind == 5
conn_inst[5].auc_3g_ind == 5
===== test_add_conn: SUCCESS
Done

View File

@@ -583,7 +583,7 @@ int main()
void *ctx = talloc_named_const(NULL, 0, "main");
osmo_init_logging2(ctx, NULL);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_level(osmo_stderr_target, 1);
log_set_print_category(osmo_stderr_target, 1);
log_set_print_category_hex(osmo_stderr_target, 0);

View File

@@ -233,7 +233,7 @@ int main()
ctx = talloc_named_const(NULL, 0, "main");
osmo_init_logging2(ctx, NULL);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_level(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);

View File

@@ -174,7 +174,7 @@ int main()
ctx = talloc_named_const(NULL, 0, "main");
osmo_init_logging2(ctx, NULL);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_level(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);

View File

@@ -73,7 +73,7 @@ int main()
ctx = talloc_named_const(NULL, 0, "main");
osmo_init_logging2(ctx, NULL);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_level(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);

View File

@@ -13,14 +13,9 @@ OsmoHLR> ?
OsmoHLR> list
...
show gsup-connections
show subscribers all
show subscribers (imei|imsi|msisdn) FILTER
show subscribers (cs|ps) (on|off)
show subscribers last-seen
subscriber (imsi|msisdn|id|imei) IDENT show
show subscriber (imsi|msisdn|id|imei) IDENT
show mslookup services
...
OsmoHLR> enable
OsmoHLR# ?
@@ -30,15 +25,12 @@ OsmoHLR# ?
OsmoHLR# configure terminal
OsmoHLR(config)# ?
...
hlr Configure the HLR
mslookup Configure Distributed GSM mslookup
...
hlr Configure the HLR
mslookup Configure Distributed GSM mslookup
OsmoHLR(config)# list
...
hlr
mslookup
...
OsmoHLR(config)# hlr
OsmoHLR(config-hlr)# ?
@@ -55,7 +47,7 @@ OsmoHLR(config-hlr)# list
database PATH
euse NAME
no euse NAME
ussd route prefix PREFIX internal (own-msisdn|own-imsi|test-idle)
ussd route prefix PREFIX internal (own-msisdn|own-imsi)
ussd route prefix PREFIX external EUSE
no ussd route prefix PREFIX
ussd default-route external EUSE
@@ -97,7 +89,7 @@ log stderr
logging level main notice
logging level db notice
logging level auc notice
logging level ss notice
logging level ss info
logging level mslookup notice
logging level lu notice
logging level dgsm notice
@@ -107,7 +99,6 @@ hlr
database hlr_vty_test.db
gsup
bind ip 127.0.0.1
ipa-name unnamed-HLR
ussd route prefix *#100# internal own-msisdn
ussd route prefix *#101# internal own-imsi
end
@@ -346,9 +337,9 @@ OsmoHLR(config-mslookup-server-msc)# show running-config
mslookup
server
mdns bind 239.192.23.42 4266
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
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 MSC-1
msc msc-901-70-23
service foo.bar at 76.54.32.10 1234
@@ -402,8 +393,8 @@ OsmoHLR(config-mslookup-client)# show running-config
mslookup
server
mdns bind 239.192.23.42 4266
service foo.bar at 123.45.67.89 1011
service baz.bar at 121.31.41.5 1617
service foo.bar at 123.45.67.89 1011
service baz.bar at 121.31.41.5 1617
msc MSC-1
msc msc-901-70-23
service foo.bar at 76.54.32.10 1234
@@ -434,9 +425,9 @@ OsmoHLR(config-mslookup-server)# show running-config
mslookup
server
mdns bind 239.192.23.42 4266
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
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 MSC-1
msc msc-901-70-23
service foo.bar at 76.54.32.10 1234

View File

@@ -11,11 +11,9 @@ OsmoHLR# list
subscriber (imsi|msisdn|id|imei) IDENT update aud2g (comp128v1|comp128v2|comp128v3|xor) ki KI
subscriber (imsi|msisdn|id|imei) IDENT update aud3g none
subscriber (imsi|msisdn|id|imei) IDENT update aud3g milenage k K (op|opc) OP_C [ind-bitlen] [<0-28>]
subscriber (imsi|msisdn|id|imei) IDENT update aud3g xor k K [ind-bitlen] [<0-28>]
subscriber (imsi|msisdn|id|imei) IDENT update imei (none|IMEI)
subscriber (imsi|msisdn|id|imei) IDENT update network-access-mode (none|cs|ps|cs+ps)
show mslookup services
...
OsmoHLR# subscriber?
subscriber Subscriber management commands
@@ -269,7 +267,6 @@ OsmoHLR# subscriber id 101 show
OsmoHLR# subscriber imsi 123456789023000 update aud3g ?
none Delete 3G authentication data
milenage Use Milenage algorithm
xor Use XOR algorithm
OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage ?
k Set Encryption Key K

View File

@@ -22,6 +22,13 @@ cat $abs_srcdir/gsup/gsup_test.err > experr
AT_CHECK([$abs_top_builddir/tests/gsup/gsup_test], [], [expout], [experr])
AT_CLEANUP
AT_SETUP([gsup_server])
AT_KEYWORDS([gsup_server])
cat $abs_srcdir/gsup_server/gsup_server_test.ok > expout
cat $abs_srcdir/gsup_server/gsup_server_test.err > experr
AT_CHECK([$abs_top_builddir/tests/gsup_server/gsup_server_test], [], [expout], [experr])
AT_CLEANUP
AT_SETUP([db])
AT_KEYWORDS([db])
cat $abs_srcdir/db/db_test.ok > expout