Compare commits

...

86 Commits

Author SHA1 Message Date
Harald Welte
c3de9a62aa Store identity of VLR/SGSN in UpdateLocation
The HLR must store the least-recently used VLR and SGSN identities in
its database to ensure subsequent mobile-terminated transactions can
be routed accordingly.

Change-Id: Ib2611421f3638eadc361787af801fffe9a34bd8a
Closes: OS#2796
2018-07-30 16:34:33 +02:00
Harald Welte
08d5204f0c USSD: Add support for internal USSD handlers
There are some requests that are best served inside the HLR, as it
has access to subscriber information such as MSISDN and IMSI.

This unfortunately required quite some restructuring of the USSD
related structures including the VTY syntax for adding routes.

The default config file has been updated to replicate the *#100#
built-in behavior of old OsmoNITB.

Closes: OS#2566
Change-Id: I1d09fab810a6bb9ab02904de72dbc9e8a414f9f9
2018-07-30 16:34:26 +02:00
Harald Welte
edc1fc0c25 Add osmo-euse-demo as minimalistic test of a External USSD (EUSE) handler
This is a small program which simply echo's the USSD request message it
gets in a quote back to the sender.  Its purpose is to illustrate how
EUSEs can be implemented using libosmo-gsup-client.

Change-Id: I3fb8554ca329cb609c591058254117006f665e73
2018-07-30 16:33:41 +02:00
Harald Welte
e2e5cefda9 USSD: Add new "DSS" logging category and use it appropriately
Change-Id: I0ac198a49ba70ea40fea18464325f1925797a6e8
2018-07-30 16:33:26 +02:00
Harald Welte
9e59287467 USSD: Further unification of log output; Use LOGPSS when possible
Change-Id: I2c508fe70337d24c4a8b48e0393ad3c979eea0e7
2018-07-30 16:33:26 +02:00
Harald Welte
7057711ca9 USSD: Send ReturnError component if USSD Code unknown / EUSE disconnected
Change-Id: Ieef06cec05dd81f600594465d18804362e0fafd6
2018-07-30 16:33:26 +02:00
Harald Welte
3a190461ee hlr_ussd: Introduce LOGPSS() macro
Change-Id: I1058ef9fd67af2224c991e43bab02bcf21c9f174
2018-07-30 16:33:26 +02:00
Harald Welte
f76578d6a8 USSD: Add basic dispatch + decode of GSUP-encapsulated SS/USSD
We don't want any SS session to run for more than 30s.  The timeout
is currently not refreshed.

If we need more comprehensive timeout handling, using osmo_fsm for SS
sessions might make sense.

Change-Id: I5c9fb6b619402d2a23fea9db99590143d85ac11a
2018-07-30 16:33:24 +02:00
Harald Welte
2c8bfe7278 USSD: Add Core USSD handling + VTY routing config to HLR
Change-Id: I3cfd7cd401ea32b7e92f1124d129099d9f7dc6e6
2018-07-30 16:33:20 +02:00
Harald Welte
0c0c3a1dde hlr: Export + Declare global g_hlr symbol
Change-Id: I6f3e50f071fb2fbbe58413b4760dc2215055a444
2018-07-30 16:33:20 +02:00
Harald Welte
d1bc55c3b6 GSUP: Log GSUP route add/remove
Change-Id: I1768d0b8ee7e2821e40a799c9a1c1d900a7ddc48
2018-07-30 16:33:20 +02:00
Harald Welte
953d27ce8f gsup_client: rename gsup_client_* to osmo_gsup_client_*
As we're moving this to a common/shared library now, we need to use
the osmo_ namespace prefix for symbol names, struct/type names and
constants.

Change-Id: I294f8f96af4c5daa2b128962534426e04909290e
2018-07-30 13:07:08 +00:00
Harald Welte
ec6915a771 import gsup_client.c as new libosmo-gsup-client
This imports the code from osmo-msc 6afef893e17bce67e4d4119acd34d480ed03ba77
with minimal changes to make it compile.  Symbol renaming ot osmo_
prefix is done separately in a follow-up patch to have a as-clean-as-possible
import first.

Requires: libosmocore.git Change-Id Ie36729996abd30b84d1c30a09f62ebc6a9794950
Change-Id: Ief50054ad135551625b684ed8a0486f7af0b2940
2018-07-30 13:07:08 +00:00
Vadim Yanitskiy
9fdb854174 hlr.c: track the use of talloc NULL memory contexts
Tracking NULL memory contexts allows one to detect memory chunks
allocated outside the application's root context, which in most
cases are the result of some mistake.

For example, the VTY implementation still uses the NULL context,
so we have to clean up it manually until this is fixed.

At the moment we have at least one chunk allocated outside the
application's root context (excluding the VTY context):

  full talloc report on 'null_context' (total 24 bytes in 2 blocks)
    struct lookup_helper  contains  24 bytes in  1 blocks

Change-Id: I7ea86730e090c06b2a5966ae4d04b8144b1cd20a
2018-07-30 04:11:20 +07:00
Vadim Yanitskiy
4793a7efc3 hlr.c: free root talloc context on exit
This makes both ASAN and Valgrind happy, because they do expect
all allocated heap chunks to be released on exit.

Change-Id: I7345dec8d06b0b71a859c16132dc0008cfe17cba
2018-07-30 03:57:03 +07:00
Vadim Yanitskiy
527d934807 hlr.c: move deinitialization code from SIGINT handler
There were a few lines of dead code below the osmo_select_main()
loop, while the actual deinitialization code was a part of SIGINT
handler. Let's reanimate this dead zone by moving the code there
and introducing a global 'loop-breaker' variable.

Change-Id: I0e2d673b420193e2bdc1a92377aca542f3a19229
2018-07-30 02:42:25 +07:00
Pau Espin Pedrol
6b274b95fc sql/Makefile: Create empty /var/lib/osmocom directory at install time
Otherwise osmo-hlr is unable to start correctly.

Change-Id: I1233fc9b3dc685561f79a34e1c32c459dc1aa685
2018-07-02 17:47:24 +02:00
Pau Espin Pedrol
edca4f88a6 sql/Makefile: Install sql files under doc/.../sql subdir
Change-Id: I1c9008d4692412a0cfe39d05216fc9c857cf1e8a
2018-07-02 17:44:35 +02:00
Pau Espin Pedrol
b9c1028cb0 sql/Makefile: Install hlr_data.sql as example together with hlr.sql
Change-Id: Id4a12252b309f03bb393fa26612c305744e14403
2018-07-02 17:27:44 +02:00
Pau Espin Pedrol
1442e3a3d3 debian: Avoid installing duplicate cfg file in /etc
Change-Id: Ieb25cc8195a2fe7f81b7a39955e0bca5d5c510eb
2018-07-02 17:15:13 +02:00
Martin Hauke
0b8f054b5f sql/Makefile.am: Make docsdir completely configurable
$(docdir) defaults to $(datadir)/doc/osmo-hlr

Change-Id: I77fa16d0edcf88a8e120921504cd088328077836
2018-06-24 17:14:04 +00:00
Harald Welte
fa7ee333f7 Add "show gsup-connections" VTY command
This can help with debugging and give operational insight.

Change-Id: I977b4b8cdb36dab42b3d736a28d8b5f17cff04cd
2018-06-24 14:06:56 +02:00
Harald Welte
8fbf82b83f gsup_router: Use "#pragma once" and add missing #includes
Change-Id: I2cc43ea5846e5b98281efc897252c8dcc3ef5728
2018-06-24 11:49:16 +02:00
Harald Welte
bd72f1331d move osmo_gsup_addr_send() declaration from luop.h to gsup_router.h
Change-Id: I33165b7b58bd8c863083ed50ce21e3c032c579f5
2018-06-24 11:49:13 +02:00
Harald Welte
32acace879 gsup_server: Add "priv" pointer and make it point to 'struct hlr'
Change-Id: Iada68996b7f4cbdcca92b254ddaf6b88b962e6f1
2018-06-16 20:21:45 +02:00
Harald Welte
b85f60477f disable blind subscriber insertion into every VLR/SGSN
During the attempt to fix OS#2785 in Change-Id
I6a92ca34cdaadca9eacc774bb1ca386c325ba865, we introduced logic that
would blindly insert a subscriber *concurrently* in all VLRs/SGSNs of
the network on any update of the subscriber.

Before that patch, we didn't update the current serving SGSN/VLR,
and after the change we created subscribers in all SGSNs/VLRs, of which
all-1 are not serving the subscriber at that time.

We'll have to go back to the original behavior until a proper fix can
be introduced.

Change-Id: Ibf6f1b21b08560d4d8f41bbe7782d40abf4585f8
Related: OS#3091
Related: OS#2785
2018-06-15 18:01:14 +02:00
Harald Welte
a1d3b048fb Return proper GSUP error in case of too short IMSI
This fixes HLR_Tests.TC_gsup_sai_err_invalid_imsi

Change-Id: I4f51abdf44dfc62d7e8792341aad6dafe58923da
Closes: OS#3028
2018-06-11 20:28:35 +02:00
Stefan Sperling
f83432c25c move creation of insert subscriber data messages to a common function
Move code to create an Insert Subscriber Data message into a common
function which can be shared by hlr.c and luop.c.

As a consequence, we always encode gsup.cn_domain in the corresponding
msgb and must adjust expected output of the 'gsup' test accordingly.

Change-Id: I6a92ca34cdaadca9eacc774bb1ca386c325ba865
Requested-by: neels
Related: OS#2785
2018-05-18 12:18:32 +02:00
Pau Espin Pedrol
78f4301025 Bump version: 0.2.0.3-1b8a → 0.2.1
Change-Id: I9c457e9baeb546bfefacacddddd48996902e587a
2018-05-04 18:41:35 +02:00
Neels Hofmeyr
1b8a1dc00a add error handling to osmo_gsup_configure_wildcard_apn()
Follow-up to I83d9ef2868bbb01e3f1ddb7920fe735aca172b15 as requested in code review.

Change-Id: Ifcee1e0d275741c1172b208600851861adb13238
2018-05-04 16:48:26 +02:00
Neels Hofmeyr
9d307ec7ae add gsup_test to catch OS#3231
Encode an Insert Subscr Data with is_ps == true to trigger the encoding bug
described in OS#3231, i.e. show that it is fixed.

Move osmo_gsup_addr_send() to a separate .c file, so that it can be overridden
in the regression test to just dump the msgb instead.

I used this test to reproduce issue OS#3231, and now that it's here we might as
well keep it, and possibly expand on it in the future.

Related: OS#3231
Change-Id: Id1453351758f3e1a9ff03bd99fefaf51886e77da
2018-05-04 16:12:19 +02:00
Neels Hofmeyr
5aeb438194 fix luop crash: use buffer for APN that remains valid
In osmo_gsup_configure_wildcard_apn(), do not compose APN into a local buffer
that becomes invalid as soon as the function exits. Instead, use a caller
provided buf.

Fixes OS#3231 crash:

  ==20030==ERROR: AddressSanitizer: stack-buffer-underflow on address 0x7fffffffd9c0 at pc 0x7ffff6e9b6c2 bp 0x7fffffffd900 sp 0x7fffffffd0b0
  READ of size 2 at 0x7fffffffd9c0 thread T0
      #0 0x7ffff6e9b6c1  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x766c1)
      #1 0x7ffff6314419 in tlv_put ../../../../src/libosmocore/include/osmocom/gsm/tlv.h:107
      #2 0x7ffff6314419 in msgb_tlv_put ../../../../src/libosmocore/include/osmocom/gsm/tlv.h:299
      #3 0x7ffff6314419 in encode_pdp_info ../../../../src/libosmocore/src/gsm/gsup.c:419
      #4 0x7ffff6314419 in osmo_gsup_encode ../../../../src/libosmocore/src/gsm/gsup.c:535
      #5 0x555555580016 in _luop_tx_gsup ../../../src/osmo-hlr/src/luop.c:54
      #6 0x5555555809d8 in lu_op_tx_insert_subscr_data ../../../src/osmo-hlr/src/luop.c:264
      #7 0x55555558b356 in rx_upd_loc_req ../../../src/osmo-hlr/src/hlr.c:306
      #8 0x55555558b356 in read_cb ../../../src/osmo-hlr/src/hlr.c:365
      #9 0x555555586671 in osmo_gsup_server_read_cb ../../../src/osmo-hlr/src/gsup_server.c:105
      #10 0x7ffff5b35911 in ipa_server_conn_read ../../../src/libosmo-abis/src/input/ipa.c:356
      #11 0x7ffff5b35911 in ipa_server_conn_cb ../../../src/libosmo-abis/src/input/ipa.c:387
      #12 0x7ffff5e5541f in osmo_fd_disp_fds ../../../src/libosmocore/src/select.c:216
      #13 0x7ffff5e5541f in osmo_select_main ../../../src/libosmocore/src/select.c:256
      #14 0x5555555791b6 in main ../../../src/osmo-hlr/src/hlr.c:600
      #15 0x7ffff4707a86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21a86)
      #16 0x555555579679 in _start (/usr/local/bin/osmo-hlr+0x25679)

  Address 0x7fffffffd9c0 is located in stack of thread T0 at offset 16 in frame
      #0 0x7ffff63131ff in osmo_gsup_encode ../../../../src/libosmocore/src/gsm/gsup.c:481

    This frame has 1 object(s):
      [32, 64) 'bcd_buf' <== Memory access at offset 16 underflows this variable

Related: OS#3231
Change-Id: I83d9ef2868bbb01e3f1ddb7920fe735aca172b15
2018-05-04 16:02:46 +02:00
Pau Espin Pedrol
cb360f06c8 Bump version: 0.1.0.39-1cb4-dirty → 0.2.0
Change-Id: I2a9fdd140d68053bc7c8354bf2b3a0293c514516
2018-05-03 16:27:14 +02:00
Stefan Sperling
1cb489231a don't forget to mark luop as a packet switched connection
In rx_upd_loc_req() we set the connection's supports_ps field but
forgot to also set the equivalent field in the corresponding luop.

Change-Id: Ie175a067ac1324cdd39d7f756a40fab923421793
Related: OS#2785
2018-05-03 14:05:56 +02:00
Stefan Sperling
93c5b10310 rewrite subscriber_update_notify() without calls into luop
This function relied on implementation details of the luop code.
Port what is necessary for an independent Insert Subscriber Data
Tx operation from the luop code into this function.

A next possible step would be to try to merge both of these
into a common implementation. This will be addressed in a
follow-up change as soon as this change is merged.

The TTCN3 test TC_vty_msisdn_isd is still passing (it currently
triggers the "circuit switched domain" case because it does not
advertise itself as an SGSN in the IPA unit name).

Change-Id: I06c43ece2b48dc63d599000eb6d6d51e08963067
Related: OS#2785
2018-05-02 13:34:38 +02:00
Pau Espin Pedrol
ec9440f1bc Remove unused src/db_test.c
There's a larger test suite in use in tests/db/db_test.c

Change-Id: Ifa409df9b4bb94bd4e8f15568486066393009494
2018-04-17 15:10:05 +02:00
Pau Espin Pedrol
51530311a8 use osmo_init_logging2
Change-Id: If4449cce1af46be22cc370abd3a6da8e22a31fa5
2018-04-17 15:07:06 +02:00
Stefan Sperling
f162252a08 notify GSUP clients when HLR subscriber information changes
Add a function which triggers subscriber update notifications to
all connected GSUP clients, and invoke it when the MSISDN of a
subscriber is changed via VTY.

This makes the TTCN3 HLR test TC_vty_msisdn_isd pass.

Note that the new function currently relies on implementation
details of the Location Update Operation (luop) code.
Because of this we currently log a slightly misleading message
when the updated Insert Subscriber Data message is sent:
  "luop.c:161 LU OP state change: LU RECEIVED -> ISD SENT"
This message is misleading because, in fact, no location update
message was received from a GSUP client at that moment.

So while this change fixes the externally visible behaviour, we may
want to follow this up with some refactoring to avoid relying on
luop internals. It seems acceptable to do that in a separate step
since such a change will be more involved and harder to review.

We may want to trigger such notifications in other situations as well.
This is left for future work, too. There are no TTCN3 test cases for
other situations yet, as far as I can see.

Related: OS#2785
Change-Id: Iffe1d7afb9fc7dbae542f70bbf5391ddc08a14b4
2018-04-10 07:57:49 +02:00
Stefan Sperling
8f8401453c more robust usage of osmo_timer API for osmo-hlr luop timer
Use osmo_timer_setup() to set up the luop timer, instead of
settting the timer up manually.

Delete the timer before the luop is freed to prevent a potential
crash in case the timer is already armed and the function call
chain leading up to lu_op_free() does not cancel the timer.

Found while studying code to prepare work on issue OS#2785.

This change has been tested with 'make check' and TTCN3 HLR tests.

Related: OS#2785
Change-Id: I1a7596675b2d94217895f0f3d3f67b86ef123c2e
2018-03-29 18:17:00 +02:00
Neels Hofmeyr
70e7f21cb1 jenkins.sh: use --enable-werror configure flag, not CFLAGS
Change-Id: I5fe776cf9ccb5d462c3c1fbbb1e31abe6f42bde6
2018-03-05 20:54:54 +01:00
Neels Hofmeyr
2e78858756 configure: add --enable-werror
Provide a sane means of adding the -Werror compiler flag.

Currently, some of our jenkins.sh add -Werror by passing 'CFLAGS="-Werror"',
but that actually *overwrites* all the other CFLAGS we might want to have set.

Maintain these exceptions from -Werror:
a) deprecation (allow upstream to mark deprecation without breaking builds);
b) "#warning" pragmas (allow to remind ourselves of errors without breaking
   builds)

As a last configure step before generating the output files, print the complete
CFLAGS and CPPFLAGS by means of AC_MSG_RESULT.

Change-Id: Id5c0740a37067cbe8986d52d63c6134769c71c47
2018-03-05 20:42:57 +01:00
Harald Welte
3f2a9a2ab1 Fix responses to PURGE MS
When performing PURGE MS, OsmoHLR before this patch used toreturn
an error even in the successful case.  The reasone for this is some
wrong assumptions about the return values of db_subscr_purge().

Change-Id: Ie3005e2eeb424715fd77f202e9fe18464ba211b7
2018-03-01 23:35:35 +01:00
Harald Welte
880a34d2ef vty: Don't print error if removing auth data while none present
It's a bit confusing to the user if he wants to set AUD=none
and it's already none.  Avoid printing error messages in that case.

Change-Id: I5f32dd5d6e4939c738faf442c7e86671d18777f8
2018-03-01 21:33:35 +01:00
Harald Welte
7ee6e554af osmo-hlr: Add talloc context introspection via VTY
This requires libosmocore with Change-Id
I43fc42880b22294d83c565ae600ac65e4f38b30d or later.

Change-Id: Ibc2c333b83f85fa69a364e3e342f12f50dbc2f70
2018-02-14 08:25:52 +01:00
Harald Welte
9214c6c7ef Fix expected test output after new 'logging print file 1' vty command
Ever since libosmocore Change-Id I1c931bff1f1723aa82bead9dfe548e4cc5b685e0
was merged, the VTY tests were broken. Let's fix that by adjusting
the expected test output.

Change-Id: I929ca7f366220b5d1238d43eddc96fa9dde916b6
2018-02-09 14:27:22 +01:00
Alexander Huemer
79fc6984ac Add missing build products in .gitignore
Change-Id: I48b93adc280c1b0521e7f5acc1f66ce5db462915
2018-01-29 09:29:18 +01:00
Neels Hofmeyr
ba1605afdc fix build: db_test: missing LIBOSMOABIS_CFLAGS and _LIBS
Change-Id: I2539f5dc7a512a57ad36c460a11195ccbd84d7d6
2018-01-16 13:35:46 +01:00
Max
b6265d1a55 Enable sanitize for CI tests
Change-Id: I112307b5eaf494062b2e8498ff2e9217fb8db925
2018-01-08 13:09:24 +00:00
Neels Hofmeyr
6c84da5942 ctrl test: fix: adjust expectations after stricter ctrl parsing
After libosmocore I96a9b6b6a3a5e0b80513aa9eaa727ae8c9c7d7a1 the CTRL interface
returns stricter errors. Adjust the expectations of
test_subscriber_errors.ctrl to fix the external tests on master.

Change-Id: I9337b6b4f3fa8822c91760deb01f18a77a073d19
2018-01-07 22:27:08 +01:00
Neels Hofmeyr
84c2f43d00 fix debug log: put 'deriving 2G from 3G' in proper place
Don't log "deriving 2G from 3G" when we're actually calculating separately; log
it when we're actually deriving from 3G.

Add log "calculating 2G separately" in the right place.

The test output changes show that each test said "separate 2G" at the top while
logging "deriving 2G from 3G" further down, which was obviously wrong.

Change-Id: I6679d7ef8fdcae39a0c2aff7ac638e63dddb10dc
2017-12-20 00:10:34 +01:00
Neels Hofmeyr
63f68ccc4c fix test_subscriber_errors.ctrl after libosmocore change
libosmocore Ie35a02555b76913bb12734a76fc40fde7ffb244d
"ctrl: on parse errors, return a detailed message to sender"
the test_subscriber_errors.ctrl test fails.

Adjust the expected error message.

Change-Id: I3aee1507721cd073f72369150d0fb3cff0fdf66f
2017-12-19 15:50:57 +01:00
Pau Espin Pedrol
b5d77012d1 contrib:jenkins.sh: Enable Werror
Change-Id: I61688a4c9b50f6f3705bd18c96c00f8703ef5042
2017-12-15 19:06:59 +01:00
Pau Espin Pedrol
fc96f688d4 luop.c: Transform FIXME from warning to pragma message
This way the issue is still visible but we can enable Werror to avoid
introducing new warnings.

Change-Id: I6c9b195bf0e3f853e202cdbdb72d35d83cd2a2ab
2017-12-15 19:05:12 +01:00
Max
43bd6069e8 Remove unused ipa.py
Change-Id: I0a6c22f38480ae75ae9efb452e4eeacb39ae4f42
2017-12-01 12:05:51 +00:00
Neels Hofmeyr
2dee60ef44 db_test: also test db_get_auc() return values
Verify that it returns -ENOENT on non-existing IMSI and -ENOKEY for no auth
data.

Move the auc_compute_vectors() stub to the top near the db_get_auc() call, and
just return num_vec to get a successful return value when auth data is present.

Change-Id: Ic0158228afbd78b8ca21f62dffa9f868674682b9
2017-11-29 16:22:52 +00:00
Neels Hofmeyr
ab4d509a83 osmo-hlr: log details for unknown IMSI / no auth data / db error
For unknown IMSI and no auth data for a known IMSI, log respective messages on
NOTICE level.

For database error, log on ERROR level.

Change-Id: I3838fa78567e7e92d797d90b8b90865d9ebba90a
2017-11-29 16:22:46 +00:00
Neels Hofmeyr
bd1dca0859 db_get_auth_data / db_get_auc: clarify return values
Differentiate between "IMSI unknown" and "IMSI has no auth data": in case of
known IMSI lacking auth data, return -ENOKEY instead of -ENOENT.

Fix API doc comments to reflect what the functions actually return, on top of
adding the -ENOKEY detail.

Adjust db_test expectations from -ENOENT to -ENOKEY where appropriate.

Adjust VTY and CTRL command rc evaluation.

A subsequent patch will use these return values to log details on each of these
situations.

Change-Id: Icf6304d23585f2ed45e050fa27c787f2d66fd3f7
2017-11-29 16:22:29 +00:00
Max
33eeeef9dc Remove unused check
We do not test on FreeBSD in jenkins anymore.

Change-Id: I1578306244f6ee218e464a6c378ff60605cf1d5c
2017-11-24 11:30:50 +01:00
Neels Hofmeyr
33cbde9ced return GMM_CAUSE_IMSI_UNKNOWN if there is no auth data
If we have a subscriber entry that lacks auth data, we currently return
GMM_CAUSE_NET_FAIL, which in the MSC log looks like the HLR is not connected or
something grave. Instead, return GMM_CAUSE_IMSI_UNKNOWN.

This changes the OsmoMSC log in this way:
Before:

  DVLR <001e> VLR_Authenticate(901700000014701)[0x5555558dabb0]{VLR_SUB_AS_NEEDS_AUTH_WAIT_AI}: GSUP: rx Auth Info Error cause: 17: Network failure

After:

  DVLR <001e> VLR_Authenticate(901700000014701)[0x5555558dabb0]{VLR_SUB_AS_NEEDS_AUTH_WAIT_AI}: GSUP: rx Auth Info Error cause: 2: IMSI unknown in HLR

A better cause value would be something that says "IMSI known, but we have no
auth data", but since such cause value is not defined, the plain "IMSI unknown"
seems to be the best match.

Change-Id: I90df7b255317df1e5d968e7ce3b9d2c404b98db8
2017-11-22 20:49:43 +01:00
Neels Hofmeyr
671db90ac3 cosmetic: rx_send_auth_info(): decide error cause with switch()
Prepare for tweaking error handling in a subsequent patch: use switch() instead
of if().

Prepares-for: I90df7b255317df1e5d968e7ce3b9d2c404b98db8
Change-Id: I1f628aa9d62b778951726bebec8cf338444fc897
2017-11-22 20:42:49 +01:00
Neels Hofmeyr
d3814b936b db_test: don't verify SQLite issued error messages, they might change
A user on openbsc@ complained that with SQLite 3.8.2, the db_test fails with

  --- expected
  +++ stderr
  -DDB (2067) abort at 18 in [INSERT INTO subscriber (imsi) VALUES ($imsi)]: UNIQUE constraint failed: subscriber.imsi
  +DDB (2067) abort at 35 in [INSERT INTO subscriber (imsi) VALUES ($imsi)]: UNIQUE constraint failed: subscriber.imsi

i.e. a trivial difference in the error message issued by SQLite.

For db_test, don't output any SQLite error messages: Add argument
enable_sqlite_logging, pass as true, except in db_test.c.
Remove the SQLite error messages from expected output.

(Note that there is a src/db_test.c program that's not of interest here, this
is about the tests/db/db_test.c)

Change-Id: I2513d71cc0072aef8d08f47d0a1959f311176229
2017-11-21 12:33:45 +01:00
Neels Hofmeyr
6f3e8d6297 add --enable-sanitize config option
Change-Id: I12b7b0e751f274a05e88c79299fd8388667cc542
2017-11-18 10:19:13 +00:00
Alexander Couzens
db5dae6c99 debian: install osmo-hlr.cfg to /etc/osmocom
Change-Id: Ifa1094da9b286a17a5c9a1ee300ec13a4a10a9a7
2017-11-17 14:27:49 +00:00
Alexander Couzens
38f08770a3 doc: install example .cfg files to $(docdir)/examples/
Change-Id: I8671ce33b9bf28c89f767dd1b4a1463aeb275158
2017-11-17 14:27:40 +00:00
Alexander Couzens
aee7be901b debian: include systemd service osmo-hlr.service
Change-Id: I6d9fd34aa42b911f074557b526adde05e03d58b9
2017-11-16 00:38:32 +01:00
Neels Hofmeyr
8db490695d db-tool: error-exit on too many arguments
Each arg parsing should increment optind, so if there are any surplus args in
the end, that's an error.

Change-Id: I9fc0a87d11db8c35061568e3f8b5a5547931a961
2017-11-12 14:22:00 +00:00
Neels Hofmeyr
c82e6ad190 db-tool: cosmetic: tweak printf output
Say <nitb.db> to indicate a filename.
No need to print the cmd and arg count, really.

Change-Id: I3be31754db5297b3f6028877068f97ce1be4d74b
2017-11-12 14:22:00 +00:00
Neels Hofmeyr
0959e8b354 db-tool: add command 'create'
Change-Id: Ic4997d17763e50fb63c36fc0001570230bf64a12
2017-11-12 14:22:00 +00:00
Neels Hofmeyr
4f3841c153 cosmetic: add comment on ignored return value
Coverity wants us to evaluate sqlite3_reset, but it is of no use to do so.

Related: coverity CID#178653
Change-Id: I64ac8c148f48be60f9c0d346df0c5152bb527494
2017-11-11 23:25:06 +00:00
Harald Welte
bd0d5bf5d8 hlr.c: Avoid overflow of lu_operation.subscr.imsi
It appears that hlr_subscriber.imsi is 16 buffers in size:
15 chars for IMSI + 1 byte NUL.  However,  osmo_gsup_message.imsi
is 17 bytes (for whatever reason), so we cannot simply do a strpy()
as this might overflow the hlr_subscriber.imsi field!

TODO: check if weactually ever receive a too-long IMSI in GSUP and
reject that at an earlier time in the code flow.

Fixes: Coverity CID#164746

Change-Id: I9ff94e6bb0ad2ad2a7c010d3ea7dad9af0f3c048
2017-11-10 21:26:53 +00:00
Neels Hofmeyr
87a04b6b95 hlr_db_tool: fix error log strerror invocation
The db API returns negative errno values, need to flip the sign before feeding
to strerror.

Fixes: coverity CID#178658
Change-Id: Iaab46f565a1112d8a7def8ea90a5cd440c0a3b41
2017-11-08 02:29:05 +00:00
Neels Hofmeyr
85e8a64bb4 vty: skip installing cmds now always installed by default
vty_install_default() and install_default() will soon be deprecated.

Depends: I5021c64a787b63314e0f2f1cba0b8fc7bff4f09b
Change-Id: I09762f110c7bcaf85c0ef2f472eb43ac543c74e9
2017-11-01 00:50:13 +01:00
Harald Welte
0dcbd47a1e Tag/Release version 0.1.0
This is the first real version tag of osmo-hlr.

