Compare commits

...

62 Commits

Author SHA1 Message Date
Harald Welte
e0c6fe5921 Bump version: 0.2.1.55-607c-dirty → 1.0.0
Change-Id: I696beb6f0b82dfaf664f62066cffbcc94e31b700
2019-01-20 20:08:14 +00:00
Harald Welte
f58f44543f test_nodes.vty: Since libosmocore 1.0.0, we only have one space
The output of "logging level" was wrongly indented with two speaces, and
in commit 0d67f483e2d240089105a4d241cb8c9085e245af of libosmocore, first
released as part of libosmcoore-1.0.0 it was fixed.

This makes "make check" pass against libosmocore-1.0.0, but not against
earlier releases.

Change-Id: I2cc12b3f0df19d9055022db41f7f3f2789bd19c6
2019-01-20 20:08:14 +00:00
Oliver Smith
15f624ec53 docs: running: s/OsmoBTS/OsmoHLR
Change-Id: Ib4e1c8460dbe0a9b7dca8d2291a5e6c5406180e7
2019-01-17 12:31:56 +00:00
Oliver Smith
d4e0e4d503 docs: running: same argument order as osmo-hlr -h
Change the Synopsis and Options sections of the Running OsmoHLR chapter
to list the arguments in the same order as osmo-hlr. This makes it
easier to compare, which options are already documented, and which ones
are missing.

A follow-up commit will document the missing -U/--db-upgrade option.

Change-Id: If866124e9cfb43c6986d458712961713541e03b6
2019-01-17 12:31:56 +00:00
Oliver Smith
2dc7d960a1 Cosmetic: fix arg desc of db_subscr_update_msisdn_by_imsi()
Properly note that NULL can only be passed as msisdn, not imsi in that
function.

Change-Id: I19b6ad0cf6e9a4bfa9bffa447ebfc7bd1bcac361
2019-01-15 14:16:16 +01:00
Oliver Smith
52c4aa09b2 gitignore: add tests/hlr_vty_test.db*
Ignore files generated from the VTY test.

Change-Id: I8f55f655fd6694ac9db7e6280670d16fad61ee72
2019-01-15 13:33:10 +01:00
Oliver Smith
66106c0992 Cosmetic: hlr.c: remove confusing indent below if
Remove a misleading block in rx_upd_loc_req() around the call to
lu_op_tx_insert_subscr_data(). At least for me, this made the block look
like it belonged to the if statement above (which has no brackets),
before I looked more closely at it.

Change-Id: I96d3ba4108f4811279caf088a9179afce24e8112
2019-01-09 12:05:15 +01:00
Oliver Smith
783ac81b9c Reply to CHECK-IMEI GSUP messages
Decode the IMEI from incoming CHECK-IMEI messages, print the IMEI to
the log and always send ACK back to the VLR/MSC.

In the future, we will not only log the IMEI, but store it in the HLR
(OS#2541). This is not the original intention of CHECK-IMEI from the
3GPP spec, but an useful side effect.

Depends: I085819df0ea7f3bfeb0cabebb5fd1942a23c6155 (libosmocore)
Related: OS#3733
Change-Id: Ib240474b0c3c603ba840cf26babb38a44dfc9364
2019-01-07 09:43:37 +00:00
Vadim Yanitskiy
df8d454919 gsupclient: introduce osmo_gsup_client_enc_send()
Several parts of OsmoMSC (e.g. GSM 04.11, 09.11, etc.) are dealing
with GSUP message encoding and sending towards OsmoHLR. In order
to avoid code duplication, let's have a shared function here.

Change-Id: I0589ff27933e9bca2bcf93b8259004935778db8f
2018-12-20 01:16:03 +07:00
Oliver Smith
9ea9bbbc7f contrib: fix makedistcheck with disabled systemd
EXTRA_DIST files need to be distributed, no matter if the systemd option
is configured or not.

Change-Id: Ic164403189510f3b20ff7906df09c78550735591
2018-12-13 15:37:03 +00:00
Stefan Sperling
5c14c9ccca display last location update timestamp in vty
Read the subscriber's last location update timestamp from the
database and display it in the output of 'show subscriber'.

For example:
  OsmoHLR> show subscriber id 1
      ID: 1
      IMSI: 123456789000000
      MSISDN: 543210123456789
      VLR number: 712
      SGSN number: 5952
      last LU seen: Fri Dec  7 11:30:51 2018 UTC

While the database stores the timestamp as a string, we
convert the timestamp into time_t for internal use.
This allows for flexible potential use of the timestamp
in contexts other than the VTY in the future.

The timestamp displayed in the VTY is created with ctime_r(3).
It does not match the format of the raw string in the database:
  sqlite> select id,last_lu_seen from subscriber;
  1|2018-12-07 11:30:51

Related: OS#2838
Change-Id: Ie180c434f02ffec0d4b2f651a73258a8126b2e1a
2018-12-10 16:12:06 +00:00
Stefan Sperling
705b61bcb7 add whitespace around PRId64 constants
Avoid string concatenations without interleaving whitespace.
Some compilers don't like "foo""bar", they only like "foo" "bar".

Requested by: Pau
https://gerrit.osmocom.org/c/osmo-hlr/+/12121/5/src/db_hlr.c#637

Change-Id: Ic7a81114f9afbefcbd62d434720854cfdd4a2dd9
2018-12-07 12:45:19 +01:00
Stefan Sperling
638ba8cc04 store a timestamp of the last location update seen from a subscriber
Timestamps are stored in the HLR DB in the new 'last_lu_seen' column
of the 'subscriber' table, in UTC and in granularity of seconds.

At present, osmo-hlr only records these timestamps but otherwise
makes no use of them. Because the timestamps are stored in a
human-readable form, they may already provide value to external
processes which need this information. For example:

  sqlite> select imsi,last_lu_seen from subscriber;
  901990000000001|2018-12-04 14:17:12

I didn't bother adding additional tests because the code added
with this commit is already being exercised by several calls
to db_subscr_lu() in db_test.c.

This change requires a HLR DB schema update. Existing databases
won't be upgraded automatically. However, osmo-hlr will refuse
to operate with databases which are not upgraded.

Change-Id: Ibeb49d45aec18451a260a6654b8c51b8fc3bec50
Related: OS#2838
2018-12-07 11:50:06 +01:00
Stefan Sperling
55f5efa568 introduce osmo_gsup_client_create2()
Add a new API which allows creating a GSUP client connection with
more identification information than just a unit name. Instead of
being selective about which idenfifiers callers may use, allow
callers to pass a full-blown struct ipaccess_unit. This allows
applications to use entirely custom identifiers on GSUP client
connections.

This change is a prerequisite for inter-MSC handover because MSCs
will need to use unique identifiers towards the HLR, which isn't
very easy to do with the old osmo_gsup_client_create() API. While
it's always been possible to pass a unique unit_name, this is not
as flexible as we would like.

The old API remains for backwards compatibility.
struct osmo_gsup_client grows in size but is allocated internally
by the library; old calling code won't notice the difference.

Change-Id: Ief09677e07d6e977247185b72c605f109aa091f5
Related: OS#3355
2018-12-06 11:03:51 +00:00
Vadim Yanitskiy
e6ce52bbde SS/USSD: fix: properly (re)schedule NCSS session timeout
It may happen that either the MS or an ESME would become
unresponsive, e.g. due to a bug, or a dropped message. This
is why we have SS session timeout, that prevents keeping
'stalled' sessions forever.

For some reason, it wasn't properly resceduled in case of
subsequent SS/USSD activity, so the lifetime of a session
was limited. Let's properly (re)schedule it.

Change-Id: I11aeacf012b06d3d0b5cc6e64baecf857b645fda
Related: OS#3717
2018-12-05 19:45:34 +00:00
Vadim Yanitskiy
d157a56361 SS/USSD: make NCSS session timeout configurable
It may happen that either the MS or an ESME would become
unresponsive, e.g. due to a bug, or a dropped message.
This is why we have SS session timeout, that prevents
keeping 'stalled' sessions forever.

Let's introduce a VTY option, which can be used to configure
this timer (by default it's set to 30 seconds):

hlr
  ...
  ! Use 0 to disable this timer
  ncss-guard-timeout 30

Change-Id: I971fc2cee6fd46d4d5d6dac6c634e0b22fff183d
Related: OS#3717
2018-12-05 19:45:34 +00:00
Vadim Yanitskiy
9c8806acf5 SS/USSD: release IUSE sessions immediately after response
At the moment, all available IUSE handlers do assume a single
request-response operation, e.g. MS requests its MSISDN - IUSE
responds. No further nor intermediate communications is required.

Let's immediately terminate such SS sessions in order to avoid
waiting for the session inactivity watchdog (i.e. timeout).

Change-Id: Iaefe37512da79e10fbe92378236bfff0eae0f8b9
2018-12-05 19:45:34 +00:00
Oliver Smith
4b8be4d12d contrib/jenkins.sh: build and publish manuals
Add new environment variables WITH_MANUALS and PUBLISH to control if
the manuals should be built and uploaded. Describe all environment vars
on top of the file.

When WITH_MANUALS is set, install osmo-gsm-manuals like any other
dependency and add --enable-manuals to the configure flags (for "make"
and "make distcheck"). Add the bin subdir of the installed files to
PATH, so osmo-gsm-manuals-check-depends can be used by ./configure.

Related: OS#3385
Change-Id: Ia1a6ab64c7912f30a693ca4f5f474f22d72b8873
2018-12-05 13:08:07 +01:00
Oliver Smith
bc9bead62a Fix DISTCHECK_CONFIGURE_FLAGS override
Set AM_DISTCHECK_CONFIGURE_FLAGS in Makefile.am instead of
DISTCHECK_CONFIGURE_FLAGS. This is the recommended way from the
automake manual, as otherwise the flag can't be changed by the user
anymore.

Related: OS#3718
Change-Id: I62a0c0e9cb384f8909229e153ade5567328e5c61
2018-12-04 15:11:07 +00:00
Neels Hofmeyr
4655e6f1fe Rename db_bootstrap.sed to db_sql2c.sed
Side effect of the db schema patch, now a mere cosmetic change.

Change-Id: I47a101e3b76b2125d786f22bf100604cf5e8eb40
2018-12-04 14:13:47 +01:00
Stefan Sperling
8f3a7cce80 add database schema versioning to the HLR database
Make use of pragma user_version to store our database schema version.
The present schema is now identitifed as 'version 0', which is also
the default value for databases on which we never ran the statement
'pragma user_version' before.

Only bootstrap the database if it hasn't been bootstrapped yet.
Previously, bootstrap SQL statements ran every time osmo-hlr
opened the database, and any errors were being ignored in SQL.
Instead, we now first run a query which checks whether tables
already exist, and only create them if necessary.
This change will allow future schema updates to work properly.

Prepare for future schema upgrades by adding a new command-line
option which enables upgrades. This option defaults to 'false'
in order to avoid accidental upgrades.

Change-Id: I8aeaa9a404b622657cbc7138106f38aa6ad8d01b
Related: OS#2838
2018-12-04 14:13:28 +01:00
Neels Hofmeyr
a820ea1f67 implement removal of MSISDN
Add the first "official" way to remove the MSISDN from a subscriber entry, to
go back to 'MSISDN: none' like just after 'subscriber create'.

Add VTY command 'subscriber <ID> update msisdn none' to drop the MSISDN from
the subscriber. (Like 'subscriber <ID> update aud3g none')

Add DB_STMT_DELETE_MSISDN_BY_IMSI.

In db_subscr_update_msisdn_by_imsi(), allow passing a NULL msisdn, and if NULL,
call above delete SQL statement.

Change-Id: I15419105ea461137776adb92d384d8985210c90e
2018-12-02 20:16:31 +01:00
Neels Hofmeyr
8aa780bf80 add 'show subscriber' command, alias for 'subscriber ... show'
When I wrote the osmo-hlr subscriber command, I failed to heed the common
'show foo' scheme and instead created a 'subscriber [...] show' command.
Relieve that weirdness by creating an alias that has 'show' at the start.

Arrange string macros so that the 'show subscriber' cmd doesn't end in a space
(the SUBSCR macro ends in a space ' ' to implicitly include the space to
commands like 'create', 'show', 'update').

Add the new command to test_nodes.vty and test_subscriber.vty.

Change-Id: I01ce9b0868302d40ed05c6a588316a194d6071e4
2018-12-02 20:06:24 +01:00
Oliver Smith
f08da2459b build manuals moved here from osmo-gsm-manuals.git
Moved to doc/manuals/, with full commit history, in preceding merge commit.
Now incorporate in the build system.

Build with:

$ autoreconf -fi
$ ./configure --enable-manuals
$ make

Shared files from osmo-gsm-manuals.git are found automatically if
- the repository is checked out in ../osmo-gsm-manuals; or
- if it osmo-gsm-manuals was installed with "make install"; or
- OSMO_GSM_MANUALS_DIR is set.

Related: OS#3385
Change-Id: I52b7b06fddd77c6dc272004f434e9e7651f6b349
2018-11-27 18:02:06 +01:00
Neels Hofmeyr
62ce834fbf Merge history from osmo-gsm-manuals.git
Change-Id: I175d694bea8f0be4edb2be75de780dee82469849
2018-11-27 18:01:37 +01:00
Neels Hofmeyr
bf6b4eb0b9 hlr: update vty reference
Change-Id: I5a2322c0488cb36b089303bbfec5d4db18c41099
2018-11-27 18:01:17 +01:00
Daniel Willmann
79efdf3474 OsmoBSC/HLR/MSC: Fix default config file name
Mention that the default is not openbsc.cfg, but osmo-*.cfg

Change-Id: I139e6004e28d6f918f31792e634214a6153edd0e
2018-11-27 18:01:17 +01:00
Harald Welte
b41394a700 hlr: Add chapter on USSD configuration
Change-Id: Ie5196ece9f340303bcb4868e3333a9698e28c3d0
2018-11-27 18:01:17 +01:00
Neels Hofmeyr
0c331abdbc hlr: clarify that aud_3g also applies to 2G
(and tweak wording)

Related: OS#3091
Change-Id: I2ae03625ccc1364f953db0611e9a29d935dab803
2018-11-27 18:01:17 +01:00
Harald Welte
25e716c849 vty-ref: Update URI of docbook 5.0 schema
... to match the /etc/xml/catalog file on debian (no "www" in hostname)

Change-Id: Id9f3579c7f2bc3af13fe30b5268f249b6f59ed0d
2018-11-27 18:01:17 +01:00
Neels Hofmeyr
92e49ef363 OsmoHLR: update vty reference
Add new (generic) logging commands, talloc context print commands.
Add the jitter buffer logging category.

Remove the common commands from the individual sections.

Change-Id: I8e92c821b26908d4baa37745c57b9e15bda2a373
2018-11-27 18:01:17 +01:00
Harald Welte
95380ab037 share chapters/gsup.adoc from OsmoSGSN to OsmoMSC + OsmoHLR
Since the NITB split, GSUP is used in all three network elements, so
make the protocol a shared chapter

Change-Id: Id2d7c27ef16eb0ebe5f60d625a1fcf42f1603f4f
2018-11-27 18:01:17 +01:00
Neels Hofmeyr
849bfd0bef OsmoHLR: update section 'Bootstrap the Database'
Change-Id: I1b7b99cc77d8cd8cce42e7cf93f5e2cd3c2344db
2018-11-27 18:01:17 +01:00
Neels Hofmeyr
7e2d3c7f4c OsmoHLR: update ctrl description and examples
The patch to refactor ctrl commands to osmo-hlr, change
I98ee6a06b3aa6a67adb868e0b63b0e04eb42eb50, was tweaked. Adjust accordingly.

Change-Id: Ie4da6115bb2eb005a9f95bf4de1bfe36468fd607
2018-11-27 18:01:17 +01:00
Neels Hofmeyr
8f725ae655 OsmoHLR: add make target to update the example ctrl and vty files
Change-Id: I88027396ec15101697a79aa5e964387d47abdae2
2018-11-27 18:01:17 +01:00
Neels Hofmeyr
1ed4bb4ff1 refactor Makefile build rules, don't use the FORCE
The initial goal was to make sure we don't have overall FORCE rules causing
unnecessary rebuilds -- annoying while writing documentation. As I looked
through possible dependencies, I finally understood what's going on here.

Remove code dup and nicely sort which belongs where in build/Makefile.*.inc. In
each, describe in a top comment how to use it, and also unify how they are
used:

- Rename Makefile.inc to Makefile.docbook.inc and refactor
- Add Makefile.vty-reference.inc
- Add Makefile.common.inc

Make sure that we accurately pick up all dependencies.

Drop use of the macro called 'command', that silenced the actual command lines
invoked and replaced them with short strings: it obscures what is actually
going on and makes the Makefiles hard to read and understand.

Each manual's makefile is greatly reduced to few definitions and a Makefile
include, e.g. one for asciidoc, one for VTY reference.

Move common/bsc_vty_additions.xml to OsmoBSC/vty/libbsc_vty_additions.xml, link
from OsmoNITB. It applies only to OsmoBSC and OsmoNITB.

Add a script that combines a VTY reference file with *all* additions files
found in a manual's vty/ dir. Call this from Makefile.vty-reference.inc.

Change-Id: I9758e04162a480e28c7dc83475b514cf7fd25ec0
2018-11-27 18:01:17 +01:00
Neels Hofmeyr
25dd785157 add OsmoHLR manual, OsmoHLR VTY reference
Change-Id: Ieb6a362a26a7e65199f68f5cd32d9b6b0e5d0fbf
2018-11-27 18:01:17 +01:00
Neels Hofmeyr
4f5f6f83f3 Importing history from osmo-gsm-manuals.git
Change-Id: I2907e34a3bdba41142ffd4be43ce0e3e9a86aa42
2018-11-27 17:59:14 +01:00
Neels Hofmeyr
e66e525e09 fix and re-enable osmo_hlr_subscriber_update_notify()
Send updated subscriber data out to exactly those GSUP clients that match the
last LU operations (depending on each client sending distinct identification).

As this adds logging on DLGSUP, also change adjacent GSUP related logging from
DMAIN to DLGSUP.

Related: OS#2785
Change-Id: I7c317de8329d9a115d072fc61ddb9abc21b7e8d8
2018-10-16 13:04:00 +02:00
Vadim Yanitskiy
4a4bdcdf97 hlr_ussd.c: fix: reject 'structured' SS requests
As we don't store any SS related information (e.g. call forwarding
preferences) in the database, we don't handle 'structured' SS
requests at all. Let's reject them by sending error message
with FACILITY_NOT_SUPPORTED code.

Change-Id: Ia1317c5d372a42473cce65c0c985103e43be77fd
Related: OS#3651
2018-10-12 21:46:11 +02:00
Neels Hofmeyr
cb364bb429 store gsup peer upon accepting LU
Store the GSUP client's IPA_IDTAG_SERNR in vlr_number or sgsn_number (depending
on is_ps), just before sending the Insert Subscriber Data message after a
successful LU Req. Log about it.