Change-Id: Ie0aff33ab9c36cd9219258a4d869de36612b6095
2017-10-28 20:43:12 +02:00
Harald Welte
71b5f5b923 Debian: Make sure we include osmo-hlr-db-tool in the package
Change-Id: Ia67ae6d4b1af982db8c342f2d8fd29deb83ccaff
2017-10-28 20:43:12 +02:00
Neels Hofmeyr
73d14af278 add osmo-hlr-db-tool, program to migrate from osmo-nitb db
Move macro copy_sqlite3_text_to_buf() to db.h, so it can be used in
hlr_db_tool.c.

Add _dbd_decode_binary() from libdbi to avoid depending on the entire libdbi
just for KI BLOB decoding. Add it in a separate file, copying its own license,
the lGPL.

Offer commandline option "import-nitb-db" to read in an old osmo-nitb database
and copy subscriber IMSIs and 2G auth data to OsmoHLR db format.

Anticipate future command line options like "import-csv", so keep the code
generalized.

Change-Id: I0dfa6ec033dd93161c1adc2ce1637195fe5b7a63
2017-10-28 20:34:01 +02:00
Neels Hofmeyr
6eb231eccc fix default logging levels to NOTICE, not DEBUG
Tweak unit test binaries to still used DEBUG loglevels, so that their expected
outputs remain unchanged (and nicely verbose).

Adjust test_nodes.vty, now expecting the 'notice' log levels upon
'show running-config'.

Change-Id: Ic061e61c9625b49cef8bc2a2c0b936e262c22268
2017-10-28 16:49:46 +00:00
Neels Hofmeyr
dbced93b5f cosmetic: rename SL3_TXT macro, use osmo_strlcpy()
Rename SL3_TXT to more accurate copy_sqlite3_text_to_buf(), and use
osmo_strlcpy() instead of essentially dup'ing it.

The macro will also be used by hlr_db_tool.c in upcoming patch. This patch
prepares for a move to db.h.

Change-Id: I1dadeddddcfe0109195c09c0e706201b0df009cc
2017-10-28 16:49:33 +00:00
Neels Hofmeyr
88c91f6fb2 cosmetic: sql/hlr.sql: move comments
By moving the comments inside the table row definitions, they are dumped back
during 'sqlite3 hlr.db .dump'. When they are between SQL statements like before
this patch, the comments are lost.

Tweak wording.

Change-Id: I280c2e2d3e9b7f1dc632722724d9e1c54d041820
2017-10-28 16:49:33 +00:00
Neels Hofmeyr
7750d2cedc automatically create db tables on osmo-hlr invocation
If a database file is missing, osmo-hlr creates it, as is the default sqlite3
API behavior -- before this patch, that db file is created, but lacks useful
tables. Actually also create initial tables in it, as osmo-nitb did.

In effect, the 'vty-test' target in tests/Makefile.am no longer needs to create
a database manually. (The 'ctrl-test' still does, because it also wants to add
subscriber data on top of the bare tables.)

Note: it could be desirable to bail if the desired database file does not
exist. That is however a different semantic from this patch; this is not
changing the fact that a db file is created, this just creates a usable one.

Note: I am about to add osmo-hlr-db-tool to do database migration from
osmo-nitb. For that, it is desirable to bootstrap a usable database, which is
the core reason for this patch.

Don't plainly duplicate hlr.sql to .c, but create db_bootstrap.h as a
BUILT_SOURCE from reading in sql/hlr.sql and mangling via sed to a list of SQL
statement strings. On each db_open(), run this bootstrap sequence.

In sql/hlr.sql, these tweaks are necessary:
* Add 'IF NOT EXISTS' to 'CREATE TABLE', so that the bootstrap sequence can be
  run on an already bootstrapped db.
* Drop the final comment at the bottom, which ended up being an empty SQL
  statement and causing sqlite3 API errors, seemed to have no purpose anyway.

Note: by composing the statement strings as multiline and including the SQL
comments, sqlite3 actually retains the comments contained in table definitions
and prints them back during 'sqlite3 hlr.db .dump'.

Change-Id: If77dbbfe1af3e66aaec91cb6295b687f37678636
2017-10-28 16:49:33 +00:00
Neels Hofmeyr
cd7fa4502c db_test: fix *FLAGS
The -I includes should be in CFLAGS, not CPPFLAGS.

I noticed problems with it when trying to add an -I$(builddir) in an upcoming
patch that adds a BUILT_SOURCE, If77dbbfe1af3e66aaec91cb6295b687f37678636.

Change-Id: Ie57a04b7efc7a1e16cf0e3625d8ad2f0ef0089b0
2017-10-28 16:49:33 +00:00
Neels Hofmeyr
99a14c8ca1 tests/Makefile: use test db var instead of repeating the path
Change-Id: I9859b522b5ffa7f2c9ed33ab849199d4b4e6696c
2017-10-28 16:49:32 +00:00
Neels Hofmeyr
c6a6d26f50 jenkins: use osmo-clean-workspace.sh before and after build
See osmo-ci change I2409b2928b4d7ebbd6c005097d4ad7337307dd93 for rationale.

Depends: I2409b2928b4d7ebbd6c005097d4ad7337307dd93
Change-Id: I9d35913f9cd60ff121d29f357919a0b0d62d6835
2017-10-28 04:37:41 +02:00
Neels Hofmeyr
3f697cdc71 test_subscriber.ctrl: test against octal/hex interpretation of id
Add a large enough subscriber id and add a test that ensures a leading zero is
not interpreted as octal, and that a leading 0x is invalid and not interpreted
as hexadecimal.

Change-Id: Ib468b7cb595cf52331ebb41e6de0e8f57f69e173
2017-10-27 02:37:20 +02:00
Neels Hofmeyr
446eb0f1bc ctrl: completely replace all CTRL commands
The previous commands are not conforming to how the CTRL interface is intended
to work:

  SET enable-ps <IMSI>
  SET disable-ps <IMSI>
  SET status-ps <IMSI>

'status-ps' is a write-only command even though it returns the status.
'enable-ps' / 'disable-ps' indicate the value instead of a variable name of an
entity. The entity <IMSI> takes the place of the variable value.

See also https://lists.osmocom.org/pipermail/openbsc/2017-September/011236.html

Instead, replace with

  SET subscriber.by-imsi-123456.ps-enabled {0,1}
  GET subscriber.by-imsi-123456.ps-enabled

and also provide further CTRL functions while at it:

  {SET,GET} subscriber.by-{imsi,msisdn,id}-123456.{cs,ps}-enabled {0,1}
  GET subscriber.by-{imsi,msisdn,id}-123456.{info,info-aud,info-all}

Provide CTRL tests in the form of transcripts.

Adjust tests/test_subscriber.sql to feature nonzero SQN, to see some values for
SQN in the CTRL transcript tests. (This does not affect the VTY tests, because
that creates its own subscribers, and there's no VTY command to set the SQN.)

This is the first time an application uses CTRL_NODE ids that are defined
outside of libosmocore, see 'Depends' below.

Implementation choice: the first idea was to have a '.' between the 'by-xxx'
and the value, like:

  subscriber.by-xxx.123456.function

but the difficulty with subscribers is that they are not in RAM, and I can't
just point node_data at a struct instance that is always there (like, say, a
global bts[0] struct in osmo-bsc). Instead, I want to store the selector and
later decide whether to read from the DB or whatever. With a '.' separating
things, the only way in a ctrl function to obtain both 'by-xxx' and '123456'
for picking a subscriber record would be to parse the entire variable path
string elements, including 'subscriber' and 'function', which would then also
clumsily fix at which node level we hook these commands; there could have been
separate CTRL_NODE_SUBSCR_BY_{IMSI,MSISDN,ID} parent nodes, but we cannot
introspect the current parent node dynamically within a ctrl function handler
(plus I'm not sure whether it's possible and a good idea to have the same
command under multiple parent nodes).

Rather than that, I store the 'by-foo-123' token in the node_data pointer to
have both bits of information pointed at by a single pointer; I use the
incoming command parsing to get this token pre-separated from surrounding node
names, and no need to re-allocate it, since the vector of tokens lives until
after command execution is complete. Each leaf command obtains this token from
cmd->node (aka node_data), and feeds this token to a common static function to
parse selector and value from it and to retrieve a subscriber record as needed.

(BTW, I have mentioned on the mailing list that this way might be necessary to
avoid numeric-only CTRL node names, but we don't need to, and that is not at
all related to this choice of structure.)

Depends: libosmocore I1bd62ae0d4eefde7e1517db15a2155640a1bab58
         libosmocore Ic9dba0e4a1eb5a7dc3cee2f181b9024ed4fc7005
Change-Id: I98ee6a06b3aa6a67adb868e0b63b0e04eb42eb50
2017-10-27 02:35:49 +02:00
Neels Hofmeyr
234f9cb701 cosmetic: tweak params of hlr_controlif_setup()
Cosmetically prepare for adding new CTRL commands in hlr_controlif_setup():
- drop unused 'gs' param.
- use ctrl_interface_setup_dynip2(), so far with default CTRL nodes; custom
  nodes will be added soon.

Prepares: I98ee6a06b3aa6a67adb868e0b63b0e04eb42eb50
Change-Id: I63004a7953b04988449697dbc5d55d7ed0c6d82d
2017-10-27 00:35:01 +00:00
Neels Hofmeyr
16140f70c5 db api: fix/add API docs
Change-Id: I854fafd8e56bd0b8394f8ed79d023c11c2fdbdca
2017-10-25 19:21:40 +02:00
65 changed files with 4601 additions and 658 deletions

11
.gitignore vendored
View File

@@ -22,10 +22,17 @@ m4
missing
.deps
src/osmo-hlr
src/db_test
*.pc
src/db_test
src/db_bootstrap.h
src/osmo-hlr
src/osmo-hlr-db-tool
tests/atconfig
tests/testsuite
tests/auc/auc_3g_test
tests/auc/auc_ts_55_205_test_sets.c
tests/auc/auc_ts_55_205_test_sets
tests/auc/auc_test
tests/gsup_server/gsup_server_test

View File

@@ -1,7 +1,9 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2
SUBDIRS = \
doc \
src \
include \
sql \
tests \
$(NULL)
@@ -10,6 +12,9 @@ EXTRA_DIST = \
.version \
$(NULL)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libosmo-gsup-client.pc
@RELMAKE@
BUILT_SOURCES = $(top_srcdir)/.version

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.9.5)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.9.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.9.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.3.2)
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(SQLITE3, sqlite3)
@@ -47,6 +47,36 @@ AC_CONFIG_MACRO_DIR([m4])
dnl checks for header files
AC_HEADER_STDC
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
[--enable-sanitize],
[Compile with address sanitizer enabled],
)],
[sanitize=$enableval], [sanitize="no"])
if test x"$sanitize" = x"yes"
then
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
fi
AC_ARG_ENABLE(werror,
[AS_HELP_STRING(
[--enable-werror],
[Turn all compiler warnings into errors, with exceptions:
a) deprecation (allow upstream to mark deprecation without breaking builds);
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
]
)],
[werror=$enableval], [werror="no"])
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
fi
AC_ARG_ENABLE([external_tests],
AC_HELP_STRING([--enable-external-tests],
[Include the VTY/CTRL tests in make check [default=no]]),
@@ -62,13 +92,21 @@ 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")
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
AC_OUTPUT(
Makefile
doc/Makefile
src/Makefile
src/gsupclient/Makefile
include/Makefile
libosmo-gsup-client.pc
sql/Makefile
tests/Makefile
tests/auc/Makefile
tests/auc/gen_ts_55_205_test_sets/Makefile
tests/gsup_server/Makefile
tests/gsup/Makefile
tests/db/Makefile
)

View File

@@ -1,278 +0,0 @@
#!/usr/bin/python3
# -*- mode: python-mode; py-indent-tabs-mode: nil -*-
"""
/*
* Copyright (C) 2016 sysmocom s.f.m.c. GmbH
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
"""
import struct, random, sys
class IPA(object):
"""
Stateless IPA protocol multiplexer: add/remove/parse (extended) header
"""
version = "0.0.5"
TCP_PORT_OML = 3002
TCP_PORT_RSL = 3003
# OpenBSC extensions: OSMO, MGCP_OLD
PROTO = dict(RSL=0x00, CCM=0xFE, SCCP=0xFD, OML=0xFF, OSMO=0xEE, MGCP_OLD=0xFC)
# ...OML Router Control, GSUP GPRS extension, Osmocom Authn Protocol
EXT = dict(CTRL=0, MGCP=1, LAC=2, SMSC=3, ORC=4, GSUP=5, OAP=6)
# OpenBSC extension: SCCP_OLD
MSGT = dict(PING=0x00, PONG=0x01, ID_GET=0x04, ID_RESP=0x05, ID_ACK=0x06, SCCP_OLD=0xFF)
_IDTAG = dict(SERNR=0, UNITNAME=1, LOCATION=2, TYPE=3, EQUIPVERS=4, SWVERSION=5, IPADDR=6, MACADDR=7, UNIT=8)
CTRL_GET = 'GET'
CTRL_SET = 'SET'
CTRL_REP = 'REPLY'
CTRL_ERR = 'ERR'
CTRL_TRAP = 'TRAP'
def _l(self, d, p):
"""
Reverse dictionary lookup: return key for a given value
"""
if p is None:
return 'UNKNOWN'
return list(d.keys())[list(d.values()).index(p)]
def _tag(self, t, v):
"""
Create TAG as TLV data
"""
return struct.pack(">HB", len(v) + 1, t) + v
def proto(self, p):
"""
Lookup protocol name
"""
return self._l(self.PROTO, p)
def ext(self, p):
"""
Lookup protocol extension name
"""
return self._l(self.EXT, p)
def msgt(self, p):
"""
Lookup message type name
"""
return self._l(self.MSGT, p)
def idtag(self, p):
"""
Lookup ID tag name
"""
return self._l(self._IDTAG, p)
def ext_name(self, proto, exten):
"""
Return proper extension byte name depending on the protocol used
"""
if self.PROTO['CCM'] == proto:
return self.msgt(exten)
if self.PROTO['OSMO'] == proto:
return self.ext(exten)
return None
def add_header(self, data, proto, ext=None):
"""
Add IPA header (with extension if necessary), data must be represented as bytes
"""
if ext is None:
return struct.pack(">HB", len(data) + 1, proto) + data
return struct.pack(">HBB", len(data) + 1, proto, ext) + data
def del_header(self, data):
"""
Strip IPA protocol header correctly removing extension if present
Returns data length, IPA protocol, extension (or None if not defined for a give protocol) and the data without header
"""
if not len(data):
return None, None, None, None
(dlen, proto) = struct.unpack('>HB', data[:3])
if self.PROTO['OSMO'] == proto or self.PROTO['CCM'] == proto: # there's extension which we have to unpack
return struct.unpack('>HBB', data[:4]) + (data[4:], ) # length, protocol, extension, data
return dlen, proto, None, data[3:] # length, protocol, _, data
def split_combined(self, data):
"""
Split the data which contains multiple concatenated IPA messages into tuple (first, rest) where rest contains remaining messages, first is the single IPA message
"""
(length, _, _, _) = self.del_header(data)
return data[:(length + 3)], data[(length + 3):]
def tag_serial(self, data):
"""
Make TAG for serial number
"""
return self._tag(self._IDTAG['SERNR'], data)
def tag_name(self, data):
"""
Make TAG for unit name
"""
return self._tag(self._IDTAG['UNITNAME'], data)
def tag_loc(self, data):
"""
Make TAG for location
"""
return self._tag(self._IDTAG['LOCATION'], data)
def tag_type(self, data):
"""
Make TAG for unit type
"""
return self._tag(self._IDTAG['TYPE'], data)
def tag_equip(self, data):
"""
Make TAG for equipment version
"""
return self._tag(self._IDTAG['EQUIPVERS'], data)
def tag_sw(self, data):
"""
Make TAG for software version
"""
return self._tag(self._IDTAG['SWVERSION'], data)
def tag_ip(self, data):
"""
Make TAG for IP address
"""
return self._tag(self._IDTAG['IPADDR'], data)
def tag_mac(self, data):
"""
Make TAG for MAC address
"""
return self._tag(self._IDTAG['MACADDR'], data)
def tag_unit(self, data):
"""
Make TAG for unit ID
"""
return self._tag(self._IDTAG['UNIT'], data)
def identity(self, unit=b'', mac=b'', location=b'', utype=b'', equip=b'', sw=b'', name=b'', serial=b''):
"""
Make IPA IDENTITY tag list, by default returns empty concatenated bytes of tag list
"""
return self.tag_unit(unit) + self.tag_mac(mac) + self.tag_loc(location) + self.tag_type(utype) + self.tag_equip(equip) + self.tag_sw(sw) + self.tag_name(name) + self.tag_serial(serial)
def ping(self):
"""
Make PING message
"""
return self.add_header(b'', self.PROTO['CCM'], self.MSGT['PING'])
def pong(self):
"""
Make PONG message
"""
return self.add_header(b'', self.PROTO['CCM'], self.MSGT['PONG'])
def id_ack(self):
"""
Make ID_ACK CCM message
"""
return self.add_header(b'', self.PROTO['CCM'], self.MSGT['ID_ACK'])
def id_get(self):
"""
Make ID_GET CCM message
"""
return self.add_header(self.identity(), self.PROTO['CCM'], self.MSGT['ID_GET'])
def id_resp(self, data):
"""
Make ID_RESP CCM message
"""
return self.add_header(data, self.PROTO['CCM'], self.MSGT['ID_RESP'])
class Ctrl(IPA):
"""
Osmocom CTRL protocol implemented on top of IPA multiplexer
"""
def __init__(self):
random.seed()
def add_header(self, data):
"""
Add CTRL header
"""
return super(Ctrl, self).add_header(data.encode('utf-8'), IPA.PROTO['OSMO'], IPA.EXT['CTRL'])
def rem_header(self, data):
"""
Remove CTRL header, check for appropriate protocol and extension
"""
(_, proto, ext, d) = super(Ctrl, self).del_header(data)
if self.PROTO['OSMO'] != proto or self.EXT['CTRL'] != ext:
return None
return d
def parse(self, data, op=None):
"""
Parse Ctrl string returning (var, value) pair
var could be None in case of ERROR message
value could be None in case of GET message
"""
(s, i, v) = data.split(' ', 2)
if s == self.CTRL_ERR:
return None, v
if s == self.CTRL_GET:
return v, None
(s, i, var, val) = data.split(' ', 3)
if s == self.CTRL_TRAP and i != '0':
return None, '%s with non-zero id %s' % (s, i)
if op is not None and i != op:
if s == self.CTRL_GET + '_' + self.CTRL_REP or s == self.CTRL_SET + '_' + self.CTRL_REP:
return None, '%s with unexpected id %s' % (s, i)
return var, val
def trap(self, var, val):
"""
Make TRAP message with given (vak, val) pair
"""
return self.add_header("%s 0 %s %s" % (self.CTRL_TRAP, var, val))
def cmd(self, var, val=None):
"""
Make SET/GET command message: returns (r, m) tuple where r is random operation id and m is assembled message
"""
r = random.randint(1, sys.maxsize)
if val is not None:
return r, self.add_header("%s %s %s %s" % (self.CTRL_SET, r, var, val))
return r, self.add_header("%s %s %s" % (self.CTRL_GET, r, var))
def verify(self, reply, r, var, val=None):
"""
Verify reply to SET/GET command: returns (b, v) tuple where v is True/False verification result and v is the variable value
"""
(k, v) = self.parse(reply)
if k != var or (val is not None and v != val):
return False, v
return True, v
if __name__ == '__main__':
print("IPA multiplexer v%s loaded." % IPA.version)

View File

@@ -14,8 +14,9 @@ deps="$base/deps"
inst="$deps/install"
export deps inst
osmo-clean-workspace.sh
mkdir "$deps" || true
rm -rf "$inst"
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
@@ -35,9 +36,9 @@ set -x
cd "$base"
autoreconf --install --force
./configure --enable-external-tests
./configure --enable-sanitize --enable-external-tests --enable-werror
$MAKE $PARALLEL_MAKE
if [ "x$label" != "xFreeBSD_amd64" ]; then
$MAKE check || cat-testlogs.sh
$MAKE distcheck || cat-testlogs.sh
fi
$MAKE check || cat-testlogs.sh
$MAKE distcheck || cat-testlogs.sh
osmo-clean-workspace.sh

202
debian/changelog vendored
View File

@@ -1,3 +1,205 @@
osmo-hlr (0.2.1) unstable; urgency=medium
[ Neels Hofmeyr ]
* fix luop crash: use buffer for APN that remains valid
* add gsup_test to catch OS#3231
* add error handling to osmo_gsup_configure_wildcard_apn()
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 04 May 2018 18:41:35 +0200
osmo-hlr (0.2.0) unstable; urgency=medium
[ Neels Hofmeyr ]
* vty: skip installing cmds now always installed by default
* hlr_db_tool: fix error log strerror invocation
* cosmetic: add comment on ignored return value
* db-tool: add command 'create'
* db-tool: cosmetic: tweak printf output
* db-tool: error-exit on too many arguments
* add --enable-sanitize config option
* db_test: don't verify SQLite issued error messages, they might change
* cosmetic: rx_send_auth_info(): decide error cause with switch()
* return GMM_CAUSE_IMSI_UNKNOWN if there is no auth data
* db_get_auth_data / db_get_auc: clarify return values
* osmo-hlr: log details for unknown IMSI / no auth data / db error
* db_test: also test db_get_auc() return values
* fix test_subscriber_errors.ctrl after libosmocore change
* fix debug log: put 'deriving 2G from 3G' in proper place
* ctrl test: fix: adjust expectations after stricter ctrl parsing
* fix build: db_test: missing LIBOSMOABIS_CFLAGS and _LIBS
* configure: add --enable-werror
* jenkins.sh: use --enable-werror configure flag, not CFLAGS
[ Harald Welte ]
* hlr.c: Avoid overflow of lu_operation.subscr.imsi
* Fix expected test output after new 'logging print file 1' vty command
* osmo-hlr: Add talloc context introspection via VTY
* vty: Don't print error if removing auth data while none present
* Fix responses to PURGE MS
[ Alexander Couzens ]
* debian: include systemd service osmo-hlr.service
* doc: install example .cfg files to $(docdir)/examples/
* debian: install osmo-hlr.cfg to /etc/osmocom
[ Max ]
* Remove unused check
* Remove unused ipa.py
* Enable sanitize for CI tests
[ Pau Espin Pedrol ]
* luop.c: Transform FIXME from warning to pragma message
* contrib:jenkins.sh: Enable Werror
* use osmo_init_logging2
* Remove unused src/db_test.c
[ Alexander Huemer ]
* Add missing build products in .gitignore
[ Stefan Sperling ]
* more robust usage of osmo_timer API for osmo-hlr luop timer
* notify GSUP clients when HLR subscriber information changes
* rewrite subscriber_update_notify() without calls into luop
* don't forget to mark luop as a packet switched connection
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 03 May 2018 16:27:13 +0200
osmo-hlr (0.1.0) unstable; urgency=medium
[ Neels Hofmeyr ]
* build with autoconf/automake, add jenkins.sh script
* fix build on FreeBSD: eliminate implicitly declared functions
* fix various compiler warnings
* fix DLGSUP logging cat after change in libosmocore
* build: recoin db_test as non-installable program
* build: actually make sqlite mandatory
* bump required libosmocore version to 0.9.5
* gsup: send subscriber MSISDN
* debug log: log computed vector kinds
* log: move a log from info to debug level
* hlr.sql: typo in comment
* auc.c: typo in comment
* main: add and use root talloc ctx
* main: add option parsing with db file and default options
* main: add VTY and '-c config-file' option
* sql: fix 3g_auc's column K data type
* cosmetic: sql: indicate VARCHAR size of key columns as 32
* sql: auc_3g: set sqn NOT NULL DEFAULT 0
* comment: sql: describe auc_2g and auc_3g columns
* Add test suite skeleton with empty test (auc_3g_test)
* tests: auc_3g_test: implement vector generation test
* auth: verify test sets from 3GPP TS 55.205
* sql: add unique constraints to IMSI and MSISDN
* UMTS AKA resync: fix argument ordering
* auc_3g_test: add AUTS resync test
* auc_gen_vectors(): ensure sane arguments, test
* auc_3g_test: allow to inc fake rand bytes upon rand request
* auc_3g_test: add AUTS test with N vectors, to show bug
* cosmetic: refactor auc_compute_vectors(), add debug log
* auc_compute_vectors(): fix AUTS resync for multiple vectors
* cosmetic: auc_3g_test: improve test debugging tools
* cosmetic: rename auc_3g_test.c to auc_test.c
* fix: properly handle error rc by osmo_gsup_conn_ccm_get()
* auc tests: adjust cosmetically to prepare for SQN changes
* auc tests: fix after SQN scheme changes from libosmocore
* fix debug log: adjust to new SQN increment scheme
* UMTS AKA: implement SQN increment according to SEQ and IND
* debug log: output ind slot, previous sqn, and sqn db update
* jenkins: add value_string termination check
* fix db_subscr_ps error handling
* add config example (mostly empty)
* install hlr.sql in prefix/doc/osmo-hlr/
* use OSMO_GSUP_PORT == 4222 instead of hardcoded 2222
* add basic CTRL interface tests
* add CTRL tests for enable-/disable-/status-ps
* cosmetic: prepend DB_STMT_ to enum stmt_idx entries
* cosmetic: rename db_subscr_get() to db_subscr_get_by_imsi()
* cosmetic: refactor db_bind_imsi() as db_bind_text()
* cosmetic: multi-line DB_STMT_AUC_BY_IMSI
* cosmetic: log IMSI='<imsi>', log "no such subscriber"
* cosmetic: log: "SQLite" with capital L
* cosmetic: db_hlr: SL3_TXT: clarify indenting
* ctrl_test_runner.py: use proper constant as test db path
* gitignore: tests/package.m4
* cosmetic: don't log about missing SQLite log cb
* add db_bind_int() and db_bind_int64()
* add db_subscr_create(), db_subscr_delete(), db_subscr_update_msisdn_by_imsi()
* add initial db_test: creating and deleting subscribers
* less noise: simplify db_remove_reset()
* db: use int64_t as subscriber id
* add db_subscr_get_by_msisdn() and db_subscr_get_by_id()
* refactor db_subscr_ps() to db_subscr_nam()
* refactor db_subscr_lu()
* refactor db_subscr_purge
* add db_subscr_update_aud_by_id(), complete db_subscr_delete_by_id()
* refactor db_get_auth_data return val
* code undup: use db_remove_reset() in db_auc.c
* fix db_update_sqn(): reset stmt in all error cases
* code undup: use db_bind_text() in db_get_auth_data()
* debian: 'make check' needs sqlite3, add to Build-Depends
* fix db_subscr_get_by_*(): clear output data; test in db_test.c
* implement subscriber vty interface, tests
* add test_nodes.vty
* replace ctrl_test_runner.py with transcript test_subscriber.ctrl
* add lu_op_free(), use in luop.c
* luop: fix mem leak upon error in lu_op_alloc_conn()
* fix mem leak in handle_cmd_ps(): free luop
* api doc: say that lu_op_tx_del_subscr_data() doesn't free
* add hlr_subsrc_nam to put GSUP client notification in proper API
* vty: fix output of empty IMSI
* db api: fix/add API docs
* cosmetic: tweak params of hlr_controlif_setup()
* ctrl: completely replace all CTRL commands
* test_subscriber.ctrl: test against octal/hex interpretation of id
* jenkins: use osmo-clean-workspace.sh before and after build
* tests/Makefile: use test db var instead of repeating the path
* db_test: fix *FLAGS
* automatically create db tables on osmo-hlr invocation
* cosmetic: sql/hlr.sql: move comments
* cosmetic: rename SL3_TXT macro, use osmo_strlcpy()
* fix default logging levels to NOTICE, not DEBUG
* add osmo-hlr-db-tool, program to migrate from osmo-nitb db
[ Max ]
* Add gerrit settings
* Add hardcoded APN
* Log error cause as a string
* Move GSUP msg init into separate function
* Use strings for GSUP message type
* Move lu_operation into separate file
* db: move duplicated code into helper functions
* Fix compiler's warning about printf security
* Add routines to update nam_ps
* Add global HLR struct
* Make subscr parameter to db_subscr_get() optional
* Add CTRL interface
* CTRL: add enable/disable packet service cmds
* Add .deb packaging
* deb: fix OBS build
* debian: remove obsolete dependency
* Attempt to fix .deb package
* deb: use python in shebang
* Another attempt at fixing .deb
* Use release helper from libosmocore
* Use value string check from osmo-ci
[ Daniel Willmann ]
* Add systemd service file
* hlr_data.sql: Insert ki and opc instead of op to example data
* tests/auc: Don't require python3
[ Pau Espin Pedrol ]
* VTY: Add hlr node and bind ip field
* debian: remove unneeded dependency libdbd-sqlite3
[ Harald Welte ]
* jenkins.sh: Proper error message if local environment isn't set up
[ Alexander Couzens ]
* debian/rules: show testsuite.log when tests are failing
-- Harald Welte <laforge@gnumonks.org> Sat, 28 Oct 2017 20:37:33 +0200
osmo-hlr (0.0.1) UNRELEASED; urgency=low
* Initial release (Closes: OS#1948)

View File

@@ -1,2 +1,6 @@
/usr/bin/osmo-hlr
/usr/share/doc/osmo-hlr/hlr.sql
/usr/bin/osmo-hlr-db-tool
/usr/share/doc/osmo-hlr/sql/hlr.sql
/usr/share/doc/osmo-hlr/sql/hlr_data.sql
/usr/share/doc/osmo-hlr/examples/osmo-hlr.cfg
/var/lib/osmocom

1
debian/osmo-hlr.service vendored Symbolic link
View File

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

22
doc/Makefile.am Normal file
View File

@@ -0,0 +1,22 @@
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

View File

@@ -17,3 +17,4 @@ ctrl
hlr
gsup
bind ip 127.0.0.1
ussd route prefix *#100# internal own-msisdn

2
include/Makefile.am Normal file
View File

@@ -0,0 +1,2 @@
nobase_include_HEADERS = osmocom/gsupclient/gsup_client.h

View File

@@ -0,0 +1,65 @@
/* GPRS Subscriber Update Protocol client */
/* (C) 2014 by Sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Jacob Erlbeck
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/timer.h>
#include <osmocom/gsm/oap_client.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
* re-established */
#define OSMO_GSUP_CLIENT_RECONNECT_INTERVAL 1
#define OSMO_GSUP_CLIENT_PING_INTERVAL 20
struct msgb;
struct ipa_client_conn;
struct osmo_gsup_client;
/* Expects message in msg->l2h */
typedef int (*osmo_gsup_client_read_cb_t)(struct osmo_gsup_client *gsupc, struct msgb *msg);
struct osmo_gsup_client {
const char *unit_name;
struct ipa_client_conn *link;
osmo_gsup_client_read_cb_t read_cb;
void *data;
struct osmo_oap_client_state oap_state;
struct osmo_timer_list ping_timer;
struct osmo_timer_list connect_timer;
int is_connected;
int got_ipa_pong;
};
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);
void osmo_gsup_client_destroy(struct osmo_gsup_client *gsupc);
int osmo_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg);
struct msgb *osmo_gsup_client_msgb_alloc(void);

11
libosmo-gsup-client.pc.in Normal file
View File

@@ -0,0 +1,11 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: Osmocom GSUP and OAP Client Library
Description: C Utility Library
Version: @VERSION@
Libs: -L${libdir} @TALLOC_LIBS@ -losmogsm -losmocore
Cflags: -I${includedir}/

View File

@@ -3,5 +3,12 @@ EXTRA_DIST = \
hlr.sql \
$(NULL)
docsdir = $(datadir)/doc/osmo-hlr
docs_DATA = $(srcdir)/hlr.sql
sqldir = $(docdir)/sql
sql_DATA = $(srcdir)/hlr.sql $(srcdir)/hlr_data.sql
install-data-local:
$(MKDIR_P) $(DESTDIR)$(localstatedir)/lib/osmocom
uninstall-hook:
rm -rf $(DESTDIR)$(localstatedir)/lib/osmocom

View File

@@ -1,6 +1,5 @@
--modelled roughly after TS 23.008 version 13.3.0
CREATE TABLE subscriber (
CREATE TABLE IF NOT EXISTS 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
imsi VARCHAR(15) UNIQUE NOT NULL,
@@ -40,24 +39,24 @@ CREATE TABLE subscriber (
ms_purged_ps BOOLEAN NOT NULL DEFAULT 0
);
CREATE TABLE subscriber_apn (
CREATE TABLE IF NOT EXISTS subscriber_apn (
subscriber_id INTEGER, -- subscriber.id
apn VARCHAR(256) NOT NULL
);
CREATE TABLE IF NOT EXISTS subscriber_multi_msisdn (
-- Chapter 2.1.3
CREATE TABLE subscriber_multi_msisdn (
subscriber_id INTEGER, -- subscriber.id
msisdn VARCHAR(15) NOT NULL
);
CREATE TABLE auc_2g (
CREATE TABLE IF NOT EXISTS 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 auc_3g (
CREATE TABLE IF NOT EXISTS 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)
@@ -68,4 +67,3 @@ CREATE TABLE auc_3g (
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_subscr_imsi ON subscriber (imsi);
-- SELECT algo_id_2g, ki, algo_id_3g, k, op, opc, sqn FROM subscriber LEFT JOIN auc_2g ON auc_2g.subscriber_id = subscriber.id LEFT JOIN auc_3g ON auc_3g.subscriber_id = subscriber.id WHERE imsi = ?

View File

@@ -1,3 +1,5 @@
SUBDIRS = gsupclient
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
@@ -10,8 +12,14 @@ AM_CFLAGS = \
EXTRA_DIST = \
populate_hlr_db.pl \
db_bootstrap.sed \
$(NULL)
BUILT_SOURCES = \
db_bootstrap.h \
$(NULL)
CLEANFILES = $(BUILT_SOURCES)
noinst_HEADERS = \
auc.h \
db.h \
@@ -24,14 +32,14 @@ noinst_HEADERS = \
ctrl.h \
hlr_vty.h \
hlr_vty_subscr.h \
hlr_ussd.h \
db_bootstrap.h \
$(NULL)
bin_PROGRAMS = \
osmo-hlr \
$(NULL)
noinst_PROGRAMS = \
db_test \
osmo-hlr-db-tool \
osmo-euse-demo \
$(NULL)
osmo_hlr_SOURCES = \
@@ -48,6 +56,8 @@ osmo_hlr_SOURCES = \
rand_urandom.c \
hlr_vty.c \
hlr_vty_subscr.c \
gsup_send.c \
hlr_ussd.c \
$(NULL)
osmo_hlr_LDADD = \
@@ -59,6 +69,21 @@ osmo_hlr_LDADD = \
$(SQLITE3_LIBS) \
$(NULL)
osmo_hlr_db_tool_SOURCES = \
hlr_db_tool.c \
db.c \
db_hlr.c \
logging.c \
rand_urandom.c \
dbd_decode_binary.c \
$(NULL)
osmo_hlr_db_tool_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(SQLITE3_LIBS) \
$(NULL)
db_test_SOURCES = \
auc.c \
db.c \
@@ -73,3 +98,24 @@ db_test_LDADD = \
$(LIBOSMOGSM_LIBS) \
$(SQLITE3_LIBS) \
$(NULL)
osmo_euse_demo_SOURCES = \
osmo-euse-demo.c \
$(NULL)
osmo_euse_demo_LDADD = \
$(top_builddir)/src/gsupclient/libosmo-gsup-client.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(NULL)
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 */" > "$@"
echo "#pragma once" >> "$@"
echo "static const char *stmt_bootstrap_sql[] = {" >> "$@"
cat "$(BOOTSTRAP_SQL)" \
| sed -f "$(srcdir)/db_bootstrap.sed" \
>> "$@"
echo "};" >> "$@"

View File

@@ -144,6 +144,7 @@ int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
if (!aud2g) {
/* use the 2G tokens from 3G keys */
DBGP("vector [%u]: deriving 2G from 3G\n", i);
DBGVB(kc);
DBGVB(sres);
DBGVV("0x%x", auth_types);
@@ -151,7 +152,7 @@ int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
}
/* calculate 2G separately */
DBGP("vector [%u]: deriving 2G from 3G\n", i);
DBGP("vector [%u]: calculating 2G separately\n", i);
rc = osmo_auth_gen_vec(&vtmp, aud2g, rand);
if (rc < 0) {

View File

@@ -21,87 +21,382 @@
*/
#include <stdbool.h>
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/ctrl/ports.h>
#include "gsup_server.h"
#include "logging.h"
#include "db.h"
#include "hlr.h"
#include "luop.h"
#include "ctrl.h"
#include "db.h"
static int handle_cmd_ps(struct hlr *ctx, struct ctrl_cmd *cmd, bool enable)
#define SEL_BY "by-"
#define SEL_BY_IMSI SEL_BY "imsi-"
#define SEL_BY_MSISDN SEL_BY "msisdn-"
#define SEL_BY_ID SEL_BY "id-"
#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
static bool startswith(const char *str, const char *start)
{
return strncmp(str, start, strlen(start)) == 0;
}
static int _get_subscriber(struct db_context *dbc,
const char *by_selector,
struct hlr_subscriber *subscr)
{
const char *val;
if (startswith(by_selector, SEL_BY_IMSI)) {
val = by_selector + strlen(SEL_BY_IMSI);
if (!osmo_imsi_str_valid(val))
return -EINVAL;
return db_subscr_get_by_imsi(dbc, val, subscr);
}
if (startswith(by_selector, SEL_BY_MSISDN)) {
val = by_selector + strlen(SEL_BY_MSISDN);
if (!osmo_msisdn_str_valid(val))
return -EINVAL;
return db_subscr_get_by_msisdn(dbc, val, subscr);
}
if (startswith(by_selector, SEL_BY_ID)) {
int64_t id;
char *endptr;
val = by_selector + strlen(SEL_BY_ID);
if (*val == '+')
return -EINVAL;
errno = 0;
id = strtoll(val, &endptr, 10);
if (errno || *endptr)
return -EINVAL;
return db_subscr_get_by_id(dbc, id, subscr);
}
return -ENOTSUP;
}
static bool get_subscriber(struct db_context *dbc,
const char *by_selector,
struct hlr_subscriber *subscr,
struct ctrl_cmd *cmd)
{
int rc = _get_subscriber(dbc, by_selector, subscr);
switch (rc) {
case 0:
return true;
case -ENOTSUP:
cmd->reply = "Not a known subscriber 'by-xxx-' selector.";
return false;
case -EINVAL:
cmd->reply = "Invalid value part of 'by-xxx-value' selector.";
return false;
case -ENOENT:
cmd->reply = "No such subscriber.";
return false;
default:
cmd->reply = "An unknown error has occured during get_subscriber().";
return false;
}
}
/* Optimization: if a subscriber operation is requested by-imsi, just return
* the IMSI right back. */
static const char *get_subscriber_imsi(struct db_context *dbc,
const char *by_selector,
struct ctrl_cmd *cmd)
{
static struct hlr_subscriber subscr;
if (startswith(by_selector, SEL_BY_IMSI))
return by_selector + strlen(SEL_BY_IMSI);
if (!get_subscriber(dbc, by_selector, &subscr, cmd))
return NULL;
return subscr.imsi;
}
/* printf fmt and arg to completely omit a string if it is empty. */
#define FMT_S "%s%s%s%s"
#define ARG_S(name, val) \
(val) && *(val) ? "\n" : "", \
(val) && *(val) ? name : "", \
(val) && *(val) ? "\t" : "", \
(val) && *(val) ? (val) : "" \
/* printf fmt and arg to completely omit bool of given value. */
#define FMT_BOOL "%s"
#define ARG_BOOL(name, val) \
val ? "\n" name "\t1" : "\n" name "\t0"
static void print_subscr_info(struct ctrl_cmd *cmd,
struct hlr_subscriber *subscr)
{
ctrl_cmd_reply_printf(cmd,
"\nid\t%"PRIu64
FMT_S
FMT_S
FMT_BOOL
FMT_BOOL
FMT_S
FMT_S
FMT_S
FMT_BOOL
FMT_BOOL
"\nperiodic_lu_timer\t%u"
"\nperiodic_rau_tau_timer\t%u"
"\nlmsi\t%08x"
,
subscr->id,
ARG_S("imsi", subscr->imsi),
ARG_S("msisdn", subscr->msisdn),
ARG_BOOL("nam_cs", subscr->nam_cs),
ARG_BOOL("nam_ps", subscr->nam_ps),
ARG_S("vlr_number", subscr->vlr_number),
ARG_S("sgsn_number", subscr->sgsn_number),
ARG_S("sgsn_address", subscr->sgsn_address),
ARG_BOOL("ms_purged_cs", subscr->ms_purged_cs),
ARG_BOOL("ms_purged_ps", subscr->ms_purged_ps),
subscr->periodic_lu_timer,
subscr->periodic_rau_tau_timer,
subscr->lmsi
);
}
static void print_subscr_info_aud2g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data *aud)
{
if (aud->algo == OSMO_AUTH_ALG_NONE)
return;
ctrl_cmd_reply_printf(cmd,
"\naud2g.algo\t%s"
"\naud2g.ki\t%s"
,
osmo_auth_alg_name(aud->algo),
hexdump_buf(aud->u.gsm.ki));
}
static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data *aud)
{
if (aud->algo == OSMO_AUTH_ALG_NONE)
return;
ctrl_cmd_reply_printf(cmd,
"\naud3g.algo\t%s"
"\naud3g.k\t%s"
,
osmo_auth_alg_name(aud->algo),
hexdump_buf(aud->u.umts.k));
/* hexdump uses a static string buffer, hence only one hexdump per
* printf(). */
ctrl_cmd_reply_printf(cmd,
"\naud3g.%s\t%s"
"\naud3g.ind_bitlen\t%u"
"\naud3g.sqn\t%"PRIu64
,
aud->u.umts.opc_is_op? "op" : "opc",
hexdump_buf(aud->u.umts.opc),
aud->u.umts.ind_bitlen,
aud->u.umts.sqn);
}
CTRL_CMD_DEFINE_RO(subscr_info, "info");
static int get_subscr_info(struct ctrl_cmd *cmd, void *data)
{
struct hlr_subscriber subscr;
struct hlr *hlr = data;
const char *by_selector = cmd->node;
if (db_subscr_get_by_imsi(ctx->dbc, cmd->value, &subscr) < 0) {
cmd->reply = "Subscriber Unknown in HLR";
if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
return CTRL_CMD_ERROR;
print_subscr_info(cmd, &subscr);
return CTRL_CMD_REPLY;
}
CTRL_CMD_DEFINE_RO(subscr_info_aud, "info-aud");
static int get_subscr_info_aud(struct ctrl_cmd *cmd, void *data)
{
const char *imsi;
struct osmo_sub_auth_data aud2g;
struct osmo_sub_auth_data aud3g;
struct hlr *hlr = data;
const char *by_selector = cmd->node;
int rc;
imsi = get_subscriber_imsi(hlr->dbc, by_selector, cmd);
if (!imsi)
return CTRL_CMD_ERROR;
rc = db_get_auth_data(hlr->dbc, imsi, &aud2g, &aud3g, NULL);
switch (rc) {
case 0:
break;
case -ENOENT:
case -ENOKEY:
/* No auth data found, tell the print*() functions about it. */
aud2g.algo = OSMO_AUTH_ALG_NONE;
aud3g.algo = OSMO_AUTH_ALG_NONE;
break;
default:
cmd->reply = "Error retrieving authentication data.";
return CTRL_CMD_ERROR;
}
if (hlr_subscr_nam(ctx, &subscr, enable, true) < 0) {
cmd->reply = "Error updating DB";
print_subscr_info_aud2g(cmd, &aud2g);
print_subscr_info_aud3g(cmd, &aud3g);
return CTRL_CMD_REPLY;
}
CTRL_CMD_DEFINE_RO(subscr_info_all, "info-all");
static int get_subscr_info_all(struct ctrl_cmd *cmd, void *data)
{
struct hlr_subscriber subscr;
struct osmo_sub_auth_data aud2g;
struct osmo_sub_auth_data aud3g;
struct hlr *hlr = data;
const char *by_selector = cmd->node;
int rc;
if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
return CTRL_CMD_ERROR;
rc = db_get_auth_data(hlr->dbc, subscr.imsi, &aud2g, &aud3g, NULL);
switch (rc) {
case 0:
break;
case -ENOENT:
case -ENOKEY:
/* No auth data found, tell the print*() functions about it. */
aud2g.algo = OSMO_AUTH_ALG_NONE;
aud3g.algo = OSMO_AUTH_ALG_NONE;
break;
default:
cmd->reply = "Error retrieving authentication data.";
return CTRL_CMD_ERROR;
}
print_subscr_info(cmd, &subscr);
print_subscr_info_aud2g(cmd, &aud2g);
print_subscr_info_aud3g(cmd, &aud3g);
return CTRL_CMD_REPLY;
}
static int verify_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, const char *value, void *data)
{
if (!value || !*value
|| (strcmp(value, "0") && strcmp(value, "1")))
return 1;
return 0;
}
static int get_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, void *data,
bool is_ps)
{
struct hlr_subscriber subscr;
struct hlr *hlr = data;
const char *by_selector = cmd->node;
if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
return CTRL_CMD_ERROR;
cmd->reply = (is_ps ? subscr.nam_ps : subscr.nam_cs)
? "1" : "0";
return CTRL_CMD_REPLY;
}
static int set_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, void *data,
bool is_ps)
{
const char *imsi;
struct hlr *hlr = data;
const char *by_selector = cmd->node;
imsi = get_subscriber_imsi(hlr->dbc, by_selector, cmd);
if (!imsi)
return CTRL_CMD_ERROR;
if (db_subscr_nam(hlr->dbc, imsi, strcmp(cmd->value, "1") == 0, is_ps))
return CTRL_CMD_ERROR;
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
CTRL_CMD_DEFINE_WO_NOVRF(enable_ps, "enable-ps");
static int set_enable_ps(struct ctrl_cmd *cmd, void *data)
CTRL_CMD_DEFINE(subscr_ps_enabled, "ps-enabled");
static int verify_subscr_ps_enabled(struct ctrl_cmd *cmd, const char *value, void *data)
{
return handle_cmd_ps(data, cmd, true);
return verify_subscr_cs_ps_enabled(cmd, value, data);
}
static int get_subscr_ps_enabled(struct ctrl_cmd *cmd, void *data)
{
return get_subscr_cs_ps_enabled(cmd, data, true);
}
static int set_subscr_ps_enabled(struct ctrl_cmd *cmd, void *data)
{
return set_subscr_cs_ps_enabled(cmd, data, true);
}
CTRL_CMD_DEFINE_WO_NOVRF(disable_ps, "disable-ps");
static int set_disable_ps(struct ctrl_cmd *cmd, void *data)
CTRL_CMD_DEFINE(subscr_cs_enabled, "cs-enabled");
static int verify_subscr_cs_enabled(struct ctrl_cmd *cmd, const char *value, void *data)
{
return handle_cmd_ps(data, cmd, false);
return verify_subscr_cs_ps_enabled(cmd, value, data);
}
CTRL_CMD_DEFINE_WO_NOVRF(status_ps, "status-ps");
static int set_status_ps(struct ctrl_cmd *cmd, void *data)
static int get_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data)
{
struct hlr *ctx = data;
struct lu_operation *luop = lu_op_alloc(ctx->gs);
if (!luop) {
cmd->reply = "Internal HLR error";
return CTRL_CMD_ERROR;
}
if (!lu_op_fill_subscr(luop, ctx->dbc, cmd->value)) {
cmd->reply = "Subscriber Unknown in HLR";
return CTRL_CMD_ERROR;
}
cmd->reply = luop->subscr.nam_ps ? "1" : "0";
return CTRL_CMD_REPLY;
return get_subscr_cs_ps_enabled(cmd, data, false);
}
static int set_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data)
{
return set_subscr_cs_ps_enabled(cmd, data, false);
}
int hlr_ctrl_cmds_install()
{
int rc = 0;
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_enable_ps);
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_disable_ps);
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_status_ps);
rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info);
rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_aud);
rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_all);
rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_ps_enabled);
rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_cs_enabled);
return rc;
}
struct ctrl_handle *hlr_controlif_setup(struct hlr *ctx,
struct osmo_gsup_server *gs)
static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type,
void **node_data, int *i)
{
const char *token = vector_slot(vline, *i);
switch (*node_type) {
case CTRL_NODE_ROOT:
if (strcmp(token, "subscriber") != 0)
return 0;
*node_data = NULL;
*node_type = CTRL_NODE_SUBSCR;
break;
case CTRL_NODE_SUBSCR:
if (!startswith(token, "by-"))
return 0;
*node_data = (void*)token;
*node_type = CTRL_NODE_SUBSCR_BY;
break;
default:
return 0;
}
return 1;
}
struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr)
{
int rc;
struct ctrl_handle *hdl = ctrl_interface_setup_dynip(ctx,
ctx->ctrl_bind_addr,
OSMO_CTRL_PORT_HLR,
NULL);
struct ctrl_handle *hdl = ctrl_interface_setup_dynip2(hlr,
hlr->ctrl_bind_addr,
OSMO_CTRL_PORT_HLR,
hlr_ctrl_node_lookup,
_LAST_CTRL_NODE_HLR);
if (!hdl)
return NULL;

View File

@@ -24,8 +24,11 @@
#include <osmocom/ctrl/control_if.h>
#include "gsup_server.h"
enum hlr_ctrl_node {
CTRL_NODE_SUBSCR = _LAST_CTRL_NODE,
CTRL_NODE_SUBSCR_BY,
_LAST_CTRL_NODE_HLR
};
int hlr_ctrl_cmds_install();
struct ctrl_handle *hlr_controlif_setup(struct hlr *ctx,
struct osmo_gsup_server *gs);
struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr);

View File

@@ -25,6 +25,7 @@
#include "logging.h"
#include "db.h"
#include "db_bootstrap.h"
#define SEL_COLUMNS \
"id," \
@@ -98,6 +99,8 @@ static void sql3_sql_log_cb(void *arg, sqlite3 *s3, const char *stmt, int type)
void db_remove_reset(sqlite3_stmt *stmt)
{
sqlite3_clear_bindings(stmt);
/* sqlite3_reset() just repeats an error code already evaluated during sqlite3_step(). */
/* coverity[CHECKED_RETURN] */
sqlite3_reset(stmt);
}
@@ -179,7 +182,36 @@ void db_close(struct db_context *dbc)
talloc_free(dbc);
}
struct db_context *db_open(void *ctx, const char *fname)
static int db_bootstrap(struct db_context *dbc)
{
int i;
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);
if (rc != SQLITE_OK) {
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n",
stmt_bootstrap_sql[i]);
return -1;
}
/* execute the statement */
rc = sqlite3_step(stmt);
db_remove_reset(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]);
return -1;
}
}
return 0;
}
struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logging)
{
struct db_context *dbc = talloc_zero(ctx, struct db_context);
unsigned int i;
@@ -201,9 +233,11 @@ struct db_context *db_open(void *ctx, const char *fname)
has_sqlite_config_sqllog = true;
}
rc = sqlite3_config(SQLITE_CONFIG_LOG, sql3_error_log_cb, NULL);
if (rc != SQLITE_OK)
LOGP(DDB, LOGL_NOTICE, "Unable to set SQLite3 error log callback\n");
if (enable_sqlite_logging) {
rc = sqlite3_config(SQLITE_CONFIG_LOG, sql3_error_log_cb, NULL);
if (rc != SQLITE_OK)
LOGP(DDB, LOGL_NOTICE, "Unable to set SQLite3 error log callback\n");
}
if (has_sqlite_config_sqllog) {
rc = sqlite3_config(SQLITE_CONFIG_SQLLOG, sql3_sql_log_cb, NULL);
@@ -231,6 +265,8 @@ struct db_context *db_open(void *ctx, const char *fname)
LOGP(DDB, LOGL_ERROR, "Unable to set Write-Ahead Logging: %s\n",
err_msg);
db_bootstrap(dbc);
/* prepare all SQL statements */
for (i = 0; i < ARRAY_SIZE(dbc->stmt); i++) {
rc = sqlite3_prepare_v2(dbc->db, stmt_sql[i], -1,

View File

@@ -38,7 +38,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);
struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite3_logging);
#include <osmocom/crypt/auth.h>
@@ -129,3 +129,14 @@ int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
bool purge_val, bool is_ps);
int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
/*! Call sqlite3_column_text() and copy result to a char[].
* \param[out] buf A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
* \param[in] stmt An sqlite3_stmt*.
* \param[in] idx Index in stmt's returned columns.
*/
#define copy_sqlite3_text_to_buf(buf, stmt, idx) \
do { \
const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \
osmo_strlcpy(buf, _txt, sizeof(buf)); \
} while (0)

View File

@@ -74,7 +74,9 @@ out:
}
/* obtain the authentication data for a given imsi
* returns -1 in case of error, 0 for unknown IMSI, 1 for success */
* returns 0 for success, negative value on error:
* -ENOENT if the IMSI is not known, -ENOKEY if the IMSI is known but has no auth data,
* -EIO on db failure */
int db_get_auth_data(struct db_context *dbc, const char *imsi,
struct osmo_sub_auth_data *aud2g,
struct osmo_sub_auth_data *aud3g,
@@ -163,15 +165,16 @@ int db_get_auth_data(struct db_context *dbc, const char *imsi,
LOGAUC(imsi, LOGL_DEBUG, "No 3G Auth Data\n");
if (aud2g->type == 0 && aud3g->type == 0)
ret = -ENOENT;
ret = -ENOKEY;
out:
db_remove_reset(stmt);
return ret;
}
/* return -1 in case of error, 0 for unknown imsi, positive for number
* of vectors generated */
/* return number of vectors generated, negative value on error:
* -ENOENT if the IMSI is not known, -ENOKEY if the IMSI is known but has no auth data,
* -EIO on db failure */
int db_get_auc(struct db_context *dbc, const char *imsi,
unsigned int auc_3g_ind, struct osmo_auth_vector *vec,
unsigned int num_vec, const uint8_t *rand_auts,

25
src/db_bootstrap.sed Normal file
View File

@@ -0,0 +1,25 @@
# Input to this is sql/hlr.sql.
#
# We want each SQL statement line wrapped in "...\n", and each end (";") to
# become a comma:
#
# SOME SQL COMMAND (
# that may span )
# MULTIPLE LINES;
# MORE;
#
# -->
#
# "SOME SQL COMMAND (\n"
# " that may span )\n"
# "MULTIPLE LINES\n", <--note the comma here
# "MORE\n",
#
# just replacing ';' with '\n,' won't work, since sed is bad in printing
# multiple lines. Also, how to input newlines to sed is not portable across
# platforms.
# Match excluding a trailing ';' as \1, keep any trailing ';' in \2
s/^\(.*[^;]\)\(;\|\)$/"\1\\n"\2/
# Replace trailing ';' as ','
s/;$/,/

View File

@@ -35,14 +35,11 @@
#define LOGHLR(imsi, level, fmt, args ...) LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)
#define SL3_TXT(x, stmt, idx) \
do { \
const char *_txt = (const char *) sqlite3_column_text(stmt, idx);\
if (_txt) \
strncpy(x, _txt, sizeof(x)); \
x[sizeof(x)-1] = '\0'; \
} while (0)
/*! Add new subscriber record to the HLR database.
* \param[in,out] dbc database context.
* \param[in] imsi ASCII string of IMSI digits, is validated.
* \returns 0 on success, -EINVAL on invalid IMSI, -EIO on database error.
*/
int db_subscr_create(struct db_context *dbc, const char *imsi)
{
sqlite3_stmt *stmt;
@@ -71,6 +68,15 @@ int db_subscr_create(struct db_context *dbc, const char *imsi)
return 0;
}
/*! Completely delete a subscriber record from the HLR database.
* Also remove authentication data.
* Future todo: also drop from all other database tables, which aren't used yet
* at the time of writing this.
* \param[in,out] dbc database context.
* \param[in] subscr_id ID of the subscriber in the HLR db.
* \returns if the subscriber was found and removed, -EIO on database error,
* -ENOENT if no such subscriber data exists.
*/
int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
{
int rc;
@@ -127,6 +133,13 @@ int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
return ret;
}
/*! 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.
* \returns 0 on success, -EINVAL in case of invalid MSISDN string, -EIO on
* database failure, -ENOENT if no such subscriber exists.
*/
int db_subscr_update_msisdn_by_imsi(struct db_context *dbc, const char *imsi,
const char *msisdn)
{
@@ -175,14 +188,17 @@ out:
}
/* Insert or update 2G or 3G authentication tokens in the database.
/*! Insert or update 2G or 3G authentication tokens in the database.
* If aud->type is OSMO_AUTH_TYPE_GSM, the auc_2g table entry for the
* subscriber will be added or modified; if aud->algo is OSMO_AUTH_ALG_NONE,
* however, the auc_2g entry for the subscriber is deleted. If aud->type is
* OSMO_AUTH_TYPE_UMTS, the auc_3g table is updated; again, if aud->algo is
* OSMO_AUTH_ALG_NONE, the auc_3g entry is deleted.
* Returns 0 if successful, -EINVAL for unknown aud->type, -ENOENT for unknown
* subscr_id, -EIO for SQL errors.
* \param[in,out] dbc database context.
* \param[in] subscr_id DB ID of the subscriber.
* \param[in] aud Pointer to new auth data (in ASCII string form).
* \returns 0 on success, -EINVAL for invalid aud, -ENOENT for unknown
* subscr_id, -EIO for database errors.
*/
int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
const struct sub_auth_data_str *aud)
@@ -386,12 +402,12 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
/* obtain the various columns */
subscr->id = sqlite3_column_int64(stmt, 0);
SL3_TXT(subscr->imsi, stmt, 1);
SL3_TXT(subscr->msisdn, stmt, 2);
copy_sqlite3_text_to_buf(subscr->imsi, stmt, 1);
copy_sqlite3_text_to_buf(subscr->msisdn, stmt, 2);
/* FIXME: These should all be BLOBs as they might contain NUL */
SL3_TXT(subscr->vlr_number, stmt, 3);
SL3_TXT(subscr->sgsn_number, stmt, 4);
SL3_TXT(subscr->sgsn_address, stmt, 5);
copy_sqlite3_text_to_buf(subscr->vlr_number, stmt, 3);
copy_sqlite3_text_to_buf(subscr->sgsn_number, stmt, 4);
copy_sqlite3_text_to_buf(subscr->sgsn_address, stmt, 5);
subscr->periodic_lu_timer = sqlite3_column_int(stmt, 6);
subscr->periodic_rau_tau_timer = sqlite3_column_int(stmt, 7);
subscr->nam_cs = sqlite3_column_int(stmt, 8);
@@ -417,6 +433,13 @@ out:
return ret;
}
/*! Retrieve subscriber data from the HLR database.
* \param[in,out] dbc database context.
* \param[in] imsi ASCII string of IMSI digits.
* \param[out] subscr place retrieved data in this struct.
* \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
* database error.
*/
int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
struct hlr_subscriber *subscr)
{
@@ -434,6 +457,13 @@ int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
return rc;
}
/*! Retrieve subscriber data from the HLR database.
* \param[in,out] dbc database context.
* \param[in] msisdn ASCII string of MSISDN digits.
* \param[out] subscr place retrieved data in this struct.
* \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
* database error.
*/
int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
struct hlr_subscriber *subscr)
{
@@ -451,6 +481,13 @@ int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
return rc;
}
/*! Retrieve subscriber data from the HLR database.
* \param[in,out] dbc database context.
* \param[in] id ID of the subscriber in the HLR db.
* \param[out] subscr place retrieved data in this struct.
* \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
* database error.
*/
int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
struct hlr_subscriber *subscr)
{
@@ -468,12 +505,14 @@ int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
return rc;
}
/* Enable or disable PS or CS for a subscriber.
* For the subscriber with the given imsi, set nam_ps (when is_ps == true) or
* nam_cs (when is_ps == false) to nam_val in the database.
* Returns 0 on success, -ENOENT when the given IMSI does not exist, -EINVAL if
* the SQL statement could not be composed, -ENOEXEC if running the SQL
* statement failed, -EIO if the amount of rows modified is unexpected.
/*! You should use hlr_subscr_nam() instead; enable or disable PS or CS for a
* subscriber without notifying GSUP clients.
* \param[in,out] dbc database context.
* \param[in] imsi ASCII string of IMSI digits.
* \param[in] nam_val True to enable CS/PS, false to disable.
* \param[in] is_ps when true, set nam_ps, else set nam_cs.
* \returns 0 on success, -ENOENT when the given IMSI does not exist, -EIO on
* database errors.
*/
int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps)
{
@@ -522,6 +561,14 @@ out:
return ret;
}
/*! Record a Location Updating in the database.
* \param[in,out] dbc database context.
* \param[in] subscr_id ID of the subscriber in the HLR db.
* \param[in] vlr_or_sgsn_number ASCII string of identifier digits.
* \param[in] is_ps when true, set sgsn_number, else set vlr_number.
* \returns 0 on success, -ENOENT when the given subscriber does not exist,
* -EIO on database errors.
*/
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
const char *vlr_or_sgsn_number, bool is_ps)
{
@@ -565,6 +612,14 @@ out:
return ret;
}
/*! Set the ms_purged_cs or ms_purged_ps values in the database.
* \param[in,out] dbc database context.
* \param[in] by_imsi ASCII string of IMSI digits.
* \param[in] purge_val true to purge, false to un-purge.
* \param[in] is_ps when true, set ms_purged_ps, else set ms_purged_cs.
* \returns 0 on success, -ENOENT when the given IMSI does not exist, -EIO on
* database errors.
*/
int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
bool purge_val, bool is_ps)
{
@@ -614,10 +669,10 @@ out:
}
/*! Update nam_cs/nam_ps in the db and trigger notifications to GSUP clients.
* \param hlr Global hlr context.
* \param subscr Subscriber from a fresh db_subscr_get_by_*() call.
* \param nam_val True to enable CS/PS, false to disable.
* \param is_ps True to enable/disable PS, false for CS.
* \param[in,out] hlr Global hlr context.
* \param[in] subscr Subscriber from a fresh db_subscr_get_by_*() call.
* \param[in] nam_val True to enable CS/PS, false to disable.
* \param[in] is_ps True to enable/disable PS, false for CS.
* \returns 0 on success, ENOEXEC if there is no need to change, a negative
* value on error.
*/