Original patch: Ib2611421f3638eadc361787af801fffe9a34bd8a by laforge
Related: OS#2796

Change-Id: If438664faa5d68404f465f8b2002c6d03bbf3ceb
2018-10-05 17:55:18 +02:00
Neels Hofmeyr
d646207553 more space for vlr_number and sgsn_number
To be able to handle our 20 chars long IPA tags, enlarge the vlr_number and
sgsn_number storage in struct hlr_subscriber.

Technically, osmo-hlr should be able to store any type of Global Title, blob of
arbitrary size. For our purposes, 32 is enough for now.

Related: OS#2796
Change-Id: I0121f1c4dbda3a076d780a3834281b21dab85493
2018-10-04 14:31:18 +02:00
Neels Hofmeyr
6cee799d5e fix is_ps logic in rx_upd_loc_req(), store CS as is_ps = false
A missing 'else' in rx_upd_loc_req() causes *all* clients to be indicated as
is_ps=true regardless of the GSUP CN Domain IE that was received.

Replace that odd if cascade with a switch() that fixes the flawed logic. Hence
osmo-hlr now correctly indicates each client's is_ps, iff the client sends CN
Domain IEs in GSUP LU Request messages.

Related: OS#2796, OS#3601
Change-Id: I2c5fa9f5cae25cfd66afbf088303edff7d045a00
2018-09-28 03:01:02 +02:00
Harald Welte
c88bdab96d fix VTY tests to expect well-formed output of logging configuration
In libosmocore Change-Id Ia75c7067284ea225cffe13ca71bad05a7747ae66
we fixed the generation (saving) of logging configuration to use
one level of indent, rather than the previous broken implementation
with two levels of indent.  This means we have to adjust the VTY
test expectations here.

Change-Id: I9282a59bbfad4cfc86e86c1212c74cccfe187ff8
2018-09-26 09:23:37 +02:00
Harald Welte
607ce5ca93 osmo-hlr.cfg: Ensure well-formed config file example
Change-Id: I136fdda6f4088f2e8094eed6fe9481f5d6476d4f
2018-09-25 20:36:40 +02:00
Neels Hofmeyr
ccdb970c57 make: always allow running python tests manually
Keep the rules to run the external-tests in tests/Makefile available for
invocation, to allow conveniently launching the tests manualy without the need
to pass --with-external-tests to ./configure first.

Change-Id: Ic28dbeabddee6b41af12b977e3fe59e663ee51a1
2018-09-18 15:09:05 +02:00
Neels Hofmeyr
a5b36a0904 tweak example config
Remove 'logging level all' setting.
Tweak some more logging details (to my current favorite).

Add USSD example for showing the IMSI.

Change-Id: I8296832704d779df5f1b20a595b568c99780e64d
2018-09-18 15:09:05 +02:00
Neels Hofmeyr
13000d8d14 fix build: adjust test_nodes.vty to logging change
Since libosmocore
commit eb9284ba577d338f74653fcf09ebca0c397823eb
Change-Id I36f17c131cc70ce5a1aef62fd9693097de230cd4
"logging vty: deprecate 'all', introduce 'force-all'"
,
'logging level all' is replaced by 'force-all'.
Adjust the test script to not expect 'logging level all'.

While at it, remove some more expectations that aren't important.

Change-Id: Ia170f8416ebb60c499d2536078f43f28b61d0554
2018-09-13 18:14:05 +02:00
Pau Espin Pedrol
7ebfd065da Install sample cfg file to /etc/osmocom
Change-Id: I2ac73f426d9489fcd64c2eea547eb1f3ec26ae2c
2018-09-12 18:31:09 +02:00
Pau Espin Pedrol
966fcb2ca8 Move doc/Makefile.am to doc/examples/Makefile.am
Change-Id: I89ef3abe86e7ceb7b389ac1cb227ea065bfbce37
2018-09-12 18:28:24 +02:00
Pau Espin Pedrol
6fe1c2220a Install systemd services with autotools
Change-Id: Id81ae8e2d8a2c9456ac0dac2c30c0d24dab3b694
2018-09-10 16:09:59 +02:00
Harald Welte
0da9f2f19c libosmo-gsup-client: License is GPLv2-or-later
Fix the unintentional AGPLv3-or-later license header in gsup_client.c

Change-Id: I6378bd59fdbe8d95cd6132a1cbc40ae29b558c42
2018-09-03 15:19:24 +02:00
Harald Welte
1eb9869d81 USSD: Fix "ussd default-route"
Before this patch, the default route logic was not implemented.  The
user could specify a default-route, but it wouldn't be used by the
actual routing logic.  Let's fix that.

Change-Id: I0b04a75dc297f088f13da413d08c52e0747e46e6
2018-08-08 08:58:54 +02:00
Vadim Yanitskiy
3adb33de93 hlr_ussd.c: avoid using CR and NL in IUSE responses
According to GSM TS 03.38, section 6.1.2.1, CR symbol at the end
is optional, and moreover libosmogsm encoding API will carry
about the bit padding itself.

Change-Id: I09e8a67758698f3b7a578eab956311e269d091ee
2018-08-08 06:21:26 +00:00
Harald Welte
791ea72ee4 debian: Make libosmo-gsup-client-dev depend on libosmo-gsup-client0
The -dev package should depend on the binary package.

Change-Id: I01e58788e8485cadecad8b8788887743b41b9f1f
2018-08-06 18:38:20 +02:00
Harald Welte
9f7b69a618 gitignore: Add .tarball-version
Change-Id: I2ed792ef248e2b62f63acc7d83add240df03336f
2018-08-06 14:23:33 +02:00
Vadim Yanitskiy
e6c839ed2d hlr_ussd.c: fix: properly print a EUSE / IUSE name
We need to distinguish between both EUSE and IUSE, and properly
print their names. Otherwise, garbage is printed in case of IUSE.

Change-Id: I497e7c1fe41279afdb1256ee69e166066a6462bb
2018-08-03 00:00:28 +07:00
Vadim Yanitskiy
b93c44f32e USSD/hlr_vty.c: print error if EUSE is not found
Change-Id: I18045c5e544a99b2414a6f0268f1343df119b9f3
2018-08-02 23:58:32 +07:00
Vadim Yanitskiy
a05efe8803 hlr_ussd.h: drop meaningless forward declaration
Change-Id: I70a5c7c83c2356b779fb1ea7ffe07ccc1e279c22
2018-08-01 10:24:06 +00:00
Martin Hauke
764514198b debian: Fix typo in package description
Change-Id: Iaa5a3b9e249ce493221569cf9411cc04c4044a0c
2018-07-31 20:13:09 +02:00
Harald Welte
5198609a5e pkg-config: Fix libosmo-gsup-client pkg-config file
Both description and actual "Libs" line were broken.

Change-Id: I92e625418d53a2f17feca87c72624b84183f8cdb
2018-07-31 19:37:45 +02:00
Harald Welte
7c2f430fc5 debian: Add sub-package for libosmo-gsup-client
Change-Id: Iad227bb477d64da30dd6bfbbe1bd0c0a55be9474
2018-07-31 19:19:43 +02:00
52 changed files with 2796 additions and 205 deletions

13
.gitignore vendored
View File

@@ -5,6 +5,7 @@
*.pyc
.*.sw?
.version
.tarball-version
Makefile
Makefile.in
aclocal.m4
@@ -45,3 +46,15 @@ tests/auc/auc_test
tests/gsup_server/gsup_server_test
tests/gsup/gsup_test
tests/db/db_test
tests/hlr_vty_test.db*
# manuals
doc/manuals/*.html
doc/manuals/*.svg
doc/manuals/*.pdf
doc/manuals/*__*.png
doc/manuals/*.check
doc/manuals/generated/
doc/manuals/osmomsc-usermanual.xml
doc/manuals/common
doc/manuals/build

View File

@@ -5,6 +5,7 @@ SUBDIRS = \
src \
include \
sql \
contrib \
tests \
$(NULL)
@@ -12,6 +13,9 @@ EXTRA_DIST = \
.version \
$(NULL)
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libosmo-gsup-client.pc

View File

@@ -34,11 +34,11 @@ PKG_PROG_PKG_CONFIG([0.20])
PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.5.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.6.0)
PKG_CHECK_MODULES(SQLITE3, sqlite3)
@@ -92,17 +92,79 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
AC_MSG_RESULT([$enable_ext_tests])
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
# Generate manuals
AC_ARG_ENABLE(manuals,
[AS_HELP_STRING(
[--enable-manuals],
[Generate manual PDFs [default=no]],
)],
[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
fallback])
if test x"$osmo_ac_build_manuals" = x"yes"
then
# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
if test -n "$OSMO_GSM_MANUALS_DIR"; then
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
else
OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
if test -n "$OSMO_GSM_MANUALS_DIR"; then
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
else
OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
fi
fi
if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
fi
# Find and run check-depends
CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
if ! test -x "$CHECK_DEPENDS"; then
CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
fi
if ! $CHECK_DEPENDS; then
AC_MSG_ERROR("missing dependencies for --enable-manuals")
fi
# Put in Makefile with absolute path
OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
AC_SUBST([OSMO_GSM_MANUALS_DIR])
fi
# https://www.freedesktop.org/software/systemd/man/daemon.html
AC_ARG_WITH([systemdsystemunitdir],
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
[with_systemdsystemunitdir=auto])
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
with_systemdsystemunitdir=no],
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
AC_OUTPUT(
Makefile
doc/Makefile
doc/examples/Makefile
src/Makefile
src/gsupclient/Makefile
include/Makefile
libosmo-gsup-client.pc
sql/Makefile
doc/manuals/Makefile
contrib/Makefile
contrib/systemd/Makefile
tests/Makefile
tests/auc/Makefile
tests/auc/gen_ts_55_205_test_sets/Makefile

1
contrib/Makefile.am Normal file
View File

@@ -0,0 +1 @@
SUBDIRS = systemd

View File

@@ -1,5 +1,10 @@
#!/bin/sh
# jenkins build helper script for osmo-hlr. This is how we build on jenkins.osmocom.org
#
# environment variables:
# * WITH_MANUALS: build manual PDFs if set to "1"
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
#
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
@@ -22,10 +27,18 @@ verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
export PATH="$inst/bin:$PATH"
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
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
set +x
echo
echo
@@ -36,9 +49,13 @@ set -x
cd "$base"
autoreconf --install --force
./configure --enable-sanitize --enable-external-tests --enable-werror
./configure --enable-sanitize --enable-external-tests --enable-werror $CONFIG
$MAKE $PARALLEL_MAKE
$MAKE check || cat-testlogs.sh
$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
osmo-clean-workspace.sh

View File

@@ -0,0 +1,6 @@
EXTRA_DIST = osmo-hlr.service
if HAVE_SYSTEMD
systemdsystemunit_DATA = \
osmo-hlr.service
endif

72
debian/changelog vendored
View File

@@ -1,3 +1,75 @@
osmo-hlr (1.0.0) unstable; urgency=medium
[ Stefan Sperling ]
* move creation of insert subscriber data messages to a common function
[ Harald Welte ]
* Return proper GSUP error in case of too short IMSI
* disable blind subscriber insertion into every VLR/SGSN
* gsup_server: Add "priv" pointer and make it point to 'struct hlr'
* move osmo_gsup_addr_send() declaration from luop.h to gsup_router.h
* gsup_router: Use "#pragma once" and add missing #includes
* Add "show gsup-connections" VTY command
* import gsup_client.c as new libosmo-gsup-client
* gsup_client: rename gsup_client_* to osmo_gsup_client_*
* GSUP: Log GSUP route add/remove
* hlr: Export + Declare global g_hlr symbol
* USSD: Add Core USSD handling + VTY routing config to HLR
* USSD: Add basic dispatch + decode of GSUP-encapsulated SS/USSD
* hlr_ussd: Introduce LOGPSS() macro
* USSD: Send ReturnError component if USSD Code unknown / EUSE disconnected
* USSD: Further unification of log output; Use LOGPSS when possible
* osmo-hlr.cfg: Don't enable DEBUG logging by default
* USSD: Add new "DSS" logging category and use it appropriately
* USSD: fix null-pointer deref in "default-route" vty/config cmd
* Add osmo-euse-demo as minimalistic test of a External USSD (EUSE) handler
* USSD: Add support for internal USSD handlers
* debian: Add sub-package for libosmo-gsup-client
* pkg-config: Fix libosmo-gsup-client pkg-config file
* gitignore: Add .tarball-version
* debian: Make libosmo-gsup-client-dev depend on libosmo-gsup-client0
* USSD: Fix "ussd default-route"
* libosmo-gsup-client: License is GPLv2-or-later
* osmo-hlr.cfg: Ensure well-formed config file example
* test_nodes.vty: Since libosmocore 1.0.0, we only have one space
[ Martin Hauke ]
* sql/Makefile.am: Make docsdir completely configurable
* debian: Fix typo in package description
[ Pau Espin Pedrol ]
* debian: Avoid installing duplicate cfg file in /etc
* sql/Makefile: Install hlr_data.sql as example together with hlr.sql
* sql/Makefile: Install sql files under doc/.../sql subdir
* sql/Makefile: Create empty /var/lib/osmocom directory at install time
* Install systemd services with autotools
* Move doc/Makefile.am to doc/examples/Makefile.am
* Install sample cfg file to /etc/osmocom
[ Vadim Yanitskiy ]
* hlr.c: move deinitialization code from SIGINT handler
* hlr.c: free root talloc context on exit
* hlr.c: track the use of talloc NULL memory contexts
* src/db.c: fix: make sure the database is properly closed
* src/db.c: don't ignore the result of db_bootstrap()
* hlr_vty_subscr.c: fix subscriber creation command help
* Update .gitignore: add missing build products
* tests/Makefile.am: also remove temporary sqlite files
* hlr_ussd.h: add #pragma once include guard
* hlr_ussd.h: use proper libc headers
* Update .gitignore: ignore osmo-euse-demo
* hlr_ussd.h: drop meaningless forward declaration
* USSD/hlr_vty.c: print error if EUSE is not found
* hlr_ussd.c: fix: properly print a EUSE / IUSE name
* hlr_ussd.c: avoid using CR and NL in IUSE responses
[ Neels Hofmeyr ]
* fix build: adjust test_nodes.vty to logging change
* tweak example config
* make: always allow running python tests manually
-- Harald Welte <laforge@gnumonks.org> Sun, 20 Jan 2019 19:29:58 +0100
osmo-hlr (0.2.1) unstable; urgency=medium
[ Neels Hofmeyr ]

25
debian/control vendored
View File

@@ -32,3 +32,28 @@ Priority: extra
Depends: osmo-hlr (= ${binary:Version}), ${misc:Depends}
Description: Debug symbols for the osmo-hlr
Make debugging possible
Package: libosmo-gsup-client0
Section: libs
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends},
${misc:Depends}
Pre-Depends: ${misc:Pre-Depends}
Description: Osmocom GSUP (General Subscriber Update Protocol) client library
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: libosmo-gsup-client-dev
Architecture: any
Multi-Arch: same
Depends: ${misc:Depends},
libosmo-gsup-client0 (= ${binary:Version}),
libosmocore-dev
Description: Development headers of Osmocom GSUP client library
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 package contains the development headers.

View File

@@ -0,0 +1,5 @@
usr/include/osmocom/gsupclient
usr/lib/*/libosmo-gsup-client*.a
usr/lib/*/libosmo-gsup-client*.so
usr/lib/*/libosmo-gsup-client*.la
usr/lib/*/pkgconfig/libosmo-gsup-client.pc

1
debian/libosmo-gsup-client0.install vendored Normal file
View File

@@ -0,0 +1 @@
usr/lib/*/libosmo-gsup-client*.so.*