View File

@@ -1,87 +0,0 @@
#include <string.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include "db.h"
#include "hlr.h"
#include "rand.h"
#include "logging.h"
static struct hlr *g_hlr;
static int test(const char *imsi, struct db_context *dbc)
{
struct osmo_auth_vector vec[3];
int rc, i;
/* initialize all vectors with a known token pattern */
memset(vec, 0x55, sizeof(vec));
for (i = 0; i < ARRAY_SIZE(vec); i++)
vec[i].res_len = 0;
rc = db_get_auc(dbc, imsi, 0, vec, ARRAY_SIZE(vec), NULL, NULL);
if (rc <= 0) {
LOGP(DMAIN, LOGL_ERROR, "Cannot obtain auth tuples for '%s'\n", imsi);
return rc;
}
LOGP(DMAIN, LOGL_INFO, "Obtained %u tuples for subscriber IMSI %s\n",
rc, imsi);
for (i = 0; i < rc; i++) {
struct osmo_auth_vector *v = vec + i;
LOGP(DMAIN, LOGL_DEBUG, "Tuple %u, auth_types=0x%x\n", i, v->auth_types);
LOGP(DMAIN, LOGL_DEBUG, "RAND=%s\n", osmo_hexdump_nospc(v->rand, sizeof(v->rand)));
LOGP(DMAIN, LOGL_DEBUG, "AUTN=%s\n", osmo_hexdump_nospc(v->autn, sizeof(v->autn)));
LOGP(DMAIN, LOGL_DEBUG, "CK=%s\n", osmo_hexdump_nospc(v->ck, sizeof(v->ck)));
LOGP(DMAIN, LOGL_DEBUG, "IK=%s\n", osmo_hexdump_nospc(v->ik, sizeof(v->ik)));
LOGP(DMAIN, LOGL_DEBUG, "RES=%s\n", osmo_hexdump_nospc(v->res, v->res_len));
LOGP(DMAIN, LOGL_DEBUG, "Kc=%s\n", osmo_hexdump_nospc(v->kc, sizeof(v->kc)));
LOGP(DMAIN, LOGL_DEBUG, "SRES=%s\n", osmo_hexdump_nospc(v->sres, sizeof(v->sres)));
}
return rc;
}
int main(int argc, char **argv)
{
int rc;
g_hlr = talloc_zero(NULL, struct hlr);
rc = osmo_init_logging(&hlr_log_info);
if (rc < 0) {
fprintf(stderr, "Error initializing logging\n");
exit(1);
}
LOGP(DMAIN, LOGL_NOTICE, "hlr starting\n");
rc = rand_init();
if (rc < 0) {
LOGP(DMAIN, LOGL_ERROR, "Error initializing random source\n");
exit(1);
}
g_hlr->dbc = db_open(NULL, "hlr.db");
if (!g_hlr->dbc) {
LOGP(DMAIN, LOGL_ERROR, "Error opening database\n");
exit(1);
}
/* non-existing subscriber */
rc = test("901990123456789", g_hlr->dbc);
/* 2G only AUC data (COMP128v1 / MILENAGE) */
rc = test("901990000000001", g_hlr->dbc);
/* 2G + 3G AUC data (COMP128v1 / MILENAGE) */
rc = test("901990000000002", g_hlr->dbc);
/* 3G AUC data (MILENAGE) */
rc = test("901990000000003", g_hlr->dbc);
LOGP(DMAIN, LOGL_NOTICE, "Exiting\n");
db_close(g_hlr->dbc);
log_fini();
exit(0);
}

42
src/dbd_decode_binary.c Normal file
View File

@@ -0,0 +1,42 @@
/* This function is blatantly copied from libdbi, from
* https://sourceforge.net/p/libdbi/libdbi/ci/master/tree/src/dbd_helper.c
* to save having to depend on the entire libdbi just for KI BLOB decoding.
*/
/*
* libdbi - database independent abstraction layer for C.
* Copyright (C) 2001-2003, David Parker and Mark Tobenkin.
* http://libdbi.sourceforge.net
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: dbd_helper.c,v 1.44 2011/08/09 11:14:14 mhoenicka Exp $
*/
#include <sys/types.h>
size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out){
int i, e;
unsigned char c;
e = *(in++);
i = 0;
while( (c = *(in++))!=0 ){
if( c==1 ){
c = *(in++) - 1;
}
out[i++] = c + e;
}
return (size_t)i;
}

View File

@@ -23,6 +23,7 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include "logging.h"
#include "gsup_server.h"
struct gsup_route {
@@ -60,6 +61,8 @@ int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addr
if (!gr)
return -ENOMEM;
LOGP(DMAIN, LOGL_INFO, "Adding GSUP route for %s\n", addr);
gr->addr = talloc_memdup(gr, addr, addrlen);
gr->conn = conn;
llist_add_tail(&gr->list, &conn->server->routes);
@@ -75,6 +78,8 @@ int gsup_route_del_conn(struct osmo_gsup_conn *conn)
llist_for_each_entry_safe(gr, gr2, &conn->server->routes, list) {
if (gr->conn == conn) {
LOGP(DMAIN, LOGL_INFO, "Removing GSUP route for %s (GSUP disconnect)\n",
gr->addr);
llist_del(&gr->list);
talloc_free(gr);
num_deleted++;

View File

@@ -1,3 +1,8 @@
#pragma once
#include <stdint.h>
#include "gsup_server.h"
struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
const uint8_t *addr, size_t addrlen);
@@ -6,3 +11,7 @@ int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addr
/* delete all routes for the given connection */
int gsup_route_del_conn(struct osmo_gsup_conn *conn);
int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
const uint8_t *addr, size_t addrlen,
struct msgb *msg);

45
src/gsup_send.c Normal file
View File

@@ -0,0 +1,45 @@
/* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* This is kept separate to be able to override the actual sending functions from unit tests. */
#include <errno.h>
#include "gsup_server.h"
#include "gsup_router.h"
#include <osmocom/core/logging.h>
/* Send a msgb to a given address using routing */
int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
const uint8_t *addr, size_t addrlen,
struct msgb *msg)
{
struct osmo_gsup_conn *conn;
conn = gsup_route_find(gs, addr, addrlen);
if (!conn) {
DEBUGP(DLGSUP, "Cannot find route for addr %s\n", addr);
msgb_free(msg);
return -ENODEV;
}
return osmo_gsup_conn_send(conn, msg);
}

View File

@@ -24,6 +24,8 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/abis/ipa.h>
#include <osmocom/abis/ipaccess.h>
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/gsm/apn.h>
#include "gsup_server.h"
#include "gsup_router.h"
@@ -290,7 +292,7 @@ failed:
struct osmo_gsup_server *
osmo_gsup_server_create(void *ctx, const char *ip_addr, uint16_t tcp_port,
osmo_gsup_read_cb_t read_cb,
struct llist_head *lu_op_lst)
struct llist_head *lu_op_lst, void *priv)
{
struct osmo_gsup_server *gsups;
int rc;
@@ -310,6 +312,7 @@ osmo_gsup_server_create(void *ctx, const char *ip_addr, uint16_t tcp_port,
goto failed;
gsups->read_cb = read_cb;
gsups->priv = priv;
rc = ipa_server_link_open(gsups->link);
if (rc < 0)
@@ -333,3 +336,79 @@ void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups)
}
talloc_free(gsups);
}
/* Set GSUP message's pdp_infos[0] to a wildcard APN.
* Use the provided apn_buf to store the produced APN data. This must remain valid until
* osmo_gsup_encode() is done. Return 0 if an entry was added, -ENOMEM if the provided buffer is too
* small. */
int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
uint8_t *apn_buf, size_t apn_buf_size)
{
int l;
l = osmo_apn_from_str(apn_buf, apn_buf_size, "*");
if (l <= 0)
return -ENOMEM;
gsup->pdp_infos[0].apn_enc = apn_buf;
gsup->pdp_infos[0].apn_enc_len = l;
gsup->pdp_infos[0].have_info = 1;
gsup->num_pdp_infos = 1;
/* FIXME: use real value: */
gsup->pdp_infos[0].context_id = 1;
return 0;
}
/**
* Populate a gsup message structure with an Insert Subscriber Data Message.
* All required memory buffers for data pointed to by pointers in struct omso_gsup_message
* must be allocated by the caller and should have the same lifetime as the gsup parameter.
*
* \param[out] gsup The gsup message to populate.
* \param[in] imsi The subscriber's IMSI.
* \param[in] msisdn The subscriber's MSISDN.
* \param[out] msisdn_enc A buffer large enough to store the MSISDN in encoded form.
* \param[in] msisdn_enc_size Size of the buffer (must be >= OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN).
* \param[out] apn_buf A buffer large enough to store an APN (required if cn_domain is OSMO_GSUP_CN_DOMAIN_PS).
* \param[in] apn_buf_size Size of APN buffer (must be >= APN_MAXLEN).
* \param[in] cn_domain The CN Domain of the subscriber connection.
* \returns 0 on success, and negative on error.
*/
int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, const char *imsi, const char *msisdn,
uint8_t *msisdn_enc, size_t msisdn_enc_size,
uint8_t *apn_buf, size_t apn_buf_size,
enum osmo_gsup_cn_domain cn_domain)
{
int len;
OSMO_ASSERT(gsup);
gsup->message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST;
osmo_strlcpy(gsup->imsi, imsi, sizeof(gsup->imsi));
if (msisdn_enc_size < OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN)
return -ENOSPC;
OSMO_ASSERT(msisdn_enc);
len = gsm48_encode_bcd_number(msisdn_enc, msisdn_enc_size, 0, msisdn);
if (len < 1) {
LOGP(DLGSUP, LOGL_ERROR, "%s: Error: cannot encode MSISDN '%s'\n", imsi, msisdn);
return -ENOSPC;
}
gsup->msisdn_enc = msisdn_enc;
gsup->msisdn_enc_len = len;
#pragma message "FIXME: deal with encoding the following data: gsup.hlr_enc"
gsup->cn_domain = cn_domain;
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) {
OSMO_ASSERT(apn_buf_size >= APN_MAXLEN);
OSMO_ASSERT(apn_buf);
/* FIXME: PDP infos - use more fine-grained access control
instead of wildcard APN */
osmo_gsup_configure_wildcard_apn(gsup, apn_buf, apn_buf_size);
}
return 0;
}

View File

@@ -4,6 +4,11 @@
#include <osmocom/core/msgb.h>
#include <osmocom/abis/ipa.h>
#include <osmocom/abis/ipaccess.h>
#include <osmocom/gsm/gsup.h>
#ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN
#define OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN 43 /* TS 24.008 10.5.4.7 */
#endif
struct osmo_gsup_conn;
@@ -11,6 +16,9 @@ struct osmo_gsup_conn;
typedef int (*osmo_gsup_read_cb_t)(struct osmo_gsup_conn *conn, struct msgb *msg);
struct osmo_gsup_server {
/* private data of the application/user */
void *priv;
/* list of osmo_gsup_conn */
struct llist_head clients;
@@ -33,6 +41,10 @@ struct osmo_gsup_conn {
struct tlv_parsed ccm;
unsigned int auc_3g_ind; /*!< IND index used for UMTS AKA SQN */
/* Set when Location Update is received: */
bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */
bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */
};
@@ -44,7 +56,14 @@ struct osmo_gsup_server *osmo_gsup_server_create(void *ctx,
const char *ip_addr,
uint16_t tcp_port,
osmo_gsup_read_cb_t read_cb,
struct llist_head *lu_op_lst);
struct llist_head *lu_op_lst,
void *priv);
void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups);
int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
uint8_t *apn_buf, size_t apn_buf_size);
int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, const char *imsi, const char *msisdn,
uint8_t *msisdn_enc, size_t msisdn_enc_size,
uint8_t *apn_buf, size_t apn_buf_size,
enum osmo_gsup_cn_domain cn_domain);

View File

@@ -0,0 +1,20 @@
# This is _NOT_ the library release version, it's an API version.
# Please read chapter "Library interface versions" of the libtool documentation
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
LIBVERSION=0:0:0
AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include \
$(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOABIS_CFLAGS)
lib_LTLIBRARIES = libosmo-gsup-client.la
libosmo_gsup_client_la_SOURCES = gsup_client.c
libosmo_gsup_client_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
libosmo_gsup_client_la_LIBADD = $(TALLOC_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS)
noinst_PROGRAMS = gsup-test-client
gsup_test_client_SOURCES = gsup_test_client.c
gsup_test_client_LDADD = $(TALLOC_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) \
libosmo-gsup-client.la

View File

@@ -0,0 +1,345 @@
/* Generic Subscriber Update Protocol client */
/* (C) 2014-2016 by Sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Jacob Erlbeck
* 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
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/gsupclient/gsup_client.h>
#include <osmocom/abis/ipa.h>
#include <osmocom/gsm/oap_client.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <errno.h>
#include <string.h>
static void start_test_procedure(struct osmo_gsup_client *gsupc);
static void gsup_client_send_ping(struct osmo_gsup_client *gsupc)
{
struct msgb *msg = osmo_gsup_client_msgb_alloc();
msg->l2h = msgb_put(msg, 1);
msg->l2h[0] = IPAC_MSGT_PING;
ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS);
ipa_client_conn_send(gsupc->link, msg);
}
static int gsup_client_connect(struct osmo_gsup_client *gsupc)
{
int rc;
if (gsupc->is_connected)
return 0;
if (osmo_timer_pending(&gsupc->connect_timer)) {
LOGP(DLGSUP, LOGL_DEBUG,
"GSUP connect: connect timer already running\n");
osmo_timer_del(&gsupc->connect_timer);
}
if (osmo_timer_pending(&gsupc->ping_timer)) {
LOGP(DLGSUP, LOGL_DEBUG,
"GSUP connect: ping timer already running\n");
osmo_timer_del(&gsupc->ping_timer);
}
if (ipa_client_conn_clear_queue(gsupc->link) > 0)
LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n");
rc = ipa_client_conn_open(gsupc->link);
if (rc >= 0) {
LOGP(DLGSUP, LOGL_NOTICE, "GSUP connecting to %s:%d\n",
gsupc->link->addr, gsupc->link->port);
return 0;
}
LOGP(DLGSUP, LOGL_ERROR, "GSUP failed to connect to %s:%d: %s\n",
gsupc->link->addr, gsupc->link->port, strerror(-rc));
if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT ||
rc == -EINVAL)
return rc;
osmo_timer_schedule(&gsupc->connect_timer,
OSMO_GSUP_CLIENT_RECONNECT_INTERVAL, 0);
LOGP(DLGSUP, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n",
gsupc->link->addr, gsupc->link->port);
return 0;
}
static void connect_timer_cb(void *gsupc_)
{
struct osmo_gsup_client *gsupc = gsupc_;
if (gsupc->is_connected)
return;
gsup_client_connect(gsupc);
}
static void client_send(struct osmo_gsup_client *gsupc, int proto_ext,
struct msgb *msg_tx)
{
ipa_prepend_header_ext(msg_tx, proto_ext);
ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO);
ipa_client_conn_send(gsupc->link, msg_tx);
/* msg_tx is now queued and will be freed. */
}
static void gsup_client_oap_register(struct osmo_gsup_client *gsupc)
{
struct msgb *msg_tx;
int rc;
rc = osmo_oap_client_register(&gsupc->oap_state, &msg_tx);
if ((rc < 0) || (!msg_tx)) {
LOGP(DLGSUP, LOGL_ERROR, "GSUP OAP set up, but cannot register.\n");
return;
}
client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
}
static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
{
struct osmo_gsup_client *gsupc = link->data;
LOGP(DLGSUP, LOGL_INFO, "GSUP link to %s:%d %s\n",
link->addr, link->port, up ? "UP" : "DOWN");
gsupc->is_connected = up;
if (up) {
start_test_procedure(gsupc);
if (gsupc->oap_state.state == OSMO_OAP_INITIALIZED)
gsup_client_oap_register(gsupc);
osmo_timer_del(&gsupc->connect_timer);
} else {
osmo_timer_del(&gsupc->ping_timer);
osmo_timer_schedule(&gsupc->connect_timer,
OSMO_GSUP_CLIENT_RECONNECT_INTERVAL, 0);
}
}
static int gsup_client_oap_handle(struct osmo_gsup_client *gsupc, struct msgb *msg_rx)
{
int rc;
struct msgb *msg_tx;
/* If the oap_state is disabled, this will reject the messages. */
rc = osmo_oap_client_handle(&gsupc->oap_state, msg_rx, &msg_tx);
msgb_free(msg_rx);
if (rc < 0)
return rc;
if (msg_tx)
client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
return 0;
}
static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg)
{
struct ipaccess_head *hh = (struct ipaccess_head *) msg->data;
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);
msg->l2h = &hh->data[0];
rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg);
if (rc < 0) {
LOGP(DLGSUP, LOGL_NOTICE,
"GSUP received an invalid IPA/CCM message from %s:%d\n",
link->addr, link->port);
/* Link has been closed */
gsupc->is_connected = 0;
msgb_free(msg);
return -1;
}
if (rc == 1) {
uint8_t msg_type = *(msg->l2h);
/* CCM message */
if (msg_type == IPAC_MSGT_PONG) {
LOGP(DLGSUP, LOGL_DEBUG, "GSUP receiving PONG\n");
gsupc->got_ipa_pong = 1;
}
msgb_free(msg);
return 0;
}
if (hh->proto != IPAC_PROTO_OSMO)
goto invalid;
if (!he || msgb_l2len(msg) < sizeof(*he))
goto invalid;
msg->l2h = &he->data[0];
if (he->proto == IPAC_PROTO_EXT_GSUP) {
OSMO_ASSERT(gsupc->read_cb != NULL);
gsupc->read_cb(gsupc, msg);
/* expecting read_cb() to free msg */
} else if (he->proto == IPAC_PROTO_EXT_OAP) {
return gsup_client_oap_handle(gsupc, msg);
/* gsup_client_oap_handle frees msg */
} else
goto invalid;
return 0;
invalid:
LOGP(DLGSUP, LOGL_NOTICE,
"GSUP received an invalid IPA message from %s:%d, size = %d\n",
link->addr, link->port, msgb_length(msg));
msgb_free(msg);
return -1;
}
static void ping_timer_cb(void *gsupc_)
{
struct osmo_gsup_client *gsupc = gsupc_;
LOGP(DLGSUP, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n",
gsupc->is_connected ? "connected" : "not connected",
gsupc->got_ipa_pong ? "got" : "didn't get");
if (gsupc->got_ipa_pong) {
start_test_procedure(gsupc);
return;
}
LOGP(DLGSUP, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n");
ipa_client_conn_close(gsupc->link);
gsupc->is_connected = 0;
gsup_client_connect(gsupc);
}
static void start_test_procedure(struct osmo_gsup_client *gsupc)
{
osmo_timer_setup(&gsupc->ping_timer, ping_timer_cb, gsupc);
gsupc->got_ipa_pong = 0;
osmo_timer_schedule(&gsupc->ping_timer, OSMO_GSUP_CLIENT_PING_INTERVAL, 0);
LOGP(DLGSUP, LOGL_DEBUG, "GSUP sending PING\n");
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)
{
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);
/* a NULL oapc_config will mark oap_state disabled. */
rc = osmo_oap_client_init(oapc_config, &gsupc->oap_state);
if (rc != 0)
goto failed;
gsupc->link = ipa_client_conn_create(gsupc,
/* no e1inp */ NULL,
0,
ip_addr, tcp_port,
gsup_client_updown_cb,
gsup_client_read_cb,
/* default write_cb */ NULL,
gsupc);
if (!gsupc->link)
goto failed;
osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc);
rc = gsup_client_connect(gsupc);
if (rc < 0)
goto failed;
gsupc->read_cb = read_cb;
return gsupc;
failed:
osmo_gsup_client_destroy(gsupc);
return NULL;
}
void osmo_gsup_client_destroy(struct osmo_gsup_client *gsupc)
{
osmo_timer_del(&gsupc->connect_timer);
osmo_timer_del(&gsupc->ping_timer);
if (gsupc->link) {
ipa_client_conn_close(gsupc->link);
ipa_client_conn_destroy(gsupc->link);
gsupc->link = NULL;
}
talloc_free(gsupc);
}
int osmo_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg)
{
if (!gsupc || !gsupc->is_connected) {
LOGP(DLGSUP, LOGL_ERROR, "GSUP not connected, unable to send %s\n", msgb_hexdump(msg));
msgb_free(msg);
return -ENOTCONN;
}
client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg);
return 0;
}
struct msgb *osmo_gsup_client_msgb_alloc(void)
{
return msgb_alloc_headroom(4000, 64, __func__);
}

View File

@@ -0,0 +1,321 @@
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/select.h>
#include <osmocom/core/application.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gsupclient/gsup_client.h>
static struct osmo_gsup_client *g_gc;
/***********************************************************************
* IMSI Operation
***********************************************************************/
static LLIST_HEAD(g_imsi_ops);
struct imsi_op_stats {
uint32_t num_alloc;
uint32_t num_released;
uint32_t num_rx_success;
uint32_t num_rx_error;
uint32_t num_timeout;
};
enum imsi_op_type {
IMSI_OP_SAI,
IMSI_OP_LU,
IMSI_OP_ISD,
_NUM_IMSI_OP
};
static const struct value_string imsi_op_names[] = {
{ IMSI_OP_SAI, "SAI" },
{ IMSI_OP_LU, "LU" },
{ IMSI_OP_ISD, "ISD" },
{ 0, NULL }
};
static struct imsi_op_stats imsi_op_stats[_NUM_IMSI_OP];
struct imsi_op {
struct llist_head list;
char imsi[17];
enum imsi_op_type type;
struct osmo_timer_list timer;
};
static struct imsi_op *imsi_op_find(const char *imsi,
enum imsi_op_type type)
{
struct imsi_op *io;
llist_for_each_entry(io, &g_imsi_ops, list) {
if (!strcmp(io->imsi, imsi) && io->type == type)
return io;
}
return NULL;
}
static void imsi_op_timer_cb(void *data);
static struct imsi_op *imsi_op_alloc(void *ctx, const char *imsi,
enum imsi_op_type type)
{
struct imsi_op *io;
if (imsi_op_find(imsi, type))
return NULL;
io = talloc_zero(ctx, struct imsi_op);
OSMO_STRLCPY_ARRAY(io->imsi, imsi);
io->type = type;
osmo_timer_setup(&io->timer, imsi_op_timer_cb, io);
llist_add(&io->list, &g_imsi_ops);
imsi_op_stats[type].num_alloc++;
return io;
}
static void imsi_op_release(struct imsi_op *io)
{
osmo_timer_del(&io->timer);
llist_del(&io->list);
imsi_op_stats[io->type].num_released++;
talloc_free(io);
}
static void imsi_op_timer_cb(void *data)
{
struct imsi_op *io = data;
printf("%s: Timer expiration\n", io->imsi);
imsi_op_stats[io->type].num_timeout++;
imsi_op_release(io);
}
/* allocate + generate + send Send-Auth-Info */
static int req_auth_info(const char *imsi)
{
struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_SAI);
struct osmo_gsup_message gsup = {0};
struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__);
int rc;
OSMO_STRLCPY_ARRAY(gsup.imsi, io->imsi);
gsup.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST;
rc = osmo_gsup_encode(msg, &gsup);
if (rc < 0) {
printf("%s: encoding failure (%s)\n", imsi, strerror(-rc));
return rc;
}
return osmo_gsup_client_send(g_gc, msg);
}
/* allocate + generate + send Send-Auth-Info */
static int req_loc_upd(const char *imsi)
{
struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_LU);
struct osmo_gsup_message gsup = {0};
struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__);
int rc;
OSMO_STRLCPY_ARRAY(gsup.imsi, io->imsi);
gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST;
rc = osmo_gsup_encode(msg, &gsup);
if (rc < 0) {
printf("%s: encoding failure (%s)\n", imsi, strerror(-rc));
return rc;
}
return osmo_gsup_client_send(g_gc, msg);
}
static int resp_isd(struct imsi_op *io)
{
struct osmo_gsup_message gsup = {0};
struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__);
int rc;
OSMO_STRLCPY_ARRAY(gsup.imsi, io->imsi);
gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT;
rc = osmo_gsup_encode(msg, &gsup);
if (rc < 0) {
printf("%s: encoding failure (%s)\n", io->imsi, strerror(-rc));
return rc;
}
imsi_op_release(io);
return osmo_gsup_client_send(g_gc, msg);
}
/* receive an incoming GSUP message */
static void imsi_op_rx_gsup(struct imsi_op *io, const struct osmo_gsup_message *gsup)
{
int is_error = 0, rc;
if (OSMO_GSUP_IS_MSGT_ERROR(gsup->message_type)) {
imsi_op_stats[io->type].num_rx_error++;
is_error = 1;
} else
imsi_op_stats[io->type].num_rx_success++;
switch (io->type) {
case IMSI_OP_SAI:
printf("%s; SAI Response%s\n", io->imsi, is_error ? ": ERROR" : "");
/* now that we have auth tuples, request LU */
rc = req_loc_upd(io->imsi);
if (rc < 0)
printf("Failed to request Location Update for %s\n", io->imsi);
imsi_op_release(io);
break;
case IMSI_OP_LU:
printf("%s; LU Response%s\n", io->imsi, is_error ? ": ERROR" : "");
imsi_op_release(io);
break;
case IMSI_OP_ISD:
printf("%s; ISD Request%s\n", io->imsi, is_error ? ": ERROR" : "");
rc = resp_isd(io);
if (rc < 0)
printf("Failed to insert subscriber data for %s\n", io->imsi);
break;
default:
printf("%s: Unknown\n", io->imsi);
imsi_op_release(io);
break;
}
}
static int op_type_by_gsup_msgt(enum osmo_gsup_message_type msg_type)
{
switch (msg_type) {
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR:
return IMSI_OP_SAI;
case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT:
case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR:
return IMSI_OP_LU;
case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST:
return IMSI_OP_ISD;
default:
printf("Unknown GSUP msg_type %u\n", msg_type);
return -1;
}
}
static int gsupc_read_cb(struct osmo_gsup_client *gsupc, struct msgb *msg)
{
struct osmo_gsup_message gsup_msg = {0};
struct imsi_op *io = NULL;
int rc;
DEBUGP(DLGSUP, "Rx GSUP %s\n", msgb_hexdump(msg));
rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg);
if (rc < 0)
return rc;
if (!gsup_msg.imsi[0])
return -1;
rc = op_type_by_gsup_msgt(gsup_msg.message_type);
if (rc < 0)
return rc;
switch (rc) {
case IMSI_OP_SAI:
case IMSI_OP_LU:
io = imsi_op_find(gsup_msg.imsi, rc);
break;
case IMSI_OP_ISD:
/* ISD is an inbound transaction */
io = imsi_op_alloc(g_gc, gsup_msg.imsi, IMSI_OP_ISD);
break;
}
if (!io)
return -1;
imsi_op_rx_gsup(io, &gsup_msg);
msgb_free(msg);
return 0;
}
static void print_report(void)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(imsi_op_stats); i++) {
struct imsi_op_stats *st = &imsi_op_stats[i];
const char *name = get_value_string(imsi_op_names, i);
printf("%s: %u alloc, %u released, %u success, %u error , %u tout\n",
name, st->num_alloc, st->num_released, st->num_rx_success,
st->num_rx_error, st->num_timeout);
}
}
static void sig_cb(int sig)
{
switch (sig) {
case SIGINT:
print_report();
exit(0);
break;
}
}
/* default categories */
static struct log_info_cat default_categories[] = {
};
static const struct log_info gsup_test_client_log_info = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};
int main(int argc, char **argv)
{
unsigned long long i;
char *server_host = "127.0.0.1";
uint16_t server_port = OSMO_GSUP_PORT;
void *ctx = talloc_named_const(NULL, 0, "gsup_test_client");
osmo_init_logging2(ctx, &gsup_test_client_log_info);
g_gc = osmo_gsup_client_create(ctx, "GSUPTEST", server_host, server_port,
gsupc_read_cb, NULL);
signal(SIGINT, sig_cb);
for (i = 0; i < 10000; i++) {
unsigned long long imsi = 901790000000000 + i;
char imsi_buf[17] = { 0 };
int rc;
snprintf(imsi_buf, sizeof(imsi_buf), "%015llu", imsi);
rc = req_auth_info(imsi_buf);
if (rc < 0)
printf("Failed to request Auth Info for %s\n", imsi_buf);
osmo_select_main(0);
}
while (1) {
osmo_select_main(0);
}
print_report();
exit(0);
}

220
src/hlr.c
View File

@@ -26,13 +26,12 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/application.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gsm/apn.h>
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/ports.h>
#include <osmocom/ctrl/control_vty.h>
#include <osmocom/gsm/apn.h>
#include "db.h"
#include "hlr.h"
@@ -43,8 +42,89 @@
#include "rand.h"
#include "luop.h"
#include "hlr_vty.h"
#include "hlr_ussd.h"
static struct hlr *g_hlr;
struct hlr *g_hlr;
static int quit = 0;
/* Trigger 'Insert Subscriber Data' messages to all connected GSUP clients.
*
* \param[in] subscr A subscriber we have new data to send for.
*/
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)
return;
llist_for_each_entry(co, &g_hlr->gs->clients, list) {
struct osmo_gsup_message gsup = { };
uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
uint8_t apn[APN_MAXLEN];
struct msgb *msg_out;
uint8_t *peer;
int peer_len;
enum osmo_gsup_cn_domain cn_domain;
if (co->supports_ps)
cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
else if (co->supports_cs)
cn_domain = OSMO_GSUP_CN_DOMAIN_CS;
else {
/* We have not yet received a location update from this subscriber .*/
continue;
}
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,
"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",
co && co->conn && co->conn->server? co->conn->server->port : 0);
continue;
}
/* Send ISD to MSC/SGSN */
msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP ISD UPDATE");
if (msg_out == NULL) {
LOGP(DMAIN, 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",
co && co->conn && co->conn->server? co->conn->server->port : 0);
continue;
}
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 "
"for %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;
}
}
#endif
}
/***********************************************************************
* Send Auth Info handling
@@ -67,12 +147,27 @@ static int rx_send_auth_info(struct osmo_gsup_conn *conn,
gsup_out.auth_vectors,
ARRAY_SIZE(gsup_out.auth_vectors),
gsup->rand, gsup->auts);
if (rc < 0) {
if (rc <= 0) {
gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
gsup_out.cause = GMM_CAUSE_NET_FAIL;
} else if (rc == 0) {
gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
switch (rc) {
case 0:
/* 0 means "0 tuples generated", which shouldn't happen.
* Treat the same as "no auth data". */
case -ENOKEY:
LOGP(DAUC, LOGL_NOTICE, "%s: IMSI known, but has no auth data;"
" Returning slightly inaccurate cause 'IMSI Unknown' via GSUP\n",
gsup->imsi);
gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
break;
case -ENOENT:
LOGP(DAUC, LOGL_NOTICE, "%s: IMSI not known\n", gsup->imsi);
gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
break;
default:
LOGP(DAUC, LOGL_ERROR, "%s: failure to look up IMSI in db\n", gsup->imsi);
gsup_out.cause = GMM_CAUSE_NET_FAIL;
break;
}
} else {
gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT;
gsup_out.num_auth_vectors = rc;
@@ -155,8 +250,19 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
lu_op_statechg(luop, LU_S_LU_RECEIVED);
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
if (gsup->cn_domain == 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 {
/* 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." */
conn->supports_ps = true;
luop->is_ps = true;
}
llist_add(&luop->list, &g_lu_ops);
/* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
@@ -164,7 +270,7 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
/* check if subscriber is known at all */
if (!lu_op_fill_subscr(luop, g_hlr->dbc, gsup->imsi)) {
/* Send Error back: Subscriber Unknown in HLR */
strcpy(luop->subscr.imsi, gsup->imsi);
osmo_strlcpy(luop->subscr.imsi, gsup->imsi, sizeof(luop->subscr.imsi));
lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
return 0;
}
@@ -192,10 +298,19 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
} else
#endif
{
int rc;
uint8_t *addr;
rc = osmo_gsup_conn_ccm_get(conn, &addr, IPAC_IDTAG_SERNR);
if (rc <= 0) {
osmo_strlcpy(luop->subscr.imsi, gsup->imsi, sizeof(luop->subscr.imsi));
lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL);
return 0;
}
/* 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 */
db_subscr_lu(g_hlr->dbc, luop->subscr.id, (char *)addr, luop->is_ps);
lu_op_tx_insert_subscr_data(luop);
}
return 0;
@@ -223,9 +338,9 @@ static int rx_purge_ms_req(struct osmo_gsup_conn *conn,
/* Perform the actual update of the DB */
rc = db_subscr_purge(g_hlr->dbc, gsup->imsi, true, is_ps);
if (rc == 1)
if (rc == 0)
gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_RESULT;
else if (rc == 0) {
else if (rc == -ENOENT) {
gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
gsup_reply.cause = GMM_CAUSE_IMSI_UNKNOWN;
} else {
@@ -238,6 +353,29 @@ static int rx_purge_ms_req(struct osmo_gsup_conn *conn,
return osmo_gsup_conn_send(conn, msg_out);
}
static int gsup_send_err_reply(struct osmo_gsup_conn *conn, const char *imsi,
enum osmo_gsup_message_type type_in, uint8_t err_cause)
{
int type_err = osmo_gsup_get_err_msg_type(type_in);
struct osmo_gsup_message gsup_reply = {0};
struct msgb *msg_out;
if (type_err < 0) {
LOGP(DMAIN, LOGL_ERROR, "unable to determine error response for %s\n",
osmo_gsup_message_type_name(type_in));
return type_err;
}
OSMO_STRLCPY_ARRAY(gsup_reply.imsi, imsi);
gsup_reply.message_type = type_err;
gsup_reply.cause = err_cause;
msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP ERR response");
OSMO_ASSERT(msg_out);
osmo_gsup_encode(msg_out, &gsup_reply);
LOGP(DMAIN, LOGL_NOTICE, "Tx %s\n", osmo_gsup_message_type_name(type_err));
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;
@@ -249,6 +387,11 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
return rc;
}
/* 3GPP TS 23.003 Section 2.2 clearly states that an IMSI with less than 5
* digits is impossible. Even 5 digits is a highly theoretical case */
if (strlen(gsup.imsi) < 5)
return gsup_send_err_reply(conn, gsup.imsi, gsup.message_type, GMM_CAUSE_INV_MAND_INFO);
switch (gsup.message_type) {
/* requests sent to us */
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
@@ -269,6 +412,13 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
LOGP(DMAIN, LOGL_ERROR, "Deleting subscriber data for IMSI %s\n",
gsup.imsi);
break;
case OSMO_GSUP_MSGT_PROC_SS_REQUEST:
case OSMO_GSUP_MSGT_PROC_SS_RESULT:
rx_proc_ss_req(conn, &gsup);
break;
case OSMO_GSUP_MSGT_PROC_SS_ERROR:
rx_proc_ss_error(conn, &gsup);
break;
case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
@@ -391,11 +541,7 @@ static void signal_hdlr(int signal)
switch (signal) {
case SIGINT:
LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
osmo_gsup_server_destroy(g_hlr->gs);
db_close(g_hlr->dbc);
log_fini();
talloc_report_full(hlr_ctx, stderr);
exit(0);
quit++;
break;
case SIGUSR1:
LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
@@ -422,12 +568,20 @@ int main(int argc, char **argv)
{
int rc;
/* Track the use of talloc NULL memory contexts */
talloc_enable_null_tracking();
hlr_ctx = talloc_named_const(NULL, 1, "OsmoHLR");
msgb_talloc_ctx_init(hlr_ctx, 0);
vty_info.tall_ctx = hlr_ctx;
g_hlr = talloc_zero(hlr_ctx, struct hlr);
INIT_LLIST_HEAD(&g_hlr->euse_list);
INIT_LLIST_HEAD(&g_hlr->iuse_list);
INIT_LLIST_HEAD(&g_hlr->ss_sessions);
INIT_LLIST_HEAD(&g_hlr->ussd_routes);
rc = osmo_init_logging(&hlr_log_info);
rc = osmo_init_logging2(hlr_ctx, &hlr_log_info);
if (rc < 0) {
fprintf(stderr, "Error initializing logging\n");
exit(1);
@@ -460,21 +614,21 @@ int main(int argc, char **argv)
exit(1);
}
g_hlr->dbc = db_open(hlr_ctx, cmdline_opts.db_file);
g_hlr->dbc = db_open(hlr_ctx, cmdline_opts.db_file, true);
if (!g_hlr->dbc) {
LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
exit(1);
}
g_hlr->gs = osmo_gsup_server_create(hlr_ctx, g_hlr->gsup_bind_addr, OSMO_GSUP_PORT,
read_cb, &g_lu_ops);
read_cb, &g_lu_ops, g_hlr);
if (!g_hlr->gs) {
LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
exit(1);
}
g_hlr->ctrl_bind_addr = ctrl_vty_get_bind_addr();
g_hlr->ctrl = hlr_controlif_setup(g_hlr, g_hlr->gs);
g_hlr->ctrl = hlr_controlif_setup(g_hlr);
osmo_init_ignore_signals();
signal(SIGINT, &signal_hdlr);
@@ -488,13 +642,29 @@ int main(int argc, char **argv)
}
}
while (1) {
while (!quit)
osmo_select_main(0);
}
osmo_gsup_server_destroy(g_hlr->gs);
db_close(g_hlr->dbc);
log_fini();
exit(0);
/**
* Report the heap state of root context, then free,
* so both ASAN and Valgrind are happy...
*/
talloc_report_full(hlr_ctx, stderr);
talloc_free(hlr_ctx);
/* FIXME: VTY code still uses NULL-context */
talloc_free(tall_vty_ctx);
/**
* Report the heap state of NULL context, then free,
* so both ASAN and Valgrind are happy...
*/
talloc_report_full(NULL, stderr);
talloc_disable_null_tracking();
return 0;
}

View File

@@ -23,6 +23,9 @@
#pragma once
#include <stdbool.h>
#include <osmocom/core/linuxlist.h>
struct hlr_euse;
struct hlr {
/* GSUP server pointer */
@@ -37,4 +40,18 @@ struct hlr {
/* Local bind addr */
char *gsup_bind_addr;
struct llist_head euse_list;
struct hlr_euse *euse_default;
struct llist_head iuse_list;
struct llist_head ussd_routes;
struct llist_head ss_sessions;
};
extern struct hlr *g_hlr;
struct hlr_subscriber;
void osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr);

439
src/hlr_db_tool.c Normal file
View File

@@ -0,0 +1,439 @@
/* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <getopt.h>
#include <inttypes.h>
#include <string.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/application.h>
#include "logging.h"
#include "db.h"
#include "rand.h"
struct hlr_db_tool_ctx {
/* DB context */
struct db_context *dbc;
};
struct hlr_db_tool_ctx *g_hlr_db_tool_ctx;
static struct {
const char *db_file;
bool bootstrap;
const char *import_nitb_db;
} cmdline_opts = {
.db_file = "hlr.db",
};
static void print_help()
{
printf("\n");
printf("Usage: osmo-hlr-db-tool [-l <hlr.db>] [create|import-nitb-db <nitb.db>]\n");
printf(" -l --database db-name The OsmoHLR database to use, default '%s'.\n",
cmdline_opts.db_file);
printf(" -h --help This text.\n");
printf(" -d option --debug=DMAIN:DDB:DAUC Enable debugging.\n");
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(" -V --version Print the version of OsmoHLR-db-tool.\n");
printf("\n");
printf("Commands:\n");
printf("\n");
printf(" create Create an empty OsmoHLR database.\n");
printf(" (All commands imply this if none exists yet.)\n");
printf("\n");
printf(" import-nitb-db <nitb.db> Add OsmoNITB db's subscribers to OsmoHLR db.\n");
printf(" Be aware that the import is lossy, only the\n");
printf(" IMSI, MSISDN, nam_cs/ps and 2G auth data are set.\n");
}
static void print_version(int print_copyright)
{
printf("OsmoHLR-db-tool version %s\n", PACKAGE_VERSION);
if (print_copyright)
printf("\n"
"Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n");
}
static void handle_options(int argc, char **argv)
{
const char *cmd;
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"database", 1, 0, 'l'},
{"debug", 1, 0, 'd'},
{"disable-color", 0, 0, 's'},
{"timestamp", 0, 0, 'T'},
{"log-level", 1, 0, 'e'},
{"version", 0, 0, 'V' },
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hl:d:sTe:V",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
print_help();
exit(EXIT_SUCCESS);
case 'l':
cmdline_opts.db_file = optarg;
break;
case 'd':
log_parse_category_mask(osmo_stderr_target, optarg);
break;
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
case 'T':
log_set_print_timestamp(osmo_stderr_target, 1);
break;
case 'e':
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
case 'V':
print_version(1);
exit(EXIT_SUCCESS);
break;
default:
/* catch unknown options *as well as* missing arguments. */
fprintf(stderr, "Error in command line options. Exiting.\n");
exit(EXIT_FAILURE);
break;
}
}
if (argc - optind <= 0) {
fprintf(stderr, "Error: You must specify a command.\n");
print_help();
exit(EXIT_FAILURE);
}
cmd = argv[optind++];
if (!strcmp(cmd, "create")) {
/* Nothing to do, just run the main program to open the database without running any
* action, which will bootstrap all tables. */
} else if (!strcmp(cmd, "import-nitb-db")) {
if (argc - optind < 1) {
fprintf(stderr, "You must specify an input db file\n");
print_help();
exit(EXIT_FAILURE);
}
cmdline_opts.import_nitb_db = argv[optind++];
} else {
fprintf(stderr, "Error: Unknown command `%s'\n", cmd);
print_help();
exit(EXIT_FAILURE);
}
if (argc - optind > 0) {
fprintf(stderr, "Too many arguments: '%s'\n", argv[optind]);
print_help();
exit(EXIT_FAILURE);
}
}
static void signal_hdlr(int signal)
{
switch (signal) {
case SIGINT:
LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
db_close(g_hlr_db_tool_ctx->dbc);
log_fini();
talloc_report_full(g_hlr_db_tool_ctx, stderr);
exit(EXIT_SUCCESS);
break;
case SIGUSR1:
LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
talloc_report_full(g_hlr_db_tool_ctx, stderr);
break;
}
}
sqlite3 *open_nitb_db(const char *filename)
{
int rc;
sqlite3 *nitb_db = NULL;
rc = sqlite3_open(filename, &nitb_db);
if (rc != SQLITE_OK) {
LOGP(DDB, LOGL_ERROR, "Unable to open OsmoNITB DB %s; rc = %d\n", filename, rc);
return NULL;
}
return nitb_db;
}
enum nitb_stmt {
NITB_SELECT_SUBSCR,
NITB_SELECT_AUTH_KEYS,
};
static const char *nitb_stmt_sql[] = {
[NITB_SELECT_SUBSCR] =
"SELECT imsi, id, extension, authorized"
" FROM Subscriber"
" ORDER BY id",
[NITB_SELECT_AUTH_KEYS] =
"SELECT algorithm_id, a3a8_ki from authkeys"
" WHERE subscriber_id = $subscr_id",
};
sqlite3_stmt *nitb_stmt[ARRAY_SIZE(nitb_stmt_sql)] = {};
size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out);
void import_nitb_subscr_aud(sqlite3 *nitb_db, const char *imsi, int64_t nitb_id, int64_t hlr_id)
{
int rc;
struct db_context *dbc = g_hlr_db_tool_ctx->dbc;
sqlite3_stmt *stmt;
int count = 0;
stmt = nitb_stmt[NITB_SELECT_AUTH_KEYS];
if (!db_bind_int(stmt, NULL, nitb_id))
return;
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
const void *blob;
unsigned int blob_size;
static unsigned char buf[4096];
static char ki[128];
int decoded_size;
struct sub_auth_data_str aud2g = {
.type = OSMO_AUTH_TYPE_GSM,
.algo = OSMO_AUTH_ALG_NONE,
.u.gsm.ki = ki,
};
aud2g.algo = sqlite3_column_int(stmt, 0);
if (count) {
LOGP(DDB, LOGL_ERROR,
"Warning: subscriber has more than one auth key,"
" importing only the first key, for IMSI=%s\n",
imsi);
break;
}
blob = sqlite3_column_blob(stmt, 1);
blob_size = sqlite3_column_bytes(stmt, 1);
if (blob_size > sizeof(buf)) {
LOGP(DDB, LOGL_ERROR,
"OsmoNITB import to %s: Cannot import auth data for IMSI %s:"
" too large blob: %u\n",
dbc->fname, imsi, blob_size);
db_remove_reset(stmt);
continue;
}
decoded_size = _dbd_decode_binary(blob, buf);
osmo_strlcpy(ki, osmo_hexdump_nospc(buf, decoded_size), sizeof(ki));
db_subscr_update_aud_by_id(dbc, hlr_id, &aud2g);
count ++;
}
if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: SQL error: (%d) %s,"
" during stmt '%s'",
rc, sqlite3_errmsg(nitb_db),
nitb_stmt_sql[NITB_SELECT_AUTH_KEYS]);
}
db_remove_reset(stmt);
}
void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
{
struct db_context *dbc = g_hlr_db_tool_ctx->dbc;
int rc;
struct hlr_subscriber subscr;
int64_t nitb_id;
int64_t imsi;
char imsi_str[32];
bool authorized;
imsi = sqlite3_column_int64(stmt, 0);
snprintf(imsi_str, sizeof(imsi_str), "%"PRId64, imsi);
rc = db_subscr_create(dbc, imsi_str);
if (rc < 0) {
LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: failed to create IMSI %s: %d: %s\n",
dbc->fname,
imsi_str,
rc,
strerror(-rc));
/* on error, still attempt to continue */
}
nitb_id = sqlite3_column_int64(stmt, 1);
copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
authorized = sqlite3_column_int(stmt, 3) ? true : false;
db_subscr_update_msisdn_by_imsi(dbc, imsi_str, subscr.msisdn);
db_subscr_nam(dbc, imsi_str, authorized, true);
db_subscr_nam(dbc, imsi_str, authorized, false);
/* find the just created id */
rc = db_subscr_get_by_imsi(dbc, imsi_str, &subscr);
if (rc < 0) {
LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: created IMSI %s,"
" but failed to get new subscriber id: %d: %s\n",
dbc->fname,
imsi_str,
rc,
strerror(-rc));
return;
}
OSMO_ASSERT(!strcmp(imsi_str, subscr.imsi));
import_nitb_subscr_aud(nitb_db, imsi_str, nitb_id, subscr.id);
}
int import_nitb_db(void)
{
int i;
int ret;
int rc;
const char *sql;
sqlite3_stmt *stmt;
sqlite3 *nitb_db = open_nitb_db(cmdline_opts.import_nitb_db);
if (!nitb_db)
return -1;
ret = 0;
for (i = 0; i < ARRAY_SIZE(nitb_stmt_sql); i++) {
sql = nitb_stmt_sql[i];
rc = sqlite3_prepare_v2(nitb_db, sql, -1, &nitb_stmt[i], NULL);
if (rc != SQLITE_OK) {
LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: Unable to prepare SQL statement '%s'\n", sql);
ret = -1;
goto out_free;
}
}
stmt = nitb_stmt[NITB_SELECT_SUBSCR];
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
import_nitb_subscr(nitb_db, stmt);
/* On failure, carry on with the rest. */
}
if (rc != SQLITE_DONE) {
LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: SQL error: (%d) %s,"
" during stmt '%s'",
rc, sqlite3_errmsg(nitb_db),
nitb_stmt_sql[NITB_SELECT_SUBSCR]);
goto out_free;
}
db_remove_reset(stmt);
sqlite3_finalize(stmt);
out_free:
sqlite3_close(nitb_db);
return ret;
}
int main(int argc, char **argv)
{
int rc;
int (*main_action)(void);
main_action = NULL;
g_hlr_db_tool_ctx = talloc_zero(NULL, struct hlr_db_tool_ctx);
OSMO_ASSERT(g_hlr_db_tool_ctx);
talloc_set_name_const(g_hlr_db_tool_ctx, "OsmoHLR-db-tool");
rc = osmo_init_logging2(g_hlr_db_tool_ctx, &hlr_log_info);
if (rc < 0) {
fprintf(stderr, "Error initializing logging\n");
exit(EXIT_FAILURE);
}
handle_options(argc, argv);
if (cmdline_opts.import_nitb_db) {
if (main_action)
goto too_many_actions;
main_action = import_nitb_db;
}
/* Future: add more main_actions, besides import-nitb-db, here.
* For command 'create', no action is required. */
/* Just in case any db actions need randomness */
rc = rand_init();
if (rc < 0) {
LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
exit(EXIT_FAILURE);
}
g_hlr_db_tool_ctx->dbc = db_open(g_hlr_db_tool_ctx, cmdline_opts.db_file, true);
if (!g_hlr_db_tool_ctx->dbc) {
LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
exit(EXIT_FAILURE);
}
osmo_init_ignore_signals();
signal(SIGINT, &signal_hdlr);
signal(SIGUSR1, &signal_hdlr);
rc = 0;
if (main_action)
rc = (*main_action)();
db_close(g_hlr_db_tool_ctx->dbc);
log_fini();
exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
too_many_actions:
fprintf(stderr, "Too many actions requested.\n");
log_fini();
exit(EXIT_FAILURE);
}
/* stubs */
void lu_op_alloc_conn(void) { OSMO_ASSERT(0); }
void lu_op_tx_del_subscr_data(void) { OSMO_ASSERT(0); }
void lu_op_free(void) { OSMO_ASSERT(0); }

565
src/hlr_ussd.c Normal file
View File