View File

@@ -1,3 +1,5 @@
/etc/osmocom/osmo-hlr.cfg
/lib/systemd/system/osmo-hlr.service
/usr/bin/osmo-hlr
/usr/bin/osmo-hlr-db-tool
/usr/share/doc/osmo-hlr/sql/hlr.sql

View File

@@ -1 +0,0 @@
../contrib/systemd/osmo-hlr.service

3
debian/rules vendored
View File

@@ -15,3 +15,6 @@ override_dh_strip:
# Print test results in case of a failure
override_dh_auto_test:
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
override_dh_auto_configure:
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system

View File

@@ -1,22 +1,4 @@
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
dist-hook:
for f in $$($(CFG_FILES)); do \
j="$(distdir)/$$f" && \
mkdir -p "$$(dirname $$j)" && \
$(INSTALL_DATA) $(srcdir)/$$f $$j; \
done
install-data-hook:
for f in $$($(CFG_FILES)); do \
j="$(DESTDIR)$(docdir)/$$f" && \
mkdir -p "$$(dirname $$j)" && \
$(INSTALL_DATA) $(srcdir)/$$f $$j; \
done
uninstall-hook:
@$(PRE_UNINSTALL)
for f in $$($(CFG_FILES)); do \
j="$(DESTDIR)$(docdir)/$$f" && \
$(RM) $$j; \
done
SUBDIRS = \
examples \
manuals \
$(NULL)

27
doc/examples/Makefile.am Normal file
View File

@@ -0,0 +1,27 @@
osmoconfdir = $(sysconfdir)/osmocom
osmoconf_DATA = osmo-hlr.cfg
EXTRA_DIST = osmo-hlr.cfg
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
dist-hook:
for f in $$($(CFG_FILES)); do \
j="$(distdir)/$$f" && \
mkdir -p "$$(dirname $$j)" && \
$(INSTALL_DATA) $(srcdir)/$$f $$j; \
done
install-data-hook:
for f in $$($(CFG_FILES)); do \
j="$(DESTDIR)$(docdir)/examples/$$f" && \
mkdir -p "$$(dirname $$j)" && \
$(INSTALL_DATA) $(srcdir)/$$f $$j; \
done
uninstall-hook:
@$(PRE_UNINSTALL)
for f in $$($(CFG_FILES)); do \
j="$(DESTDIR)$(docdir)/examples/$$f" && \
$(RM) $$j; \
done

View File

@@ -2,17 +2,18 @@
! OsmoHLR example configuration
!
log stderr
logging filter all 1
logging color 1
logging print category 1
logging timestamp 1
logging print extended-timestamp 1
logging level all notice
logging level main notice
logging level db notice
logging level auc notice
logging level ss info
logging level linp error
logging filter all 1
logging color 1
logging print category 1
logging print category-hex 0
logging print level 1
logging print file basename last
logging print extended-timestamp 1
logging level main notice
logging level db notice
logging level auc notice
logging level ss info
logging level linp error
!
line vty
bind 127.0.0.1
@@ -22,3 +23,4 @@ hlr
gsup
bind ip 127.0.0.1
ussd route prefix *#100# internal own-msisdn
ussd route prefix *#101# internal own-imsi

59
doc/manuals/Makefile.am Normal file
View File