@@ -0,0 +1,565 @@
/* OsmoHLR SS/USSD implementation */
/* (C) 2018 Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gsm/gsm0480.h>
#include <osmocom/gsm/protocol/gsm_04_80.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include "hlr.h"
#include "hlr_ussd.h"
#include "gsup_server.h"
#include "gsup_router.h"
#include "logging.h"
/***********************************************************************
* core data structures expressing config from VTY
***********************************************************************/
struct hlr_euse *euse_find(struct hlr *hlr, const char *name)
{
struct hlr_euse *euse;
llist_for_each_entry(euse, &hlr->euse_list, list) {
if (!strcmp(euse->name, name))
return euse;
}
return NULL;
}
struct hlr_euse *euse_alloc(struct hlr *hlr, const char *name)
{
struct hlr_euse *euse = euse_find(hlr, name);
if (euse)
return NULL;
euse = talloc_zero(hlr, struct hlr_euse);
euse->name = talloc_strdup(euse, name);
euse->hlr = hlr;
llist_add_tail(&euse->list, &hlr->euse_list);
return euse;
}
void euse_del(struct hlr_euse *euse)
{
llist_del(&euse->list);
talloc_free(euse);
}
struct hlr_ussd_route *ussd_route_find_prefix(struct hlr *hlr, const char *prefix)
{
struct hlr_ussd_route *rt;
llist_for_each_entry(rt, &hlr->ussd_routes, list) {
if (!strcmp(rt->prefix, prefix))
return rt;
}
return NULL;
}
struct hlr_ussd_route *ussd_route_prefix_alloc_int(struct hlr *hlr, const char *prefix,
const struct hlr_iuse *iuse)
{
struct hlr_ussd_route *rt;
if (ussd_route_find_prefix(hlr, prefix))
return NULL;
rt = talloc_zero(hlr, struct hlr_ussd_route);
rt->prefix = talloc_strdup(rt, prefix);
rt->u.iuse = iuse;
llist_add_tail(&rt->list, &hlr->ussd_routes);
return rt;
}
struct hlr_ussd_route *ussd_route_prefix_alloc_ext(struct hlr *hlr, const char *prefix,
struct hlr_euse *euse)
{
struct hlr_ussd_route *rt;
if (ussd_route_find_prefix(hlr, prefix))
return NULL;
rt = talloc_zero(hlr, struct hlr_ussd_route);
rt->prefix = talloc_strdup(rt, prefix);
rt->is_external = true;
rt->u.euse = euse;
llist_add_tail(&rt->list, &hlr->ussd_routes);
return rt;
}
void ussd_route_del(struct hlr_ussd_route *rt)
{
llist_del(&rt->list);
talloc_free(rt);
}
static struct hlr_ussd_route *ussd_route_lookup_7bit(struct hlr *hlr, const char *ussd_code)
{
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);
return rt;
}
}
LOGP(DSS, LOGL_DEBUG, "Could not find Route for USSD Code '%s'\n", ussd_code);
return NULL;
}
/***********************************************************************
* handling functions for individual GSUP messages
***********************************************************************/
#define LOGPSS(ss, lvl, fmt, args...) \
LOGP(DSS, lvl, "%s/0x%08x: " fmt, (ss)->imsi, (ss)->session_id, ## args)
struct ss_session {
/* link us to hlr->ss_sessions */
struct llist_head list;
/* imsi of this session */
char imsi[GSM23003_IMSI_MAX_DIGITS+2];
/* ID of this session (unique per IMSI) */
uint32_t session_id;
/* state of the session */
enum osmo_gsup_session_state state;
/* time-out when we will delete the session */
struct osmo_timer_list timeout;
/* is this USSD for an external handler (EUSE): true */
bool is_external;
union {
/* external USSD Entity responsible for this session */
struct hlr_euse *euse;
/* internal USSD Entity responsible for this session */
const struct hlr_iuse *iuse;
} u;
/* we don't keep a pointer to the osmo_gsup_{route,conn} towards the MSC/VLR here,
* as this might change during inter-VLR hand-over, and we simply look-up the serving MSC/VLR
* every time we receive an USSD component from the EUSE */
};
struct ss_session *ss_session_find(struct hlr *hlr, const char *imsi, uint32_t session_id)
{
struct ss_session *ss;
llist_for_each_entry(ss, &hlr->ss_sessions, list) {
if (!strcmp(ss->imsi, imsi) && ss->session_id == session_id)
return ss;
}
return NULL;
}
void ss_session_free(struct ss_session *ss)
{
osmo_timer_del(&ss->timeout);
llist_del(&ss->list);
talloc_free(ss);
}
static void ss_session_timeout(void *data)
{
struct ss_session *ss = data;
LOGPSS(ss, LOGL_NOTICE, "SS Session Timeout, destroying\n");
/* FIXME: should we send a ReturnError component to the MS? */
ss_session_free(ss);
}
struct ss_session *ss_session_alloc(struct hlr *hlr, const char *imsi, uint32_t session_id)
{
struct ss_session *ss;
OSMO_ASSERT(!ss_session_find(hlr, imsi, session_id));
ss = talloc_zero(hlr, struct ss_session);
OSMO_ASSERT(ss);
OSMO_STRLCPY_ARRAY(ss->imsi, imsi);
ss->session_id = session_id;
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);
llist_add_tail(&ss->list, &hlr->ss_sessions);
return ss;
}
/***********************************************************************
* handling functions for encoding SS messages + wrapping them in GSUP
***********************************************************************/
static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_msg_type,
bool final, struct msgb *ss_msg)
{
struct osmo_gsup_message resp = {0};
struct msgb *resp_msg;
resp.message_type = gsup_msg_type;
OSMO_STRLCPY_ARRAY(resp.imsi, ss->imsi);
if (final)
resp.session_state = OSMO_GSUP_SESSION_STATE_END;
else
resp.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
resp.session_id = ss->session_id;
if (ss_msg) {
resp.ss_info = msgb_data(ss_msg);
resp.ss_info_len = msgb_length(ss_msg);
}
resp_msg = gsm0480_msgb_alloc_name(__func__);
OSMO_ASSERT(resp_msg);
osmo_gsup_encode(resp_msg, &resp);
msgb_free(ss_msg);
/* FIXME: resolve this based on the database vlr_addr */
return osmo_gsup_addr_send(g_hlr->gs, (uint8_t *)"MSC-00-00-00-00-00-00", 22, resp_msg);
}
#if 0
static int ss_tx_reject(struct ss_session *ss, int invoke_id, uint8_t problem_tag,
uint8_t problem_code)
{
struct msgb *msg = gsm0480_gen_reject(invoke_id, problem_tag, problem_code);
LOGPSS(ss, LOGL_NOTICE, "Tx Reject(%u, 0x%02x, 0x%02x)\n", invoke_id,
problem_tag, problem_code);
OSMO_ASSERT(msg);
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
}
#endif
static int ss_tx_error(struct ss_session *ss, uint8_t invoke_id, uint8_t error_code)
{
struct msgb *msg = gsm0480_gen_return_error(invoke_id, error_code);
LOGPSS(ss, LOGL_NOTICE, "Tx ReturnError(%u, 0x%02x)\n", invoke_id, error_code);
OSMO_ASSERT(msg);
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
}
static int ss_tx_ussd_7bit(struct ss_session *ss, bool final, uint8_t invoke_id, const char *text)
{
struct msgb *msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text);
LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text);
OSMO_ASSERT(msg);
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, final, msg);
}
/***********************************************************************
* Internal USSD Handlers
***********************************************************************/
#include "db.h"
static int handle_ussd_own_msisdn(struct osmo_gsup_conn *conn, struct ss_session *ss,
const struct osmo_gsup_message *gsup, const struct ss_request *req)
{
struct hlr_subscriber subscr;
char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
int rc;
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
switch (rc) {
case 0:
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);
ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
break;
case -ENOENT:
ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
break;
case -EIO:
default:
ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
break;
}
return 0;
}
static int handle_ussd_own_imsi(struct osmo_gsup_conn *conn, struct ss_session *ss,
const struct osmo_gsup_message *gsup, const struct ss_request *req)
{
char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
snprintf(buf, sizeof(buf), "Your IMSI is %s!\n", ss->imsi);
ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
return 0;
}
static const struct hlr_iuse hlr_iuses[] = {
{
.name = "own-msisdn",
.handle_ussd = handle_ussd_own_msisdn,
},
{
.name = "own-imsi",
.handle_ussd = handle_ussd_own_imsi,
},
};
const struct hlr_iuse *iuse_find(const char *name)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(hlr_iuses); i++) {
const struct hlr_iuse *iuse = &hlr_iuses[i];
if (!strcmp(name, iuse->name))
return iuse;
}
return NULL;
}
/***********************************************************************
* handling functions for individual GSUP messages
***********************************************************************/
static bool ss_op_is_ussd(uint8_t opcode)
{
switch (opcode) {
case GSM0480_OP_CODE_PROCESS_USS_DATA:
case GSM0480_OP_CODE_PROCESS_USS_REQ:
case GSM0480_OP_CODE_USS_REQUEST:
case GSM0480_OP_CODE_USS_NOTIFY:
return true;
default:
return false;
}
}
/* is this GSUP connection an EUSE (true) or not (false)? */
static bool conn_is_euse(struct osmo_gsup_conn *conn)
{
int rc;
uint8_t *addr;
rc = osmo_gsup_conn_ccm_get(conn, &addr, IPAC_IDTAG_SERNR);
if (rc <= 5)
return false;
if (!strncmp((char *)addr, "EUSE-", 5))
return true;
else
return false;
}
static struct hlr_euse *euse_by_conn(struct osmo_gsup_conn *conn)
{
int rc;
char *addr;
struct hlr *hlr = conn->server->priv;
rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &addr, IPAC_IDTAG_SERNR);
if (rc <= 5)
return NULL;
if (strncmp(addr, "EUSE-", 5))
return NULL;
return euse_find(hlr, addr+5);
}
static int handle_ss(struct ss_session *ss, const struct osmo_gsup_message *gsup,
const struct ss_request *req)
{
uint8_t comp_type = gsup->ss_info[0];
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;
}
/* Handle a USSD GSUP message for a given SS Session received from VLR or EUSE */
static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss,
const struct osmo_gsup_message *gsup, const struct ss_request *req)
{
uint8_t comp_type = gsup->ss_info[0];
struct msgb *msg_out;
bool is_euse_originated = conn_is_euse(conn);
LOGPSS(ss, LOGL_INFO, "USSD CompType=%s, OpCode=%s '%s'\n",
gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode),
req->ussd_text);
if ((ss->is_external && !ss->u.euse) || !ss->u.iuse) {
LOGPSS(ss, LOGL_NOTICE, "USSD for unknown code '%s'\n", req->ussd_text);
ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SS_NOT_AVAILABLE);
return 0;
}
if (is_euse_originated) {
msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP USSD FW");
OSMO_ASSERT(msg_out);
/* Received from EUSE, Forward to VLR */
osmo_gsup_encode(msg_out, gsup);
/* FIXME: resolve this based on the database vlr_addr */
osmo_gsup_addr_send(conn->server, (uint8_t *)"MSC-00-00-00-00-00-00", 22, msg_out);
} else {
/* Received from VLR (MS) */
if (ss->is_external) {
/* Forward to EUSE */
char addr[128];
strcpy(addr, "EUSE-");
osmo_strlcpy(addr+5, ss->u.euse->name, sizeof(addr)-5);
conn = gsup_route_find(conn->server, (uint8_t *)addr, strlen(addr)+1);
if (!conn) {
LOGPSS(ss, LOGL_ERROR, "Cannot find conn for EUSE %s\n", addr);
ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);
} else {
msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP USSD FW");
OSMO_ASSERT(msg_out);
osmo_gsup_encode(msg_out, gsup);
osmo_gsup_conn_send(conn, msg_out);
}
} else {
/* Handle internally */
ss->u.iuse->handle_ussd(conn, ss, gsup, req);
}
}
return 0;
}
/* this function is called for any SS_REQ/SS_RESP messages from both the MSC/VLR side as well
* as from the EUSE side */
int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
{
struct hlr *hlr = conn->server->priv;
struct ss_session *ss;
struct ss_request req = {0};
LOGP(DSS, LOGL_DEBUG, "%s/0x%08x: Process SS (%s)\n", gsup->imsi, gsup->session_id,
osmo_gsup_session_state_name(gsup->session_state));
/* decode and find out what kind of SS message it is */
if (gsup->ss_info && gsup->ss_info_len) {
if (gsm0480_parse_facility_ie(gsup->ss_info, gsup->ss_info_len, &req)) {
LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Unable to parse SS request: %s\n",
gsup->imsi, gsup->session_id,
osmo_hexdump(gsup->ss_info, gsup->ss_info_len));
/* FIXME: Send a Reject component? */
goto out_err;
}
}
switch (gsup->session_state) {
case OSMO_GSUP_SESSION_STATE_BEGIN:
/* Check for overlapping Session ID usage */
if (ss_session_find(hlr, gsup->imsi, gsup->session_id)) {
LOGP(DSS, LOGL_ERROR, "%s/0x%08x: BEGIN with non-unique session ID!\n",
gsup->imsi, gsup->session_id);
goto out_err;
}
ss = ss_session_alloc(hlr, gsup->imsi, gsup->session_id);
if (!ss) {
LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unable to allocate SS session\n",
gsup->imsi, gsup->session_id);
goto out_err;
}
if (ss_op_is_ussd(req.opcode)) {
if (conn_is_euse(conn)) {
/* EUSE->VLR: MT USSD. EUSE is known ('conn'), VLR is to be resolved */
ss->u.euse = euse_by_conn(conn);
} else {
/* VLR->EUSE: MO USSD. VLR is known ('conn'), EUSE is to be resolved */
struct hlr_ussd_route *rt;
rt = ussd_route_lookup_7bit(hlr, (const char *) req.ussd_text);
if (rt) {
if (rt->is_external) {
ss->is_external = true;
ss->u.euse = rt->u.euse;
} else if (rt) {
ss->is_external = false;
ss->u.iuse = rt->u.iuse;
}
}
}
/* dispatch unstructured SS to routing */
handle_ussd(conn, ss, gsup, &req);
} else {
/* dispatch non-call SS to internal code */
handle_ss(ss, gsup, &req);
}
break;
case OSMO_GSUP_SESSION_STATE_CONTINUE:
ss = ss_session_find(hlr, gsup->imsi, gsup->session_id);
if (!ss) {
LOGP(DSS, LOGL_ERROR, "%s/0x%08x: CONTINUE for unknown SS session\n",
gsup->imsi, gsup->session_id);
goto out_err;
}
if (ss_op_is_ussd(req.opcode)) {
/* dispatch unstructured SS to routing */
handle_ussd(conn, ss, gsup, &req);
} else {
/* dispatch non-call SS to internal code */
handle_ss(ss, gsup, &req);
}
break;
case OSMO_GSUP_SESSION_STATE_END:
ss = ss_session_find(hlr, gsup->imsi, gsup->session_id);
if (!ss) {
LOGP(DSS, LOGL_ERROR, "%s/0x%08x: END for unknown SS session\n",
gsup->imsi, gsup->session_id);
goto out_err;
}
if (ss_op_is_ussd(req.opcode)) {
/* dispatch unstructured SS to routing */
handle_ussd(conn, ss, gsup, &req);
} else {
/* dispatch non-call SS to internal code */
handle_ss(ss, gsup, &req);
}
ss_session_free(ss);
break;
default:
LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unknown SS State %d\n", gsup->imsi,
gsup->session_id, gsup->session_state);
goto out_err;
}
return 0;
out_err:
return 0;
}
int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
{
LOGP(DSS, LOGL_NOTICE, "%s/0x%08x: Process SS ERROR (%s)\n", gsup->imsi, gsup->session_id,
osmo_gsup_session_state_name(gsup->session_state));
return 0;
}

57
src/hlr_ussd.h Normal file
View File

@@ -0,0 +1,57 @@
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/gsup.h>
#include "gsup_server.h"
struct osmo_gsup_conn;
struct hlr_ussd_route {
/* g_hlr.routes */
struct llist_head list;
const char *prefix;
bool is_external;
union {
struct hlr_euse *euse;
const struct hlr_iuse *iuse;
} u;
};
struct hlr_euse {
/* list in the per-hlr list of EUSEs */
struct llist_head list;
struct hlr *hlr;
/* name (must match the IPA ID tag) */
const char *name;
/* human-readable description */
const char *description;
/* GSUP connection to the EUSE, if any */
struct osmo_gsup_conn *conn;
};
struct hlr_euse *euse_find(struct hlr *hlr, const char *name);
struct hlr_euse *euse_alloc(struct hlr *hlr, const char *name);
void euse_del(struct hlr_euse *euse);
const struct hlr_iuse *iuse_find(const char *name);
struct hlr_ussd_route *ussd_route_find_prefix(struct hlr *hlr, const char *prefix);
struct hlr_ussd_route *ussd_route_prefix_alloc_int(struct hlr *hlr, const char *prefix,
const struct hlr_iuse *iuse);
struct hlr_ussd_route *ussd_route_prefix_alloc_ext(struct hlr *hlr, const char *prefix,
struct hlr_euse *euse);
void ussd_route_del(struct hlr_ussd_route *rt);
int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
struct ss_session;
struct ss_request;
/* Internal USSD Handler */
struct hlr_iuse {
const char *name;
/* call-back to be called for any incoming USSD messages for this IUSE */
int (*handle_ussd)(struct osmo_gsup_conn *conn, struct ss_session *ss,
const struct osmo_gsup_message *gsup, const struct ss_request *req);
};

View File

@@ -1,9 +1,14 @@
/* OsmoHLR VTY implementation */
/* (C) 2016 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
* (C) 2018 Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
* (C) 2018 Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -24,11 +29,13 @@
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/misc.h>
#include <osmocom/abis/ipa.h>
#include "hlr.h"
#include "hlr_vty.h"
#include "hlr_vty_subscr.h"
static struct hlr *g_hlr = NULL;
#include "gsup_server.h"
struct cmd_node hlr_node = {
HLR_NODE,
@@ -74,6 +81,33 @@ static int config_write_hlr_gsup(struct vty *vty)
return CMD_SUCCESS;
}
static void show_one_conn(struct vty *vty, const struct osmo_gsup_conn *conn)
{
const struct ipa_server_conn *isc = conn->conn;
char *name;
int rc;
rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &name, IPAC_IDTAG_SERNR);
OSMO_ASSERT(rc);
vty_out(vty, " '%s' from %s:%5u, CS=%u, PS=%u, 3G_IND=%u%s",
name, isc->addr, isc->port, conn->supports_cs, conn->supports_ps, conn->auc_3g_ind,
VTY_NEWLINE);
}
DEFUN(show_gsup_conn, show_gsup_conn_cmd,
"show gsup-connections",
SHOW_STR "GSUP Connections from VLRs, SGSNs, EUSEs\n")
{
struct osmo_gsup_server *gs = g_hlr->gs;
struct osmo_gsup_conn *conn;
llist_for_each_entry(conn, &gs->clients, list)
show_one_conn(vty, conn);
return CMD_SUCCESS;
}
DEFUN(cfg_hlr_gsup_bind_ip,
cfg_hlr_gsup_bind_ip_cmd,
"bind ip A.B.C.D",
@@ -88,12 +122,180 @@ DEFUN(cfg_hlr_gsup_bind_ip,
return CMD_SUCCESS;
}
/***********************************************************************
* USSD Entity
***********************************************************************/
#include "hlr_ussd.h"
#define USSD_STR "USSD Configuration\n"
#define UROUTE_STR "Routing Configuration\n"
#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
#define INT_CHOICE "(own-msisdn|own-imsi)"
#define INT_STR "Internal USSD Handler\n" \
"Respond with subscribers' own MSISDN\n" \
"Respond with subscribers' own IMSI\n"
#define EXT_STR "External USSD Handler\n" \
"Name of External USSD Handler (IPA CCM ID)\n"
DEFUN(cfg_ussd_route_pfx_int, cfg_ussd_route_pfx_int_cmd,
"ussd route prefix PREFIX internal " INT_CHOICE,
USSD_STR UROUTE_STR PREFIX_STR INT_STR)
{
const struct hlr_iuse *iuse = iuse_find(argv[1]);
struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]);
if (rt) {
vty_out(vty, "%% Cannot add [another?] route for prefix %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ussd_route_prefix_alloc_int(g_hlr, argv[0], iuse);
return CMD_SUCCESS;
}
DEFUN(cfg_ussd_route_pfx_ext, cfg_ussd_route_pfx_ext_cmd,
"ussd route prefix PREFIX external EUSE",
USSD_STR UROUTE_STR PREFIX_STR EXT_STR)
{
struct hlr_euse *euse = euse_find(g_hlr, argv[1]);
struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]);
if (rt) {
vty_out(vty, "%% Cannot add [another?] route for prefix %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
if (!euse) {
vty_out(vty, "%% Cannot find euse '%s'%s", argv[1], VTY_NEWLINE);
return CMD_WARNING;
}
ussd_route_prefix_alloc_ext(g_hlr, argv[0], euse);
return CMD_SUCCESS;
}
DEFUN(cfg_ussd_no_route_pfx, cfg_ussd_no_route_pfx_cmd,
"no ussd route prefix PREFIX",
NO_STR USSD_STR UROUTE_STR PREFIX_STR)
{
struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]);
if (!rt) {
vty_out(vty, "%% Cannot find route for prefix %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ussd_route_del(rt);
return CMD_SUCCESS;
}
DEFUN(cfg_ussd_defaultroute, cfg_ussd_defaultroute_cmd,
"ussd default-route external EUSE",
USSD_STR "Configure default-route for all USSD to unknown destinations\n"
EXT_STR)
{
struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
if (g_hlr->euse_default != euse) {
vty_out(vty, "Switching default route from %s to %s%s",
g_hlr->euse_default->name, euse->name, VTY_NEWLINE);
g_hlr->euse_default = euse;
}
return CMD_SUCCESS;
}
DEFUN(cfg_ussd_no_defaultroute, cfg_ussd_no_defaultroute_cmd,
"no ussd default-route",
NO_STR USSD_STR "Remove the default-route for all USSD to unknown destinations\n")
{
g_hlr->euse_default = NULL;
return CMD_SUCCESS;
}
struct cmd_node euse_node = {
EUSE_NODE,
"%s(config-hlr-euse)# ",
1,
};
DEFUN(cfg_euse, cfg_euse_cmd,
"euse NAME",
"Configure a particular External USSD Entity\n"
"Alphanumeric name of the External USSD Entity\n")
{
struct hlr_euse *euse;
const char *id = argv[0];
euse = euse_find(g_hlr, id);
if (!euse) {
euse = euse_alloc(g_hlr, id);
if (!euse)
return CMD_WARNING;
}
vty->index = euse;
vty->index_sub = &euse->description;
vty->node = EUSE_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_no_euse, cfg_no_euse_cmd,
"no euse NAME",
NO_STR "Remove a particular External USSD Entity\n"
"Alphanumeric name of the External USSD Entity\n")
{
struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
if (!euse) {
vty_out(vty, "%% Cannot remove non-existant EUSE %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
if (g_hlr->euse_default == euse) {
vty_out(vty, "%% Cannot remove EUSE %s, it is the default route%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
euse_del(euse);
return CMD_SUCCESS;
}
static void dump_one_euse(struct vty *vty, struct hlr_euse *euse)
{
vty_out(vty, " euse %s%s", euse->name, VTY_NEWLINE);
if (g_hlr->euse_default == euse)
vty_out(vty, " default-route%s", VTY_NEWLINE);
}
static int config_write_euse(struct vty *vty)
{
struct hlr_euse *euse;
struct hlr_ussd_route *rt;
llist_for_each_entry(euse, &g_hlr->euse_list, list)
dump_one_euse(vty, euse);
llist_for_each_entry(rt, &g_hlr->ussd_routes, list) {
vty_out(vty, " ussd route prefix %s %s %s%s", rt->prefix,
rt->is_external ? "external" : "internal",
rt->is_external ? rt->u.euse->name : rt->u.iuse->name,
VTY_NEWLINE);
}
return 0;
}
/***********************************************************************
* Common Code
***********************************************************************/
int hlr_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
case GSUP_NODE:
case EUSE_NODE:
vty->node = HLR_NODE;
vty->index = NULL;
vty->index_sub = NULL;
break;
default:
case HLR_NODE:
@@ -123,19 +325,27 @@ int hlr_vty_is_config_node(struct vty *vty, int node)
void hlr_vty_init(struct hlr *hlr, const struct log_info *cat)
{
g_hlr = hlr;
logging_vty_add_cmds(cat);
osmo_talloc_vty_add_cmds();
install_element_ve(&show_gsup_conn_cmd);
install_element(CONFIG_NODE, &cfg_hlr_cmd);
install_node(&hlr_node, config_write_hlr);
install_default(HLR_NODE);
install_element(HLR_NODE, &cfg_gsup_cmd);
install_node(&gsup_node, config_write_hlr_gsup);
install_default(GSUP_NODE);
install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
install_element(HLR_NODE, &cfg_euse_cmd);
install_element(HLR_NODE, &cfg_no_euse_cmd);
install_node(&euse_node, config_write_euse);
install_element(HLR_NODE, &cfg_ussd_route_pfx_int_cmd);
install_element(HLR_NODE, &cfg_ussd_route_pfx_ext_cmd);
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);
hlr_vty_subscriber_init(hlr);
}

View File

@@ -30,6 +30,7 @@
enum hlr_vty_node {
HLR_NODE = _LAST_OSMOVTY_NODE + 1,
GSUP_NODE,
EUSE_NODE,
};
int hlr_vty_is_config_node(struct vty *vty, int node);

View File

@@ -33,8 +33,6 @@ struct vty;
#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
static struct hlr *g_hlr = NULL;
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
{
int rc;
@@ -72,14 +70,17 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
OSMO_ASSERT(g_hlr);
rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL);
if (rc) {
if (rc == -ENOENT) {
aud2g.algo = OSMO_AUTH_ALG_NONE;
aud3g.algo = OSMO_AUTH_ALG_NONE;
} else {
vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
return;
}
switch (rc) {
case 0:
break;
case -ENOENT:
case -ENOKEY:
aud2g.algo = OSMO_AUTH_ALG_NONE;
aud3g.algo = OSMO_AUTH_ALG_NONE;
break;
default:
vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
return;
}
if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
@@ -254,6 +255,10 @@ DEFUN(subscriber_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);
return CMD_SUCCESS;
}
@@ -331,7 +336,7 @@ DEFUN(subscriber_no_aud2g,
rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
if (rc) {
if (rc && rc != -ENOENT) {
vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s",
subscr.imsi, VTY_NEWLINE);
return CMD_WARNING;
@@ -402,7 +407,7 @@ DEFUN(subscriber_no_aud3g,
rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
if (rc) {
if (rc && rc != -ENOENT) {
vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s",
subscr.imsi, VTY_NEWLINE);
return CMD_WARNING;
@@ -471,8 +476,6 @@ DEFUN(subscriber_aud3g,
void hlr_vty_subscriber_init(struct hlr *hlr)
{
g_hlr = hlr;
install_element_ve(&subscriber_show_cmd);
install_element(ENABLE_NODE, &subscriber_create_cmd);
install_element(ENABLE_NODE, &subscriber_delete_cmd);

View File

@@ -5,20 +5,27 @@ const struct log_info_cat hlr_log_info_cat[] = {
[DMAIN] = {
.name = "DMAIN",
.description = "Main Program",
.enabled = 1, .loglevel = LOGL_DEBUG,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DDB] = {
.name = "DDB",
.description = "Database Layer",
.color = "\033[1;31m",
.enabled = 1, .loglevel = LOGL_DEBUG,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DAUC] = {
.name = "DAUC",
.description = "Authentication Center",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_DEBUG,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DSS] = {
.name = "DSS",
.description = "Supplementary Services",
.color = "\033[1;34m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
const struct log_info hlr_log_info = {

View File

@@ -7,6 +7,7 @@ enum {
DDB,
DGSUP,
DAUC,
DSS,
};
extern const struct log_info hlr_log_info;

View File

@@ -25,7 +25,6 @@
#include <errno.h>
#include <osmocom/core/logging.h>
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gsm/apn.h>
@@ -52,6 +51,7 @@ static void _luop_tx_gsup(struct lu_operation *luop,
struct msgb *msg_out;
msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP LUOP");
OSMO_ASSERT(msg_out);
osmo_gsup_encode(msg_out, gsup);
osmo_gsup_addr_send(luop->gsup_server, luop->peer,
@@ -108,8 +108,7 @@ struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv)
luop = talloc_zero(srv, struct lu_operation);
OSMO_ASSERT(luop);
luop->gsup_server = srv;
luop->timer.cb = lu_op_timer_cb;
luop->timer.data = luop;
osmo_timer_setup(&luop->timer, lu_op_timer_cb, luop);
return luop;
}
@@ -119,6 +118,10 @@ void lu_op_free(struct lu_operation *luop)
/* Only attempt to remove when it was ever added to a list. */
if (luop->list.next)
llist_del(&luop->list);
/* Delete timer just in case it is still pending. */
osmo_timer_del(&luop->timer);
talloc_free(luop);
}
@@ -162,23 +165,6 @@ void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state)
luop->state = new_state;
}
/* Send a msgb to a given address using routing */
int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
const uint8_t *addr, size_t addrlen,
struct msgb *msg)
{
struct osmo_gsup_conn *conn;
conn = gsup_route_find(gs, addr, addrlen);
if (!conn) {
DEBUGP(DMAIN, "Cannot find route for addr %s\n", addr);
msgb_free(msg);
return -ENODEV;
}
return osmo_gsup_conn_send(conn, msg);
}
/*! Transmit UPD_LOC_ERROR and destroy lu_operation */
void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause)
{
@@ -229,44 +215,27 @@ void lu_op_tx_cancel_old(struct lu_operation *luop)
/*! Transmit Insert Subscriber Data to new VLR/SGSN */
void lu_op_tx_insert_subscr_data(struct lu_operation *luop)
{
struct osmo_gsup_message gsup;
struct hlr_subscriber *subscr = &luop->subscr;
struct osmo_gsup_message gsup = { };
uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
uint8_t apn[APN_MAXLEN];
uint8_t msisdn_enc[43]; /* TODO use constant; TS 24.008 10.5.4.7 */
int l;
enum osmo_gsup_cn_domain cn_domain;
OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED ||
luop->state == LU_S_CANCEL_ACK_RECEIVED);
fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_INSERT_DATA_REQUEST);
if (luop->is_ps)
cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
else
cn_domain = OSMO_GSUP_CN_DOMAIN_CS;
l = gsm48_encode_bcd_number(msisdn_enc, sizeof(msisdn_enc), 0,
luop->subscr.msisdn);
if (l < 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,
"%s: Error: cannot encode MSISDN '%s'\n",
luop->subscr.imsi, luop->subscr.msisdn);
lu_op_tx_error(luop, GMM_CAUSE_PROTO_ERR_UNSPEC);
"IMSI='%s': Cannot notify GSUP client; could not create gsup message "
"for %s\n", subscr->imsi, luop->peer);
return;
}
gsup.msisdn_enc = msisdn_enc;
gsup.msisdn_enc_len = l;
/* FIXME: deal with encoding the following data */
gsup.hlr_enc;
if (luop->is_ps) {
/* FIXME: PDP infos - use more fine-grained access control
instead of wildcard APN */
l = osmo_apn_from_str(apn, sizeof(apn), "*");
if (l > 0) {
gsup.pdp_infos[0].apn_enc = apn;
gsup.pdp_infos[0].apn_enc_len = l;
gsup.pdp_infos[0].have_info = 1;
gsup.num_pdp_infos = 1;
/* FIXME: use real value: */
gsup.pdp_infos[0].context_id = 1;
}
}
/* Send ISD to new VLR/SGSN */
_luop_tx_gsup(luop, &gsup);

View File

@@ -28,6 +28,7 @@
#include <osmocom/gsm/gsup.h>
#include "db.h"
#include "gsup_server.h"
#define CANCEL_TIMEOUT_SECS 30
#define ISD_TIMEOUT_SECS 30
@@ -62,9 +63,6 @@ struct lu_operation {
uint8_t *peer;
};
int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
const uint8_t *addr, size_t addrlen,
struct msgb *msg);
struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv);
struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn);

196
src/osmo-euse-demo.c Normal file
View File

@@ -0,0 +1,196 @@
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/select.h>
#include <osmocom/core/application.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gsm/gsm0480.h>
#include <osmocom/gsm/protocol/gsm_04_80.h>
#include <osmocom/gsupclient/gsup_client.h>
#include "logging.h"
static struct osmo_gsup_client *g_gc;
/*! send a SS/USSD response to a given imsi/session.
* \param[in] gsupc GSUP client connection through which to send
* \param[in] imsi IMSI of the subscriber
* \param[in] session_id Unique identifier of SS session for which this response is
* \param[in] gsup_msg_type GSUP message type (OSMO_GSUP_MSGT_PROC_SS_{REQUEST,RESULT,ERROR})
* \param[in] final Is this the final result (true=END) or an intermediate result (false=CONTINUE)
* \param[in] msg Optional binary/BER encoded SS date (for FACILITY IE). Can be NULL. Freed in
* this function call.
*/
static int euse_tx_ss(struct osmo_gsup_client *gsupc, const char *imsi, uint32_t session_id,
enum osmo_gsup_message_type gsup_msg_type, bool final, struct msgb *ss_msg)
{
struct osmo_gsup_message resp = {0};
struct msgb *resp_msg;
switch (gsup_msg_type) {
case OSMO_GSUP_MSGT_PROC_SS_REQUEST:
case OSMO_GSUP_MSGT_PROC_SS_RESULT:
case OSMO_GSUP_MSGT_PROC_SS_ERROR:
break;
default:
msgb_free(ss_msg);
return -EINVAL;
}
resp.message_type = gsup_msg_type;
OSMO_STRLCPY_ARRAY(resp.imsi, imsi);
if (final)
resp.session_state = OSMO_GSUP_SESSION_STATE_END;
else
resp.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
resp.session_id = session_id;
if (ss_msg) {
resp.ss_info = msgb_data(ss_msg);
resp.ss_info_len = msgb_length(ss_msg);
}
resp_msg = gsm0480_msgb_alloc_name(__func__);
OSMO_ASSERT(resp_msg);
osmo_gsup_encode(resp_msg, &resp);
msgb_free(ss_msg);
return osmo_gsup_client_send(gsupc, resp_msg);
}
/*! send a SS/USSD reject to a given IMSI/session.
* \param[in] gsupc GSUP client connection through which to send
* \param[in] imsi IMSI of the subscriber
* \param[in] session_id Unique identifier of SS session for which this response is
* \param[in] invoke_id InvokeID of the request
* \param[in] problem_tag Problem code tag (table 3.13)
* \param[in] problem_code Problem code (table 3.14-3.17)
*/
static int euse_tx_ussd_reject(struct osmo_gsup_client *gsupc, const char *imsi, uint32_t session_id,
int invoke_id, uint8_t problem_tag, uint8_t problem_code)
{
struct msgb *msg = gsm0480_gen_reject(invoke_id, problem_tag, problem_code);
LOGP(DMAIN, LOGL_NOTICE, "Tx %s/0x%08x: Reject(%d, 0x%02x, 0x%02x)\n", imsi, session_id,
invoke_id, problem_tag, problem_code);
OSMO_ASSERT(msg);
return euse_tx_ss(gsupc, imsi, session_id, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
}
/*! send a SS/USSD response in 7-bit GSM default alphabet o a given imsi/session.
* \param[in] gsupc GSUP client connection through which to send
* \param[in] imsi IMSI of the subscriber
* \param[in] session_id Unique identifier of SS session for which this response is
* \param[in] final Is this the final result (true=END) or an intermediate result
* (false=CONTINUE)
* \param[in] invoke_id InvokeID of the request
*/
static int euse_tx_ussd_resp_7bit(struct osmo_gsup_client *gsupc, const char *imsi, uint32_t session_id,
bool final, uint8_t invoke_id, const char *text)
{
struct msgb *ss_msg;
/* encode response; remove L3 header */
ss_msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text);
LOGP(DMAIN, LOGL_DEBUG, "Tx %s/0x%08x: USSD Result(%d, %s, '%s')\n", imsi, session_id,
invoke_id, final ? "END" : "CONTINUE", text);
OSMO_ASSERT(ss_msg);
return euse_tx_ss(gsupc, imsi, session_id, OSMO_GSUP_MSGT_PROC_SS_RESULT, final, ss_msg);
}
static int euse_rx_proc_ss_req(struct osmo_gsup_client *gsupc, const struct osmo_gsup_message *gsup)
{
char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
struct ss_request req = {0};
if (gsup->ss_info && gsup->ss_info_len) {
if (gsm0480_parse_facility_ie(gsup->ss_info, gsup->ss_info_len, &req)) {
return euse_tx_ussd_reject(gsupc, gsup->imsi, gsup->session_id, -1,
GSM_0480_PROBLEM_CODE_TAG_GENERAL,
GSM_0480_GEN_PROB_CODE_BAD_STRUCTURE);
}
}
LOGP(DMAIN, LOGL_INFO, "Rx %s/0x%08x: USSD SessionState=%s, OpCode=%s, '%s'\n", gsup->imsi,
gsup->session_id, osmo_gsup_session_state_name(gsup->session_state),
gsm0480_op_code_name(req.opcode), req.ussd_text);
/* we only handle single-request-response USSD in this demo */
if (gsup->session_state != OSMO_GSUP_SESSION_STATE_BEGIN) {
return euse_tx_ussd_reject(gsupc, gsup->imsi, gsup->session_id, req.invoke_id,
GSM_0480_PROBLEM_CODE_TAG_GENERAL,
GSM_0480_GEN_PROB_CODE_UNRECOGNISED);
}
snprintf(buf, sizeof(buf), "You sent \"%s\"", req.ussd_text);
return euse_tx_ussd_resp_7bit(gsupc, gsup->imsi, gsup->session_id, true, req.invoke_id, buf);
}
static int gsupc_read_cb(struct osmo_gsup_client *gsupc, struct msgb *msg)
{
struct osmo_gsup_message gsup_msg = {0};
int rc;
rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg);
if (rc < 0) {
LOGP(DMAIN, LOGL_ERROR, "Error decoding GSUP: %s\n", msgb_hexdump(msg));
return rc;
}
DEBUGP(DMAIN, "Rx GSUP %s: %s\n", osmo_gsup_message_type_name(gsup_msg.message_type),
msgb_hexdump(msg));
//if (strlen(gsup_msg.imsi) < 5)
//return gsup_send_err_reply(gsupc, gsup.imsi, gsup.message_type, GMM_CAUSE_INV_MAND_INFO);
switch (gsup_msg.message_type) {
case OSMO_GSUP_MSGT_PROC_SS_REQUEST:
case OSMO_GSUP_MSGT_PROC_SS_RESULT:
euse_rx_proc_ss_req(gsupc, &gsup_msg);
break;
case OSMO_GSUP_MSGT_PROC_SS_ERROR:
break;
default:
LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
osmo_gsup_message_type_name(gsup_msg.message_type));
break;
}
msgb_free(msg);
return 0;
}
static struct log_info_cat default_categories[] = {
[DMAIN] = {
.name = "DMAIN",
.description = "Main Program",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
};
static const struct log_info gsup_log_info = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};
int main(int argc, char **argv)
{
char *server_host = "127.0.0.1";
uint16_t server_port = OSMO_GSUP_PORT;
void *ctx = talloc_named_const(NULL, 0, "demo-euse");
osmo_init_logging2(ctx, &gsup_log_info);
g_gc = osmo_gsup_client_create(ctx, "EUSE-foobar", server_host, server_port, gsupc_read_cb, NULL);
while (1) {
osmo_select_main(0);
}
exit(0);
}

View File

@@ -2,6 +2,7 @@ SUBDIRS = \
auc \
gsup_server \
db \
gsup \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
@@ -51,10 +52,9 @@ VTY_TEST_DB = hlr_vty_test.db
# make vty-test U=-u
vty-test:
-rm -f $(VTY_TEST_DB)
sqlite3 $(VTY_TEST_DB) < $(top_srcdir)/sql/hlr.sql
osmo_verify_transcript_vty.py -v \
-n OsmoHLR -p 4258 \
-r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l hlr_vty_test.db" \
-r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l $(VTY_TEST_DB)" \
$(U) $(srcdir)/*.vty
-rm -f $(VTY_TEST_DB)
@@ -69,7 +69,7 @@ ctrl-test:
sqlite3 $(CTRL_TEST_DB) < $(srcdir)/test_subscriber.sql
osmo_verify_transcript_ctrl.py -v \
-p 4259 \
-r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l hlr_ctrl_test.db" \
-r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l $(CTRL_TEST_DB)" \
$(U) $(srcdir)/*.ctrl
-rm -f $(CTRL_TEST_DB)

View File

@@ -610,11 +610,14 @@ int main(int argc, char **argv)
handle_options(argc, argv);
osmo_init_logging(&hlr_log_info);
void *tall_ctx = talloc_named_const(NULL, 1, "auc_test");
osmo_init_logging2(tall_ctx, &hlr_log_info);
log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
test_gen_vectors_2g_only();
test_gen_vectors_2g_plus_3g();

View File

@@ -35,7 +35,7 @@ DAUC vector [0]: ck = f64735036e5871319c679f4742a75ea1
DAUC vector [0]: ik = 27497388b6cb044648f396aa155b95ef
DAUC vector [0]: res = e229c19e791f2e410000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: calculating 2G separately
DAUC vector [0]: kc = 241a5b16aeb8e400
DAUC vector [0]: sres = 429d5b27
DAUC vector [0]: auth_types = 0x3
@@ -55,7 +55,7 @@ DAUC vector [0]: ck = f64735036e5871319c679f4742a75ea1
DAUC vector [0]: ik = 27497388b6cb044648f396aa155b95ef
DAUC vector [0]: res = e229c19e791f2e410000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: calculating 2G separately
DAUC vector [0]: kc = 241a5b16aeb8e400
DAUC vector [0]: sres = 429d5b27
DAUC vector [0]: auth_types = 0x3
@@ -78,6 +78,7 @@ DAUC vector [0]: ck = f64735036e5871319c679f4742a75ea1
DAUC vector [0]: ik = 27497388b6cb044648f396aa155b95ef
DAUC vector [0]: res = e229c19e791f2e410000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 059a4f668f6fbe39
DAUC vector [0]: sres = 9b36efdf
DAUC vector [0]: auth_types = 0x3
@@ -96,6 +97,7 @@ DAUC vector [0]: ck = f64735036e5871319c679f4742a75ea1
DAUC vector [0]: ik = 27497388b6cb044648f396aa155b95ef
DAUC vector [0]: res = e229c19e791f2e410000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 059a4f668f6fbe39
DAUC vector [0]: sres = 9b36efdf
DAUC vector [0]: auth_types = 0x3
@@ -117,6 +119,7 @@ DAUC vector [0]: ck = e9922bd036718ed9e40bd1d02c3b81a5
DAUC vector [0]: ik = f19c20ca863137f8892326d959ec5e01
DAUC vector [0]: res = 9af5a557902d2db80000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 7526fc13c5976685
DAUC vector [0]: sres = 0ad888ef
DAUC vector [0]: auth_types = 0x3
@@ -137,6 +140,7 @@ DAUC vector [0]: ck = e9922bd036718ed9e40bd1d02c3b81a5
DAUC vector [0]: ik = f19c20ca863137f8892326d959ec5e01
DAUC vector [0]: res = 9af5a557902d2db80000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 7526fc13c5976685
DAUC vector [0]: sres = 0ad888ef
DAUC vector [0]: auth_types = 0x3
@@ -147,6 +151,7 @@ DAUC vector [1]: ck = 3686f05df057d1899c66ae4eb18cf941
DAUC vector [1]: ik = 79f21ed53bcb47787de57d136ff803a5
DAUC vector [1]: res = 43023475cb29292c0000000000000000
DAUC vector [1]: res_len = 8
DAUC vector [1]: deriving 2G from 3G
DAUC vector [1]: kc = aef73dd515e86c15
DAUC vector [1]: sres = 882b1d59
DAUC vector [1]: auth_types = 0x3
@@ -157,6 +162,7 @@ DAUC vector [2]: ck = d86c3191a36fc0602e48202ef2080964
DAUC vector [2]: ik = 648dab72016181406243420649e63dc9
DAUC vector [2]: res = 010cab11cc63a6e40000000000000000
DAUC vector [2]: res_len = 8
DAUC vector [2]: deriving 2G from 3G
DAUC vector [2]: kc = f0eaf8cb19e0758d
DAUC vector [2]: sres = cd6f0df5
DAUC vector [2]: auth_types = 0x3
@@ -179,6 +185,7 @@ DAUC vector [0]: ck = e9922bd036718ed9e40bd1d02c3b81a5
DAUC vector [0]: ik = f19c20ca863137f8892326d959ec5e01
DAUC vector [0]: res = 9af5a557902d2db80000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 7526fc13c5976685
DAUC vector [0]: sres = 0ad888ef
DAUC vector [0]: auth_types = 0x3
@@ -189,6 +196,7 @@ DAUC vector [1]: ck = 3686f05df057d1899c66ae4eb18cf941
DAUC vector [1]: ik = 79f21ed53bcb47787de57d136ff803a5
DAUC vector [1]: res = 43023475cb29292c0000000000000000
DAUC vector [1]: res_len = 8
DAUC vector [1]: deriving 2G from 3G
DAUC vector [1]: kc = aef73dd515e86c15
DAUC vector [1]: sres = 882b1d59
DAUC vector [1]: auth_types = 0x3
@@ -199,6 +207,7 @@ DAUC vector [2]: ck = d86c3191a36fc0602e48202ef2080964
DAUC vector [2]: ik = 648dab72016181406243420649e63dc9
DAUC vector [2]: res = 010cab11cc63a6e40000000000000000
DAUC vector [2]: res_len = 8
DAUC vector [2]: deriving 2G from 3G
DAUC vector [2]: kc = f0eaf8cb19e0758d
DAUC vector [2]: sres = cd6f0df5
DAUC vector [2]: auth_types = 0x3

View File

@@ -12,6 +12,7 @@ DAUC vector [0]: ck = b40ba9a3c58b2a05bbf0d987b21bf8cb
DAUC vector [0]: ik = f769bcd751044604127672711c6d3441
DAUC vector [0]: res = a54211d5e3ba50bf0000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = eae4be823af9a08b
DAUC vector [0]: sres = 46f8416a
DAUC vector [0]: auth_types = 0x3
@@ -34,6 +35,7 @@ DAUC vector [0]: ck = 5dbdbb2954e8f3cde665b046179a5098
DAUC vector [0]: ik = 59a92d3b476a0443487055cf88b2307b
DAUC vector [0]: res = 8011c48c0c214ed20000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = aa01739b8caa976d
DAUC vector [0]: sres = 8c308a5e
DAUC vector [0]: auth_types = 0x3
@@ -56,6 +58,7 @@ DAUC vector [0]: ck = e203edb3971574f5a94b0d61b816345d
DAUC vector [0]: ik = 0c4524adeac041c4dd830d20854fc46b
DAUC vector [0]: res = f365cd683cd92e960000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 9a8ec95f408cc507
DAUC vector [0]: sres = cfbce3fe
DAUC vector [0]: auth_types = 0x3
@@ -78,6 +81,7 @@ DAUC vector [0]: ck = 7657766b373d1c2138f307e3de9242f9
DAUC vector [0]: ik = 1c42e960d89b8fa99f2744e0708ccb53
DAUC vector [0]: res = 5860fc1bce351e7e0000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = cdc1dc0841b81a22
DAUC vector [0]: sres = 9655e265
DAUC vector [0]: auth_types = 0x3
@@ -100,6 +104,7 @@ DAUC vector [0]: ck = 3f8c7587fe8e4b233af676aede30ba3b
DAUC vector [0]: ik = a7466cc1e6b2a1337d49d3b66e95d7b4
DAUC vector [0]: res = 16c8233f05a0ac280000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = df75bc5ea899879f
DAUC vector [0]: sres = 13688f17
DAUC vector [0]: auth_types = 0x3
@@ -122,6 +127,7 @@ DAUC vector [0]: ck = 4cd0846020f8fa0731dd47cbdc6be411
DAUC vector [0]: ik = 88ab80a415f15c73711254a1d388f696
DAUC vector [0]: res = 8c25a16cd918a1df0000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 84b417ae3aeab4f3
DAUC vector [0]: sres = 553d00b3
DAUC vector [0]: auth_types = 0x3
@@ -144,6 +150,7 @@ DAUC vector [0]: ck = 10f05bab75a99a5fbb98a9c287679c3b
DAUC vector [0]: ik = f9ec0865eb32f22369cade40c59c3a44
DAUC vector [0]: res = a63241e1ffc3e5ab0000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 3b4e244cdc60ce03
DAUC vector [0]: sres = 59f1a44a
DAUC vector [0]: auth_types = 0x3
@@ -166,6 +173,7 @@ DAUC vector [0]: ck = 71236b7129f9b22ab77ea7a54c96da22
DAUC vector [0]: ik = 90527ebaa5588968db41727325a04d9e
DAUC vector [0]: res = 4a90b2171ac83a760000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 8d4ec01de597acfe
DAUC vector [0]: sres = 50588861
DAUC vector [0]: auth_types = 0x3
@@ -188,6 +196,7 @@ DAUC vector [0]: ck = 08cef6d004ec61471a3c3cda048137fa
DAUC vector [0]: ik = ed0318ca5deb9206272f6e8fa64ba411
DAUC vector [0]: res = 4bc2212d8624910a0000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = d8debc4ffbcd60aa
DAUC vector [0]: sres = cde6b027
DAUC vector [0]: auth_types = 0x3
@@ -210,6 +219,7 @@ DAUC vector [0]: ck = 69b1cae7c7429d975e245cacb05a517c
DAUC vector [0]: ik = 74f24e8c26df58e1b38d7dcd4f1b7fbd
DAUC vector [0]: res = 6fc30fee6d1235230000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = f0eaa50a1edcebb7
DAUC vector [0]: sres = 02d13acd
DAUC vector [0]: auth_types = 0x3
@@ -232,6 +242,7 @@ DAUC vector [0]: ck = 908c43f0569cb8f74bc971e706c36c5f
DAUC vector [0]: ik = c251df0d888dd9329bcf46655b226e40
DAUC vector [0]: res = aefa357beac2a87a0000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 82dbab7f83f063da
DAUC vector [0]: sres = 44389d01
DAUC vector [0]: auth_types = 0x3
@@ -254,6 +265,7 @@ DAUC vector [0]: ck = 44c0f23c5493cfd241e48f197e1d1012
DAUC vector [0]: ik = 0c9fb81613884c2535dd0eabf3b440d8
DAUC vector [0]: res = 98dbbd099b3b408d0000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 3c66cb98cab2d33d
DAUC vector [0]: sres = 03e0fd84
DAUC vector [0]: auth_types = 0x3
@@ -276,6 +288,7 @@ DAUC vector [0]: ck = 5af86b80edb70df5292cc1121cbad50c
DAUC vector [0]: ik = 7f4d6ae7440e18789a8b75ad3f42f03a
DAUC vector [0]: res = af4a411e1139f2c20000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 9612b5d88a4130bb
DAUC vector [0]: sres = be73b3dc
DAUC vector [0]: auth_types = 0x3
@@ -298,6 +311,7 @@ DAUC vector [0]: ck = 3f8c3f3ccf7625bf77fc94bcfd22fd26
DAUC vector [0]: ik = abcbae8fd46115e9961a55d0da5f2078
DAUC vector [0]: res = 7bffa5c2f41fbc050000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 75a150df3c6aed08
DAUC vector [0]: sres = 8fe019c7
DAUC vector [0]: auth_types = 0x3
@@ -320,6 +334,7 @@ DAUC vector [0]: ck = d42b2d615e49a03ac275a5aef97af892
DAUC vector [0]: ik = 0b3f8d024fe6bfafaa982b8f82e319c2
DAUC vector [0]: res = 7e3f44c7591f6f450000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = b7f92e426a36fec5
DAUC vector [0]: sres = 27202b82
DAUC vector [0]: auth_types = 0x3
@@ -342,6 +357,7 @@ DAUC vector [0]: ck = 6edaf99e5bd9f85d5f36d91c1272fb4b
DAUC vector [0]: ik = d61c853c280dd9c46f297baec386de17
DAUC vector [0]: res = 70f6bdb9ad21525f0000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 88d9de10a22004c5
DAUC vector [0]: sres = ddd7efe6
DAUC vector [0]: auth_types = 0x3
@@ -364,6 +380,7 @@ DAUC vector [0]: ck = 66195dbed0313274c5ca7766615fa25e
DAUC vector [0]: ik = 66bec707eb2afc476d7408a8f2927b36
DAUC vector [0]: res = 479dd25c20792d630000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = a819e577a8d6175b
DAUC vector [0]: sres = 67e4ff3f
DAUC vector [0]: auth_types = 0x3
@@ -386,6 +403,7 @@ DAUC vector [0]: ck = 5349fbe098649f948f5d2e973a81c00f
DAUC vector [0]: ik = 9744871ad32bf9bbd1dd5ce54e3e2e5a
DAUC vector [0]: res = 28d7b0f2a2ec3de50000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = 9a8d0e883ff0887a
DAUC vector [0]: sres = 8a3b8d17
DAUC vector [0]: auth_types = 0x3
@@ -408,6 +426,7 @@ DAUC vector [0]: ck = b5f2da03883b69f96bf52e029ed9ac45
DAUC vector [0]: ik = b4721368bc16ea67875c5598688bb0ef
DAUC vector [0]: res = a95100e2760952cd0000000000000000
DAUC vector [0]: res_len = 8
DAUC vector [0]: deriving 2G from 3G
DAUC vector [0]: kc = ed29b2f1c27f9f34
DAUC vector [0]: sres = df58522f
DAUC vector [0]: auth_types = 0x3

View File

@@ -29,6 +29,7 @@
#include <osmocom/core/application.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/msgb.h>
#include <osmocom/crypt/auth.h>
@@ -102,11 +103,14 @@ FUNCTIONS
int main()
{
printf("3GPP TS 55.205 Test Sets\n");
osmo_init_logging(&hlr_log_info);
void *tall_ctx = talloc_named_const(NULL, 1, "test");
msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &hlr_log_info);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
FUNCTION_CALLS

View File

@@ -1,19 +1,15 @@
AM_CPPFLAGS = \
AM_CFLAGS = \
$(all_includes) \
-I$(top_srcdir)/src \
$(NULL)
AM_CFLAGS = \
-I$(top_builddir)/src \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(SQLITE3_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(NULL)
EXTRA_DIST = \
db_test.ok \
db_test.err \
@@ -32,6 +28,7 @@ db_test_LDADD = \
$(top_srcdir)/src/logging.c \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(SQLITE3_LIBS) \
$(NULL)

View File

@@ -100,6 +100,22 @@ static void _fill_invalid(void *dest, size_t size)
fprintf(stderr, "\n"); \
} while (0)
#define N_VECTORS 3
#define ASSERT_DB_GET_AUC(imsi, expect_rc) \
do { \
struct osmo_auth_vector vec[N_VECTORS]; \
ASSERT_RC(db_get_auc(dbc, imsi, 3, vec, N_VECTORS, NULL, NULL), expect_rc); \
} while (0)
/* Not linking the real auc_compute_vectors(), just returning num_vec.
* This gets called by db_get_auc(), but we're only interested in its rc. */
int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
struct osmo_sub_auth_data *aud2g,
struct osmo_sub_auth_data *aud3g,
const uint8_t *rand_auts, const uint8_t *auts)
{ return num_vec; }
static struct db_context *dbc = NULL;
static void *ctx = NULL;
static struct hlr_subscriber g_subscr;
@@ -457,6 +473,7 @@ static void test_subscr_aud()
comment("Get auth data for non-existent subscriber");
ASSERT_SEL_AUD(unknown_imsi, -ENOENT, 0);
ASSERT_DB_GET_AUC(imsi0, -ENOENT);
comment("Create subscriber");
@@ -464,7 +481,8 @@ static void test_subscr_aud()
ASSERT_SEL(imsi, imsi0, 0);
id = g_subscr.id;
ASSERT_SEL_AUD(imsi0, -ENOENT, id);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_DB_GET_AUC(imsi0, -ENOKEY);
comment("Set auth data, 2G only");
@@ -473,6 +491,7 @@ static void test_subscr_aud()
mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_DB_GET_AUC(imsi0, N_VECTORS);
/* same again */
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
@@ -500,7 +519,8 @@ static void test_subscr_aud()
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)),
0);
ASSERT_SEL_AUD(imsi0, -ENOENT, id);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_DB_GET_AUC(imsi0, -ENOKEY);
/* Removing nothing results in -ENOENT */
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
@@ -515,7 +535,8 @@ static void test_subscr_aud()
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_NONE, "f000000000000f00000000000f000000")),
0);
ASSERT_SEL_AUD(imsi0, -ENOENT, id);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_DB_GET_AUC(imsi0, -ENOKEY);
comment("Set auth data, 3G only");
@@ -526,6 +547,7 @@ static void test_subscr_aud()
"C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_DB_GET_AUC(imsi0, N_VECTORS);
/* same again */
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
@@ -562,7 +584,8 @@ static void test_subscr_aud()
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)),
0);
ASSERT_SEL_AUD(imsi0, -ENOENT, id);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_DB_GET_AUC(imsi0, -ENOKEY);
/* Removing nothing results in -ENOENT */
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
@@ -575,13 +598,15 @@ static void test_subscr_aud()
"BeefedCafeFaceAcedAddedDecadeFee", 5)),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_DB_GET_AUC(imsi0, N_VECTORS);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_3g(OSMO_AUTH_ALG_NONE,
"asdfasdfasd", false,
"asdfasdfasdf", 99999)),
0);
ASSERT_SEL_AUD(imsi0, -ENOENT, id);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_DB_GET_AUC(imsi0, -ENOKEY);
comment("Set auth data, 2G and 3G");
@@ -595,6 +620,7 @@ static void test_subscr_aud()
"DeafBeddedBabeAcceededFadedDecaf", 5)),
0);
ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_DB_GET_AUC(imsi0, N_VECTORS);
comment("Set invalid auth data");
@@ -669,10 +695,12 @@ static void test_subscr_aud()
/* For this test to work, we want to get the same subscriber ID back,
* and make sure there are no auth data leftovers for this ID. */
OSMO_ASSERT(id == g_subscr.id);
ASSERT_SEL_AUD(imsi0, -ENOENT, id);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_DB_GET_AUC(imsi0, -ENOKEY);
ASSERT_RC(db_subscr_delete_by_id(dbc, id), 0);
ASSERT_SEL(imsi, imsi0, -ENOENT);
ASSERT_DB_GET_AUC(imsi0, -ENOENT);
comment_end();
}
@@ -697,15 +725,15 @@ static void test_subscr_sqn()
ASSERT_SEL(imsi, imsi0, 0);
id = g_subscr.id;
ASSERT_SEL_AUD(imsi0, -ENOENT, id);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
comment("Set SQN, but no 3G auth data present");
ASSERT_RC(db_update_sqn(dbc, id, 123), -ENOENT);
ASSERT_SEL_AUD(imsi0, -ENOENT, id);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
ASSERT_RC(db_update_sqn(dbc, id, 543), -ENOENT);
ASSERT_SEL_AUD(imsi0, -ENOENT, id);
ASSERT_SEL_AUD(imsi0, -ENOKEY, id);
comment("Set auth 3G data");
@@ -811,15 +839,18 @@ int main(int argc, char **argv)
handle_options(argc, argv);
osmo_init_logging(&hlr_log_info);
osmo_init_logging2(ctx, &hlr_log_info);
log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
/* omit the SQLite version and compilation flags from test output */
log_set_log_level(osmo_stderr_target, LOGL_ERROR);
dbc = db_open(ctx, "db_test.db");
/* 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);
log_set_log_level(osmo_stderr_target, 0);
OSMO_ASSERT(dbc);
@@ -832,11 +863,6 @@ int main(int argc, char **argv)
}
/* stubs */
int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
struct osmo_sub_auth_data *aud2g,
struct osmo_sub_auth_data *aud3g,
const uint8_t *rand_auts, const uint8_t *auts)
{ OSMO_ASSERT(false); return -1; }
void *lu_op_alloc_conn(void *conn)
{ OSMO_ASSERT(false); return NULL; }
void lu_op_tx_del_subscr_data(void *luop)

View File

@@ -28,7 +28,6 @@ struct hlr_subscriber {
}
db_subscr_create(dbc, imsi0) --> -EIO
DDB (2067) abort at 18 in [INSERT INTO subscriber (imsi) VALUES ($imsi)]: UNIQUE constraint failed: subscriber.imsi
DAUC IMSI='123456789000000': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
@@ -38,11 +37,9 @@ struct hlr_subscriber {
}
db_subscr_create(dbc, imsi1) --> -EIO
DDB (2067) abort at 18 in [INSERT INTO subscriber (imsi) VALUES ($imsi)]: UNIQUE constraint failed: subscriber.imsi
DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
db_subscr_create(dbc, imsi1) --> -EIO
DDB (2067) abort at 18 in [INSERT INTO subscriber (imsi) VALUES ($imsi)]: UNIQUE constraint failed: subscriber.imsi
DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
db_subscr_get_by_imsi(dbc, imsi1, &g_subscr) --> 0
@@ -52,11 +49,9 @@ struct hlr_subscriber {
}
db_subscr_create(dbc, imsi2) --> -EIO
DDB (2067) abort at 18 in [INSERT INTO subscriber (imsi) VALUES ($imsi)]: UNIQUE constraint failed: subscriber.imsi
DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
db_subscr_create(dbc, imsi2) --> -EIO
DDB (2067) abort at 18 in [INSERT INTO subscriber (imsi) VALUES ($imsi)]: UNIQUE constraint failed: subscriber.imsi
DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> 0
@@ -720,6 +715,9 @@ db_get_auth_data(dbc, unknown_imsi, &g_aud2g, &g_aud3g, &g_id) --> -2
DAUC IMSI='999999999': No such subscriber
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -2
DAUC IMSI='123456789000000': No such subscriber
--- Create subscriber
@@ -731,11 +729,15 @@ struct hlr_subscriber {
.imsi = '123456789000000',
}
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -2
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
--- Set auth data, 2G only
@@ -751,6 +753,11 @@ DAUC IMSI='123456789000000': No 3G Auth Data
}
3G: none
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> 3
DAUC IMSI='123456789000000': No 3G Auth Data
DAUC IMSI='123456789000000': Calling to generate 3 vectors
DAUC IMSI='123456789000000': Generated 3 vectors
db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")) --> 0
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
@@ -804,11 +811,15 @@ DAUC IMSI='123456789000000': No 3G Auth Data
db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)) --> 0
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -2
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)) --> -ENOENT
db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")) --> 0
@@ -825,11 +836,15 @@ DAUC IMSI='123456789000000': No 3G Auth Data
db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_NONE, "f000000000000f00000000000f000000")) --> 0
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -2
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
--- Set auth data, 3G only
@@ -849,6 +864,12 @@ DAUC IMSI='123456789000000': No 2G Auth Data
.u.umts.ind_bitlen = 5,
}
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> 3
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': Calling to generate 3 vectors
DAUC IMSI='123456789000000': Generated 3 vectors
DAUC IMSI='123456789000000': Updating SQN=0 in DB
db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "BeefedCafeFaceAcedAddedDecadeFee", true, "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)) --> 0
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
@@ -917,11 +938,15 @@ DAUC IMSI='123456789000000': No 2G Auth Data
db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)) --> 0
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -2
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)) --> -ENOENT
db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "CededEffacedAceFacedBadFadedBeef", false, "BeefedCafeFaceAcedAddedDecadeFee", 5)) --> 0
@@ -940,13 +965,23 @@ DAUC IMSI='123456789000000': No 2G Auth Data
.u.umts.ind_bitlen = 5,
}
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> 3
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': Calling to generate 3 vectors
DAUC IMSI='123456789000000': Generated 3 vectors
DAUC IMSI='123456789000000': Updating SQN=0 in DB
db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_NONE, "asdfasdfasd", false, "asdfasdfasdf", 99999)) --> 0
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -2
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
--- Set auth data, 2G and 3G
@@ -971,6 +1006,11 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
.u.umts.ind_bitlen = 5,
}
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> 3
DAUC IMSI='123456789000000': Calling to generate 3 vectors
DAUC IMSI='123456789000000': Generated 3 vectors
DAUC IMSI='123456789000000': Updating SQN=0 in DB
--- Set invalid auth data
@@ -1179,16 +1219,23 @@ struct hlr_subscriber {
.imsi = '123456789000000',
}
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -2
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
db_subscr_delete_by_id(dbc, id) --> 0
db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT
DAUC Cannot read subscriber from db: IMSI='123456789000000': No such subscriber
db_get_auc(dbc, imsi0, 3, vec, N_VECTORS, NULL, NULL) --> -2
DAUC IMSI='123456789000000': No such subscriber
===== test_subscr_aud: SUCCESS
@@ -1219,7 +1266,7 @@ struct hlr_subscriber {
.imsi = '123456789000000',
}
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -2
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
@@ -1230,7 +1277,7 @@ DAUC IMSI='123456789000000': No 3G Auth Data
db_update_sqn(dbc, id, 123) --> -ENOENT
DAUC Cannot update SQN for subscriber ID=1: no auc_3g entry for such subscriber
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -2
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data
@@ -1238,7 +1285,7 @@ DAUC IMSI='123456789000000': No 3G Auth Data
db_update_sqn(dbc, id, 543) --> -ENOENT
DAUC Cannot update SQN for subscriber ID=1: no auc_3g entry for such subscriber
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -2
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -126
DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': No 3G Auth Data