@@ -0,0 +1,59 @@
EXTRA_DIST = example_subscriber_add_update_delete.vty \
example_subscriber_cs_ps_enabled.ctrl \
example_subscriber_info.ctrl \
osmohlr-usermanual.adoc \
osmohlr-usermanual-docinfo.xml \
osmohlr-vty-reference.xml \
chapters \
vty
if BUILD_MANUALS
ASCIIDOC = osmohlr-usermanual.adoc
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc $(srcdir)/*.vty $(srcdir)/*.ctrl
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
VTY_REFERENCE = osmohlr-vty-reference.xml
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
endif
TMP_DB = generated/hlr.db
update-examples: update-examples-ctrl update-examples-vty
.PHONY: found-update-deps
found-update-deps:
@if [ ! -f "$(top_srcdir)/sql/hlr.sql" ]; then \
echo "You need to define OSMO_HLR_PATH to point at an osmo-hlr.git"; \
exit 1; \
fi
@if [ -z "$(shell which osmo-hlr)" ]; then \
echo "osmo-hlr needs to be installed / available in the PATH"; \
exit 1; \
fi
@if [ -z "$(shell which osmo_verify_transcript_ctrl.py)" ]; then \
echo "You need to install git.osmocom.org/python/osmo-python-tests.git"; \
exit 1; \
fi
@if [ -z "$(shell which osmo_verify_transcript_vty.py)" ]; then \
echo "You need to install git.osmocom.org/python/osmo-python-tests.git"; \
exit 1; \
fi
update-examples-ctrl: found-update-deps
mkdir -p generated
rm -f "$(TMP_DB)"
sqlite3 "$(TMP_DB)" < "$(top_srcdir)/sql/hlr.sql"
sqlite3 "$(TMP_DB)" < "$(top_srcdir)/tests/test_subscriber.sql"
osmo_verify_transcript_ctrl.py \
-r "osmo-hlr -l $(TMP_DB) -c $(top_srcdir)/doc/examples/osmo-hlr.cfg" \
-p 4259 --update *.ctrl
update-examples-vty: found-update-deps
mkdir -p generated
rm -f "$(TMP_DB)"
sqlite3 "$(TMP_DB)" < "$(top_srcdir)/sql/hlr.sql"
osmo_verify_transcript_vty.py \
-r "osmo-hlr -l $(TMP_DB) -c $(top_srcdir)/doc/examples/osmo-hlr.cfg" \
-p 4258 --update *.vty

View File

@@ -0,0 +1,106 @@
[[hlr-ctrl]]
== Control interface
The actual protocol is described in <<common-control-if>>, the variables common
to all programs using it are described in <<ctrl_common_vars>>. This section
describes the CTRL interface variables specific to OsmoHLR.
All subscriber variables are available by different selectors, which are freely
interchangeable:
.Subscriber selectors available on OsmoHLR's Control interface
[options="header",width="100%",cols="35%,65%"]
|===
|Selector|Comment
|subscriber.*by-imsi-*'123456'.*|Subscriber selector by IMSI, replace "123456" with the actual IMSI
|subscriber.*by-msisdn-*'123456'.*|Subscriber selector by MSISDN
|subscriber.*by-id-*'123456'.*|Subscriber selector by database ID
|===
Each of the above selectors feature all of these control variables:
.Subscriber variables available on OsmoHLR's Control interface
[options="header",width="100%",cols="35%,8%,8%,8%,41%"]
|===
|Name|Access|Trap|Value|Comment
|subscriber.by-\*.*info*|R|No||List (short) subscriber information
|subscriber.by-\*.*info-aud*|R|No||List subscriber authentication tokens
|subscriber.by-\*.*info-all*|R|No||List both 'info' and 'info-aud' in one
|subscriber.by-\*.*cs-enabled*|RW|No|'1' or '0'|Enable/disable circuit-switched access
|subscriber.by-\*.*ps-enabled*|RW|No|'1' or '0'|Enable/disable packet-switched access
|===
=== subscriber.by-*.info, info-aud, info-all
Query the HLR database and return current subscriber record, in multiple lines
of the format
----
name<tab>value
----
To keep the reply as short as possible, some values are omitted if they are
empty. These are the returned values and their presence
modalities; for their meaning, see <<subscriber-params>>:
.Returned values by OsmoHLR's 'info', 'info-all' and 'info-aud' commands
[options="header",width="100%",cols="15%,15%,30%,40%"]
|===
|Returned by 'info-all' and|Name|Format|Presence
|'info'|id|-9223372036854775808 .. 9223372036854775807 (usually not negative)|always
|'info'|imsi|6 to 15 decimal digits|always
|'info'|msisdn|1 to 15 decimal digits|when non-empty
|'info'|nam_cs|'1' if CS is enabled, or '0'|always
|'info'|nam_ps|'1' if PS is enabled, or '0'|always
|'info'|vlr_number|up to 15 decimal digits|when non-empty
|'info'|sgsn_number|up to 15 decimal digits|when non-empty
|'info'|sgsn_address||when non-empty
|'info'|ms_purged_cs|'1' if CS is purged, or '0'|always
|'info'|ms_purged_ps|'1' if PS is purged, or '0'|always
|'info'|periodic_lu_timer|0..4294967295|always
|'info'|periodic_rau_tau_timer|0..4294967295|always
|'info'|lmsi|8 hex digits|always
|'info-aud'|aud2g.algo|one of 'comp128v1', 'comp128v2', 'comp128v3' or 'xor'|when valid 2G auth data is set
|'info-aud'|aud2g.ki|32 hexadecimal digits|when valid 2G auth data is set
|'info-aud'|aud3g.algo|so far always 'milenage'|when valid 3G auth data is set
|'info-aud'|aud3g.k|32 hexadecimal digits|when valid 3G auth data is set
|'info-aud'|aud3g.op|32 hexadecimal digits|when valid 3G auth data is set, *not* when OPC is set
|'info-aud'|aud3g.opc|32 hexadecimal digits|when valid 3G auth data is set, *not* when OP is set
|'info-aud'|aud3g.ind_bitlen|0..28|when valid 3G auth data is set
|'info-aud'|aud3g.sqn|0 .. 18446744073709551615|when valid 3G auth data is set
|===
This is an example Control Interface transcript that illustrates the various
'info' commands:
----
include::../example_subscriber_info.ctrl[]
----
=== subscriber.by-*.ps-enabled, cs-enabled
Disable or enable packet-/circuit-switched access for the given IMSI;
* 'ps-enabled' switches access to GPRS or UMTS data services,
* 'cs-enabled' switches access to voice services.
When disabled, the next time this subscriber attempts to do a Location Updating
GSUP operation for the given domain (i.e. from the SGSN for 'ps-enabled', from
the MSC/VLR for 'cs-enabled'), it will be rejected by OsmoHLR. Currently
connected GSUP clients will be notified via GSUP when a subscriber is being
disabled, so that the subscriber can be dropped in case it is currently
attached.
The current 'ps-enabled'/'cs-enabled' status can be queried by 'GET' commands,
and also by looking at 'nam_ps' and 'nam_cs' in a 'subscriber.by-*.info'
response.
A value of "1" indicates that the given domain is enabled, which is the
default; a value of "0" disables access.
This is an example transcript that illustrates 'ps-enabled' and 'cs-enabled'
commands:
----
include::../example_subscriber_cs_ps_enabled.ctrl[]
----

View File

@@ -0,0 +1,69 @@
[[overview]]
== Overview
This manual should help you getting started with OsmoHLR. It will cover
aspects of configuring and running the OsmoHLR.
[[intro_overview]]
=== About OsmoHLR
OsmoHLR is Osmocom's minimal implementation of a Home Location Register (HLR)
for 2G and 3G GSM and UMTS mobile core networks. Its interfaces are:
- GSUP, serving towards OsmoMSC and OsmoSGSN;
- A local SQLite database;
- The Osmocom typical telnet VTY and CTRL interfaces.
Originally, the OpenBSC project's OsmoNITB all-in-one implementation had an
integrated HLR, managing subscribers and SMS in the same local database. Along
with the separate OsmoMSC and its new VLR component, OsmoHLR was implemented
from scratch to alleviate various shortcomings of the internal HLR:
- The separate HLR allows using centralized subscriber management for both
circuit-switched and packet-switched domains (i.e. one OsmoHLR for both
OsmoMSC and OsmoSGSN).
- VLR and HLR brought full UMTS AKA (Authentication and Key Agreement) support,
i.e. Milenage authentication in both the full 3G variant as well as the
backwards compatible 2G variant.
- In contrast to the OsmoNITB, the specific way the new OsmoMSC's VLR accesses
OsmoHLR brings fully asynchronous subscriber database access.
Find the OsmoHLR issue tracker and wiki online at
- https://osmocom.org/projects/osmo-hlr
- https://osmocom.org/projects/osmo-hlr/wiki
[[fig-gsm]]
.Typical GSM network architecture used with OsmoHLR
[graphviz]
----
digraph G {
rankdir=LR;
subgraph cluster_hlr {
label = "OsmoHLR";
GSUP [label="GSUP server"]
DB [label="SQLite DB"]
GSUP->DB
DB->CTRL [dir="back"]
DB->VTY [dir="back"]
}
Admin [label="Admin and\nMaintenance"]
SW [label="3rd party software\nintegration"]
VTY->Admin [dir="back"]
CTRL->SW [dir="back"]
MSC [label="MSC/VLR"]
MSC->GSUP [label="GSUP"]
SGSN->GSUP [label="GSUP"]
BSC->MSC
HNBGW->MSC
HNBGW->SGSN
PCU->SGSN
}
----

View File

@@ -0,0 +1,82 @@
== Running OsmoHLR
The OsmoHLR executable (`osmo-hlr`) offers the following command-line
arguments:
=== SYNOPSIS
*osmo-hlr* [-h] [-c 'CONFIGFILE'] [-l 'DATABASE'] [-d 'DBGMASK'] [-D] [-s] [-T] [-e 'LOGLEVEL'] [-V]
=== OPTIONS
// Keep the order the same as in osmo-hlr --help!
*-h, --help*::
Print a short help message about the supported options
*-c, --config-file 'CONFIGFILE'*::
Specify the file and path name of the configuration file to be
used. If none is specified, use `osmo-hlr.cfg` in the current
working directory.
*-l, --database 'DATABASE'*::
Specify the file name of the SQLite3 database to use as HLR/AUC
storage
*-d, --debug 'DBGMASK','DBGLEVELS'*::
Set the log subsystems and levels for logging to stderr. This
has mostly been superseded by VTY-based logging configuration,
see <<logging>> for further information.
*-D, --daemonize*::
Fork the process as a daemon into background.
*-s, --disable-color*::
Disable colors for logging to stderr. This has mostly been
deprecated by VTY based logging configuration, see <<logging>>
for more information.
*-T, --timestamp*::
Enable time-stamping of log messages to stderr. This has mostly
been deprecated by VTY based logging configuration, see
<<logging>> for more information.
*-e, --log-level 'LOGLEVEL'*::
Set the global log level for logging to stderr. This has mostly
been deprecated by VTY based logging configuration, see
<<logging>> for more information.
*-V, --version*::
Print the compile-time version number of the OsmoHLR program
=== Bootstrap the Database
If no database exists yet, OsmoHLR will automatically create and bootstrap a
database file with empty tables. If no `-l` command-line option is provided,
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 `osmo-hlr-db-tool --help`.
=== Multiple instances
Running multiple instances of `osmo-hlr` on the same computer is possible if
all interfaces (VTY, CTRL) are separated using the appropriate configuration
options. The IP based interfaces are binding to local host by default. In order
to separate the processes, the user has to bind those services to specific but
different IP addresses and/or ports.
The VTY and the Control interface can be bound to IP addresses from the loopback
address range, for example:
----
line vty
bind 127.0.0.2
ctrl
bind 127.0.0.2
----
The GSUP interface can be bound to a specific IP address by the following
configuration options:
----
hlr
gsup
bind ip 10.23.42.1
----
NOTE: At the time of writing, OsmoHLR lacks a config option to change the GSUP
port, which is by default TCP port 4222.

View File

@@ -0,0 +1,69 @@
== Managing Subscribers
Subscribers are kept in a local SQLite database file and can be managed via VTY
and CTRL interfaces.
This section provides some examples; also refer to the OsmoHLR VTY reference
manual <<vty-ref-osmohlr>> as well as the Control interface described in
<<hlr-ctrl>>.
=== Example: Add/Update/Delete Subscriber via VTY
The following telnet VTY session adds a subscriber complete with GSM (2G) and
UMTS (3G and 2G) authentication tokens, and finally removes the subscriber
again; it assumes that osmo-hlr is running and listening for telnet VTY
connections on localhost:
----
$ telnet localhost 4258
include::../example_subscriber_add_update_delete.vty[]
----
[[subscriber-params]]
=== Subscriber Parameters
The following parameters are managed for each subscriber of the HLR, modelled
roughly after 3GPP TS 23.008, version 13.3.0; note that not all of these
parameters are necessarily in active use.
The `aud3g` table also applies to 2G networks: it provides UMTS AKA tokens for
Milenage authentication, which is available both on 3G and 2G networks. On 2G,
when both MS and network are R99 capable (like OsmoMSC and OsmoSGSN are), the
full UMTS AKA with Milenage keys from `aud_3g`, using AUTN and extended RES
tokens, is available. With pre-R99 MS or network configurations, the GSM AKA
compatible variant of Milenage, still using the Milenage keys from `aud_3g` but
transceiving only RAND and SRES, may be applicable. (See 3GPP TS 33.102, chapter
6.8.1, Authentication and key agreement of UMTS subscribers.)
.OsmoHLR's subscriber parameters
[options="header",width="100%",cols="20%,20%,60%"]
|===
|Name|Example|Description
|imsi|901700000014701|identity of the SIM/USIM, 3GPP TS 23.008 chapter 2.1.1.1
|msisdn|2342123|number to dial to reach this subscriber (multiple MSISDNs can be stored per subscriber), 3GPP TS 23.008 chapter 2.1.2
|imeisv|4234234234234275|identity of the mobile device and software version, 3GPP TS 23.008 chapter 2.2.3
|aud2g.algo|comp128v3|Authentication algorithm ID for GSM AKA, corresponds to enum osmo_auth_algo
|aud2g.ki||Subscriber's secret key (128bit)
|aud3g.algo|milenage|Authentication algorithm ID for UMTS AKA (applies to both 3G and 2G networks), corresponds to enum osmo_auth_algo
|aud3g.k|(32 hexadecimal digits)|Subscriber's secret key (128bit)
|aud3g.op|(32 hexadecimal digits)|Operator's secret key (128bit)
|aud3g.opc|(32 hexadecimal digits)|Secret key derived from OP and K (128bit), alternative to using OP which does not disclose OP to subscribers
|aud3g.sqn|123|Sequence number of last used key (64bit unsigned)
|aud3g.ind_bitlen|5|Nr of index bits at lower SQN end
|apn||
|vlr_number||3GPP TS 23.008 chapter 2.4.5
|hlr_number||3GPP TS 23.008 chapter 2.4.6
|sgsn_number||3GPP TS 23.008 chapter 2.4.8.1
|sgsn_address||3GPP TS 23.008 chapter 2.13.10
|ggsn_number||3GPP TS 23.008 chapter 2.4.8.2
|gmlc_number||3GPP TS 23.008 chapter 2.4.9.2
|smsc_number||3GPP TS 23.008 chapter 2.4.23
|periodic_lu_tmr||3GPP TS 23.008 chapter 2.4.24
|periodic_rau_tau_tmr||3GPP TS 23.008 chapter 2.13.115
|nam_cs|1|Enable/disable voice access (3GPP TS 23.008 chapter 2.1.1.2: network access mode)
|nam_ps|0|Enable/disable data access (3GPP TS 23.008 chapter 2.1.1.2: network access mode)
|lmsi||3GPP TS 23.008 chapter 2.1.8
|ms_purged_cs|0|3GPP TS 23.008 chapter 2.7.5
|ms_purged_ps|1|3GPP TS 23.008 chapter 2.7.6
|===

View File

@@ -0,0 +1,78 @@
[[ussd]]
== Unstructured Supplementary Services Data (USSD)
The _Unstructured Supplementary Services Data (USSD)_ is one service within
2G/3G networks next to other services such as circuit-switched voice, packet-switched
data and SMS (Short Message Service).
It is on an abstract level quite similar to SMS in that USSD can be used to send
textual messages. However, there are the following differences:
* USSD is between the MS (phone) and an USSD application on the network, while
SMS is primarily between two subscribers identified by their MSISDN
* USSD is faster, as it doesn't suffer from the complicated three-layer CP/RP/TP
protocol stack of SMS with it's acknowledgement of the acknowledged acknowledgement.
* USSD is session-oriented, i.e. a dialogue/session between subscriber and application
can persist for the transfer of more than one message. The dedicated radio channel
on the RAN remains established throughout that dialogue.
=== USSD in Osmocom
Until August 2018, OsmoMSC contained some minimalistic internal USSD
handling with no
ability to attach/extend it with external USSD applications.
From August 2018 onwards, OsmoMSC doesn't contain any internal USSD
handlers/applications anymore. Instead, all USSD is transported to/from
OsmoHLR via the GSUP protocol.
OsmoHLR contains some intenal USSD handlers and can route USSD messages
to any number of external USSD entities (EUSEs). The EUSE also use GSUP
to communicate USSD from/to OsmoHLR.
Each EUSE is identified by its name. The name consists of a single-word
string preceding a currently fixed ("-00-00-00-00-00-00") suffix.
There is no authentication between EUSE and OsmoHLR: Any client program
able to connect to the GSUP port of OsmoHLR can register as any EUSE
(name).
NOTE:: We plan to remove the requirement for this suffix as soon as we
are done resolving all more important issues.
=== USSD Configuration
USSD configuration in OsmoHLR happens within the `hlr` VTY node.
`euse foobar-00-00-00-00-00-00` defines an EUSE with the given name `foobar`
`ussd route prefix *123 external foobar-00-00-00-00-00-00` installs a
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. There above command will restore
the old behavior, in which *#100# will return a text message containing
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.
=== Example EUSE program
We have provided an example EUSE developed in C language using existing
Osmocom libraries for GSUP protocol handling and USSD encoding/decoding.
It will register as `foobar` EUSE to OsmoHLR on localhost. You can run
it on a different machine by specifying e.g. `osmo-euse-demo 1.2.3.4 5678`
to make it connect to OsmoHLR on IP address 1.2.3.4 and GSUP/TCP port
5678.
The idea is that you can use this as a template to develop your own USSD
applications, or any gateways to other protocols or interfaces.
You can find it in `osmo-hlr/src/osmo-euse-demo.c` or online by
following the link to http://git.osmocom.org/osmo-hlr/tree/src/osmo-euse-demo.c
This demonstration program will echo back any USSD message sent/routed
to it, quoted like _You sent "..."_.

View File

@@ -0,0 +1,34 @@
OsmoHLR> enable
OsmoHLR# subscriber imsi 123456789023000 create
% Created subscriber 123456789023000
ID: 1
IMSI: 123456789023000
MSISDN: none
OsmoHLR# subscriber imsi 123456789023000 update msisdn 423
% Updated subscriber IMSI='123456789023000' to MSISDN='423'
OsmoHLR# subscriber msisdn 423 update aud3g milenage k deaf0ff1ced0d0dabbedd1ced1cef00d opc cededeffacedacefacedbadfadedbeef
OsmoHLR# subscriber msisdn 423 show
ID: 1
IMSI: 123456789023000
MSISDN: 423
3G auth: MILENAGE
K=deaf0ff1ced0d0dabbedd1ced1cef00d
OPC=cededeffacedacefacedbadfadedbeef
IND-bitlen=5
OsmoHLR# subscriber msisdn 423 update aud2g comp128v3 ki beefedcafefaceacedaddeddecadefee
OsmoHLR# subscriber msisdn 423 show
ID: 1
IMSI: 123456789023000
MSISDN: 423
2G auth: COMP128v3
KI=beefedcafefaceacedaddeddecadefee
3G auth: MILENAGE
K=deaf0ff1ced0d0dabbedd1ced1cef00d
OPC=cededeffacedacefacedbadfadedbeef
IND-bitlen=5
OsmoHLR# subscriber imsi 123456789023000 delete
% Deleted subscriber for IMSI '123456789023000'

View File

@@ -0,0 +1,71 @@
GET 1 subscriber.by-msisdn-103.info
GET_REPLY 1 subscriber.by-msisdn-103.info
id 3
imsi 901990000000003
msisdn 103
nam_cs 1
nam_ps 1
ms_purged_cs 0
ms_purged_ps 0
periodic_lu_timer 0
periodic_rau_tau_timer 0
lmsi 00000000
GET 2 subscriber.by-msisdn-103.ps-enabled
GET_REPLY 2 subscriber.by-msisdn-103.ps-enabled 1
SET 3 subscriber.by-msisdn-103.ps-enabled 0
SET_REPLY 3 subscriber.by-msisdn-103.ps-enabled OK
GET 4 subscriber.by-msisdn-103.ps-enabled
GET_REPLY 4 subscriber.by-msisdn-103.ps-enabled 0
GET 5 subscriber.by-msisdn-103.info
GET_REPLY 5 subscriber.by-msisdn-103.info
id 3
imsi 901990000000003
msisdn 103
nam_cs 1
nam_ps 0
ms_purged_cs 0
ms_purged_ps 0
periodic_lu_timer 0
periodic_rau_tau_timer 0
lmsi 00000000
SET 6 subscriber.by-msisdn-103.cs-enabled 0
SET_REPLY 6 subscriber.by-msisdn-103.cs-enabled OK
GET 7 subscriber.by-msisdn-103.cs-enabled
GET_REPLY 7 subscriber.by-msisdn-103.cs-enabled 0
GET 8 subscriber.by-msisdn-103.info
GET_REPLY 8 subscriber.by-msisdn-103.info
id 3
imsi 901990000000003
msisdn 103
nam_cs 0
nam_ps 0
ms_purged_cs 0
ms_purged_ps 0
periodic_lu_timer 0
periodic_rau_tau_timer 0
lmsi 00000000
SET 9 subscriber.by-msisdn-103.cs-enabled 1
SET_REPLY 9 subscriber.by-msisdn-103.cs-enabled OK
SET 10 subscriber.by-msisdn-103.ps-enabled 1
SET_REPLY 10 subscriber.by-msisdn-103.ps-enabled OK
GET 11 subscriber.by-msisdn-103.info
GET_REPLY 11 subscriber.by-msisdn-103.info
id 3
imsi 901990000000003
msisdn 103
nam_cs 1
nam_ps 1
ms_purged_cs 0
ms_purged_ps 0
periodic_lu_timer 0
periodic_rau_tau_timer 0
lmsi 00000000

View File

@@ -0,0 +1,42 @@
GET 1 subscriber.by-imsi-901990000000003.info
GET_REPLY 1 subscriber.by-imsi-901990000000003.info
id 3
imsi 901990000000003
msisdn 103
nam_cs 1
nam_ps 1
ms_purged_cs 0
ms_purged_ps 0
periodic_lu_timer 0
periodic_rau_tau_timer 0
lmsi 00000000
GET 2 subscriber.by-msisdn-103.info-aud
GET_REPLY 2 subscriber.by-msisdn-103.info-aud
aud2g.algo COMP128v1
aud2g.ki 000102030405060708090a0b0c0d0e0f
aud3g.algo MILENAGE
aud3g.k 000102030405060708090a0b0c0d0e0f
aud3g.opc 101112131415161718191a1b1c1d1e1f
aud3g.ind_bitlen 5
aud3g.sqn 0
GET 3 subscriber.by-id-3.info-all
GET_REPLY 3 subscriber.by-id-3.info-all
id 3
imsi 901990000000003
msisdn 103
nam_cs 1
nam_ps 1
ms_purged_cs 0
ms_purged_ps 0
periodic_lu_timer 0
periodic_rau_tau_timer 0
lmsi 00000000
aud2g.algo COMP128v1
aud2g.ki 000102030405060708090a0b0c0d0e0f
aud3g.algo MILENAGE
aud3g.k 000102030405060708090a0b0c0d0e0f
aud3g.opc 101112131415161718191a1b1c1d1e1f
aud3g.ind_bitlen 5
aud3g.sqn 0

View File

@@ -0,0 +1,47 @@
<revhistory>
<revision>
<revnumber>1</revnumber>
<date>September 18th, 2017</date>
<authorinitials>NH</authorinitials>
<revremark>
Initial version; based on OsmoNITB manual version 2.
</revremark>
</revision>
</revhistory>
<authorgroup>
<author>
<firstname>Neels</firstname>
<surname>Hofmeyr</surname>
<email>nhofmeyr@sysmocom.de</email>
<authorinitials>NH</authorinitials>
<affiliation>
<shortaffil>sysmocom</shortaffil>
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
<jobtitle>Senior Developer</jobtitle>
</affiliation>
</author>
</authorgroup>
<copyright>
<year>2017</year>
<holder>sysmocom - s.f.m.c. GmbH</holder>
</copyright>
<legalnotice>
<para>
Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU Free Documentation License,
Version 1.3 or any later version published by the Free Software
Foundation; with the Invariant Sections being just 'Foreword',
'Acknowledgements' and 'Preface', with no Front-Cover Texts,
and no Back-Cover Texts. A copy of the license is included in
the section entitled "GNU Free Documentation License".
</para>
<para>
The Asciidoc source code of this manual can be found at
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
http://git.osmocom.org/osmo-gsm-manuals/
</ulink>
</para>
</legalnotice>

View File

@@ -0,0 +1,36 @@
:gfdl-enabled:
:program-name: OsmoHLR
OsmoHLR User Manual
====================
Neels Hofmeyr <nhofmeyr@sysmocom.de>
include::./common/chapters/preface.adoc[]
include::{srcdir}/chapters/overview.adoc[]
include::{srcdir}/chapters/running.adoc[]
include::{srcdir}/chapters/subscribers.adoc[]
include::{srcdir}/chapters/ussd.adoc[]
include::./common/chapters/vty.adoc[]
include::./common/chapters/logging.adoc[]
include::{srcdir}/chapters/control.adoc[]
include::./common/chapters/control_if.adoc[]
include::./common/chapters/gsup.adoc[]
include::./common/chapters/port_numbers.adoc[]
include::./common/chapters/bibliography.adoc[]
include::./common/chapters/glossary.adoc[]
include::./common/chapters/gfdl.adoc[]

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
ex:ts=2:sw=42sts=2:et
-*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-->
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml" >
<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
]>
<book>
<info>
<revhistory>
<revision>
<revnumber>v1</revnumber>
<date>18th September 2017</date>
<authorinitials>nh</authorinitials>
<revremark>Initial</revremark>
</revision>
</revhistory>
<title>OsmoHLR VTY Reference</title>
<copyright>
<year>2017</year>
</copyright>
<legalnotice>
<para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
</para>
</legalnotice>
</info>
<!-- Main chapters-->
&chapter-vty;
</book>

View File

@@ -0,0 +1,2 @@
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
</vtydoc>

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,8 @@
#include <osmocom/core/timer.h>
#include <osmocom/gsm/oap_client.h>
#include <osmocom/gsm/ipa.h>
#include <osmocom/gsm/gsup.h>
/* a loss of GSUP between MSC and HLR is considered quite serious, let's try to recover as quickly as
* possible. Even one new connection attempt per second should be quite acceptable until the link is
@@ -38,7 +40,7 @@ struct osmo_gsup_client;
typedef int (*osmo_gsup_client_read_cb_t)(struct osmo_gsup_client *gsupc, struct msgb *msg);
struct osmo_gsup_client {
const char *unit_name;
const char *unit_name; /* same as ipa_dev->unit_name, for backwards compat */
struct ipa_client_conn *link;
osmo_gsup_client_read_cb_t read_cb;
@@ -50,8 +52,16 @@ struct osmo_gsup_client {
struct osmo_timer_list connect_timer;
int is_connected;
int got_ipa_pong;
struct ipaccess_unit *ipa_dev; /* identification information sent to IPA server */
};
struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
struct ipaccess_unit *ipa_dev,
const char *ip_addr,
unsigned int tcp_port,
osmo_gsup_client_read_cb_t read_cb,
struct osmo_oap_client_config *oapc_config);
struct osmo_gsup_client *osmo_gsup_client_create(void *talloc_ctx,
const char *unit_name,
const char *ip_addr,
@@ -61,5 +71,7 @@ struct osmo_gsup_client *osmo_gsup_client_create(void *talloc_ctx,
void osmo_gsup_client_destroy(struct osmo_gsup_client *gsupc);
int osmo_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg);
int osmo_gsup_client_enc_send(struct osmo_gsup_client *gsupc,
const struct osmo_gsup_message *gsup_msg);
struct msgb *osmo_gsup_client_msgb_alloc(void);

View File

@@ -3,9 +3,9 @@ exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: Osmocom GSUP and OAP Client Library
Name: Osmocom GSUP Client Library
Description: C Utility Library
Version: @VERSION@
Libs: -L${libdir} @TALLOC_LIBS@ -losmogsm -losmocore
Libs: -L${libdir} -losmo-gsup-client
Cflags: -I${includedir}/

View File

@@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS subscriber (
CREATE TABLE subscriber (
-- OsmoHLR's DB scheme is modelled roughly after TS 23.008 version 13.3.0
id INTEGER PRIMARY KEY,
-- Chapter 2.1.1.1
@@ -36,27 +36,31 @@ CREATE TABLE IF NOT EXISTS subscriber (
-- Chapter 2.7.5
ms_purged_cs BOOLEAN NOT NULL DEFAULT 0,
-- Chapter 2.7.6
ms_purged_ps BOOLEAN NOT NULL DEFAULT 0
ms_purged_ps BOOLEAN NOT NULL DEFAULT 0,
-- Timestamp of last location update seen from subscriber
-- The value is a string which encodes a UTC timestamp in granularity of seconds.
last_lu_seen TIMESTAMP default NULL
);
CREATE TABLE IF NOT EXISTS subscriber_apn (
CREATE TABLE subscriber_apn (
subscriber_id INTEGER, -- subscriber.id
apn VARCHAR(256) NOT NULL
);
CREATE TABLE IF NOT EXISTS subscriber_multi_msisdn (
CREATE TABLE subscriber_multi_msisdn (
-- Chapter 2.1.3
subscriber_id INTEGER, -- subscriber.id
msisdn VARCHAR(15) NOT NULL
);
CREATE TABLE IF NOT EXISTS auc_2g (
CREATE TABLE auc_2g (
subscriber_id INTEGER PRIMARY KEY, -- subscriber.id
algo_id_2g INTEGER NOT NULL, -- enum osmo_auth_algo value
ki VARCHAR(32) NOT NULL -- hex string: subscriber's secret key (128bit)
);
CREATE TABLE IF NOT EXISTS auc_3g (
CREATE TABLE auc_3g (
subscriber_id INTEGER PRIMARY KEY, -- subscriber.id
algo_id_3g INTEGER NOT NULL, -- enum osmo_auth_algo value
k VARCHAR(32) NOT NULL, -- hex string: subscriber's secret key (128bit)
@@ -66,4 +70,8 @@ CREATE TABLE IF NOT EXISTS auc_3g (
ind_bitlen INTEGER NOT NULL DEFAULT 5 -- nr of index bits at lower SQN end
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_subscr_imsi ON subscriber (imsi);
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
-- Set HLR database schema version number
-- Note: This constant is currently duplicated in src/db.c and must be kept in sync!
PRAGMA user_version = 1;

View File

@@ -15,7 +15,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include \
EXTRA_DIST = \
populate_hlr_db.pl \
db_bootstrap.sed \
db_sql2c.sed \
$(NULL)
BUILT_SOURCES = \
@@ -114,11 +114,11 @@ osmo_euse_demo_LDADD = \
BOOTSTRAP_SQL = $(top_srcdir)/sql/hlr.sql
db_bootstrap.h: $(BOOTSTRAP_SQL) $(srcdir)/db_bootstrap.sed
echo "/* DO NOT EDIT THIS FILE. It is generated from osmo-hlr.git/sql/hlr.sql */" > "$@"
db_bootstrap.h: $(BOOTSTRAP_SQL) $(srcdir)/db_sql2c.sed
echo "/* DO NOT EDIT THIS FILE. It is generated from files in osmo-hlr.git/sql/ */" > "$@"
echo "#pragma once" >> "$@"
echo "static const char *stmt_bootstrap_sql[] = {" >> "$@"
cat "$(BOOTSTRAP_SQL)" \
| sed -f "$(srcdir)/db_bootstrap.sed" \
| sed -f "$(srcdir)/db_sql2c.sed" \
>> "$@"
echo "};" >> "$@"

View File

@@ -132,7 +132,7 @@ static void print_subscr_info(struct ctrl_cmd *cmd,
struct hlr_subscriber *subscr)
{
ctrl_cmd_reply_printf(cmd,
"\nid\t%"PRIu64
"\nid\t%" PRIu64
FMT_S
FMT_S
FMT_BOOL
@@ -189,7 +189,7 @@ static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_d
ctrl_cmd_reply_printf(cmd,
"\naud3g.%s\t%s"
"\naud3g.ind_bitlen\t%u"
"\naud3g.sqn\t%"PRIu64
"\naud3g.sqn\t%" PRIu64
,
aud->u.umts.opc_is_op? "op" : "opc",
hexdump_buf(aud->u.umts.opc),

169
src/db.c
View File

@@ -27,6 +27,9 @@
#include "db.h"
#include "db_bootstrap.h"
/* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */
#define CURRENT_SCHEMA_VERSION 1
#define SEL_COLUMNS \
"id," \
"imsi," \
@@ -40,7 +43,8 @@
"nam_ps," \
"lmsi," \
"ms_purged_cs," \
"ms_purged_ps"
"ms_purged_ps," \
"last_lu_seen"
static const char *stmt_sql[] = {
[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
@@ -62,6 +66,7 @@ static const char *stmt_sql[] = {
[DB_STMT_SUBSCR_CREATE] = "INSERT INTO subscriber (imsi) VALUES ($imsi)",
[DB_STMT_DEL_BY_ID] = "DELETE FROM subscriber WHERE id = $subscriber_id",
[DB_STMT_SET_MSISDN_BY_IMSI] = "UPDATE subscriber SET msisdn = $msisdn WHERE imsi = $imsi",
[DB_STMT_DELETE_MSISDN_BY_IMSI] = "UPDATE subscriber SET msisdn = NULL WHERE imsi = $imsi",
[DB_STMT_AUC_2G_INSERT] =
"INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki)"
" VALUES($subscriber_id, $algo_id_2g, $ki)",
@@ -70,6 +75,7 @@ static const char *stmt_sql[] = {
"INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)"
" VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)",
[DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id",
[DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch') WHERE id = $subscriber_id",
};
static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
@@ -196,36 +202,125 @@ static int db_bootstrap(struct db_context *dbc)
for (i = 0; i < ARRAY_SIZE(stmt_bootstrap_sql); i++) {
int rc;
sqlite3_stmt *stmt;
rc = sqlite3_prepare_v2(dbc->db, stmt_bootstrap_sql[i], -1,
&stmt, NULL);
rc = sqlite3_prepare_v2(dbc->db, stmt_bootstrap_sql[i], -1, &stmt, NULL);
if (rc != SQLITE_OK) {
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n",
stmt_bootstrap_sql[i]);
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", stmt_bootstrap_sql[i]);
return rc;
}
/* execute the statement */
rc = sqlite3_step(stmt);
db_remove_reset(stmt);
sqlite3_finalize(stmt);
if (rc != SQLITE_DONE) {
LOGP(DDB, LOGL_ERROR, "Cannot bootstrap database: SQL error: (%d) %s,"
" during stmt '%s'",
rc, sqlite3_errmsg(dbc->db),
stmt_bootstrap_sql[i]);
rc, sqlite3_errmsg(dbc->db), stmt_bootstrap_sql[i]);
return rc;
}
}
return SQLITE_OK;
}
struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logging)
/* https://www.sqlite.org/fileformat2.html#storage_of_the_sql_database_schema */
static bool db_table_exists(struct db_context *dbc, const char *table_name)
{
const char *table_exists_sql = "SELECT name FROM sqlite_master WHERE type='table' AND name=?";
sqlite3_stmt *stmt;
int rc;
rc = sqlite3_prepare_v2(dbc->db, table_exists_sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", table_exists_sql);
return false;
}
if (!db_bind_text(stmt, NULL, table_name))
return false;
rc = sqlite3_step(stmt);
db_remove_reset(stmt);
sqlite3_finalize(stmt);
return (rc == SQLITE_ROW);
}
/* Indicate whether the database is initialized with tables for schema version 0.
* We only check for the 'subscriber' table here because Neels said so. */
static bool db_is_bootstrapped_v0(struct db_context *dbc)
{
if (!db_table_exists(dbc, "subscriber")) {
LOGP(DDB, LOGL_DEBUG, "Table 'subscriber' not found in database '%s'\n", dbc->fname);
return false;
}
return true;
}
static int
db_upgrade_v1(struct db_context *dbc)
{
sqlite3_stmt *stmt;
int rc;
const char *update_stmt_sql = "ALTER TABLE subscriber ADD COLUMN last_lu_seen TIMESTAMP default NULL";
const char *set_schema_version_sql = "PRAGMA user_version = 1";
rc = sqlite3_prepare_v2(dbc->db, update_stmt_sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", update_stmt_sql);
return rc;
}
rc = sqlite3_step(stmt);
db_remove_reset(stmt);
sqlite3_finalize(stmt);
if (rc != SQLITE_DONE) {
LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version %d\n", 1);
return rc;
}
rc = sqlite3_prepare_v2(dbc->db, set_schema_version_sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", set_schema_version_sql);
return rc;
}
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE)
LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version %d\n", 1);
db_remove_reset(stmt);
sqlite3_finalize(stmt);
return rc;
}
static int db_get_user_version(struct db_context *dbc)
{
const char *user_version_sql = "PRAGMA user_version";
sqlite3_stmt *stmt;
int version, rc;
rc = sqlite3_prepare_v2(dbc->db, user_version_sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", user_version_sql);
return -1;
}
rc = sqlite3_step(stmt);
if (rc == SQLITE_ROW) {
version = sqlite3_column_int(stmt, 0);
} else {
LOGP(DDB, LOGL_ERROR, "SQL statement '%s' failed: %d\n", user_version_sql, rc);
version = -1;
}
db_remove_reset(stmt);
sqlite3_finalize(stmt);
return version;
}
struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logging, bool allow_upgrade)
{
struct db_context *dbc = talloc_zero(ctx, struct db_context);
unsigned int i;
int rc;
bool has_sqlite_config_sqllog = false;
int version;
LOGP(DDB, LOGL_NOTICE, "using database: %s\n", fname);
LOGP(DDB, LOGL_INFO, "Compiled against SQLite3 lib version %s\n", SQLITE_VERSION);
@@ -274,10 +369,56 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
LOGP(DDB, LOGL_ERROR, "Unable to set Write-Ahead Logging: %s\n",
err_msg);
rc = db_bootstrap(dbc);
if (rc != SQLITE_OK) {
LOGP(DDB, LOGL_ERROR, "Failed to bootstrap DB: (rc=%d) %s\n",
rc, sqlite3_errmsg(dbc->db));
version = db_get_user_version(dbc);
if (version < 0) {
LOGP(DDB, LOGL_ERROR, "Unable to read user version number from database '%s'\n", dbc->fname);
goto out_free;
}
/* An empty database will always report version zero. */
if (version == 0 && !db_is_bootstrapped_v0(dbc)) {
LOGP(DDB, LOGL_NOTICE, "Missing database tables detected; Bootstrapping database '%s'\n", dbc->fname);
rc = db_bootstrap(dbc);
if (rc != SQLITE_OK) {
LOGP(DDB, LOGL_ERROR, "Failed to bootstrap DB: (rc=%d) %s\n",
rc, sqlite3_errmsg(dbc->db));
goto out_free;
}
version = CURRENT_SCHEMA_VERSION;
}
LOGP(DDB, LOGL_NOTICE, "Database '%s' has HLR DB schema version %d\n", dbc->fname, version);
if (version < CURRENT_SCHEMA_VERSION && allow_upgrade) {
switch (version) {
case 0:
rc = db_upgrade_v1(dbc);
if (rc != SQLITE_DONE) {
LOGP(DDB, LOGL_ERROR, "Failed to upgrade HLR DB schema to version 1: (rc=%d) %s\n",
rc, sqlite3_errmsg(dbc->db));
goto out_free;
}
version = 1;
/* fall through */
/* case N: ... */
default:
break;
}
LOGP(DDB, LOGL_NOTICE, "Database '%s' has been upgraded to HLR DB schema version %d\n",
dbc->fname, version);
}
if (version != CURRENT_SCHEMA_VERSION) {
if (version < CURRENT_SCHEMA_VERSION) {
LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version);
if (!allow_upgrade) {
LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database to schema version %d; "
"use the --db-upgrade option to allow HLR database upgrades\n",
CURRENT_SCHEMA_VERSION);
}
} else
LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version);
goto out_free;
}

View File

@@ -20,10 +20,12 @@ enum stmt_idx {
DB_STMT_SUBSCR_CREATE,
DB_STMT_DEL_BY_ID,
DB_STMT_SET_MSISDN_BY_IMSI,
DB_STMT_DELETE_MSISDN_BY_IMSI,
DB_STMT_AUC_2G_INSERT,
DB_STMT_AUC_2G_DELETE,
DB_STMT_AUC_3G_INSERT,
DB_STMT_AUC_3G_DELETE,
DB_STMT_SET_LAST_LU_SEEN,
_NUM_DB_STMT
};
@@ -38,7 +40,7 @@ bool db_bind_text(sqlite3_stmt *stmt, const char *param_name, const char *text);
bool db_bind_int(sqlite3_stmt *stmt, const char *param_name, int nr);
bool db_bind_int64(sqlite3_stmt *stmt, const char *param_name, int64_t nr);
void db_close(struct db_context *dbc);
struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite3_logging);
struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite3_logging, bool allow_upgrades);
#include <osmocom/crypt/auth.h>
@@ -69,8 +71,8 @@ struct hlr_subscriber {
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
char msisdn[GT_MAX_DIGITS+1];
/* imeisv? */
char vlr_number[GT_MAX_DIGITS+1];
char sgsn_number[GT_MAX_DIGITS+1];
char vlr_number[32];
char sgsn_number[32];
char sgsn_address[GT_MAX_DIGITS+1];
/* ggsn number + address */
/* gmlc number */
@@ -82,8 +84,14 @@ struct hlr_subscriber {
uint32_t lmsi;
bool ms_purged_cs;
bool ms_purged_ps;
time_t last_lu_seen;
};
/* A format string for use with strptime(3). This format string is
* used to parse the last_lu_seen column stored in the HLR database.
* See https://sqlite.org/lang_datefunc.html, function datetime(). */
#define DB_LAST_LU_SEEN_FMT "%Y-%m-%d %H:%M:%S"
/* Like struct osmo_sub_auth_data, but the keys are in hexdump representation.
* This is useful because SQLite requires them in hexdump format, and callers
* like the VTY and CTRL interface also have them available as hexdump to begin

View File

@@ -49,7 +49,7 @@ int db_update_sqn(struct db_context *dbc, int64_t subscr_id, uint64_t new_sqn)
/* execute the statement */
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%"PRId64
LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%" PRId64
": SQL error: (%d) %s\n",
subscr_id, rc, sqlite3_errmsg(dbc->db));
ret = -EIO;
@@ -59,11 +59,11 @@ int db_update_sqn(struct db_context *dbc, int64_t subscr_id, uint64_t new_sqn)
/* verify execution result */
rc = sqlite3_changes(dbc->db);
if (!rc) {
LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%"PRId64
LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%" PRId64
": no auc_3g entry for such subscriber\n", subscr_id);
ret = -ENOENT;
} else if (rc != 1) {
LOGP(DAUC, LOGL_ERROR, "Update SQN for subscriber ID=%"PRId64
LOGP(DAUC, LOGL_ERROR, "Update SQN for subscriber ID=%" PRId64
": SQL modified %d rows (expected 1)\n", subscr_id, rc);
ret = -EIO;
}

View File

@@ -17,9 +17,15 @@
*
*/
#define _POSIX_C_SOURCE 200809L /* for strptime(3) */
/* These are needed as well due to the above _POSIX_C_SOURCE definition: */
#define _DEFAULT_SOURCE /* for struct timezone */
#define _XOPEN_SOURCE /* for clockid_t */
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include <time.h>
#include <osmocom/core/utils.h>
#include <osmocom/crypt/auth.h>
@@ -92,7 +98,7 @@ int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
LOGP(DAUC, LOGL_ERROR,
"Cannot delete subscriber ID=%"PRId64": SQL error: (%d) %s\n",
"Cannot delete subscriber ID=%" PRId64 ": SQL error: (%d) %s\n",
subscr_id, rc, sqlite3_errmsg(dbc->db));
db_remove_reset(stmt);
return -EIO;
@@ -101,11 +107,11 @@ int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
/* verify execution result */
rc = sqlite3_changes(dbc->db);
if (!rc) {
LOGP(DAUC, LOGL_ERROR, "Cannot delete: no such subscriber: ID=%"PRId64"\n",
LOGP(DAUC, LOGL_ERROR, "Cannot delete: no such subscriber: ID=%" PRId64 "\n",
subscr_id);
ret = -ENOENT;
} else if (rc != 1) {
LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%"PRId64
LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%" PRId64
": SQL modified %d rows (expected 1)\n", subscr_id, rc);
ret = -EIO;
}
@@ -135,8 +141,8 @@ int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
/*! Set a subscriber's MSISDN in the HLR database.
* \param[in,out] dbc database context.
* \param[in] imsi ASCII string of IMSI digits.
* \param[in] msisdn ASCII string of MSISDN digits.
* \param[in] imsi ASCII string of IMSI digits
* \param[in] msisdn ASCII string of MSISDN digits, or NULL to remove the MSISDN.
* \returns 0 on success, -EINVAL in case of invalid MSISDN string, -EIO on
* database failure, -ENOENT if no such subscriber exists.
*/
@@ -146,19 +152,22 @@ int db_subscr_update_msisdn_by_imsi(struct db_context *dbc, const char *imsi,
int rc;
int ret = 0;
if (!osmo_msisdn_str_valid(msisdn)) {
if (msisdn && !osmo_msisdn_str_valid(msisdn)) {
LOGHLR(imsi, LOGL_ERROR,
"Cannot update subscriber: invalid MSISDN: '%s'\n",
msisdn);
return -EINVAL;
}
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SET_MSISDN_BY_IMSI];
sqlite3_stmt *stmt = dbc->stmt[
msisdn ? DB_STMT_SET_MSISDN_BY_IMSI : DB_STMT_DELETE_MSISDN_BY_IMSI];
if (!db_bind_text(stmt, "$imsi", imsi))
return -EIO;
if (!db_bind_text(stmt, "$msisdn", msisdn))
return -EIO;
if (msisdn) {
if (!db_bind_text(stmt, "$msisdn", msisdn))
return -EIO;
}
/* execute the statement */
rc = sqlite3_step(stmt);
@@ -312,7 +321,7 @@ int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
* empty, and no entry is not an error then.*/
ret = -ENOENT;
else if (rc != 1) {
LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%"PRId64
LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%" PRId64
" from %s: SQL modified %d rows (expected 1)\n",
subscr_id, label, rc);
ret = -EIO;
@@ -383,6 +392,8 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
{
int rc;
int ret = 0;
const char *last_lu_seen_str;
struct tm tm;
/* execute the statement */
rc = sqlite3_step(stmt);
@@ -415,6 +426,20 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
subscr->lmsi = sqlite3_column_int(stmt, 10);
subscr->ms_purged_cs = sqlite3_column_int(stmt, 11);
subscr->ms_purged_ps = sqlite3_column_int(stmt, 12);
last_lu_seen_str = (const char *)sqlite3_column_text(stmt, 13);
if (last_lu_seen_str && last_lu_seen_str[0] != '\0') {
if (strptime(last_lu_seen_str, DB_LAST_LU_SEEN_FMT, &tm) == NULL) {
LOGP(DAUC, LOGL_ERROR, "Cannot parse last LU timestamp '%s' of subscriber with IMSI='%s': %s\n",
last_lu_seen_str, subscr->imsi, strerror(errno));
} else {
subscr->last_lu_seen = mktime(&tm);
if (subscr->last_lu_seen == -1) {
LOGP(DAUC, LOGL_ERROR, "Cannot convert LU timestamp '%s' to time_t: %s\n",
last_lu_seen_str, strerror(errno));
subscr->last_lu_seen = 0;
}
}
}
out:
db_remove_reset(stmt);
@@ -500,7 +525,7 @@ int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
rc = db_sel(dbc, stmt, subscr, &err);
if (rc)
LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: ID=%"PRId64": %s\n",
LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: ID=%" PRId64 ": %s\n",
id, err);
return rc;
}
@@ -574,6 +599,7 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
{
sqlite3_stmt *stmt;
int rc, ret = 0;
struct timespec localtime;
stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
: DB_STMT_UPD_VLR_BY_ID];
@@ -587,7 +613,7 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
/* execute the statement */
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%"PRId64": SQL Error: %s\n",
LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%" PRId64 ": SQL Error: %s\n",
is_ps? "SGSN" : "VLR", subscr_id, sqlite3_errmsg(dbc->db));
ret = -EIO;
goto out;
@@ -596,17 +622,58 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
/* verify execution result */
rc = sqlite3_changes(dbc->db);
if (!rc) {
LOGP(DAUC, LOGL_ERROR, "Cannot update %s number for subscriber ID=%"PRId64
LOGP(DAUC, LOGL_ERROR, "Cannot update %s number for subscriber ID=%" PRId64
": no such subscriber\n",
is_ps? "SGSN" : "VLR", subscr_id);
ret = -ENOENT;
goto out;
} else if (rc != 1) {
LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%"PRId64
LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%" PRId64
": SQL modified %d rows (expected 1)\n",
is_ps? "SGSN" : "VLR", subscr_id, rc);
ret = -EIO;
goto out;
}
db_remove_reset(stmt);
if (osmo_clock_gettime(CLOCK_REALTIME, &localtime) != 0) {
LOGP(DAUC, LOGL_ERROR, "Cannot get the current time: (%d) %s\n", errno, strerror(errno));
ret = -errno;
goto out;
}
stmt = dbc->stmt[DB_STMT_SET_LAST_LU_SEEN];
if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
return -EIO;
/* The timestamp will be converted to UTC by SQLite. */
if (!db_bind_int64(stmt, "$val", (int64_t)localtime.tv_sec)) {
ret = -EIO;
goto out;
}
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
LOGP(DAUC, LOGL_ERROR,
"Cannot update LU timestamp for subscriber ID=%" PRId64 ": SQL error: (%d) %s\n",
subscr_id, rc, sqlite3_errmsg(dbc->db));
ret = -EIO;
goto out;
}
/* verify execution result */
rc = sqlite3_changes(dbc->db);
if (!rc) {
LOGP(DAUC, LOGL_ERROR, "Cannot update LU timestamp for subscriber ID=%" PRId64
": no such subscriber\n", subscr_id);
ret = -ENOENT;
goto out;
} else if (rc != 1) {
LOGP(DAUC, LOGL_ERROR, "Update LU timestamp for subscriber ID=%" PRId64
": SQL modified %d rows (expected 1)\n", subscr_id, rc);
ret = -EIO;
}
out:
db_remove_reset(stmt);
return ret;

View File

@@ -1,4 +1,4 @@
# Input to this is sql/hlr.sql.
# Input to this are sql/*.sql files.
#
# We want each SQL statement line wrapped in "...\n", and each end (";") to
# become a comma:

View File

@@ -7,16 +7,16 @@
* Author: Neels Hofmeyr
*
* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -170,16 +170,12 @@ static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg)
struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg);
struct osmo_gsup_client *gsupc = (struct osmo_gsup_client *)link->data;
int rc;
struct ipaccess_unit ipa_dev = {
/* see gsup_client_create() on const vs non-const */
.unit_name = (char*)gsupc->unit_name,
};
OSMO_ASSERT(ipa_dev.unit_name);
OSMO_ASSERT(gsupc->unit_name);
msg->l2h = &hh->data[0];
rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg);
rc = ipaccess_bts_handle_ccm(link, gsupc->ipa_dev, msg);
if (rc < 0) {
LOGP(DLGSUP, LOGL_NOTICE,
@@ -262,24 +258,33 @@ static void start_test_procedure(struct osmo_gsup_client *gsupc)
gsup_client_send_ping(gsupc);
}
struct osmo_gsup_client *osmo_gsup_client_create(void *talloc_ctx,
const char *unit_name,
const char *ip_addr,
unsigned int tcp_port,
osmo_gsup_client_read_cb_t read_cb,
struct osmo_oap_client_config *oapc_config)
/*!
* Create a gsup client connecting to the specified IP address and TCP port.
* Use the provided ipaccess unit as the client-side identifier; ipa_dev should
* be allocated in talloc_ctx talloc_ctx as well.
* \param[in] talloc_ctx talloc context.
* \param[in] ipa_dev IP access unit which contains client identification information; must be allocated
* in talloc_ctx as well to ensure it lives throughout the lifetime of the connection.
* \param[in] ip_addr GSUP server IP address.
* \param[in] tcp_port GSUP server TCP port.
* \param[in] read_cb callback for reading from the GSUP connection.
* \param[in] oapc_config OPA client configuration.
* \returns a GSUP client connection or NULL on failure.
*/
struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
struct ipaccess_unit *ipa_dev,
const char *ip_addr,
unsigned int tcp_port,
osmo_gsup_client_read_cb_t read_cb,
struct osmo_oap_client_config *oapc_config)
{
struct osmo_gsup_client *gsupc;
int rc;
gsupc = talloc_zero(talloc_ctx, struct osmo_gsup_client);
OSMO_ASSERT(gsupc);
/* struct ipaccess_unit has a non-const unit_name, so let's copy to be
* able to have a non-const unit_name here as well. To not taint the
* public gsup_client API, let's store it in a const char* anyway. */
gsupc->unit_name = talloc_strdup(gsupc, unit_name);
OSMO_ASSERT(gsupc->unit_name);
gsupc->unit_name = (const char *)ipa_dev->unit_name; /* API backwards compat */
gsupc->ipa_dev = ipa_dev;
/* a NULL oapc_config will mark oap_state disabled. */
rc = osmo_oap_client_init(oapc_config, &gsupc->oap_state);
@@ -313,6 +318,22 @@ failed:
return NULL;
}
/**
* Like osmo_gsup_client_create2() except it expects a unit name instead
* of a full-blown ipacess_unit as the client-side identifier.
*/
struct osmo_gsup_client *osmo_gsup_client_create(void *talloc_ctx,
const char *unit_name,
const char *ip_addr,
unsigned int tcp_port,
osmo_gsup_client_read_cb_t read_cb,
struct osmo_oap_client_config *oapc_config)
{
struct ipaccess_unit *ipa_dev = talloc_zero(talloc_ctx, struct ipaccess_unit);
ipa_dev->unit_name = talloc_strdup(ipa_dev, unit_name);
return osmo_gsup_client_create2(talloc_ctx, ipa_dev, ip_addr, tcp_port, read_cb, oapc_config);
}
void osmo_gsup_client_destroy(struct osmo_gsup_client *gsupc)
{
osmo_timer_del(&gsupc->connect_timer);
@@ -339,6 +360,42 @@ int osmo_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg)
return 0;
}
/*! Encode and send a GSUP message.
* \param[in] gsupc GSUP client.
* \param[in] gsup_msg GSUP message to be sent.
* \returns 0 in case of success, negative on error.
*/
int osmo_gsup_client_enc_send(struct osmo_gsup_client *gsupc,
const struct osmo_gsup_message *gsup_msg)
{
struct msgb *gsup_msgb;
int rc;
gsup_msgb = osmo_gsup_client_msgb_alloc();
if (!gsup_msgb) {
LOGP(DLGSUP, LOGL_ERROR, "Couldn't allocate GSUP message\n");
return -ENOMEM;
}
rc = osmo_gsup_encode(gsup_msgb, gsup_msg);
if (rc) {
LOGP(DLGSUP, LOGL_ERROR, "Couldn't encode GSUP message\n");
goto error;
}
rc = osmo_gsup_client_send(gsupc, gsup_msgb);
if (rc) {
LOGP(DLGSUP, LOGL_ERROR, "Couldn't send GSUP message\n");
goto error;
}
return 0;
error:
talloc_free(gsup_msgb);
return rc;
}
struct msgb *osmo_gsup_client_msgb_alloc(void)
{
return msgb_alloc_headroom(4000, 64, __func__);

151
src/hlr.c
View File

@@ -32,6 +32,7 @@
#include <osmocom/vty/ports.h>
#include <osmocom/ctrl/control_vty.h>
#include <osmocom/gsm/apn.h>
#include <osmocom/gsm/gsm48_ie.h>
#include "db.h"
#include "hlr.h"
@@ -54,16 +55,15 @@ static int quit = 0;
void
osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
{
/* FIXME: the below code can only be re-enabled after we make sure that an ISD
* is only sent tot the currently serving VLR and/or SGSN (if there are any).
* We cannot blindly flood the entire PLMN with this, as it would create subscriber
* state in every VLR/SGSN out there, even those that have never seen the subscriber.
* See https://osmocom.org/issues/3154 for details. */
#if 0
struct osmo_gsup_conn *co;
if (g_hlr->gs == NULL)
if (g_hlr->gs == NULL) {
LOGP(DLGSUP, LOGL_DEBUG,
"IMSI %s: NOT Notifying peers of subscriber data change,"
" there is no GSUP server\n",
subscr->imsi);
return;
}
llist_for_each_entry(co, &g_hlr->gs->clients, list) {
struct osmo_gsup_message gsup = { };
@@ -72,20 +72,50 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
struct msgb *msg_out;
uint8_t *peer;
int peer_len;
size_t peer_strlen;
const char *peer_compare;
enum osmo_gsup_cn_domain cn_domain;
if (co->supports_ps)
if (co->supports_ps) {
cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
else if (co->supports_cs)
peer_compare = subscr->sgsn_number;
} else if (co->supports_cs) {
cn_domain = OSMO_GSUP_CN_DOMAIN_CS;
else {
/* We have not yet received a location update from this subscriber .*/
peer_compare = subscr->vlr_number;
} else {
/* We have not yet received a location update from this GSUP client.*/
continue;
}
peer_len = osmo_gsup_conn_ccm_get(co, &peer, IPAC_IDTAG_SERNR);
if (peer_len < 0) {
LOGP(DLGSUP, LOGL_ERROR,
"IMSI='%s': cannot get peer name for connection %s:%u\n", subscr->imsi,
co && co->conn && co->conn->server? co->conn->server->addr : "unset",
co && co->conn && co->conn->server? co->conn->server->port : 0);
continue;
}
peer_strlen = strnlen((const char*)peer, peer_len);
if (strlen(peer_compare) != peer_strlen || strncmp(peer_compare, (const char *)peer, peer_len)) {
/* Mismatch. The subscriber is not subscribed with this GSUP client. */
/* I hope peer is always nul terminated... */
if (peer_strlen < peer_len)
LOGP(DLGSUP, LOGL_DEBUG,
"IMSI %s: subscriber change: skipping %s peer %s\n",
subscr->imsi, cn_domain == OSMO_GSUP_CN_DOMAIN_PS ? "PS" : "CS",
osmo_quote_str((char*)peer, -1));
continue;
}
LOGP(DLGSUP, LOGL_DEBUG,
"IMSI %s: subscriber change: notifying %s peer %s\n",
subscr->imsi, cn_domain == OSMO_GSUP_CN_DOMAIN_PS ? "PS" : "CS",
osmo_quote_str(peer_compare, -1));
if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi, subscr->msisdn, msisdn_enc,
sizeof(msisdn_enc), apn, sizeof(apn), cn_domain) != 0) {
LOGP(DMAIN, LOGL_ERROR,
LOGP(DLGSUP, LOGL_ERROR,
"IMSI='%s': Cannot notify GSUP client; could not create gsup message "
"for %s:%u\n", subscr->imsi,
co && co->conn && co->conn->server? co->conn->server->addr : "unset",
@@ -96,7 +126,7 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
/* Send ISD to MSC/SGSN */
msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP ISD UPDATE");
if (msg_out == NULL) {
LOGP(DMAIN, LOGL_ERROR,
LOGP(DLGSUP, LOGL_ERROR,
"IMSI='%s': Cannot notify GSUP client; could not allocate msg buffer "
"for %s:%u\n", subscr->imsi,
co && co->conn && co->conn->server? co->conn->server->addr : "unset",
@@ -105,15 +135,6 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
}
osmo_gsup_encode(msg_out, &gsup);
peer_len = osmo_gsup_conn_ccm_get(co, &peer, IPAC_IDTAG_SERNR);
if (peer_len < 0) {
LOGP(DMAIN, LOGL_ERROR,
"IMSI='%s': cannot get peer name for connection %s:%u\n", subscr->imsi,
co && co->conn && co->conn->server? co->conn->server->addr : "unset",
co && co->conn && co->conn->server? co->conn->server->port : 0);
continue;
}
if (osmo_gsup_addr_send(g_hlr->gs, peer, peer_len, msg_out) < 0) {
LOGP(DMAIN, LOGL_ERROR,
"IMSI='%s': Cannot notify GSUP client; send operation failed "
@@ -123,7 +144,6 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
continue;
}
}
#endif
}
/***********************************************************************
@@ -242,26 +262,30 @@ void lu_op_rx_gsup(struct lu_operation *luop,
static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
const struct osmo_gsup_message *gsup)
{
struct hlr_subscriber *subscr;
struct lu_operation *luop = lu_op_alloc_conn(conn);
if (!luop) {
LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
return -EINVAL;
}
subscr = &luop->subscr;
lu_op_statechg(luop, LU_S_LU_RECEIVED);
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_CS)
switch (gsup->cn_domain) {
case OSMO_GSUP_CN_DOMAIN_CS:
conn->supports_cs = true;
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) {
conn->supports_ps = true;
luop->is_ps = true;
} else {
break;
default:
/* The client didn't send a CN_DOMAIN IE; assume packet-switched in
* accordance with the GSUP spec in osmo-hlr's user manual (section
* 11.6.15 "CN Domain" says "if no CN Domain IE is present within
* a request, the PS Domain is assumed." */
case OSMO_GSUP_CN_DOMAIN_PS:
conn->supports_ps = true;
luop->is_ps = true;
break;
}
llist_add(&luop->list, &g_lu_ops);
@@ -297,13 +321,21 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
lu_op_tx_cancel_old(luop);
} else
#endif
{
/* TODO: Subscriber allowed to roam in PLMN? */
/* TODO: Update RoutingInfo */
/* TODO: Reset Flag MS Purged (cs/ps) */
/* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
lu_op_tx_insert_subscr_data(luop);
}
/* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */
LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s\n",
subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",
osmo_quote_str((const char*)luop->peer, -1));
if (db_subscr_lu(g_hlr->dbc, subscr->id, (const char *)luop->peer, luop->is_ps))
LOGP(DAUC, LOGL_ERROR, "IMSI='%s': Cannot update %s in the database\n",
subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number");
/* TODO: Subscriber allowed to roam in PLMN? */
/* TODO: Update RoutingInfo */
/* TODO: Reset Flag MS Purged (cs/ps) */
/* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
lu_op_tx_insert_subscr_data(luop);
return 0;
}
@@ -367,6 +399,38 @@ static int gsup_send_err_reply(struct osmo_gsup_conn *conn, const char *imsi,
return osmo_gsup_conn_send(conn, msg_out);
}
static int rx_check_imei_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
{
struct osmo_gsup_message gsup_reply = {0};
struct msgb *msg_out;
char imei[GSM23003_IMEI_NUM_DIGITS+1] = {0};
/* Encoded IMEI length check */
if (!gsup->imei_enc || gsup->imei_enc_len < 1 || gsup->imei_enc[0] >= sizeof(imei)) {
LOGP(DMAIN, LOGL_ERROR, "%s: wrong encoded IMEI length\n", gsup->imsi);
gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);
return -1;
}
/* Decode IMEI */
if (gsm48_decode_bcd_number(imei, sizeof(imei), gsup->imei_enc, 0) < 0) {
LOGP(DMAIN, LOGL_ERROR, "%s: failed to decode IMEI\n", gsup->imsi);
gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);
return -1;
}
/* Only print the IMEI for now, it's planned to store it here (OS#2541) */
LOGP(DMAIN, LOGL_INFO, "%s: has IMEI: %s\n", gsup->imsi, imei);
/* Accept all IMEIs */
gsup_reply.imei_result = OSMO_GSUP_IMEI_RESULT_ACK;
gsup_reply.message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT;
msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP Check_IMEI response");
memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));
osmo_gsup_encode(msg_out, &gsup_reply);
return osmo_gsup_conn_send(conn, msg_out);
}
static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
{
static struct osmo_gsup_message gsup;
@@ -427,6 +491,9 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
lu_op_rx_gsup(luop, &gsup);
}
break;
case OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST:
rx_check_imei_req(conn, &gsup);
break;
default:
LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
osmo_gsup_message_type_name(gsup.message_type));
@@ -451,6 +518,7 @@ static void print_help()
printf(" -s --disable-color Do not print ANSI colors in the log\n");
printf(" -T --timestamp Prefix every log line with a timestamp.\n");
printf(" -e --log-level number Set a global loglevel.\n");
printf(" -U --db-upgrade Allow HLR database schema upgrades.\n");
printf(" -V --version Print the version of OsmoHLR.\n");
}
@@ -458,10 +526,12 @@ static struct {
const char *config_file;
const char *db_file;
bool daemonize;
bool db_upgrade;
} cmdline_opts = {
.config_file = "osmo-hlr.cfg",
.db_file = "hlr.db",
.daemonize = false,
.db_upgrade = false,
};
static void handle_options(int argc, char **argv)
@@ -477,11 +547,12 @@ static void handle_options(int argc, char **argv)
{"disable-color", 0, 0, 's'},
{"log-level", 1, 0, 'e'},
{"timestamp", 0, 0, 'T'},
{"db-upgrade", 0, 0, 'U' },
{"version", 0, 0, 'V' },
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hc:l:d:Dse:TV",
c = getopt_long(argc, argv, "hc:l:d:Dse:TUV",
long_options, &option_index);
if (c == -1)
break;
@@ -512,6 +583,9 @@ static void handle_options(int argc, char **argv)
case 'T':
log_set_print_timestamp(osmo_stderr_target, 1);
break;
case 'U':
cmdline_opts.db_upgrade = true;
break;
case 'V':
print_version(1);
exit(0);
@@ -572,6 +646,9 @@ int main(int argc, char **argv)
INIT_LLIST_HEAD(&g_hlr->ss_sessions);
INIT_LLIST_HEAD(&g_hlr->ussd_routes);
/* Init default (call independent) SS session guard timeout value */
g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT;
rc = osmo_init_logging2(hlr_ctx, &hlr_log_info);
if (rc < 0) {
fprintf(stderr, "Error initializing logging\n");
@@ -605,7 +682,7 @@ int main(int argc, char **argv)
exit(1);
}
g_hlr->dbc = db_open(hlr_ctx, cmdline_opts.db_file, true);
g_hlr->dbc = db_open(hlr_ctx, cmdline_opts.db_file, true, cmdline_opts.db_upgrade);
if (!g_hlr->dbc) {
LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
exit(1);

View File

@@ -45,6 +45,9 @@ struct hlr {
struct hlr_euse *euse_default;
struct llist_head iuse_list;
/* NCSS (call independent) session guard timeout value */
int ncss_guard_timeout;
struct llist_head ussd_routes;
struct llist_head ss_sessions;

View File

@@ -44,8 +44,10 @@ static struct {
const char *db_file;
bool bootstrap;
const char *import_nitb_db;
bool db_upgrade;
} cmdline_opts = {
.db_file = "hlr.db",
.db_upgrade = false,
};
static void print_help()
@@ -59,6 +61,7 @@ static void print_help()
printf(" -s --disable-color Do not print ANSI colors in the log\n");
printf(" -T --timestamp Prefix every log line with a timestamp.\n");
printf(" -e --log-level number Set a global loglevel.\n");
printf(" -U --db-upgrade Allow HLR database schema upgrades.\n");
printf(" -V --version Print the version of OsmoHLR-db-tool.\n");
printf("\n");
printf("Commands:\n");
@@ -96,11 +99,12 @@ static void handle_options(int argc, char **argv)
{"disable-color", 0, 0, 's'},
{"timestamp", 0, 0, 'T'},
{"log-level", 1, 0, 'e'},
{"db-upgrade", 0, 0, 'U' },
{"version", 0, 0, 'V' },
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hl:d:sTe:V",
c = getopt_long(argc, argv, "hl:d:sTe:UV",
long_options, &option_index);
if (c == -1)
break;
@@ -124,6 +128,9 @@ static void handle_options(int argc, char **argv)
case 'e':
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
case 'U':
cmdline_opts.db_upgrade = true;
break;
case 'V':
print_version(1);
exit(EXIT_SUCCESS);
@@ -293,7 +300,7 @@ void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
imsi = sqlite3_column_int64(stmt, 0);
snprintf(imsi_str, sizeof(imsi_str), "%"PRId64, imsi);
snprintf(imsi_str, sizeof(imsi_str), "%" PRId64, imsi);
rc = db_subscr_create(dbc, imsi_str);
if (rc < 0) {
@@ -409,7 +416,7 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
g_hlr_db_tool_ctx->dbc = db_open(g_hlr_db_tool_ctx, cmdline_opts.db_file, true);
g_hlr_db_tool_ctx->dbc = db_open(g_hlr_db_tool_ctx, cmdline_opts.db_file, true, cmdline_opts.db_upgrade);
if (!g_hlr_db_tool_ctx->dbc) {
LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
exit(EXIT_FAILURE);

View File

@@ -126,8 +126,10 @@ static struct hlr_ussd_route *ussd_route_lookup_7bit(struct hlr *hlr, const char
struct hlr_ussd_route *rt;
llist_for_each_entry(rt, &hlr->ussd_routes, list) {
if (!strncmp(ussd_code, rt->prefix, strlen(rt->prefix))) {
LOGP(DSS, LOGL_DEBUG, "Found EUSE %s (prefix %s) for USSD Code '%s'\n",
rt->u.euse->name, rt->prefix, ussd_code);
LOGP(DSS, LOGL_DEBUG, "Found %s '%s' (prefix '%s') for USSD "
"Code '%s'\n", rt->is_external ? "EUSE" : "IUSE",
rt->is_external ? rt->u.euse->name : rt->u.iuse->name,
rt->prefix, ussd_code);
return rt;
}
}
@@ -206,11 +208,11 @@ struct ss_session *ss_session_alloc(struct hlr *hlr, const char *imsi, uint32_t
OSMO_STRLCPY_ARRAY(ss->imsi, imsi);
ss->session_id = session_id;
/* Schedule self-destruction timer */
osmo_timer_setup(&ss->timeout, ss_session_timeout, ss);
/* NOTE: The timeout is currently global and not refreshed with subsequent messages
* within the SS/USSD session. So 30s after the initial SS message, the session will
* timeout! */
osmo_timer_schedule(&ss->timeout, 30, 0);
if (g_hlr->ncss_guard_timeout > 0)
osmo_timer_schedule(&ss->timeout, g_hlr->ncss_guard_timeout, 0);
llist_add_tail(&ss->list, &hlr->ss_sessions);
return ss;
@@ -295,7 +297,7 @@ static int handle_ussd_own_msisdn(struct osmo_gsup_conn *conn, struct ss_session
if (strlen(subscr.msisdn) == 0)
snprintf(buf, sizeof(buf), "You have no MSISDN!");
else
snprintf(buf, sizeof(buf), "Your extension is %s\r", subscr.msisdn);
snprintf(buf, sizeof(buf), "Your extension is %s", subscr.msisdn);
ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
break;
case -ENOENT:
@@ -313,7 +315,7 @@ static int handle_ussd_own_imsi(struct osmo_gsup_conn *conn, struct ss_session *
const struct osmo_gsup_message *gsup, const struct ss_request *req)
{
char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
snprintf(buf, sizeof(buf), "Your IMSI is %s!\n", ss->imsi);
snprintf(buf, sizeof(buf), "Your IMSI is %s", ss->imsi);
ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
return 0;
}
@@ -397,8 +399,15 @@ static int handle_ss(struct ss_session *ss, const struct osmo_gsup_message *gsup
LOGPSS(ss, LOGL_INFO, "SS CompType=%s, OpCode=%s\n",
gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode));
/* FIXME */
return 0;
/**
* FIXME: As we don't store any SS related information
* (e.g. call forwarding preferences) in the database,
* we don't handle "structured" SS requests at all.
*/
LOGPSS(ss, LOGL_NOTICE, "Structured SS requests are not supported, rejecting...\n");
ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED);
return -ENOTSUP;
}
/* Handle a USSD GSUP message for a given SS Session received from VLR or EUSE */
@@ -446,6 +455,8 @@ static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss,
} else {
/* Handle internally */
ss->u.iuse->handle_ussd(conn, ss, gsup, req);
/* Release session immediately */
ss_session_free(ss);
}
}
@@ -505,6 +516,11 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
ss->is_external = false;
ss->u.iuse = rt->u.iuse;
}
} else {
if (hlr->euse_default) {
ss->is_external = true;
ss->u.euse = hlr->euse_default;
}
}
}
/* dispatch unstructured SS to routing */
@@ -521,6 +537,11 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
gsup->imsi, gsup->session_id);
goto out_err;
}
/* Reschedule self-destruction timer */
if (g_hlr->ncss_guard_timeout > 0)
osmo_timer_schedule(&ss->timeout, g_hlr->ncss_guard_timeout, 0);
if (ss_op_is_ussd(req.opcode)) {
/* dispatch unstructured SS to routing */
handle_ussd(conn, ss, gsup, &req);

View File

@@ -4,9 +4,10 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/gsup.h>
#include "gsup_server.h"
struct osmo_gsup_conn;
#define NCSS_GUARD_TIMEOUT_DEFAULT 30
struct hlr_ussd_route {
/* g_hlr.routes */

View File

@@ -35,6 +35,7 @@
#include "hlr.h"
#include "hlr_vty.h"
#include "hlr_vty_subscr.h"
#include "hlr_ussd.h"
#include "gsup_server.h"
struct cmd_node hlr_node = {
@@ -193,7 +194,13 @@ DEFUN(cfg_ussd_defaultroute, cfg_ussd_defaultroute_cmd,
USSD_STR "Configure default-route for all USSD to unknown destinations\n"
EXT_STR)
{
struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
struct hlr_euse *euse;
euse = euse_find(g_hlr, argv[0]);
if (!euse) {
vty_out(vty, "%% Cannot find EUSE %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
if (g_hlr->euse_default != euse) {
vty_out(vty, "Switching default route from %s to %s%s",
@@ -282,9 +289,22 @@ static int config_write_euse(struct vty *vty)
if (g_hlr->euse_default)
vty_out(vty, " ussd default-route external %s%s", g_hlr->euse_default->name, VTY_NEWLINE);
if (g_hlr->ncss_guard_timeout != NCSS_GUARD_TIMEOUT_DEFAULT)
vty_out(vty, " ncss-guard-timeout %i%s",
g_hlr->ncss_guard_timeout, VTY_NEWLINE);
return 0;
}
DEFUN(cfg_ncss_guard_timeout, cfg_ncss_guard_timeout_cmd,
"ncss-guard-timeout <0-255>",
"Set guard timer for NCSS (call independent SS) session activity\n"
"Guard timer value (sec.), or 0 to disable")
{
g_hlr->ncss_guard_timeout = atoi(argv[0]);
return CMD_SUCCESS;
}
/***********************************************************************
* Common Code
***********************************************************************/
@@ -347,6 +367,7 @@ void hlr_vty_init(const struct log_info *cat)
install_element(HLR_NODE, &cfg_ussd_no_route_pfx_cmd);
install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd);
install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd);
install_element(HLR_NODE, &cfg_ncss_guard_timeout_cmd);
hlr_vty_subscriber_init();
}

View File

@@ -20,6 +20,8 @@
#include <inttypes.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <time.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/vty/vty.h>
@@ -33,11 +35,24 @@ struct vty;
#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
static char *
get_datestr(const time_t *t, char *datebuf)
{
char *p, *s = ctime_r(t, datebuf);
/* Strip trailing newline. */
p = strchr(s, '\n');
if (p)
*p = '\0';
return s;
}
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
{
int rc;
struct osmo_sub_auth_data aud2g;
struct osmo_sub_auth_data aud3g;
char datebuf[26]; /* for ctime_r(3) */
vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
@@ -63,6 +78,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);
if (subscr->last_lu_seen)
vty_out(vty, " last LU seen: %s UTC%s", get_datestr(&subscr->last_lu_seen, datebuf), VTY_NEWLINE);
if (!*subscr->imsi)
return;
@@ -130,18 +147,19 @@ static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id,
#define SUBSCR_CMD "subscriber "
#define SUBSCR_CMD_HELP "Subscriber management commands\n"
#define SUBSCR_ID "(imsi|msisdn|id) IDENT "
#define SUBSCR_ID "(imsi|msisdn|id) IDENT"
#define SUBSCR_ID_HELP \
"Identify subscriber by IMSI\n" \
"Identify subscriber by MSISDN (phone number)\n" \
"Identify subscriber by database ID\n" \
"IMSI/MSISDN/ID of the subscriber\n"
#define SUBSCR SUBSCR_CMD SUBSCR_ID
#define SUBSCR SUBSCR_CMD SUBSCR_ID " "
#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
#define SUBSCR_UPDATE SUBSCR "update "
#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
#define SUBSCR_MSISDN_HELP "Set MSISDN (phone number) of the subscriber\n"
DEFUN(subscriber_show,
subscriber_show_cmd,
@@ -159,6 +177,10 @@ DEFUN(subscriber_show,
return CMD_SUCCESS;
}
ALIAS(subscriber_show, show_subscriber_cmd,
"show " SUBSCR_CMD SUBSCR_ID,
SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
DEFUN(subscriber_create,
subscriber_create_cmd,
SUBSCR_CMD "imsi IDENT create",
@@ -224,9 +246,9 @@ DEFUN(subscriber_delete,
DEFUN(subscriber_msisdn,
subscriber_msisdn_cmd,
SUBSCR_UPDATE "msisdn MSISDN",
SUBSCR_UPDATE_HELP
"Set MSISDN (phone number) of the subscriber\n"
SUBSCR_UPDATE "msisdn (none|MSISDN)",
SUBSCR_UPDATE_HELP SUBSCR_MSISDN_HELP
"Remove MSISDN (phone number)\n"
"New MSISDN (phone number)\n")
{
struct hlr_subscriber subscr;
@@ -234,15 +256,19 @@ DEFUN(subscriber_msisdn,
const char *id = argv[1];
const char *msisdn = argv[2];
if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
sizeof(subscr.msisdn)-1, VTY_NEWLINE);
return CMD_WARNING;
}
if (strcmp(msisdn, "none") == 0)
msisdn = NULL;
else {
if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
sizeof(subscr.msisdn)-1, VTY_NEWLINE);
return CMD_WARNING;
}
if (!osmo_msisdn_str_valid(msisdn)) {
vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
return CMD_WARNING;
if (!osmo_msisdn_str_valid(msisdn)) {
vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
return CMD_WARNING;
}
}
if (get_subscr_by_argv(vty, id_type, id, &subscr))
@@ -254,11 +280,18 @@ DEFUN(subscriber_msisdn,
return CMD_WARNING;
}
vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
subscr.imsi, msisdn, VTY_NEWLINE);
if (msisdn) {
vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
subscr.imsi, msisdn, VTY_NEWLINE);
if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
osmo_hlr_subscriber_update_notify(&subscr);
} else {
vty_out(vty, "%% Updated subscriber IMSI='%s': removed MSISDN%s",
subscr.imsi, VTY_NEWLINE);
if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
osmo_hlr_subscriber_update_notify(&subscr);
}
return CMD_SUCCESS;
}
@@ -478,6 +511,7 @@ DEFUN(subscriber_aud3g,
void hlr_vty_subscriber_init(void)
{
install_element_ve(&subscriber_show_cmd);
install_element_ve(&show_subscriber_cmd);
install_element(ENABLE_NODE, &subscriber_create_cmd);
install_element(ENABLE_NODE, &subscriber_delete_cmd);
install_element(ENABLE_NODE, &subscriber_msisdn_cmd);

View File

@@ -44,6 +44,10 @@ python-tests:
# don't run vty and ctrl tests concurrently so that the ports don't conflict
$(MAKE) vty-test
$(MAKE) ctrl-test
else
python-tests:
echo "Not running python-based external tests (determined at configure-time)"
endif
VTY_TEST_DB = hlr_vty_test.db
@@ -75,11 +79,6 @@ ctrl-test:
-rm -f $(CTRL_TEST_DB)
-rm $(CTRL_TEST_DB)-*
else
python-tests:
echo "Not running python-based tests (determined at configure-time)"
endif
check-local: atconfig $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
$(MAKE) $(AM_MAKEFLAGS) python-tests

View File

@@ -850,7 +850,7 @@ int main(int argc, char **argv)
log_set_log_level(osmo_stderr_target, LOGL_ERROR);
/* Disable SQLite logging so that we're not vulnerable on SQLite error messages changing across
* library versions. */
dbc = db_open(ctx, "db_test.db", false);
dbc = db_open(ctx, "db_test.db", false, false);
log_set_log_level(osmo_stderr_target, 0);
OSMO_ASSERT(dbc);

View File

@@ -18,6 +18,7 @@ OsmoHLR> list
show talloc-context (application|all) (full|brief|DEPTH) filter REGEXP
show gsup-connections
subscriber (imsi|msisdn|id) IDENT show
show subscriber (imsi|msisdn|id) IDENT
OsmoHLR> enable
OsmoHLR# list
@@ -77,6 +78,7 @@ OsmoHLR(config-hlr)# list
no ussd route prefix PREFIX
ussd default-route external EUSE
no ussd default-route
ncss-guard-timeout <0-255>
OsmoHLR(config-hlr)# gsup
OsmoHLR(config-hlr-gsup)# list
@@ -107,25 +109,15 @@ Current configuration:
!
!
log stderr
logging filter all 1
logging color 1
logging print category 1
logging print extended-timestamp 1
logging print file 1
logging level all notice
logging level main notice
logging level db notice
logging level auc notice
logging level ss info
...
!
line vty
no login
!
ctrl
bind 127.0.0.1
logging level main notice
logging level db notice
logging level auc notice
logging level ss info
...
hlr
gsup
bind ip 127.0.0.1
ussd route prefix *#100# internal own-msisdn
ussd route prefix *#101# internal own-imsi
end

View File

@@ -3,9 +3,10 @@ OsmoHLR> enable
OsmoHLR# list
...
subscriber (imsi|msisdn|id) IDENT show
show subscriber (imsi|msisdn|id) IDENT
subscriber imsi IDENT create
subscriber (imsi|msisdn|id) IDENT delete
subscriber (imsi|msisdn|id) IDENT update msisdn MSISDN
subscriber (imsi|msisdn|id) IDENT update msisdn (none|MSISDN)
subscriber (imsi|msisdn|id) IDENT update aud2g none
subscriber (imsi|msisdn|id) IDENT update aud2g (comp128v1|comp128v2|comp128v3|xor) ki KI
subscriber (imsi|msisdn|id) IDENT update aud3g none
@@ -33,6 +34,13 @@ OsmoHLR# subscriber id 1 show
OsmoHLR# subscriber msisdn 12345 show
% No subscriber for msisdn = '12345'
OsmoHLR# show subscriber imsi 123456789023000
% No subscriber for imsi = '123456789023000'
OsmoHLR# show subscriber id 1
% No subscriber for id = '1'
OsmoHLR# show subscriber msisdn 12345
% No subscriber for msisdn = '12345'
OsmoHLR# subscriber imsi 1234567890230001 create
% Not a valid IMSI: 1234567890230001
OsmoHLR# subscriber imsi 12345678902300x create
@@ -78,6 +86,21 @@ OsmoHLR# subscriber msisdn 12345 update msisdn 423
OsmoHLR# subscriber msisdn 12345 show
% No subscriber for msisdn = '12345'
OsmoHLR# subscriber msisdn 423 update msisdn none
% Updated subscriber IMSI='123456789023000': removed MSISDN
OsmoHLR# subscriber msisdn 423 show
% No subscriber for msisdn = '423'
OsmoHLR# subscriber imsi 123456789023000 show
ID: 1
IMSI: 123456789023000
MSISDN: none
OsmoHLR# subscriber imsi 123456789023000 update msisdn 423
% Updated subscriber IMSI='123456789023000' to MSISDN='423'
OsmoHLR# subscriber msisdn 423 show
ID: 1
IMSI: 123456789023000
MSISDN: 423
OsmoHLR# subscriber imsi 123456789023000 show
ID: 1
IMSI: 123456789023000
@@ -96,6 +119,10 @@ OsmoHLR# subscriber imsi 123456789023000 update ?
aud2g Set 2G authentication data
aud3g Set UMTS authentication data (3G, and 2G with UMTS AKA)
OsmoHLR# subscriber imsi 123456789023000 update msisdn ?
none Remove MSISDN (phone number)
MSISDN New MSISDN (phone number)
OsmoHLR# subscriber imsi 123456789023000 update aud2g ?
none Delete 2G authentication data
comp128v1 Use COMP128v1 algorithm