41
tests/gsup/Makefile.am Normal file
View File

@@ -0,0 +1,41 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/src \
$(NULL)
AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(NULL)
EXTRA_DIST = \
gsup_test.ok \
gsup_test.err \
$(NULL)
noinst_PROGRAMS = \
gsup_test \
$(NULL)
gsup_test_SOURCES = \
gsup_test.c \
$(NULL)
gsup_test_LDADD = \
$(top_srcdir)/src/luop.c \
$(top_srcdir)/src/gsup_server.c \
$(top_srcdir)/src/gsup_router.c \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(NULL)
.PHONY: update_exp
update_exp:
$(builddir)/gsup_test >"$(srcdir)/gsup_test.ok" 2>"$(srcdir)/gsup_test.err"

91
tests/gsup/gsup_test.c Normal file
View File

@@ -0,0 +1,91 @@
/* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/gsm/gsup.h>
#include "logging.h"
#include "luop.h"
struct osmo_gsup_server;
/* override osmo_gsup_addr_send() to not actually send anything. */
int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
const uint8_t *addr, size_t addrlen,
struct msgb *msg)
{
LOGP(DMAIN, LOGL_DEBUG, "%s\n", msgb_hexdump(msg));
msgb_free(msg);
return 0;
}
int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
struct hlr_subscriber *subscr)
{
return 0;
}
/* Verify that the internally allocated msgb is large enough */
void test_gsup_tx_insert_subscr_data()
{
struct lu_operation luop = {
.state = LU_S_LU_RECEIVED,
.subscr = {
.imsi = "123456789012345",
.msisdn = "987654321098765",
.nam_cs = true,
.nam_ps = true,
},
.is_ps = true,
};
lu_op_tx_insert_subscr_data(&luop);
}
const struct log_info_cat default_categories[] = {
[DMAIN] = {
.name = "DMAIN",
.description = "Main Program",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
};
static struct log_info info = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};
int main(int argc, char **argv)
{
void *ctx = talloc_named_const(NULL, 0, "gsup_test");
osmo_init_logging2(ctx, &info);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
test_gsup_tx_insert_subscr_data();
printf("Done.\n");
return EXIT_SUCCESS;
}

2
tests/gsup/gsup_test.err Normal file
View File

@@ -0,0 +1,2 @@
DMAIN 10 01 08 21 43 65 87 09 21 43 f5 08 09 08 89 67 45 23 01 89 67 f5 05 07 10 01 01 12 02 01 2a 28 01 01
DMAIN LU OP state change: LU RECEIVED -> ISD SENT

1
tests/gsup/gsup_test.ok Normal file
View File

@@ -0,0 +1 @@
Done.

View File

@@ -13,6 +13,10 @@ OsmoHLR> list
...
show logging vty
show alarms
show talloc-context (application|all) (full|brief|DEPTH)
show talloc-context (application|all) (full|brief|DEPTH) tree ADDRESS
show talloc-context (application|all) (full|brief|DEPTH) filter REGEXP
show gsup-connections
subscriber (imsi|msisdn|id) IDENT show
OsmoHLR> enable
@@ -100,10 +104,11 @@ log stderr
logging color 1
logging print category 1
logging print extended-timestamp 1
logging print file 1
logging level all debug
logging level main debug
logging level db debug
logging level auc debug
logging level main notice
logging level db notice
logging level auc notice
...
!
line vty

View File

@@ -1,27 +1,614 @@
GET 1 invalid
ERROR 1 Command not found
SET 2 invalid nonsense
ERROR 2 Command not found
GET 1 subscriber.by-imsi-901990000000001.info
GET_REPLY 1 subscriber.by-imsi-901990000000001.info
id 1
imsi 901990000000001
msisdn 1
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
SET 3 enable-ps 901990000000001
SET_REPLY 3 enable-ps OK
SET 4 status-ps 901990000000001
SET_REPLY 4 status-ps 1
SET 5 enable-ps 901990000000001
SET_REPLY 5 enable-ps OK
SET 6 status-ps 901990000000001
SET_REPLY 6 status-ps 1
GET 2 subscriber.by-imsi-901990000000001.info-aud
GET_REPLY 2 subscriber.by-imsi-901990000000001.info-aud
aud2g.algo COMP128v1
aud2g.ki 000102030405060708090a0b0c0d0e0f
SET 7 disable-ps 901990000000001
SET_REPLY 7 disable-ps OK
SET 8 status-ps 901990000000001
SET_REPLY 8 status-ps 0
SET 9 disable-ps 901990000000001
SET_REPLY 9 disable-ps OK
SET 10 status-ps 901990000000001
SET_REPLY 10 status-ps 0
GET 3 subscriber.by-imsi-901990000000001.info-all
GET_REPLY 3 subscriber.by-imsi-901990000000001.info-all
id 1
imsi 901990000000001
msisdn 1
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
SET 11 enable-ps 901990000000001
SET_REPLY 11 enable-ps OK
SET 12 status-ps 901990000000001
SET_REPLY 12 status-ps 1
GET 4 subscriber.by-imsi-901990000000002.info
GET_REPLY 4 subscriber.by-imsi-901990000000002.info
id 2
imsi 901990000000002
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 5 subscriber.by-imsi-901990000000002.info-aud
GET_REPLY 5 subscriber.by-imsi-901990000000002.info-aud
aud3g.algo MILENAGE
aud3g.k 000102030405060708090a0b0c0d0e0f
aud3g.opc 101112131415161718191a1b1c1d1e1f
aud3g.ind_bitlen 5
aud3g.sqn 4223
GET 6 subscriber.by-imsi-901990000000002.info-all
GET_REPLY 6 subscriber.by-imsi-901990000000002.info-all
id 2
imsi 901990000000002
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
aud3g.algo MILENAGE
aud3g.k 000102030405060708090a0b0c0d0e0f
aud3g.opc 101112131415161718191a1b1c1d1e1f
aud3g.ind_bitlen 5
aud3g.sqn 4223
GET 7 subscriber.by-imsi-901990000000003.info
GET_REPLY 7 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 8 subscriber.by-imsi-901990000000003.info-aud
GET_REPLY 8 subscriber.by-imsi-901990000000003.info-aud
aud2g.algo COMP128v1
aud2g.ki 000102030405060708090a0b0c0d0e0f
aud3g.algo MILENAGE
aud3g.k 000102030405060708090a0b0c0d0e0f
aud3g.opc 101112131415161718191a1b1c1d1e1f
aud3g.ind_bitlen 5
aud3g.sqn 2342
GET 9 subscriber.by-imsi-901990000000003.info-all
GET_REPLY 9 subscriber.by-imsi-901990000000003.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 2342
GET 10 subscriber.by-imsi-901990000000003.ps-enabled
GET_REPLY 10 subscriber.by-imsi-901990000000003.ps-enabled 1
SET 11 subscriber.by-imsi-901990000000003.ps-enabled 0
SET_REPLY 11 subscriber.by-imsi-901990000000003.ps-enabled OK
GET 12 subscriber.by-imsi-901990000000003.ps-enabled
GET_REPLY 12 subscriber.by-imsi-901990000000003.ps-enabled 0
GET 13 subscriber.by-imsi-901990000000003.info
GET_REPLY 13 subscriber.by-imsi-901990000000003.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 14 subscriber.by-imsi-901990000000003.ps-enabled 0
SET_REPLY 14 subscriber.by-imsi-901990000000003.ps-enabled OK
GET 15 subscriber.by-imsi-901990000000003.ps-enabled
GET_REPLY 15 subscriber.by-imsi-901990000000003.ps-enabled 0
SET 16 subscriber.by-imsi-901990000000003.ps-enabled 1
SET_REPLY 16 subscriber.by-imsi-901990000000003.ps-enabled OK
GET 17 subscriber.by-imsi-901990000000003.ps-enabled
GET_REPLY 17 subscriber.by-imsi-901990000000003.ps-enabled 1
GET 18 subscriber.by-imsi-901990000000003.info
GET_REPLY 18 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
SET 19 subscriber.by-imsi-901990000000003.ps-enabled 1
SET_REPLY 19 subscriber.by-imsi-901990000000003.ps-enabled OK
GET 20 subscriber.by-imsi-901990000000003.ps-enabled
GET_REPLY 20 subscriber.by-imsi-901990000000003.ps-enabled 1
GET 21 subscriber.by-imsi-901990000000003.cs-enabled
GET_REPLY 21 subscriber.by-imsi-901990000000003.cs-enabled 1
SET 22 subscriber.by-imsi-901990000000003.cs-enabled 0
SET_REPLY 22 subscriber.by-imsi-901990000000003.cs-enabled OK
GET 23 subscriber.by-imsi-901990000000003.cs-enabled
GET_REPLY 23 subscriber.by-imsi-901990000000003.cs-enabled 0
GET 24 subscriber.by-imsi-901990000000003.info
GET_REPLY 24 subscriber.by-imsi-901990000000003.info
id 3
imsi 901990000000003
msisdn 103
nam_cs 0
nam_ps 1
ms_purged_cs 0
ms_purged_ps 0
periodic_lu_timer 0
periodic_rau_tau_timer 0
lmsi 00000000
SET 25 subscriber.by-imsi-901990000000003.cs-enabled 0
SET_REPLY 25 subscriber.by-imsi-901990000000003.cs-enabled OK
GET 26 subscriber.by-imsi-901990000000003.cs-enabled
GET_REPLY 26 subscriber.by-imsi-901990000000003.cs-enabled 0
SET 27 subscriber.by-imsi-901990000000003.cs-enabled 1
SET_REPLY 27 subscriber.by-imsi-901990000000003.cs-enabled OK
GET 28 subscriber.by-imsi-901990000000003.cs-enabled
GET_REPLY 28 subscriber.by-imsi-901990000000003.cs-enabled 1
GET 29 subscriber.by-imsi-901990000000003.info
GET_REPLY 29 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
SET 30 subscriber.by-imsi-901990000000003.cs-enabled 1
SET_REPLY 30 subscriber.by-imsi-901990000000003.cs-enabled OK
GET 31 subscriber.by-imsi-901990000000003.cs-enabled
GET_REPLY 31 subscriber.by-imsi-901990000000003.cs-enabled 1
SET 32 subscriber.by-imsi-901990000000003.ps-enabled 0
SET_REPLY 32 subscriber.by-imsi-901990000000003.ps-enabled OK
SET 33 subscriber.by-imsi-901990000000003.cs-enabled 0
SET_REPLY 33 subscriber.by-imsi-901990000000003.cs-enabled OK
GET 34 subscriber.by-imsi-901990000000003.info
GET_REPLY 34 subscriber.by-imsi-901990000000003.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 35 subscriber.by-imsi-901990000000003.ps-enabled 1
SET_REPLY 35 subscriber.by-imsi-901990000000003.ps-enabled OK
SET 36 subscriber.by-imsi-901990000000003.cs-enabled 1
SET_REPLY 36 subscriber.by-imsi-901990000000003.cs-enabled OK
GET 37 subscriber.by-imsi-901990000000003.info
GET_REPLY 37 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 38 subscriber.by-msisdn-103.info
GET_REPLY 38 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 39 subscriber.by-msisdn-103.info-aud
GET_REPLY 39 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 2342
GET 40 subscriber.by-msisdn-103.info-all
GET_REPLY 40 subscriber.by-msisdn-103.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 2342
GET 41 subscriber.by-msisdn-103.ps-enabled
GET_REPLY 41 subscriber.by-msisdn-103.ps-enabled 1
SET 42 subscriber.by-msisdn-103.ps-enabled 0
SET_REPLY 42 subscriber.by-msisdn-103.ps-enabled OK
GET 43 subscriber.by-msisdn-103.ps-enabled
GET_REPLY 43 subscriber.by-msisdn-103.ps-enabled 0
GET 44 subscriber.by-msisdn-103.info
GET_REPLY 44 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 45 subscriber.by-msisdn-103.ps-enabled 0
SET_REPLY 45 subscriber.by-msisdn-103.ps-enabled OK
GET 46 subscriber.by-msisdn-103.ps-enabled
GET_REPLY 46 subscriber.by-msisdn-103.ps-enabled 0
SET 47 subscriber.by-msisdn-103.ps-enabled 1
SET_REPLY 47 subscriber.by-msisdn-103.ps-enabled OK
GET 48 subscriber.by-msisdn-103.ps-enabled
GET_REPLY 48 subscriber.by-msisdn-103.ps-enabled 1
GET 49 subscriber.by-msisdn-103.info
GET_REPLY 49 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
SET 50 subscriber.by-msisdn-103.ps-enabled 1
SET_REPLY 50 subscriber.by-msisdn-103.ps-enabled OK
GET 51 subscriber.by-msisdn-103.ps-enabled
GET_REPLY 51 subscriber.by-msisdn-103.ps-enabled 1
GET 52 subscriber.by-msisdn-103.cs-enabled
GET_REPLY 52 subscriber.by-msisdn-103.cs-enabled 1
SET 53 subscriber.by-msisdn-103.cs-enabled 0
SET_REPLY 53 subscriber.by-msisdn-103.cs-enabled OK
GET 54 subscriber.by-msisdn-103.cs-enabled
GET_REPLY 54 subscriber.by-msisdn-103.cs-enabled 0
GET 55 subscriber.by-msisdn-103.info
GET_REPLY 55 subscriber.by-msisdn-103.info
id 3
imsi 901990000000003
msisdn 103
nam_cs 0
nam_ps 1
ms_purged_cs 0
ms_purged_ps 0
periodic_lu_timer 0
periodic_rau_tau_timer 0
lmsi 00000000
SET 56 subscriber.by-msisdn-103.cs-enabled 0
SET_REPLY 56 subscriber.by-msisdn-103.cs-enabled OK
GET 57 subscriber.by-msisdn-103.cs-enabled
GET_REPLY 57 subscriber.by-msisdn-103.cs-enabled 0
SET 58 subscriber.by-msisdn-103.cs-enabled 1
SET_REPLY 58 subscriber.by-msisdn-103.cs-enabled OK
GET 59 subscriber.by-msisdn-103.cs-enabled
GET_REPLY 59 subscriber.by-msisdn-103.cs-enabled 1
GET 60 subscriber.by-msisdn-103.info
GET_REPLY 60 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
SET 61 subscriber.by-msisdn-103.cs-enabled 1
SET_REPLY 61 subscriber.by-msisdn-103.cs-enabled OK
GET 62 subscriber.by-msisdn-103.cs-enabled
GET_REPLY 62 subscriber.by-msisdn-103.cs-enabled 1
SET 63 subscriber.by-msisdn-103.ps-enabled 0
SET_REPLY 63 subscriber.by-msisdn-103.ps-enabled OK
SET 64 subscriber.by-msisdn-103.cs-enabled 0
SET_REPLY 64 subscriber.by-msisdn-103.cs-enabled OK
GET 65 subscriber.by-msisdn-103.info
GET_REPLY 65 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 66 subscriber.by-msisdn-103.ps-enabled 1
SET_REPLY 66 subscriber.by-msisdn-103.ps-enabled OK
SET 67 subscriber.by-msisdn-103.cs-enabled 1
SET_REPLY 67 subscriber.by-msisdn-103.cs-enabled OK
GET 68 subscriber.by-msisdn-103.info
GET_REPLY 68 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 69 subscriber.by-id-3.info
GET_REPLY 69 subscriber.by-id-3.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 70 subscriber.by-id-3.info-aud
GET_REPLY 70 subscriber.by-id-3.info-aud
aud2g.algo COMP128v1
aud2g.ki 000102030405060708090a0b0c0d0e0f
aud3g.algo MILENAGE
aud3g.k 000102030405060708090a0b0c0d0e0f
aud3g.opc 101112131415161718191a1b1c1d1e1f
aud3g.ind_bitlen 5
aud3g.sqn 2342
GET 71 subscriber.by-id-3.info-all
GET_REPLY 71 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 2342
GET 72 subscriber.by-id-3.ps-enabled
GET_REPLY 72 subscriber.by-id-3.ps-enabled 1
SET 73 subscriber.by-id-3.ps-enabled 0
SET_REPLY 73 subscriber.by-id-3.ps-enabled OK
GET 74 subscriber.by-id-3.ps-enabled
GET_REPLY 74 subscriber.by-id-3.ps-enabled 0
GET 75 subscriber.by-id-3.info
GET_REPLY 75 subscriber.by-id-3.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 76 subscriber.by-id-3.ps-enabled 0
SET_REPLY 76 subscriber.by-id-3.ps-enabled OK
GET 77 subscriber.by-id-3.ps-enabled
GET_REPLY 77 subscriber.by-id-3.ps-enabled 0
SET 78 subscriber.by-id-3.ps-enabled 1
SET_REPLY 78 subscriber.by-id-3.ps-enabled OK
GET 79 subscriber.by-id-3.ps-enabled
GET_REPLY 79 subscriber.by-id-3.ps-enabled 1
GET 80 subscriber.by-id-3.info
GET_REPLY 80 subscriber.by-id-3.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
SET 81 subscriber.by-id-3.ps-enabled 1
SET_REPLY 81 subscriber.by-id-3.ps-enabled OK
GET 82 subscriber.by-id-3.ps-enabled
GET_REPLY 82 subscriber.by-id-3.ps-enabled 1
GET 83 subscriber.by-id-3.cs-enabled
GET_REPLY 83 subscriber.by-id-3.cs-enabled 1
SET 84 subscriber.by-id-3.cs-enabled 0
SET_REPLY 84 subscriber.by-id-3.cs-enabled OK
GET 85 subscriber.by-id-3.cs-enabled
GET_REPLY 85 subscriber.by-id-3.cs-enabled 0
GET 86 subscriber.by-id-3.info
GET_REPLY 86 subscriber.by-id-3.info
id 3
imsi 901990000000003
msisdn 103
nam_cs 0
nam_ps 1
ms_purged_cs 0
ms_purged_ps 0
periodic_lu_timer 0
periodic_rau_tau_timer 0
lmsi 00000000
SET 87 subscriber.by-id-3.cs-enabled 0
SET_REPLY 87 subscriber.by-id-3.cs-enabled OK
GET 88 subscriber.by-id-3.cs-enabled
GET_REPLY 88 subscriber.by-id-3.cs-enabled 0
SET 89 subscriber.by-id-3.cs-enabled 1
SET_REPLY 89 subscriber.by-id-3.cs-enabled OK
GET 90 subscriber.by-id-3.cs-enabled
GET_REPLY 90 subscriber.by-id-3.cs-enabled 1
GET 91 subscriber.by-id-3.info
GET_REPLY 91 subscriber.by-id-3.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
SET 92 subscriber.by-id-3.cs-enabled 1
SET_REPLY 92 subscriber.by-id-3.cs-enabled OK
GET 93 subscriber.by-id-3.cs-enabled
GET_REPLY 93 subscriber.by-id-3.cs-enabled 1
SET 94 subscriber.by-id-3.ps-enabled 0
SET_REPLY 94 subscriber.by-id-3.ps-enabled OK
SET 95 subscriber.by-id-3.cs-enabled 0
SET_REPLY 95 subscriber.by-id-3.cs-enabled OK
GET 96 subscriber.by-id-3.info
GET_REPLY 96 subscriber.by-id-3.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 97 subscriber.by-id-3.ps-enabled 1
SET_REPLY 97 subscriber.by-id-3.ps-enabled OK
SET 98 subscriber.by-id-3.cs-enabled 1
SET_REPLY 98 subscriber.by-id-3.cs-enabled OK
GET 99 subscriber.by-id-3.info
GET_REPLY 99 subscriber.by-id-3.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 100 subscriber.by-id-00123.info
GET_REPLY 100 subscriber.by-id-00123.info
id 123
imsi 123123
msisdn 123
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 101 subscriber.by-id-0x0123.info
ERROR 101 Invalid value part of 'by-xxx-value' selector.

View File

@@ -1,13 +1,17 @@
-- 2G only subscriber
INSERT INTO subscriber (id, imsi) VALUES (1, '901990000000001');
INSERT INTO subscriber (id, imsi, msisdn) VALUES (1, '901990000000001', '1');
INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki) VALUES (1, 1, '000102030405060708090a0b0c0d0e0f');
-- 3G only subscriber
INSERT INTO subscriber (id, imsi) VALUES (2, '901990000000002');
INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, opc, sqn) VALUES (2, 5, '000102030405060708090a0b0c0d0e0f', '101112131415161718191a1b1c1d1e1f', 0);
INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, opc, sqn) VALUES (2, 5, '000102030405060708090a0b0c0d0e0f', '101112131415161718191a1b1c1d1e1f', 4223);
-- 2G + 3G subscriber
INSERT INTO subscriber (id, imsi) VALUES (3, '901990000000003');
INSERT INTO subscriber (id, imsi, msisdn) VALUES (3, '901990000000003', '103');
INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki) VALUES (3, 1, '000102030405060708090a0b0c0d0e0f');
INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, opc, sqn) VALUES (3, 5, '000102030405060708090a0b0c0d0e0f', '101112131415161718191a1b1c1d1e1f', 0);
INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, opc, sqn) VALUES (3, 5, '000102030405060708090a0b0c0d0e0f', '101112131415161718191a1b1c1d1e1f', 2342);
-- A subscriber id > 7 and > 15 to check against octal and hex notations
INSERT INTO subscriber (id, imsi, msisdn) VALUES (123, '123123', '123');
INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki) VALUES (123, 3, 'BeefedCafeFaceAcedAddedDecadeFee');

View File

@@ -305,6 +305,7 @@ OsmoHLR# subscriber imsi 123456789023000 show
OPC=cededeffacedacefacedbadfadedbeef
IND-bitlen=23
OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d op C01ffedC1cadaeAc1d1f1edAcac1aB0a
OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d op CoiffedCicadaeAcidifiedAcaciaBoa
% Invalid value for OP: 'CoiffedCicadaeAcidifiedAcaciaBoa'
OsmoHLR# subscriber imsi 123456789023000 show
@@ -313,8 +314,8 @@ OsmoHLR# subscriber imsi 123456789023000 show
MSISDN: 423
3G auth: MILENAGE
K=deaf0ff1ced0d0dabbedd1ced1cef00d
OPC=cededeffacedacefacedbadfadedbeef
IND-bitlen=23
OP=c01ffedc1cadaeac1d1f1edacac1ab0a
IND-bitlen=5
OsmoHLR# subscriber id 1 update aud2g comp128v2 ki CededEffacedAceFacedBadFadedBeef
OsmoHLR# subscriber id 1 show
@@ -325,8 +326,8 @@ OsmoHLR# subscriber id 1 show
KI=cededeffacedacefacedbadfadedbeef
3G auth: MILENAGE
K=deaf0ff1ced0d0dabbedd1ced1cef00d
OPC=cededeffacedacefacedbadfadedbeef
IND-bitlen=23
OP=c01ffedc1cadaeac1d1f1edacac1ab0a
IND-bitlen=5
OsmoHLR# subscriber imsi 123456789023000 delete
% Deleted subscriber for IMSI '123456789023000'

View File

@@ -0,0 +1,107 @@
GET 1 invalid
ERROR 1 Command not found
SET 2 invalid nonsense
ERROR 2 Command not found
GET 3 subscriber.by-imsi-nonsense.info
ERROR 3 Invalid value part of 'by-xxx-value' selector.
GET 4 subscriber.by-msisdn-nonsense.info
ERROR 4 Invalid value part of 'by-xxx-value' selector.
GET 5 subscriber.by-id-nonsense.info
ERROR 5 Invalid value part of 'by-xxx-value' selector.
GET 6 subscriber
ERROR 6 Command not present.
GET 7 subscriber.
ERROR 7 Command not present.
GET 8 subscriber.by-nonsense
ERROR 8 Command not present.
GET 9 subscriber.by-nonsense-
ERROR 9 Command not present.
GET 10 subscriber.by-nonsense-123456
ERROR 10 Command not present.
GET 11 subscriber.by-nonsense-123456.
ERROR 11 Command not present.
GET 12 subscriber.by-imsi-
ERROR 12 Command not present.
GET 13 subscriber.by-imsi-.
ERROR 13 Command not present.
GET 14 subscriber.by-imsi-901990000000003
ERROR 14 Command not present.
GET 15 subscriber.by-imsi-901990000000003.
ERROR 15 Command not present.
GET 16 subscriber.by-nonsense-123456.info
ERROR 16 Not a known subscriber 'by-xxx-' selector.
GET 17 subscriber.by-123456.info
ERROR 17 Not a known subscriber 'by-xxx-' selector.
GET 18 subscriber.by-imsi-.info
ERROR 18 Invalid value part of 'by-xxx-value' selector.
GET 19 subscriber.by-imsi--.info
ERROR 19 Invalid value part of 'by-xxx-value' selector.
GET 20 subscriber.by-imsi-12345678901234567.info
ERROR 20 Invalid value part of 'by-xxx-value' selector.
GET 21 subscriber.by-imsi-12345.info
ERROR 21 Invalid value part of 'by-xxx-value' selector.
GET 22 subscriber.by-imsi-1234567890123456.info
ERROR 22 Invalid value part of 'by-xxx-value' selector.
GET 23 subscriber.by-id-99999999999999999999999999.info
ERROR 23 Invalid value part of 'by-xxx-value' selector.
GET 24 subscriber.by-id-9223372036854775807.info
ERROR 24 No such subscriber.
GET 25 subscriber.by-id-9223372036854775808.info
ERROR 25 Invalid value part of 'by-xxx-value' selector.
GET 26 subscriber.by-id--1.info
ERROR 26 No such subscriber.
GET 27 subscriber.by-id--9223372036854775808.info
ERROR 27 No such subscriber.
GET 28 subscriber.by-id--9223372036854775809.info
ERROR 28 Invalid value part of 'by-xxx-value' selector.
GET 29 subscriber.by-id-1+1.info
ERROR 29 GET variable contains invalid characters
GET 30 subscriber.by-id--.info
ERROR 30 Invalid value part of 'by-xxx-value' selector.
GET 31 subscriber.by-id-+1.info
ERROR 31 GET variable contains invalid characters
GET 32 subscriber.by-id-+-1.info
ERROR 32 GET variable contains invalid characters
GET 33 subscriber.by-id--+1.info
ERROR 33 GET variable contains invalid characters
GET 34 subscriber.by-id-++1.info
ERROR 34 GET variable contains invalid characters
GET 35 subscriber.by-id---1.info
ERROR 35 Invalid value part of 'by-xxx-value' selector.
GET 36 subscriber.by-id- 1.info
ERROR 36 GET with trailing characters
GET 37 subscriber.by-id-+ 1.info
ERROR 37 GET variable contains invalid characters
GET 38 subscriber.by-id-- 1.info
ERROR 38 GET with trailing characters
SET 39 subscriber.by-imsi-901990000000001.info foo
ERROR 39 Read Only attribute
SET 40 subscriber.by-imsi-901990000000001.info-aud foo
ERROR 40 Read Only attribute
SET 41 subscriber.by-imsi-901990000000001.info-all foo
ERROR 41 Read Only attribute
SET 42 subscriber.by-imsi-901990000000001.ps-enabled nonsense
ERROR 42 Value failed verification.
SET 43 subscriber.by-imsi-901990000000001.cs-enabled nonsense
ERROR 43 Value failed verification.
SET 44 subscriber.by-imsi-901990000000001.ps-enabled
ERROR 44 SET incomplete
SET 45 subscriber.by-imsi-901990000000001.cs-enabled
ERROR 45 SET incomplete
GET 46 subscriber.by-imsi-1234567890123456.ps-enabled
ERROR 46 Invalid value part of 'by-xxx-value' selector.
GET 47 subscriber.by-imsi-1234567890123456.cs-enabled
ERROR 47 Invalid value part of 'by-xxx-value' selector.

View File

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