Compare commits

..

37 Commits
1.7.0 ... 36c3

Author SHA1 Message Date
Neels Hofmeyr
1f8f1b9777 cosmetic comment tweak
Change-Id: I0482d663e6c9042545677f9280c751323d332439
2019-12-25 19:09:45 +01:00
Piotr Krysik
a5ed663dea Enabling/disabling 4G RAN for given subscriber
The change adds support for enabling and disabling connection
through 4G RAN by a subscriber with use of USSD codes.

Change-Id: Idf964d9c770a0a1cb5c486eb2a7592d6bf44171c
2019-12-25 19:09:45 +01:00
Harald Welte
15031855bf AUC: Add support for setting the AMF separation bit to '1' for EUTRAN
Change-Id: Ic766bc40f6126bb479bd0a05b0e96bec3e240008
2019-12-25 19:09:45 +01:00
Neels Hofmeyr
83b909e4f3 Revert "mention *#201#, *#301# in USSD get_ran (HACK)"
This reverts commit 05e888ed7c.
2019-12-25 19:09:45 +01:00
Neels Hofmeyr
fde8f64d1d mention *#201#, *#301# in USSD get_ran (HACK)
Change-Id: Idbedbc679868a88dae1c85f82527f3f20067b879
2019-12-25 19:09:45 +01:00
gsmevent admin
d8e8a8425a send GMM_CAUSE_ROAMING_NOTALLOWED instead of GMM_CAUSE_IMSI_UNKNOWN HARDCODED 2019-12-25 19:09:45 +01:00
Neels Hofmeyr
c1d56fd2e9 add USSD handlers to enable/disable 2G access
Change-Id: Ib266bf6ebc7692362a443efbf9b4ae69aee671d6
2019-12-25 19:09:45 +01:00
Neels Hofmeyr
6ac66ac3ad for ussd_get_ran, show current RAT
Change-Id: I81adb1785c1a46e9153a6914ef2c699e9c90b731
2019-12-25 19:09:45 +01:00
Neels Hofmeyr
769a7f46f4 auto upgrade hlr.db in the service file
Change-Id: Icd3af5160e6d8f31fed5e84055fbb09322c54c2b
2019-12-25 19:09:45 +01:00
Neels Hofmeyr
5bc457e19a add column 'last_lu_rat', show last RAN type
Change-Id: I5d73b1d928b61175d3198326706b7f49ba50e16f
2019-12-25 19:09:45 +01:00
Vadim Yanitskiy
e3c788d9a8 SS/USSD: update configuretion example
Change-Id: I3801968d29bae917bf8669bb4ac03a9c4f3cb96c
2019-12-25 19:09:45 +01:00
Vadim Yanitskiy
e6751dd207 SS/USSD: add IUSEs to enable / disable UMTS
Change-Id: Icf85ec8dd71813a9bbf359b9011456844f398337
2019-12-25 19:09:45 +01:00
Vadim Yanitskiy
e8b7b8634b SS/USSD: implement an IUSE for getting RAN type(s)
Change-Id: I8d95413784b039df50a4cdcac49289060f0414c6
2019-12-25 19:09:45 +01:00
Neels Hofmeyr
ce172efc0b Add DB storage for enabling / disabling arbitrary RAT types.
So far we have only GERAN-A and UTRAN-Iu, but to be future compatible,
implement an arbitrary length list of RAT types: add DB table subscriber_rat.

Backwards compatibility: if there is no entry in subscriber_rat, all RAT types
shall be allowed. As soon as there is an entry, it can either be false to
forbid a RAT or true to still allow a RAT type.

Depends: I93850710ab55a605bf61b95063a69682a2899bb1 (libosmocore)
Change-Id: I3e399ca8a85421f77a9a15e608413d1507722955
2019-12-25 19:09:45 +01:00
Neels Hofmeyr
2f1049827b trial: SMS-over-GSUP 2019-12-23 16:52:24 +01:00
Neels Hofmeyr
92d1c4a0de db v6: determine 3G AUC IND from VLR name
Each VLR requesting auth tuples should use a distinct IND pool for 3G auth.  So
far we tied the IND to the GSUP peer connection; MSC and SGSN were always
distinct GSUP peers, they ended up using distinct INDs.

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

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

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

Add an IND test to db_test.c

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

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

Change-Id: I716d8a8a249235c8093d7a6a78b3535d893d867e
2019-12-16 17:19:16 +01:00
Neels Hofmeyr
011e781da7 vty: show subscriber: show lu d,h,m,s ago, not just seconds
Change-Id: I0fe34e0f065160ef959b2b7b4dd040f3f2985f43
2019-12-16 17:17:58 +01:00
Neels Hofmeyr
cb88c34aca vty: show subscriber: change format of 'last LU seen'
So far, the time string format comes from ctime_r, and we manually add "UTC" to it.

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

Adding "UTC" to it is non-standard.

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

Change-Id: I6731968f05050399f4dd43b241290186e0c59e1a
2019-12-16 17:17:58 +01:00
Neels Hofmeyr
e18e3a42f1 drop error log for when a subscriber does not exist
Checking for existence of a subscriber and seeing that there is none is not
inherently an error. However, osmo-hlr currently logs on all occasions:

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

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

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

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

Change-Id: I5044e9b4519b948edc4e451cef0f7830d315619b
2019-12-16 17:17:58 +01:00
Neels Hofmeyr
710e0c9486 adoc: add D-GSM chapter to osmohlr-usermanual
Change-Id: I392b5523870c2ef3267179160028d26f3f761b77
2019-12-16 17:17:58 +01:00
Oliver Smith
f6d457e7ef hlr_vty_subscr: prettier output for last LU seen
Extend the "last LU seen on ..." line with the amount of seconds that
passed since now, or "(invalid timestamp)".

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

Change-Id: I24f9e86c1aa0b1576290094e024562f41b988f37
2019-12-16 17:17:58 +01:00
Neels Hofmeyr
fa287c579c gsup_server: send routing error back to the correct peer
If a peer attempts to add a route to an ipa-name that we already have in the
routing system, don't send the routing error to the peer that already has the
name, but to the peer that attempts to re-use it and would cause the collision.

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

Change-Id: Icafaedc11b5925149d338bdcb987ae985a7323d6
2019-12-16 17:17:58 +01:00
Neels Hofmeyr
e78241ae07 proxy routing refactor
Change-Id: I43ad27f6d768df02abb3459ac4c43bb80cc1cbeb
2019-12-16 17:17:58 +01:00
Neels Hofmeyr
62d916f3cd D-GSM 3/n: implement roaming by mslookup in osmo-hlr
Add mslookup client to find remote home HLRs of unknown IMSIs, and
proxy/forward GSUP for those to the right remote HLR instances.

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

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

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

Add full VTY config and VTY tests.

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

Change-Id: I2fe453553c90e6ee527ed13a13089900efd488aa
2019-12-16 16:56:33 +01:00
Neels Hofmeyr
3ad481afbf D-GSM 2/n: implement mDNS method of mslookup server
Implement the mslookup server's mDNS responder, to actually service remote
mslookup requests:
- VTY mslookup/server config with service names,
- the mslookup_mdns_server listening for mslookup requests,

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

Change-Id: I5cae6459090588b4dd292be90a5e8903432669d2
2019-12-16 16:21:53 +01:00
Neels Hofmeyr
6aa871db71 D-GSM 1/n: add mslookup server in osmo-hlr
Implement the mslookup server to service remote mslookup requests.

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

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

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

Change-Id: Ife4a61d71926d08f310a1aeed9d9f1974f64178b
2019-12-16 16:21:53 +01:00
Neels Hofmeyr
16a16e6aa4 test_nodes.vty: remove cruft
This stuff is not testing osmo-hlr specific nodes, remove.

Change-Id: Ia11a209778b78ab02424e2abf3f9004fe97cf570
2019-12-16 16:21:53 +01:00
Neels Hofmeyr
fac52fbd6b enlarge the GSUP message headroom
Make room for (more) arbitrary IPA headers, like longer IPA names as configured
by the user.

Change-Id: I7d86f2dadcae29fe1550ea2c9773394ab31a837b
2019-12-16 16:21:53 +01:00
Neels Hofmeyr
f0c02ad9c7 db v5: prep for D-GSM: add vlr_via_proxy and sgsn_via_proxy
D-GSM will store in the HLR DB whether a locally connected MSC has attached the
subscriber (last_lu_seen[_ps]), or whether the attach happened via a GSUP proxy
from a different site.

Add columns for this separately in this patch.

Change-Id: I98c7b3870559ede84adf56e4bf111f53c7487745
2019-12-16 16:21:53 +01:00
Neels Hofmeyr
43c36f99dd gsup client: add up_down_cb(), add osmo_gsup_client_create3()
For the GSUP clients in upcoming D-GSM enabled osmo-hlr, it will be necessary
to trigger an event as soon as a GSUP client connection becomes ready for
communication. Add the osmo_gsup_client->up_down_cb.

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

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

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

Change-Id: I6f181e42b678465bc9945f192559dc57d2083c6d
2019-12-16 16:21:53 +01:00
Neels Hofmeyr
008ce4bd43 2/2: fixup: add osmo_gsup_peer_id with type enum and union
During code review it was requested to insert an ability to handle different
kinds of peer id, in order to be able to add a Global Title in the future.

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

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

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

** lu_fsm:

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

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

** osmo_gsup_req:

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

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

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

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

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

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

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

** gsup_peer_id.c / osmo_ipa_name:

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

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

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

Depends: libosmocore Id9692880079ea0f219f52d81b1923a76fc640566
Change-Id: I3a8dff3d4a1cbe10d6ab08257a0138d6b2a082d9
2019-12-16 16:21:53 +01:00
Oliver Smith
50bf7b775b contrib/dgsm/ add example esme and dialplan
Add example scripts for the distributed GSM network:

esme_dgsm.py: connect to the SMPP port of OsmoMSC A and forward SMS to the SMPP
port of OsmoMSC B. The IP and port of OsmoMSC B is retrieved by the receiver's
MSISDN using osmo-mslookup-client.

contrib/dgsm/freeswitch_dialplan_dgsm.py: resolve the destination SIP servers
of calls with osmo-mslookup-client and bridge the calls accordingly.

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

Related: OS#4254
Related: OS#4255
Change-Id: I26e8dd8d9a08187fccb3e74ee91366bc24f6c608
2019-12-16 16:21:53 +01:00
Neels Hofmeyr
cb508554df add osmo-mslookup-client program
Standalone program using libosmo-mslookup to easily integrate with programs
that want to connect services (SIP, SMS,...) to the current location of a
subscriber. Also useful for manual testing.

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

Change-Id: Ie68a5c1db04fb4dff00dc3c774a1162f5b9fabf7
2019-12-16 16:21:53 +01:00
Oliver Smith
06455eac9c add mDNS lookup method to libosmo-mslookup
Add the first actually useful lookup method to the mslookup library: multicast
DNS.

The server side is added in a subsequent commit, when the mslookup server is
implemented for the osmo-hlr program.

Use custom DNS encoding instead of libc-ares (which we use in OsmoSGSN
already), because libc-ares is only a DNS client implementation and we will
need both client and server.

Related: OS#4237
Patch-by: osmith, nhofmeyr
Change-Id: I03a0ffa1d4dc1b24ac78a5ad0975bca90a49c728
2019-12-16 16:21:53 +01:00
Oliver Smith
2bdcc8eec9 add libosmo-mslookup abstract client
mslookup is a key concept in Distributed GSM, which allows querying the current
location of a subscriber in a number of cooperating but independent core
network sites, by arbitrary service names and by MSISDN/IMSI.

Add the abstract mslookup client library. An actual lookup method (besides
mslookup_client_fake.c) is added in a subsequent patch.

For a detailed overview of this and upcoming patches, please see the elaborate
comment at the top of mslookup.c.

Add as separate library, libosmo-mslookup, to allow adding D-GSM capability to
arbitrary client programs.

osmo-hlr will be the only mslookup server implementation, added in a subsequent
patch.

osmo-hlr itself will also use this library and act as an mslookup client, when
requesting the home HLR for locally unknown IMSIs.

Related: OS#4237
Patch-by: osmith, nhofmeyr
Change-Id: I83487ab8aad1611eb02e997dafbcb8344da13df1
2019-12-16 16:21:53 +01:00
114 changed files with 3867 additions and 3541 deletions

19
.gitignore vendored
View File

@@ -26,7 +26,6 @@ m4
*.m4 *.m4
missing missing
.deps .deps
*~
*.pc *.pc
.libs .libs
@@ -68,21 +67,3 @@ doc/manuals/generated/
doc/manuals/osmomsc-usermanual.xml doc/manuals/osmomsc-usermanual.xml
doc/manuals/common doc/manuals/common
doc/manuals/build doc/manuals/build
contrib/osmo-hlr.spec
/debian/.debhelper/
/debian/libosmo-gsup-client-dev/
/debian/files
/debian/autoreconf.after
/debian/autoreconf.before
/debian/libosmo-gsup-client0/
/debian/libosmo-mslookup0/
/debian/osmo-hlr-dbg/
/debian/tmp/
/doc/manuals/vty/hlr_vty_reference.xml
/debian/libosmo-mslookup-dev/
/debian/osmo-hlr-doc/
/debian/osmo-hlr/
/debian/osmo-mslookup-utils/
/debian/*.log
/debian/*.substvars

View File

@@ -1,9 +1,9 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2 AUTOMAKE_OPTIONS = foreign dist-bzip2
SUBDIRS = \ SUBDIRS = \
doc \
src \ src \
include \ include \
doc \
sql \ sql \
contrib \ contrib \
tests \ tests \
@@ -11,16 +11,13 @@ SUBDIRS = \
EXTRA_DIST = \ EXTRA_DIST = \
.version \ .version \
contrib/osmo-hlr.spec.in \
debian \
$(NULL) $(NULL)
AM_DISTCHECK_CONFIGURE_FLAGS = \ AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
pkgconfigdir = $(libdir)/pkgconfig pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libosmo-gsup-client.pc \ pkgconfig_DATA = libosmo-gsup-client.pc
libosmo-mslookup.pc
@RELMAKE@ @RELMAKE@

View File

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

View File

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

View File

@@ -12,8 +12,6 @@ AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.9])
AC_CONFIG_TESTDIR(tests) AC_CONFIG_TESTDIR(tests)
CFLAGS="$CFLAGS -std=gnu11"
dnl kernel style compile messages dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -27,11 +25,6 @@ AC_PROG_MKDIR_P
AC_PROG_CC AC_PROG_CC
AC_PROG_INSTALL AC_PROG_INSTALL
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
AS_CASE(["$LD"],[*clang*],
[AS_CASE(["${host_os}"],
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
dnl check for pkg-config (explained in detail in libosmocore/configure.ac) dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no) AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
@@ -41,11 +34,11 @@ PKG_PROG_PKG_CONFIG([0.20])
PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1]) PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0) PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.2.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0) PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.2.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.2.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0) PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.2.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0) PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.6.0)
PKG_CHECK_MODULES(SQLITE3, sqlite3) PKG_CHECK_MODULES(SQLITE3, sqlite3)
@@ -93,7 +86,6 @@ AC_ARG_ENABLE(werror,
if test x"$werror" = x"yes" if test x"$werror" = x"yes"
then then
WERROR_FLAGS="-Werror" WERROR_FLAGS="-Werror"
WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS" CFLAGS="$CFLAGS $WERROR_FLAGS"
@@ -108,22 +100,13 @@ if test "x$enable_ext_tests" = "xyes" ; then
AM_PATH_PYTHON AM_PATH_PYTHON
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes) AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.]) AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
fi fi
fi fi
AC_MSG_CHECKING([whether to enable VTY/CTRL tests]) AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
AC_MSG_RESULT([$enable_ext_tests]) AC_MSG_RESULT([$enable_ext_tests])
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes") AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
# mslookup_client_mdns_test (OS#4385: does not work everywhere)
AC_ARG_ENABLE([mslookup_client_mdns_test],
AC_HELP_STRING([--enable-mslookup-client-mdns-test],
[Include the mslookup_client_mdns_test in make check [default=no]]),
[enable_mslookup_client_mdns_test="$enableval"],[enable_mslookup_client_mdns_test="no"])
AC_MSG_CHECKING([whether to enable mslookup_client_mdns_test])
AC_MSG_RESULT([$enable_mslookup_client_mdns_test])
AM_CONDITIONAL(ENABLE_MSLOOKUP_CLIENT_MDNS_TEST, test "x$enable_mslookup_client_mdns_test" = "xyes")
# Generate manuals # Generate manuals
AC_ARG_ENABLE(manuals, AC_ARG_ENABLE(manuals,
[AS_HELP_STRING( [AS_HELP_STRING(
@@ -203,10 +186,10 @@ AC_OUTPUT(
contrib/Makefile contrib/Makefile
contrib/systemd/Makefile contrib/systemd/Makefile
contrib/dgsm/Makefile contrib/dgsm/Makefile
contrib/osmo-hlr.spec
tests/Makefile tests/Makefile
tests/auc/Makefile tests/auc/Makefile
tests/auc/gen_ts_55_205_test_sets/Makefile tests/auc/gen_ts_55_205_test_sets/Makefile
tests/gsup_server/Makefile
tests/gsup/Makefile tests/gsup/Makefile
tests/db/Makefile tests/db/Makefile
tests/db_upgrade/Makefile tests/db_upgrade/Makefile

View File

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

View File

@@ -1,13 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
SPDX-License-Identifier: MIT SPDX-License-Identifier: AGPL-3.0-or-later
Copyright 2019 sysmocom s.f.m.c GmbH <info@sysmocom.de> Copyright 2019 sysmocom s.f.m.c GmbH <info@sysmocom.de>
WARNING: this is just a proof-of-concept implementation, it blocks for every WARNING: this is just a proof-of-concept implementation, it blocks for every
received SMPP request and is not suitable for servicing more than one request received SMPP request and is not suitable for servicing more than one request
at a time. at a time.
Based on esme.py from RCCN (license changed with permission from author): Based on esme.py from RCCN:
https://github.com/Rhizomatica/rccn/blob/master/rccn/esme.py https://github.com/Rhizomatica/rccn/blob/master/rccn/esme.py
Copyright 2017 keith <keith@rhizomatica.org> Copyright 2017 keith <keith@rhizomatica.org>
@@ -39,7 +39,13 @@ import time
def can_handle_pdu(pdu): def can_handle_pdu(pdu):
if not isinstance(pdu, smpplib.command.DeliverSM): if not isinstance(pdu, smpplib.command.DeliverSM):
logging.info('PDU is not a DeliverSM, ignoring') logging.info('PDU is not a DeliverSM. Is OsmoMSC configured properly?')
return False
# Multipart SMS etc. not handled here (see RCCN's esme.py)
if pdu.esm_class & smpplib.consts.SMPP_GSMFEAT_UDHI:
logging.info("UDH (User Data Header) handling not implemented in this"
" example, dropping message.")
return False return False
if int(pdu.dest_addr_ton) == smpplib.consts.SMPP_TON_INTL: if int(pdu.dest_addr_ton) == smpplib.consts.SMPP_TON_INTL:
@@ -63,8 +69,7 @@ def query_mslookup(service_type, id, id_type='msisdn'):
return json.loads(result_line) return json.loads(result_line)
def tx_sms(dst_host, dst_port, source, destination, registered_delivery, def tx_sms(dst_host, dst_port, source, destination, unicode_text):
unicode_text):
smpp_client = smpplib.client.Client(dst_host, dst_port, 90) smpp_client = smpplib.client.Client(dst_host, dst_port, 90)
smpp_client.connect() smpp_client.connect()
smpp_client.bind_transceiver(system_id=args.dst_id, password=args.dst_pass) smpp_client.bind_transceiver(system_id=args.dst_id, password=args.dst_pass)
@@ -79,7 +84,7 @@ def tx_sms(dst_host, dst_port, source, destination, registered_delivery,
dest_addr_npi=smpplib.consts.SMPP_NPI_ISDN, dest_addr_npi=smpplib.consts.SMPP_NPI_ISDN,
destination_addr=destination.decode(), destination_addr=destination.decode(),
short_message=unicode_text, short_message=unicode_text,
registered_delivery=registered_delivery, registered_delivery=False,
) )
smpp_client.unbind() smpp_client.unbind()
@@ -100,9 +105,6 @@ def rx_deliver_sm(pdu):
time.sleep(args.sleep) time.sleep(args.sleep)
logging.info("Sleep done") logging.info("Sleep done")
if args.always_fail is not None:
return args.always_fail
result = query_mslookup("smpp.sms", msisdn) result = query_mslookup("smpp.sms", msisdn)
if 'v4' not in result or not result['v4']: if 'v4' not in result or not result['v4']:
logging.info('No IPv4 result from mslookup! This example only' logging.info('No IPv4 result from mslookup! This example only'
@@ -111,8 +113,7 @@ def rx_deliver_sm(pdu):
dst_host, dst_port = result['v4'] dst_host, dst_port = result['v4']
tx_sms(dst_host, dst_port, pdu.source_addr, tx_sms(dst_host, dst_port, pdu.source_addr,
pdu.destination_addr, int(pdu.registered_delivery), pdu.destination_addr, pdu.short_message)
pdu.short_message)
return smpplib.consts.SMPP_ESME_ROK return smpplib.consts.SMPP_ESME_ROK
@@ -150,35 +151,12 @@ def main():
parser.add_argument('--sleep', default=0, type=float, parser.add_argument('--sleep', default=0, type=float,
help='sleep time in seconds before forwarding an SMS,' help='sleep time in seconds before forwarding an SMS,'
' to test multithreading (default: 0)') ' to test multithreading (default: 0)')
parser.add_argument('--always-fail', default=None, metavar='SMPP_ESME_ERRCODE',
help='test delivery failure: always return an error code on Deliver-SM,'
' pass an smpplib error code name like RDELIVERYFAILURE (see smpplib/consts.py),'
' or an SMPP error code in hex digits')
args = parser.parse_args() args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format='[%(asctime)s]' logging.basicConfig(level=logging.INFO, format='[%(asctime)s]'
' (%(threadName)s) %(message)s', datefmt="%H:%M:%S") ' (%(threadName)s) %(message)s', datefmt="%H:%M:%S")
if args.always_fail:
resolved = None
name = 'SMPP_ESME_' + args.always_fail
if hasattr(smpplib.consts, name):
resolved = getattr(smpplib.consts, name)
if resolved is None:
try:
resolved = int(args.always_fail, 16)
except ValueError:
resolved = None
if resolved is None:
print('Invalid argument for --always-fail: %r' % args.always_fail)
exit(1)
args.always_fail = resolved
logging.info('--always-fail: returning error code %s to all Deliver-SM' % hex(args.always_fail))
smpp_bind() smpp_bind()
if __name__ == "__main__": if __name__ == "__main__":
main() main()
# vim: expandtab tabstop=4 shiftwidth=4

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
SPDX-License-Identifier: MIT SPDX-License-Identifier: AGPL-3.0-or-later
Copyright 2019 sysmocom s.f.m.c GmbH <info@sysmocom.de> Copyright 2019 sysmocom s.f.m.c GmbH <info@sysmocom.de>
This is a freeswitch dialplan implementation, see: This is a freeswitch dialplan implementation, see:

View File

@@ -35,6 +35,7 @@ osmo-build-dep.sh libosmo-abis
# Additional configure options and depends # Additional configure options and depends
CONFIG="" CONFIG=""
if [ "$WITH_MANUALS" = "1" ]; then if [ "$WITH_MANUALS" = "1" ]; then
osmo-build-dep.sh osmo-gsm-manuals
CONFIG="--enable-manuals" CONFIG="--enable-manuals"
fi fi
@@ -48,19 +49,14 @@ set -x
cd "$base" cd "$base"
autoreconf --install --force autoreconf --install --force
./configure \ ./configure --enable-sanitize --enable-external-tests --enable-werror $CONFIG
--enable-sanitize \
--enable-external-tests \
--enable-mslookup-client-mdns-test \
--enable-werror \
$CONFIG
$MAKE $PARALLEL_MAKE $MAKE $PARALLEL_MAKE
$MAKE check || cat-testlogs.sh $MAKE check || cat-testlogs.sh
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck || cat-testlogs.sh DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck || cat-testlogs.sh
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish make -C "$base/doc/manuals" publish
fi fi
$MAKE $PARALLEL_MAKE maintainer-clean $MAKE maintainer-clean
osmo-clean-workspace.sh osmo-clean-workspace.sh

View File

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

View File

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

View File

@@ -1,17 +1,12 @@
[Unit] [Unit]
Description=Osmocom Home Location Register (OsmoHLR) Description=Osmocom Home Location Register (OsmoHLR)
Documentation=https://osmocom.org/projects/osmo-hlr/wiki/OsmoHLR Documentation=https://osmocom.org/projects/osmo-hlr/wiki/OsmoHLR
After=network-online.target
Wants=network-online.target
[Service] [Service]
Type=simple Type=simple
Restart=always Restart=always
StateDirectory=osmocom ExecStart=/usr/bin/osmo-hlr -U -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db
WorkingDirectory=%S/osmocom
ExecStart=/usr/bin/osmo-hlr -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db
RestartSec=2 RestartSec=2
ProtectHome=true
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

264
debian/changelog vendored
View File

@@ -1,267 +1,3 @@
osmo-hlr (1.7.0) unstable; urgency=medium
[ Oliver Smith ]
* Run struct_endianness.py
* tests/db/db_test.err: adjust to XOR-3G rename
* debian: set compat level to 10
* systemd: depend on networking-online.target
[ Pau Espin Pedrol ]
* mslookup: Call osmo_fd_unregister() before closing and changing bfd->fd
[ Vadim Yanitskiy ]
* tests/auc/Makefile.am: put object files to LDADD
* tests/*/Makefile.am: move -I to AM_CPPFLAGS
* lu_fsm: fix memleak in lu_fsm_wait_insert_data_result()
* ussd: fix GSUP memleaks in rx_proc_ss_{req,error}()
* gsup_server: fix msgb memleak in osmo_gsup_server_read_cb()
* USSD: fix handling of ussd-DataCodingScheme != 0x0f
[ Alexander Couzens ]
* hlr: use talloc for memory allocation in osmo_gsup_create_insert_subscriber_data_msg
* Add support for multiple APN profiles for subscriber data
[ Harald Welte ]
* Introduce support for XOR-2G algorithm
* cosmetic: gen_ts_55_205_test_sets/func_template.c: Use tab-indent
* cosmetic: gen_ts_55_205_test_sets/main_template tabs istead of spaces
* Port to new libosmogsm 'struct osmo_sub_auth_data2'
* src/db.c: Switch from "const char *statements" to "const char * const"
* db: extend database schema to support 256bit K and/or OP[c] values
* Add VTY support for TUAK algorithm
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 14:41:33 +0200
osmo-hlr (1.6.0) unstable; urgency=medium
[ Vadim Yanitskiy ]
* db_auc: hexparse_stmt(): check value returned by osmo_hexparse()
[ Max ]
* Set working directory in systemd service file
* Ignore .deb build byproducts
* Debian: bump copyright year to match current
* Debian: reformat package description
* systemd: enable basic hardening
* Debian: install osmo-hlr-dgsm.cfg as example config
* hlr_vty.c: fix typo
* ctrl: take both address and port from vty config
[ Harald Welte ]
* Support building with -Werror=strict-prototypes / -Werror=old-style-definition
* Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
[ arehbein ]
* osmo-hlr: Transition to use of 'telnet_init_default'
[ Oliver Smith ]
* osmo_mdns_rfc_record_decode: check ret of talloc
* osmo_mdns_rfc_record_decode: proper free on err
* mslookup: use apn functions from libosmocore
* osmo_mdns_rfc_record/question_encode: remove ctx
[ Keith ]
* Vty: Fixup config shown/written from vty
[ Neels Hofmeyr ]
* fix memleak of proxy_subscr_listentry
[ Alexander Couzens ]
* Add vty `reject-cause` to set the reject cause
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 16:49:14 +0100
osmo-hlr (1.5.0) unstable; urgency=medium
[ Oliver Smith ]
* treewide: remove FSF address
[ Vadim Yanitskiy ]
* fixup: debian: remove unneeded dependency libdbd-sqlite3
* debian: add new 'osmo-mslookup-utils' package
* tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
[ Pau Espin Pedrol ]
* ctrl: Mark function as static
* tests: Allow specyfing specific ctrl test to run
* tests/ctrl: Move ERROR test scenario to proper file
* Fix db_subscr_create() not returning -EEXIST expected by VTY subscriber create cmd
* ctrl: Introduce cmd SET subscriber.create <imsi>
* ctrl: Introduce CTRL command subscriber.by-*.msisdn
* cosmetic: hlr_vty_subscr.c: Fix trailing whitespace
* ctrl: Introduce cmd SET subscriber.delete <imsi>
* ctrl: Introduce CTRL command subscriber.by-*.aud2g <algo[,ki]>
* ctrl: Introduce CTRL command subscriber.by-*.aud3g <algo[,KI,(op|opc),OP_C[,ind_bitlen]]>
* doc: Document new subscriber CTRL commands
[ Harald Welte ]
* update git URLs (git -> https; gitea)
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 18:38:31 +0200
osmo-hlr (1.4.0) unstable; urgency=medium
[ Keith ]
* Correct configuration written from vty
* vty: enable show subscribers filtered by IMEI
[ Harald Welte ]
* add README.md file as customary for cgit, github, gitlab, etc.
[ Oliver Smith ]
* Add post-upgrade script for automatic db upgrade
* debian/control: remove dh-systemd build-depend
[ Pau Espin Pedrol ]
* db: Avoid use uninitialized rc if running 0 statements
[ Neels Hofmeyr ]
* db v6: determine 3G AUC IND from VLR name
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 14:56:41 +0100
osmo-hlr (1.3.0) unstable; urgency=medium
[ Alexander Couzens ]
* hlr: respect the num_auth_vectors requested
* hlr: remove unused internal USSD list
[ Oliver Smith ]
* add libosmo-mslookup abstract client
* add mDNS lookup method to libosmo-mslookup
* Makefile.am: fix pkgconfig_DATA
* add mDNS lookup method to libosmo-mslookup (#2)
* contrib/dgsm/ add example esme and dialplan
* mslookup_client.c: fix dereferencing null pointer
* mdns_msg.c: always call va_end
* mslookup_client_mdns.c: fix dereferencing null
* osmo-mslookup-client.c: fix dereferencing null
* osmo-mslookup-client: fix dereferencing null
* mdns_sock.c: fix resource leak of sock
* mdns_rfc.c: fix possible access of uninit. mem
* mslookup_client_mdns_test: disable by default
* mslookup_client_mdns_test: no automatic skip
* Cosmetic: mention OS#4491 in location cancel code
* hlr_vty_subscr: prettier output for last LU seen
* contrib: import RPM spec
* contrib: integrate RPM spec
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
* contrib/jenkins: don't build osmo-gsm-manuals
* configure.ac: set -std=gnu11
[ Neels Hofmeyr ]
* add osmo-mslookup-client program
* add osmo-mslookup-client program (#2)
* fix missing braces in LOGP_GSUP_FWD
* gsup_client.c: fix deprecation for client create func
* 1/2: refactor: add and use lu_fsm, osmo_gsup_req, osmo_ipa_name
* 2/2: wrap ipa_name in osmo_cni_peer_id with type enum and union
* gsup client: add up_down_cb(), add osmo_gsup_client_create3()
* db v5: prep for D-GSM: add vlr_via_proxy and sgsn_via_proxy
* enlarge the GSUP message headroom
* test_nodes.vty: remove cruft
* D-GSM 1/n: add mslookup server in osmo-hlr
* D-GSM 2/n: implement mDNS method of mslookup server
* D-GSM 3/n: implement roaming by mslookup in osmo-hlr
* gsup_server: send routing error back to the correct peer
* adoc: add D-GSM chapter to osmohlr-usermanual
* drop error log for when a subscriber does not exist
* vty: show subscriber: change format of 'last LU seen'
* vty: show subscriber: show lu d,h,m,s ago, not just seconds
* auc3g: officially wrap IND around IND_bitlen space
* make osmo_cni_peer_id_cmp() NULL safe
* osmo_gsup_req_new(): require from_peer != NULL
* gsup_server.c: properly handle negative rc from osmo_gsup_conn_ccm_get()
* osmo_mslookup_server_mdns_rx(): handle read() rc == 0
* hlr_subscr_nam(): fix condition to fix nam=false notifications
* esme_dgsm.py: add --always-fail option for debugging SMPP
* osmo-mslookup-client: fix segfault for respond_error() caller
* manual: describe subscriber import by SQL
[ Harald Welte ]
* Revert "add osmo-mslookup-client program"
* Revert "add mDNS lookup method to libosmo-mslookup"
* Use OSMO_FD_* instead of deprecated BSC_FD_*
* support the XOR algorithm for UMTS AKA
* auc_test.c: Add some comments on what the test cases actually do
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
* manuals: generate vty reference xml at build time
[ Vadim Yanitskiy ]
* db: fix possible SQLite3 allocated memory leak in db_open()
* gsup_server: fix typo: s/omso_gsup_message/osmo_gsup_message/
* debian/control: change maintainer to the Osmocom team / mailing list
* cosmetic: fix spelling in logging message: existAnt -> existEnt
* doc/manuals: fix s/There/The/ in 'USSD Configuration'
* doc/manuals: re-organize description of internal USSD handlers
* USSD: fix handle_ussd(): do not free() unconditionally
* USSD: add special 'idle' handler to IUSE for testing
[ Eric ]
* configure.ac: fix libtool issue with clang and sanitizer
[ Philipp Maier ]
* doc: do not use loglevel info for log category ss
[ Pau Espin Pedrol ]
* configure.ac: Fix trailing whitespace
* doc: Update VTY reference xml file
* Support setting rt-prio and cpu-affinity mask through VTY
* Set TCP NODELAY sockopt to GSUP cli and srv connections
* contrib/jenkins: Enable parallel make in make distcheck
* .gitignore: Ignore new autofoo tmp files
* tests: Replace deprecated API log_set_print_filename
[ Keith ]
* osmo-hlr-db-tool: Make import from osmo-nitb less "lossy"
* Correct vty inline help for show subscriber
* Add vty command to show summary of all or filtered subscribers
* Fix Coverity Warnings
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 18:13:53 +0100
osmo-hlr (1.2.0) unstable; urgency=medium
[ Ruben Undheim ]
* Fix test for return codes on mipsel and alpha archs
[ Thorsten Alteholz ]
* fix spelling errors detected by lintian
[ Pau Espin Pedrol ]
* tests: Fix db_test err file to expect error code name instead of value
[ Oliver Smith ]
* tests/test_nodes.vty: check less libosmocore cmds
* tests/db_upgrade: disable for old sqlite versions
* gitignore: add tests/db_upgrade/*.dump
* gsup_client.h: fix license header: GPLv2+
* tests/auc: change back to python3
[ Neels Hofmeyr ]
* fix double free in osmo_gsup_client_enc_send()
* db upgrade to v2: log version 2, not 1
* fix upgrade to version 2: imei column default value
* add --db-check option
* hlr.sql: move comment
* add db_upgrade test
* hlr db schema 3: hlr_number -> msc_number
* db.c: code dup: add db_run_statements() for arrays of statements
* move headers to include/osmocom/hlr
* fix upgrade test in presence of ~/.sqliterc
* db upgrade: remove some code dup
* add osmo_gsup_msgb_alloc()
* Makefile convenience: add VTY_TEST var to run only one test
* remove gsup_test
* test_nodes.vty: tweak: add some '?' checks
* db v4: add column last_lu_seen_ps
[ Harald Welte ]
* AUC: Add support for setting the AMF separation bit to '1' for EUTRAN
* hlr: exit(2) on unsupported positional arguments on command line
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 03 Jan 2020 12:37:35 +0100
osmo-hlr (1.1.0) unstable; urgency=medium osmo-hlr (1.1.0) unstable; urgency=medium
[ Oliver Smith ] [ Oliver Smith ]

2
debian/compat vendored
View File

@@ -1 +1 @@
10 9

38
debian/control vendored
View File

@@ -1,30 +1,30 @@
Source: osmo-hlr Source: osmo-hlr
Section: net Section: net
Priority: optional Priority: optional
Maintainer: Osmocom team <openbsc@lists.osmocom.org> Maintainer: Max Suraev <msuraev@sysmocom.de>
Build-Depends: debhelper (>= 10), Build-Depends: debhelper (>= 9),
pkg-config, pkg-config,
dh-autoreconf, dh-autoreconf,
dh-systemd (>= 1.5),
autotools-dev, autotools-dev,
python3-minimal, python3-minimal,
libosmocore-dev (>= 1.9.0), libosmocore-dev,
libosmo-abis-dev (>= 1.5.0), libosmo-abis-dev,
libosmo-netif-dev (>= 1.4.0), libosmo-netif-dev,
libsqlite3-dev, libsqlite3-dev,
sqlite3, sqlite3,
osmo-gsm-manuals-dev (>= 1.5.0) osmo-gsm-manuals-dev
Standards-Version: 3.9.6 Standards-Version: 3.9.6
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr Vcs-Browser: http://cgit.osmocom.org/osmo-hlr
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr Vcs-Git: git://git.osmocom.org/osmo-hlr
Homepage: https://projects.osmocom.org/projects/osmo-hlr Homepage: https://projects.osmocom.org/projects/osmo-hlr
Package: osmo-hlr Package: osmo-hlr
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends} Depends: ${shlibs:Depends}, ${misc:Depends}, libdbd-sqlite3
Description: Osmocom Home Location Register Description: Osmocom Home Location Register
OsmoHLR is a Osmocom implementation of HLR (Home Location Registrar) which OsmoHLR is a Osmocom implementation of HLR (Home Location Registrar) which works over GSUP
works over GSUP protocol. The subscribers are store in sqlite DB. protocol. The subscribers are store in sqlite DB. It supports both 2G and 3G authentication.
It supports both 2G and 3G authentication.
Package: osmo-hlr-dbg Package: osmo-hlr-dbg
Architecture: any Architecture: any
@@ -59,7 +59,7 @@ Description: Development headers of Osmocom GSUP client library
. .
This package contains the development headers. This package contains the development headers.
Package: libosmo-mslookup1 Package: libosmo-mslookup0
Section: libs Section: libs
Architecture: any Architecture: any
Multi-Arch: same Multi-Arch: same
@@ -73,7 +73,7 @@ Package: libosmo-mslookup-dev
Architecture: any Architecture: any
Multi-Arch: same Multi-Arch: same
Depends: ${misc:Depends}, Depends: ${misc:Depends},
libosmo-mslookup1 (= ${binary:Version}), libosmo-mslookup0 (= ${binary:Version}),
libosmocore-dev libosmocore-dev
Pre-Depends: ${misc:Pre-Depends} Pre-Depends: ${misc:Pre-Depends}
Description: Development headers of Osmocom MS lookup library Description: Development headers of Osmocom MS lookup library
@@ -81,16 +81,6 @@ Description: Development headers of Osmocom MS lookup library
. .
This package contains the development headers. This package contains the development headers.
Package: osmo-mslookup-utils
Architecture: any
Section: utils
Depends: ${shlibs:Depends},
libosmo-mslookup1 (= ${binary:Version}),
${misc:Depends}
Multi-Arch: same
Description: Utilities for Osmocom MS lookup
This package contains a simple MS lookup client utility.
Package: osmo-hlr-doc Package: osmo-hlr-doc
Architecture: all Architecture: all
Section: doc Section: doc

2
debian/copyright vendored
View File

@@ -3,7 +3,7 @@ Upstream-Name: OsmoHLR
Source: http://cgit.osmocom.org/osmo-hlr/ Source: http://cgit.osmocom.org/osmo-hlr/
Files: * Files: *
Copyright: 2016-2022 Sysmocom s. f. m. c. GmbH <info@sysmocom.de> Copyright: 2016-2017 Sysmocom s. f. m. c. GmbH <info@sysmocom.de>
License: AGPL-3+ License: AGPL-3+
License: AGPL-3+ License: AGPL-3+

View File

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

View File

@@ -1 +0,0 @@
usr/bin/osmo-mslookup-client

5
debian/postinst vendored
View File

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

View File

@@ -1,22 +0,0 @@
# OsmoHLR example configuration for Distributed GSM (mslookup)
hlr
gsup
# For D-GSM roaming, osmo-hlr's GSUP must listen on a public IP:
bind ip 10.9.8.7
# Each HLR should identify with a distinct name
ipa-name hlr-23
mslookup
# Bind mslookup mDNS server and client on default multicast address and port:
# 239.192.23.42 port 4266
mdns bind
# Tell the mslookup server in osmo-hlr which IP+ports to return when a
# remote voice call or SMS wants to deliver to a local subscriber:
server
# local osmo-sip-connector SIP port
service sip.voice at 10.9.8.7 5060
# local osmo-msc SMPP port
service smpp.sms at 10.9.8.7 2775
# experimental: SMS-over-GSUP: this HLR's GSUP port
service gsup.sms at 10.9.8.7 4222
# only required if different from above 'gsup'/'bind ip':
#service gsup.hlr at 10.9.8.7 4222

View File

@@ -12,7 +12,7 @@ log stderr
logging level main notice logging level main notice
logging level db notice logging level db notice
logging level auc notice logging level auc notice
logging level ss notice logging level ss info
logging level linp error logging level linp error
! !
line vty line vty
@@ -24,9 +24,10 @@ hlr
bind ip 127.0.0.1 bind ip 127.0.0.1
ussd route prefix *#100# internal own-msisdn ussd route prefix *#100# internal own-msisdn
ussd route prefix *#101# internal own-imsi ussd route prefix *#101# internal own-imsi
ps ussd route prefix *#102# internal get-ran
pdp-profiles default ussd route prefix *#200# internal gsm-off
profile 1 ussd route prefix *#201# internal gsm-on
apn internet ussd route prefix *#300# internal umts-off
profile 2 ussd route prefix *#301# internal umts-on
apn * ussd route prefix *#400# internal lte-off
ussd route prefix *#401# internal lte-on

View File

@@ -1,10 +1,6 @@
EXTRA_DIST = \ EXTRA_DIST = example_subscriber_add_update_delete.vty \
example_subscriber_add_update_delete.vty \
example_subscriber_aud2g.ctrl \
example_subscriber_aud3g.ctrl \
example_subscriber_cs_ps_enabled.ctrl \ example_subscriber_cs_ps_enabled.ctrl \
example_subscriber_info.ctrl \ example_subscriber_info.ctrl \
example_subscriber_msisdn.ctrl \
osmohlr-usermanual.adoc \ osmohlr-usermanual.adoc \
osmohlr-usermanual-docinfo.xml \ osmohlr-usermanual-docinfo.xml \
osmohlr-vty-reference.xml \ osmohlr-vty-reference.xml \
@@ -18,12 +14,6 @@ if BUILD_MANUALS
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
VTY_REFERENCE = osmohlr-vty-reference.xml VTY_REFERENCE = osmohlr-vty-reference.xml
BUILT_REFERENCE_XML = $(builddir)/vty/hlr_vty_reference.xml
$(builddir)/vty/hlr_vty_reference.xml: $(top_builddir)/src/osmo-hlr
mkdir -p $(builddir)/vty
$(top_builddir)/src/osmo-hlr --vty-ref-xml > $@
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
OSMO_REPOSITORY = osmo-hlr OSMO_REPOSITORY = osmo-hlr

View File

@@ -5,16 +5,6 @@ The actual protocol is described in <<common-control-if>>, the variables common
to all programs using it are described in <<ctrl_common_vars>>. This section to all programs using it are described in <<ctrl_common_vars>>. This section
describes the CTRL interface variables specific to OsmoHLR. describes the CTRL interface variables specific to OsmoHLR.
Subscribers can be created and deleted using the following SET commands:
.Subscriber management commands available on OsmoHLR's Control interface
[options="header",width="100%",cols="35%,65%"]
|===
|Command|Comment
|subscriber.create '123456'|Create a new subscriber with IMSI "123456" to the database. Returns database ID of the subscriber being created.
|subscriber.delete '123456'|Delete subscriber with IMSI "123456" from database. Returns database ID of the subscriber being deleted.
|===
All subscriber variables are available by different selectors, which are freely All subscriber variables are available by different selectors, which are freely
interchangeable: interchangeable:
@@ -38,9 +28,6 @@ Each of the above selectors feature all of these control variables:
|subscriber.by-\*.*info-all*|R|No||List both 'info' and 'info-aud' in one |subscriber.by-\*.*info-all*|R|No||List both 'info' and 'info-aud' in one
|subscriber.by-\*.*cs-enabled*|RW|No|'1' or '0'|Enable/disable circuit-switched access |subscriber.by-\*.*cs-enabled*|RW|No|'1' or '0'|Enable/disable circuit-switched access
|subscriber.by-\*.*ps-enabled*|RW|No|'1' or '0'|Enable/disable packet-switched access |subscriber.by-\*.*ps-enabled*|RW|No|'1' or '0'|Enable/disable packet-switched access
|subscriber.by-\*.*msisdn*|RW|No|valid MSISDN string|Get/Set assigned MSISDN
|subscriber.by-\*.*aud2g*|RW|No|'algo[,KI]'|Get/Set 2g Authentication Data
|subscriber.by-\*.*aud2g*|RW|No|'algo[,KI,("op"|"opc"),OP_C[,ind_bitlen]]'|Get/Set 3g Authentication Data
|=== |===
=== subscriber.by-*.info, info-aud, info-all === subscriber.by-*.info, info-aud, info-all
@@ -117,63 +104,3 @@ commands:
---- ----
include::../example_subscriber_cs_ps_enabled.ctrl[] include::../example_subscriber_cs_ps_enabled.ctrl[]
---- ----
=== subscriber.by-*.msisdn
Get or set the MSISDN currently assigned to a subscriber.
This is an example transcript that illustrates use of this command:
----
include::../example_subscriber_msisdn.ctrl[]
----
=== subscriber.by-*.aud2g
Get or set the 2G Authentication data of a subscriber.
The information is stored/retrieved as a comma separated list of fields:
----
algo[,KI]
----
Where::
* *KI* is the KI as a hexadecimal string.
* *algo* is one of the following algorithms: _none, xor, comp128v1, comp128v2,
comp128v3_.
All values are case insensitive.
This is an example transcript that illustrates use of this command:
----
include::../example_subscriber_aud2g.ctrl[]
----
=== subscriber.by-*.aud3g
Get or set the 3G Authentication data of a subscriber.
The information is stored/retrieved as a comma separated list of fields:
----
algo[,KI,("op"|"opc"),OP_C[,ind_bitlen]]
----
Where:
* *KI* is the KI as a hexadecimal string.
* *algo* is one of the following algorithms: _none, xor, milenage_.
* "op" or "opc" indicates whether next field is an OP or OPC value.
* *OP_C* contains an OP or OPC values as hexadecimal string, based on what the
previous field specifies.
* *ind_bitlen* is set to 5 by default if not provided.
All values are case insensitive.
This is an example transcript that illustrates use of this command:
----
include::../example_subscriber_aud3g.ctrl[]
----

View File

@@ -38,7 +38,7 @@ There are two fundamentally distinct subscriber lookups provided by the mslookup
---- ----
digraph G { digraph G {
rankdir=LR rankdir=LR
subgraph cluster_village_b { subgraph cluster_village_b {
label="Village B" label="Village B"
ms_bob [label="Bob\n(from village B)",shape=box] ms_bob [label="Bob\n(from village B)",shape=box]
@@ -113,7 +113,7 @@ msc {
---- ----
digraph G { digraph G {
rankdir=LR rankdir=LR
subgraph cluster_village_b { subgraph cluster_village_b {
label="Village B" label="Village B"
@@ -214,12 +214,6 @@ port, but beware that the IP address must be from a multicast range, see <<ietf-
mslookup mslookup
mdns bind 239.192.23.42 4266 mdns bind 239.192.23.42 4266
Domain names generated from mslookup queries (e.g. "sip.voice.123.msisdn") should not collide with IANA permitted
domains. Therefore we add the "mdns.osmocom.org" suffix. It can be overridden as follows:
mslookup
mdns domain-suffix mdns.osmocom.org
==== Server: Site Services ==== Server: Site Services
The mslookup server requires a list of service addresses provided at the local site, in order to respond to service The mslookup server requires a list of service addresses provided at the local site, in order to respond to service
@@ -230,25 +224,23 @@ requests matching locally attached subscribers.
service sip.voice at 10.9.8.7 5060 service sip.voice at 10.9.8.7 5060
service smpp.sms at 10.9.8.7 2775 service smpp.sms at 10.9.8.7 2775
In this example: In this example, "10.9.8.7 5060" would be the IP address and port on which the local site's PBX is bound to receive SIP
Invite requests; "10.9.8.7 2775" would be the local site's OsmoMSC SMPP bind address and port.
- "10.9.8.7 5060" are the IP address and port on which the local site's osmo-sip-connector is bound to receive SIP Obviously, these IP addresses must be routable back to this site from all other sites. If, for example, the PBX is
Invite requests. configured to bind on "0.0.0.0", it won't work to enter the same as service address -- remote sites cannot route to
- "10.9.8.7 2775" are the local site's OsmoMSC SMPP bind address and port. 0.0.0.0. Instead, the mslookup service requires a public IP address of a local interface. For the same reasons, never
add link-local addresses like 127.0.0.1 as mslookup services.
Obviously, these IP addresses must be routable back to this site from all other sites. Using link-local or "ANY"
addresses, like 127.0.0.1 or 0.0.0.0, will not work here. Instead, each service config requires a public IP address that
all remote requestors are able to reach (not necessarily on the host that osmo-hlr is running on).
If a site has more than one MSC, services can also be configured for each MSC individually, keyed by the IPA unit name If a site has more than one MSC, services can also be configured for each MSC individually, keyed by the IPA unit name
that each MSC sends on the GSUP link: that each MSC sends on the GSUP link:
mslookup mslookup
server server
msc ipa-name msc-262-42-0 msc msc-262-42-0
service sip.voice at 10.11.12.13 5060 service sip.voice at 10.11.12.13 5060
service smpp.sms at 10.11.12.13 2775 service smpp.sms at 10.11.12.13 2775
msc ipa-name msc-901-70-0 msc msc-901-70-0
service sip.voice at 10.9.8.7 5060 service sip.voice at 10.9.8.7 5060
service smpp.sms at 10.9.8.7 2775 service smpp.sms at 10.9.8.7 2775
@@ -332,7 +324,7 @@ The mslookup ID types are fixed, while service names can be chosen arbitrarily.
|=== |===
|Service Name|Protocol|Description |Service Name|Protocol|Description
|gsup.hlr | GSUP | Home HLR's GSUP server, to handle Location Updating related procedures |gsup.hlr | GSUP | Home HLR's GSUP server, to handle Location Updating related procedures
|sip.voice | SIP | OsmoSIPConnector, to receive a SIP Invite (MT side of a call) |sip.voice | SIP | SIP PBX or OsmoSIPConnector, to receive a SIP Invite (MT side of a call)
|smpp.sms | SMPP | Destination OsmoMSC (or other SMPP server) to deliver an SMS to the recipient |smpp.sms | SMPP | Destination OsmoMSC (or other SMPP server) to deliver an SMS to the recipient
|gsup.sms | GSUP | GSUP peer to deliver an SMS to the recipient using SMS-over-GSUP |gsup.sms | GSUP | GSUP peer to deliver an SMS to the recipient using SMS-over-GSUP
|=== |===
@@ -420,7 +412,6 @@ dialplan implementation. An example dialplan implementation for FreeSWITCH that
osmo-hlr source tree under `contrib`, called `freeswitch_dialplan_dgsm.py`. osmo-hlr source tree under `contrib`, called `freeswitch_dialplan_dgsm.py`.
To integrate it with your FREESWITCH setup, add a new `extension` block to your `dialplan/public.xml`: To integrate it with your FREESWITCH setup, add a new `extension` block to your `dialplan/public.xml`:
---- ----
<extension name="outbound"> <extension name="outbound">
<condition field="destination_number" expression=".*"> <condition field="destination_number" expression=".*">
@@ -434,7 +425,6 @@ To integrate it with your FREESWITCH setup, add a new `extension` block to your
Make sure that the dir containing `freeswitch_dialplan_dgsm.py` is in your `PYTHONPATH` environment variable, and start Make sure that the dir containing `freeswitch_dialplan_dgsm.py` is in your `PYTHONPATH` environment variable, and start
the server: the server:
---- ----
$ export PYTHONPATH="$PYTHONPATH:/home/user/code/osmo-hlr/contrib/dgsm" $ export PYTHONPATH="$PYTHONPATH:/home/user/code/osmo-hlr/contrib/dgsm"
$ freeswitch -nf -nonat -nonatmap -nocal -nort -c $ freeswitch -nf -nonat -nonatmap -nocal -nort -c

View File

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

View File

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

View File

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

View File

@@ -1,14 +0,0 @@
GET 1 subscriber.by-imsi-901991234567891.aud2g
GET_REPLY 1 subscriber.by-imsi-901991234567891.aud2g none
SET 2 subscriber.by-imsi-901991234567891.aud2g xor,c01ffedc1cadaeac1d1f1edacac1ab0a
SET_REPLY 2 subscriber.by-imsi-901991234567891.aud2g OK
GET 3 subscriber.by-imsi-901991234567891.aud2g
GET_REPLY 3 subscriber.by-imsi-901991234567891.aud2g XOR,c01ffedc1cadaeac1d1f1edacac1ab0a
SET 4 subscriber.by-imsi-901991234567891.aud2g none
SET_REPLY 4 subscriber.by-imsi-901991234567891.aud2g OK
GET 5 subscriber.by-imsi-901991234567891.aud2g
GET_REPLY 5 subscriber.by-imsi-901991234567891.aud2g none

View File

@@ -1,20 +0,0 @@
GET 117 subscriber.by-imsi-901991234567891.aud3g
GET_REPLY 117 subscriber.by-imsi-901991234567891.aud3g none
SET 118 subscriber.by-imsi-901991234567891.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OP,FB2A3D1B360F599ABAB99DB8669F8308
SET_REPLY 118 subscriber.by-imsi-901991234567891.aud3g OK
GET 119 subscriber.by-imsi-901991234567891.aud3g
GET_REPLY 119 subscriber.by-imsi-901991234567891.aud3g MILENAGE,c01ffedc1cadaeac1d1f1edacac1ab0a,OP,fb2a3d1b360f599abab99db8669f8308,5
SET 120 subscriber.by-imsi-901991234567891.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC,FB2A3D1B360F599ABAB99DB8669F8308,7
SET_REPLY 120 subscriber.by-imsi-901991234567891.aud3g OK
GET 121 subscriber.by-imsi-901991234567891.aud3g
GET_REPLY 121 subscriber.by-imsi-901991234567891.aud3g MILENAGE,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC,fb2a3d1b360f599abab99db8669f8308,7
SET 122 subscriber.by-imsi-901991234567891.aud3g none
SET_REPLY 122 subscriber.by-imsi-901991234567891.aud3g OK
GET 123 subscriber.by-imsi-901991234567891.aud3g
GET_REPLY 123 subscriber.by-imsi-901991234567891.aud3g none

View File

@@ -1,8 +0,0 @@
GET 1 subscriber.by-imsi-901991234567891.msisdn
GET_REPLY 1 subscriber.by-imsi-901991234567891.msisdn none
SET 2 subscriber.by-imsi-901991234567891.msisdn 555666
SET_REPLY 2 subscriber.by-imsi-901991234567891.msisdn OK
GET 3 subscriber.by-imsi-901991234567891.msisdn
GET_REPLY 3 subscriber.by-imsi-901991234567891.msisdn 555666

View File

@@ -28,8 +28,6 @@ include::{srcdir}/chapters/dgsm.adoc[]
include::./common/chapters/gsup.adoc[] include::./common/chapters/gsup.adoc[]
include::./common/chapters/vty_cpu_sched.adoc[]
include::./common/chapters/port_numbers.adoc[] include::./common/chapters/port_numbers.adoc[]
include::./common/chapters/bibliography.adoc[] include::./common/chapters/bibliography.adoc[]
@@ -37,3 +35,4 @@ include::./common/chapters/bibliography.adoc[]
include::./common/chapters/glossary.adoc[] include::./common/chapters/glossary.adoc[]
include::./common/chapters/gfdl.adoc[] include::./common/chapters/gfdl.adoc[]

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
SUBDIRS = osmocom SUBDIRS = osmocom
nobase_include_HEADERS = \ nobase_include_HEADERS = \
osmocom/gsupclient/cni_peer_id.h \ osmocom/gsupclient/gsup_peer_id.h \
osmocom/gsupclient/gsup_client.h \ osmocom/gsupclient/gsup_client.h \
osmocom/gsupclient/gsup_req.h \ osmocom/gsupclient/gsup_req.h \
osmocom/mslookup/mdns.h \ osmocom/mslookup/mdns.h \

View File

@@ -32,35 +32,33 @@ struct osmo_ipa_name {
uint8_t val[128]; uint8_t val[128];
}; };
bool osmo_ipa_name_is_empty(const struct osmo_ipa_name *ipa_name); bool osmo_ipa_name_is_empty(struct osmo_ipa_name *ipa_name);
int osmo_ipa_name_set(struct osmo_ipa_name *ipa_name, const uint8_t *val, size_t len); int osmo_ipa_name_set(struct osmo_ipa_name *ipa_name, const uint8_t *val, size_t len);
int osmo_ipa_name_set_str(struct osmo_ipa_name *ipa_name, const char *str_fmt, ...); int osmo_ipa_name_set_str(struct osmo_ipa_name *ipa_name, const char *str_fmt, ...);
int osmo_ipa_name_cmp(const struct osmo_ipa_name *a, const struct osmo_ipa_name *b); int osmo_ipa_name_cmp(const struct osmo_ipa_name *a, const struct osmo_ipa_name *b);
const char *osmo_ipa_name_to_str_c(void *ctx, const struct osmo_ipa_name *ipa_name);
const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name); const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name);
enum osmo_cni_peer_id_type { enum osmo_gsup_peer_id_type {
OSMO_CNI_PEER_ID_EMPTY=0, OSMO_GSUP_PEER_ID_EMPTY=0,
OSMO_CNI_PEER_ID_IPA_NAME, OSMO_GSUP_PEER_ID_IPA_NAME,
/* OSMO_CNI_PEER_ID_GLOBAL_TITLE, <-- currently not implemented, but likely future possibility */ /* OSMO_GSUP_PEER_ID_GLOBAL_TITLE, <-- currently not implemented, but likely future possibility */
}; };
extern const struct value_string osmo_cni_peer_id_type_names[]; extern const struct value_string osmo_gsup_peer_id_type_names[];
static inline const char *osmo_cni_peer_id_type_name(enum osmo_cni_peer_id_type val) static inline const char *osmo_gsup_peer_id_type_name(enum osmo_gsup_peer_id_type val)
{ return get_value_string(osmo_cni_peer_id_type_names, val); } { return get_value_string(osmo_gsup_peer_id_type_names, val); }
struct osmo_cni_peer_id { struct osmo_gsup_peer_id {
enum osmo_cni_peer_id_type type; enum osmo_gsup_peer_id_type type;
union { union {
struct osmo_ipa_name ipa_name; struct osmo_ipa_name ipa_name;
}; };
}; };
bool osmo_cni_peer_id_is_empty(const struct osmo_cni_peer_id *cni_peer_id); bool osmo_gsup_peer_id_is_empty(struct osmo_gsup_peer_id *gsup_peer_id);
int osmo_cni_peer_id_set(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni_peer_id_type type, int osmo_gsup_peer_id_set(struct osmo_gsup_peer_id *gsup_peer_id, enum osmo_gsup_peer_id_type type,
const uint8_t *val, size_t len); const uint8_t *val, size_t len);
int osmo_cni_peer_id_set_str(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni_peer_id_type type, int osmo_gsup_peer_id_set_str(struct osmo_gsup_peer_id *gsup_peer_id, enum osmo_gsup_peer_id_type type,
const char *str_fmt, ...); const char *str_fmt, ...);
int osmo_cni_peer_id_cmp(const struct osmo_cni_peer_id *a, const struct osmo_cni_peer_id *b); int osmo_gsup_peer_id_cmp(const struct osmo_gsup_peer_id *a, const struct osmo_gsup_peer_id *b);
const char *osmo_cni_peer_id_to_str(const struct osmo_cni_peer_id *cni_peer_id); const char *osmo_gsup_peer_id_to_str(const struct osmo_gsup_peer_id *gsup_peer_id);
const char *osmo_cni_peer_id_to_str_c(void *ctx, const struct osmo_cni_peer_id *cni_peer_id);

View File

@@ -19,14 +19,14 @@
#pragma once #pragma once
#include <osmocom/gsm/gsup.h> #include <osmocom/gsm/gsup.h>
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
struct osmo_gsup_req; struct osmo_gsup_req;
#define LOG_GSUP_REQ_CAT_SRC(req, subsys, level, file, line, fmt, args...) \ #define LOG_GSUP_REQ_CAT_SRC(req, subsys, level, file, line, fmt, args...) \
LOGPSRC(subsys, level, file, line, "GSUP %u: %s: IMSI-%s %s: " fmt, \ LOGPSRC(subsys, level, file, line, "GSUP %u: %s: IMSI-%s %s: " fmt, \
(req) ? (req)->nr : 0, \ (req) ? (req)->nr : 0, \
(req) ? osmo_cni_peer_id_to_str(&(req)->source_name) : "NULL", \ (req) ? osmo_gsup_peer_id_to_str(&(req)->source_name) : "NULL", \
(req) ? (req)->gsup.imsi : "NULL", \ (req) ? (req)->gsup.imsi : "NULL", \
(req) ? osmo_gsup_message_type_name((req)->gsup.message_type) : "NULL", \ (req) ? osmo_gsup_message_type_name((req)->gsup.message_type) : "NULL", \
##args) ##args)
@@ -56,11 +56,11 @@ struct osmo_gsup_req {
/* The ultimate source of this message: the source_name form the GSUP message, or, if not present, then the /* The ultimate source of this message: the source_name form the GSUP message, or, if not present, then the
* immediate GSUP peer. GSUP messages going via a proxy reflect the initial source in the source_name. * immediate GSUP peer. GSUP messages going via a proxy reflect the initial source in the source_name.
* This source_name is implicitly added to the routes for the conn the message was received on. */ * This source_name is implicitly added to the routes for the conn the message was received on. */
struct osmo_cni_peer_id source_name; struct osmo_gsup_peer_id source_name;
/* If the source_name is not an immediate GSUP peer, this is set to the closest intermediate peer between here /* If the source_name is not an immediate GSUP peer, this is set to the closest intermediate peer between here
* and source_name. */ * and source_name. */
struct osmo_cni_peer_id via_proxy; struct osmo_gsup_peer_id via_proxy;
/* Identify this request by number, for logging. */ /* Identify this request by number, for logging. */
unsigned int nr; unsigned int nr;
@@ -82,28 +82,24 @@ struct osmo_gsup_req {
struct msgb *msg; struct msgb *msg;
}; };
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id *from_peer, struct msgb *msg, struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_gsup_peer_id *from_peer, struct msgb *msg,
osmo_gsup_req_send_response_t send_response_cb, void *cb_data, osmo_gsup_req_send_response_t send_response_cb, void *cb_data,
struct llist_head *add_to_list); struct llist_head *add_to_list);
void osmo_gsup_req_free(struct osmo_gsup_req *req); void osmo_gsup_req_free(struct osmo_gsup_req *req);
/*! See _osmo_gsup_req_respond() for details. /*! Call _osmo_gsup_req_respond() to convey the sender's source file and line in the logs. */
* Call _osmo_gsup_req_respond(), passing the caller's source file and line for logging. */
#define osmo_gsup_req_respond(REQ, RESPONSE, ERROR, FINAL_RESPONSE) \ #define osmo_gsup_req_respond(REQ, RESPONSE, ERROR, FINAL_RESPONSE) \
_osmo_gsup_req_respond(REQ, RESPONSE, ERROR, FINAL_RESPONSE, __FILE__, __LINE__) _osmo_gsup_req_respond(REQ, RESPONSE, ERROR, FINAL_RESPONSE, __FILE__, __LINE__)
int _osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response, int _osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response,
bool error, bool final_response, const char *file, int line); bool error, bool final_response, const char *file, int line);
/*! See _osmo_gsup_req_respond_msgt() for details. /*! Call _osmo_gsup_req_respond_msgt() to convey the sender's source file and line in the logs. */
* Call _osmo_gsup_req_respond_msgt(), passing the caller's source file and line for logging. */
#define osmo_gsup_req_respond_msgt(REQ, MESSAGE_TYPE, FINAL_RESPONSE) \ #define osmo_gsup_req_respond_msgt(REQ, MESSAGE_TYPE, FINAL_RESPONSE) \
_osmo_gsup_req_respond_msgt(REQ, MESSAGE_TYPE, FINAL_RESPONSE, __FILE__, __LINE__) _osmo_gsup_req_respond_msgt(REQ, MESSAGE_TYPE, FINAL_RESPONSE, __FILE__, __LINE__)
int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type, int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type,
bool final_response, const char *file, int line); bool final_response, const char *file, int line);
/*! See _osmo_gsup_req_respond_err() for details. /*! Log an error message, and call _osmo_gsup_req_respond() to convey the sender's source file and line in the logs. */
* Log an error message, and call _osmo_gsup_req_respond_err(), passing the caller's source file and line for logging.
*/
#define osmo_gsup_req_respond_err(REQ, CAUSE, FMT, args...) do { \ #define osmo_gsup_req_respond_err(REQ, CAUSE, FMT, args...) do { \
LOG_GSUP_REQ(REQ, LOGL_ERROR, "%s: " FMT "\n", \ LOG_GSUP_REQ(REQ, LOGL_ERROR, "%s: " FMT "\n", \
get_value_string(gsm48_gmm_cause_names, CAUSE), ##args); \ get_value_string(gsm48_gmm_cause_names, CAUSE), ##args); \

View File

@@ -16,5 +16,6 @@ noinst_HEADERS = \
proxy.h \ proxy.h \
rand.h \ rand.h \
remote_hlr.h \ remote_hlr.h \
sms_over_gsup.h \
timestamp.h \ timestamp.h \
$(NULL) $(NULL)

View File

@@ -3,6 +3,6 @@
#include <osmocom/crypt/auth.h> #include <osmocom/crypt/auth.h>
int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec, int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
struct osmo_sub_auth_data2 *aud2g, struct osmo_sub_auth_data *aud2g,
struct osmo_sub_auth_data2 *aud3g, struct osmo_sub_auth_data *aud3g,
const uint8_t *rand_auts, const uint8_t *auts); const uint8_t *rand_auts, const uint8_t *auts);

View File

@@ -30,4 +30,5 @@ enum hlr_ctrl_node {
_LAST_CTRL_NODE_HLR _LAST_CTRL_NODE_HLR
}; };
int hlr_ctrl_cmds_install();
struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr); struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr);

View File

@@ -3,19 +3,13 @@
#include <stdbool.h> #include <stdbool.h>
#include <sqlite3.h> #include <sqlite3.h>
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
#include <osmocom/gsm/gsup.h> #include <osmocom/gsm/gsup.h>
#include <osmocom/gsm/gsm_utils.h>
struct hlr; struct hlr;
enum stmt_idx { enum stmt_idx {
DB_STMT_SEL_ALL,
DB_STMT_SEL_ALL_ORDER_LAST_SEEN,
DB_STMT_SEL_FILTER_MSISDN,
DB_STMT_SEL_FILTER_IMSI,
DB_STMT_SEL_FILTER_IMEI,
DB_STMT_SEL_FILTER_CS,
DB_STMT_SEL_FILTER_PS,
DB_STMT_SEL_BY_IMSI, DB_STMT_SEL_BY_IMSI,
DB_STMT_SEL_BY_MSISDN, DB_STMT_SEL_BY_MSISDN,
DB_STMT_SEL_BY_ID, DB_STMT_SEL_BY_ID,
@@ -44,6 +38,8 @@ enum stmt_idx {
DB_STMT_IND_ADD, DB_STMT_IND_ADD,
DB_STMT_IND_SELECT, DB_STMT_IND_SELECT,
DB_STMT_IND_DEL, DB_STMT_IND_DEL,
DB_STMT_UPD_RAT_FLAG,
DB_STMT_RAT_BY_ID,
_NUM_DB_STMT _NUM_DB_STMT
}; };
@@ -70,8 +66,8 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite3_log
/* obtain the authentication data for a given imsi */ /* obtain the authentication data for a given imsi */
int db_get_auth_data(struct db_context *dbc, const char *imsi, int db_get_auth_data(struct db_context *dbc, const char *imsi,
struct osmo_sub_auth_data2 *aud2g, struct osmo_sub_auth_data *aud2g,
struct osmo_sub_auth_data2 *aud3g, struct osmo_sub_auth_data *aud3g,
int64_t *subscr_id); int64_t *subscr_id);
int db_update_sqn(struct db_context *dbc, int64_t id, int db_update_sqn(struct db_context *dbc, int64_t id,
@@ -111,17 +107,24 @@ struct hlr_subscriber {
bool ms_purged_ps; bool ms_purged_ps;
time_t last_lu_seen; time_t last_lu_seen;
time_t last_lu_seen_ps; time_t last_lu_seen_ps;
char last_lu_rat_cs[128];
char last_lu_rat_ps[128];
/* talloc'd IPA unit name */ /* talloc'd IPA unit name */
struct osmo_ipa_name vlr_via_proxy; struct osmo_ipa_name vlr_via_proxy;
struct osmo_ipa_name sgsn_via_proxy; struct osmo_ipa_name sgsn_via_proxy;
bool rat_types[OSMO_RAT_COUNT];
}; };
static const struct hlr_subscriber hlr_subscriber_empty = {
.rat_types = { true, true, true },
};
/* A format string for use with strptime(3). This format string is /* A format string for use with strptime(3). This format string is
* used to parse the last_lu_seen column stored in the HLR database. * used to parse the last_lu_seen column stored in the HLR database.
* See https://sqlite.org/lang_datefunc.html, function datetime(). */ * See https://sqlite.org/lang_datefunc.html, function datetime(). */
#define DB_LAST_LU_SEEN_FMT "%Y-%m-%d %H:%M:%S" #define DB_LAST_LU_SEEN_FMT "%Y-%m-%d %H:%M:%S"
/* Like struct osmo_sub_auth_data2, but the keys are in hexdump representation. /* Like struct osmo_sub_auth_data, but the keys are in hexdump representation.
* This is useful because SQLite requires them in hexdump format, and callers * This is useful because SQLite requires them in hexdump format, and callers
* like the VTY and CTRL interface also have them available as hexdump to begin * like the VTY and CTRL interface also have them available as hexdump to begin
* with. In the binary format, a VTY command would first need to hexparse, * with. In the binary format, a VTY command would first need to hexparse,
@@ -159,9 +162,6 @@ int db_subscr_update_imei_by_imsi(struct db_context *dbc, const char* imsi, cons
int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi); int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi);
int db_subscr_exists_by_msisdn(struct db_context *dbc, const char *msisdn); int db_subscr_exists_by_msisdn(struct db_context *dbc, const char *msisdn);
int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,
void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,
int *count, const char **err);
int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi, int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
struct hlr_subscriber *subscr); struct hlr_subscriber *subscr);
int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn, int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
@@ -172,13 +172,18 @@ int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_s
int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps); int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps);
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id, int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
const struct osmo_ipa_name *vlr_name, bool is_ps, const struct osmo_ipa_name *vlr_name, bool is_ps,
const struct osmo_ipa_name *via_proxy); const struct osmo_ipa_name *via_proxy,
const enum osmo_rat_type rat_types[], size_t rat_types_len);
int db_subscr_purge(struct db_context *dbc, const char *by_imsi, int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
bool purge_val, bool is_ps); bool purge_val, bool is_ps);
int db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr, unsigned int *ind); int db_ind(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr, unsigned int *ind);
int db_ind_del(struct db_context *dbc, const struct osmo_cni_peer_id *vlr); int db_ind_del(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr);
int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed);
int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr);
int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed);
/*! Call sqlite3_column_text() and copy result to a char[]. /*! Call sqlite3_column_text() and copy result to a char[].
* \param[out] buf A char[] used as sizeof() arg(!) and osmo_strlcpy() target. * \param[out] buf A char[] used as sizeof() arg(!) and osmo_strlcpy() target.

View File

@@ -22,10 +22,9 @@
#include <osmocom/mslookup/mslookup.h> #include <osmocom/mslookup/mslookup.h>
#include <osmocom/hlr/gsup_server.h> #include <osmocom/hlr/gsup_server.h>
#include <osmocom/hlr/logging.h> #include <osmocom/hlr/logging.h>
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
#include <osmocom/gsupclient/gsup_req.h> #include <osmocom/gsupclient/gsup_req.h>
#define OSMO_DGSM_DEFAULT_RESULT_TIMEOUT_MS 2000
#define LOG_DGSM(imsi, level, fmt, args...) \ #define LOG_DGSM(imsi, level, fmt, args...) \
LOGP(DDGSM, level, "(IMSI-%s) " fmt, imsi, ##args) LOGP(DDGSM, level, "(IMSI-%s) " fmt, imsi, ##args)

View File

@@ -5,7 +5,7 @@
#include <osmocom/abis/ipa.h> #include <osmocom/abis/ipa.h>
#include <osmocom/abis/ipaccess.h> #include <osmocom/abis/ipaccess.h>
#include <osmocom/gsm/gsup.h> #include <osmocom/gsm/gsup.h>
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
#include <osmocom/gsupclient/gsup_req.h> #include <osmocom/gsupclient/gsup_req.h>
#ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN #ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN
@@ -42,6 +42,8 @@ struct osmo_gsup_conn {
//struct oap_state oap_state; //struct oap_state oap_state;
struct tlv_parsed ccm; struct tlv_parsed ccm;
unsigned int auc_3g_ind; /*!< IND index used for UMTS AKA SQN */
/* Set when Location Update is received: */ /* Set when Location Update is received: */
bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */ bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */
bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */ bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */
@@ -69,6 +71,8 @@ void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups);
int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup, int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
uint8_t *apn_buf, size_t apn_buf_size); 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, int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, const char *imsi, const char *msisdn,
enum osmo_gsup_cn_domain cn_domain, void *talloc_ctx); uint8_t *msisdn_enc, size_t msisdn_enc_size,
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_cni_peer_id *to_peer, uint8_t *apn_buf, size_t apn_buf_size,
enum osmo_gsup_cn_domain cn_domain);
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_gsup_peer_id *to_peer,
struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup); struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup);

View File

@@ -23,7 +23,6 @@
#pragma once #pragma once
#include <stdbool.h> #include <stdbool.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/core/linuxlist.h> #include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/ipa.h> #include <osmocom/gsm/ipa.h>
#include <osmocom/core/tdef.h> #include <osmocom/core/tdef.h>
@@ -49,6 +48,7 @@ struct hlr {
/* Control Interface */ /* Control Interface */
struct ctrl_handle *ctrl; struct ctrl_handle *ctrl;
const char *ctrl_bind_addr;
/* Local bind addr */ /* Local bind addr */
char *gsup_bind_addr; char *gsup_bind_addr;
@@ -56,16 +56,7 @@ struct hlr {
struct llist_head euse_list; struct llist_head euse_list;
struct hlr_euse *euse_default; struct hlr_euse *euse_default;
enum gsm48_gmm_cause reject_cause; struct llist_head iuse_list;
enum gsm48_gmm_cause no_proxy_reject_cause;
/* PS: APN default configuration used by Subscription Data on ISR */
struct {
struct {
bool enabled;
struct osmo_gsup_pdp_info pdp_infos[OSMO_GSUP_MAX_NUM_PDP_INFO];
size_t num_pdp_infos;
} pdp_profile;
} ps;
/* NCSS (call independent) session guard timeout value */ /* NCSS (call independent) session guard timeout value */
int ncss_guard_timeout; int ncss_guard_timeout;
@@ -121,6 +112,14 @@ struct hlr {
} mdns; } mdns;
} client; } client;
} mslookup; } mslookup;
struct {
/* FIXME actually use branch fixeria/sms for SMSC routing. completely unimplemented */
struct osmo_gsup_peer_id smsc;
/* If no SMSC is present / responsible, try punching the SMS through directly when this is true. */
bool try_direct_delivery;
} sms_over_gsup;
}; };
extern struct hlr *g_hlr; extern struct hlr *g_hlr;

View File

@@ -35,19 +35,9 @@ enum hlr_vty_node {
MSLOOKUP_SERVER_NODE, MSLOOKUP_SERVER_NODE,
MSLOOKUP_SERVER_MSC_NODE, MSLOOKUP_SERVER_MSC_NODE,
MSLOOKUP_CLIENT_NODE, MSLOOKUP_CLIENT_NODE,
PS_NODE,
PS_PDP_PROFILES_NODE,
PS_PDP_PROFILES_PROFILE_NODE,
}; };
#define A38_XOR_MIN_KEY_LEN 12
#define A38_XOR_MAX_KEY_LEN 16
#define A38_XOR2G_KEY_LEN 16
#define A38_COMP128_KEY_LEN 16
#define MILENAGE_KEY_LEN 16
int hlr_vty_is_config_node(struct vty *vty, int node); int hlr_vty_is_config_node(struct vty *vty, int node);
int hlr_vty_go_parent(struct vty *vty); int hlr_vty_go_parent(struct vty *vty);
void hlr_vty_init(void *hlr_ctx); void hlr_vty_init(void);
void dgsm_vty_init(void); void dgsm_vty_init(void);

View File

@@ -11,7 +11,6 @@ enum {
DMSLOOKUP, DMSLOOKUP,
DLU, DLU,
DDGSM, DDGSM,
DCTRL,
}; };
extern const struct log_info hlr_log_info; extern const struct log_info hlr_log_info;

View File

@@ -19,7 +19,7 @@
#pragma once #pragma once
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
#include <osmocom/mslookup/mslookup.h> #include <osmocom/mslookup/mslookup.h>
struct osmo_mslookup_query; struct osmo_mslookup_query;

View File

@@ -22,7 +22,7 @@
#include <time.h> #include <time.h>
#include <osmocom/gsm/protocol/gsm_23_003.h> #include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/core/sockaddr_str.h> #include <osmocom/core/sockaddr_str.h>
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
#include <osmocom/hlr/timestamp.h> #include <osmocom/hlr/timestamp.h>
struct osmo_gsup_req; struct osmo_gsup_req;

View File

@@ -0,0 +1,8 @@
#pragma once
#include <stdbool.h>
#include <osmocom/gsupclient/gsup_req.h>
#define OSMO_MSLOOKUP_SERVICE_SMS_GSUP "gsup.sms"
bool sms_over_gsup_check_handle_msg(struct osmo_gsup_req *req);

View File

@@ -71,7 +71,7 @@ struct osmo_mdns_rfc_header {
uint16_t nscount; /* Number of authority records */ uint16_t nscount; /* Number of authority records */
uint16_t arcount; /* Number of additional records */ uint16_t arcount; /* Number of additional records */
#elif OSMO_IS_BIG_ENDIAN #elif OSMO_IS_BIG_ENDIAN
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ /* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
uint16_t id; uint16_t id;
uint8_t qr:1, opcode:4, aa:1, tc:1, rd:1; uint8_t qr:1, opcode:4, aa:1, tc:1, rd:1;
uint8_t ra:1, z:3, rcode:4; uint8_t ra:1, z:3, rcode:4;
@@ -99,12 +99,15 @@ struct osmo_mdns_rfc_record {
uint8_t *rdata; uint8_t *rdata;
}; };
char *osmo_mdns_rfc_qname_encode(void *ctx, const char *domain);
char *osmo_mdns_rfc_qname_decode(void *ctx, const char *qname, size_t qname_len);
void osmo_mdns_rfc_header_encode(struct msgb *msg, const struct osmo_mdns_rfc_header *hdr); void osmo_mdns_rfc_header_encode(struct msgb *msg, const struct osmo_mdns_rfc_header *hdr);
int osmo_mdns_rfc_header_decode(const uint8_t *data, size_t data_len, struct osmo_mdns_rfc_header *hdr); int osmo_mdns_rfc_header_decode(const uint8_t *data, size_t data_len, struct osmo_mdns_rfc_header *hdr);
int osmo_mdns_rfc_question_encode(struct msgb *msg, const struct osmo_mdns_rfc_question *qst); int osmo_mdns_rfc_question_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_question *qst);
struct osmo_mdns_rfc_question *osmo_mdns_rfc_question_decode(void *ctx, const uint8_t *data, size_t data_len); struct osmo_mdns_rfc_question *osmo_mdns_rfc_question_decode(void *ctx, const uint8_t *data, size_t data_len);
int osmo_mdns_rfc_record_encode(struct msgb *msg, const struct osmo_mdns_rfc_record *rec); int osmo_mdns_rfc_record_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_record *rec);
struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_t *data, size_t data_len, struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_t *data, size_t data_len,
size_t *record_len); size_t *record_len);

View File

@@ -45,6 +45,12 @@ CREATE TABLE subscriber (
last_lu_seen TIMESTAMP default NULL, last_lu_seen TIMESTAMP default NULL,
last_lu_seen_ps TIMESTAMP default NULL, last_lu_seen_ps TIMESTAMP default NULL,
-- Last Radio Access Type list as sent during Location Updating Request.
-- This is usually just one RAT name, but can be a comma separated list of strings
-- of all the RAT types sent during Location Updating Request.
last_lu_rat_cs TEXT default NULL,
last_lu_rat_ps TEXT default NULL,
-- When a LU was received via a proxy, that proxy's hlr_number is stored here, -- When a LU was received via a proxy, that proxy's hlr_number is stored here,
-- while vlr_number reflects the MSC on the far side of that proxy. -- while vlr_number reflects the MSC on the far side of that proxy.
vlr_via_proxy VARCHAR, vlr_via_proxy VARCHAR,
@@ -71,9 +77,9 @@ CREATE TABLE auc_2g (
CREATE TABLE auc_3g ( CREATE TABLE auc_3g (
subscriber_id INTEGER PRIMARY KEY, -- subscriber.id subscriber_id INTEGER PRIMARY KEY, -- subscriber.id
algo_id_3g INTEGER NOT NULL, -- enum osmo_auth_algo value algo_id_3g INTEGER NOT NULL, -- enum osmo_auth_algo value
k VARCHAR(64) NOT NULL, -- hex string: subscriber's secret key (128/256bit) k VARCHAR(32) NOT NULL, -- hex string: subscriber's secret key (128bit)
op VARCHAR(64), -- hex string: operator's secret key (128/256bit) op VARCHAR(32), -- hex string: operator's secret key (128bit)
opc VARCHAR(64), -- hex string: derived from OP and K (128/256bit) opc VARCHAR(32), -- hex string: derived from OP and K (128bit)
sqn INTEGER NOT NULL DEFAULT 0, -- sequence number of key usage sqn INTEGER NOT NULL DEFAULT 0, -- sequence number of key usage
-- nr of index bits at lower SQN end -- nr of index bits at lower SQN end
ind_bitlen INTEGER NOT NULL DEFAULT 5 ind_bitlen INTEGER NOT NULL DEFAULT 5
@@ -87,8 +93,18 @@ CREATE TABLE ind (
UNIQUE (vlr) UNIQUE (vlr)
); );
-- Optional: add subscriber entries to allow or disallow specific RATs (2G or 3G or ...).
-- If a subscriber has no entry, that means that all RATs are allowed (backwards compat).
CREATE TABLE subscriber_rat (
subscriber_id INTEGER, -- subscriber.id
rat TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu','EUTRAN-SGs')) NOT NULL, -- Radio Access Technology, see enum ran_type
allowed BOOLEAN CHECK(allowed in (0, 1)) NOT NULL DEFAULT 0,
UNIQUE (subscriber_id, rat)
);
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi); CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
CREATE UNIQUE INDEX idx_subscr_rat_flag ON subscriber_rat (subscriber_id, rat);
-- Set HLR database schema version number -- Set HLR database schema version number
-- Note: This constant is currently duplicated in src/db.c and must be kept in sync! -- Note: This constant is currently duplicated in src/db.c and must be kept in sync!
PRAGMA user_version = 7; PRAGMA user_version = 8;

8
sql/upgrade_v2_to_v3.sql Normal file
View File

@@ -0,0 +1,8 @@
CREATE TABLE subscriber_rat (
subscriber_id INTEGER, -- subscriber.id
rat TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu')) NOT NULL, -- Radio Access Technology, see enum ran_type
allowed BOOLEAN NOT NULL DEFAULT 0,
);
PRAGMA user_version = 3;

View File

@@ -61,6 +61,7 @@ osmo_hlr_SOURCES = \
mslookup_server.c \ mslookup_server.c \
mslookup_server_mdns.c \ mslookup_server_mdns.c \
dgsm_vty.c \ dgsm_vty.c \
sms_over_gsup.c \
$(NULL) $(NULL)
osmo_hlr_LDADD = \ osmo_hlr_LDADD = \
@@ -82,7 +83,7 @@ osmo_hlr_db_tool_SOURCES = \
logging.c \ logging.c \
rand_urandom.c \ rand_urandom.c \
dbd_decode_binary.c \ dbd_decode_binary.c \
$(srcdir)/gsupclient/cni_peer_id.c \ $(srcdir)/gsupclient/gsup_peer_id.c \
$(NULL) $(NULL)
osmo_hlr_db_tool_LDADD = \ osmo_hlr_db_tool_LDADD = \
@@ -111,7 +112,7 @@ BOOTSTRAP_SQL = $(top_srcdir)/sql/hlr.sql
db_bootstrap.h: $(BOOTSTRAP_SQL) $(srcdir)/db_sql2c.sed db_bootstrap.h: $(BOOTSTRAP_SQL) $(srcdir)/db_sql2c.sed
echo "/* DO NOT EDIT THIS FILE. It is generated from files in osmo-hlr.git/sql/ */" > "$@" echo "/* DO NOT EDIT THIS FILE. It is generated from files in osmo-hlr.git/sql/ */" > "$@"
echo "#pragma once" >> "$@" echo "#pragma once" >> "$@"
echo "static const char * const stmt_bootstrap_sql[] = {" >> "$@" echo "static const char *stmt_bootstrap_sql[] = {" >> "$@"
cat "$(BOOTSTRAP_SQL)" \ cat "$(BOOTSTRAP_SQL)" \
| sed -f "$(srcdir)/db_sql2c.sed" \ | sed -f "$(srcdir)/db_sql2c.sed" \
>> "$@" >> "$@"

View File

@@ -1,4 +1,4 @@
/* (C) 2015-2023 by Harald Welte <laforge@gnumonks.org> /* (C) 2015 by Harald Welte <laforge@gnumonks.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@@ -32,8 +32,8 @@
/* compute given number of vectors using either aud2g or aud2g or a combination /* compute given number of vectors using either aud2g or aud2g or a combination
* of both. Handles re-synchronization if rand_auts and auts are set */ * of both. Handles re-synchronization if rand_auts and auts are set */
int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec, int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
struct osmo_sub_auth_data2 *aud2g, struct osmo_sub_auth_data *aud2g,
struct osmo_sub_auth_data2 *aud3g, struct osmo_sub_auth_data *aud3g,
const uint8_t *rand_auts, const uint8_t *auts) const uint8_t *rand_auts, const uint8_t *auts)
{ {
unsigned int i; unsigned int i;
@@ -93,10 +93,10 @@ int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
: "2G only", : "2G only",
auts? ", with AUTS resync" : ""); auts? ", with AUTS resync" : "");
if (aud3g) { if (aud3g) {
DBGP("3G: k = %s\n", hex(aud3g->u.umts.k, aud3g->u.umts.k_len)); DBGP("3G: k = %s\n", hexb(aud3g->u.umts.k));
DBGP("3G: %s = %s\n", DBGP("3G: %s = %s\n",
aud3g->u.umts.opc_is_op? "OP" : "opc", aud3g->u.umts.opc_is_op? "OP" : "opc",
hex(aud3g->u.umts.opc, aud3g->u.umts.opc_len)); hexb(aud3g->u.umts.opc));
DBGP("3G: for sqn ind %u, previous sqn was %" PRIu64 "\n", DBGP("3G: for sqn ind %u, previous sqn was %" PRIu64 "\n",
aud3g->u.umts.ind, aud3g->u.umts.sqn); aud3g->u.umts.ind, aud3g->u.umts.sqn);
} }
@@ -115,9 +115,6 @@ int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
if (aud3g) { if (aud3g) {
/* 3G or 3G + 2G case */ /* 3G or 3G + 2G case */
/* backwards-compatibiliy: We assume all RES are 8 bytes long */
vec[i].res_len = 8;
/* Do AUTS only for the first vector or we would use /* Do AUTS only for the first vector or we would use
* the same SQN for each following key. */ * the same SQN for each following key. */
if ((i == 0) && auts) { if ((i == 0) && auts) {
@@ -126,10 +123,10 @@ int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
DBGP("vector [%u]: resync: rand_auts = %s\n", DBGP("vector [%u]: resync: rand_auts = %s\n",
i, hex(rand_auts, 16)); i, hex(rand_auts, 16));
rc = osmo_auth_gen_vec_auts2(vec+i, aud3g, auts, rc = osmo_auth_gen_vec_auts(vec+i, aud3g, auts,
rand_auts, rand); rand_auts, rand);
} else { } else {
rc = osmo_auth_gen_vec2(vec+i, aud3g, rand); rc = osmo_auth_gen_vec(vec+i, aud3g, rand);
} }
if (rc < 0) { if (rc < 0) {
LOGP(DAUC, LOGL_ERROR, "Error in 3G vector " LOGP(DAUC, LOGL_ERROR, "Error in 3G vector "
@@ -157,7 +154,7 @@ int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
DBGP("vector [%u]: calculating 2G separately\n", i); DBGP("vector [%u]: calculating 2G separately\n", i);
rc = osmo_auth_gen_vec2(&vtmp, aud2g, rand); rc = osmo_auth_gen_vec(&vtmp, aud2g, rand);
if (rc < 0) { if (rc < 0) {
LOGP(DAUC, LOGL_ERROR, "Error in 2G vector" LOGP(DAUC, LOGL_ERROR, "Error in 2G vector"
"generation: [%u]: rc = %d\n", i, rc); "generation: [%u]: rc = %d\n", i, rc);
@@ -168,7 +165,7 @@ int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
vec[i].auth_types |= OSMO_AUTH_TYPE_GSM; vec[i].auth_types |= OSMO_AUTH_TYPE_GSM;
} else { } else {
/* 2G only case */ /* 2G only case */
rc = osmo_auth_gen_vec2(vec+i, aud2g, rand); rc = osmo_auth_gen_vec(vec+i, aud2g, rand);
if (rc < 0) { if (rc < 0) {
LOGP(DAUC, LOGL_ERROR, "Error in 2G vector " LOGP(DAUC, LOGL_ERROR, "Error in 2G vector "
"generation: [%u]: rc = %d\n", i, rc); "generation: [%u]: rc = %d\n", i, rc);

View File

@@ -1,6 +1,6 @@
/* OsmoHLR Control Interface implementation */ /* OsmoHLR Control Interface implementation */
/* (C) 2017-2023 sysmocom s.f.m.c. GmbH <info@sysmocom.de> /* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved * All Rights Reserved
* *
* Author: Max Suraev <msuraev@sysmocom.de> * Author: Max Suraev <msuraev@sysmocom.de>
@@ -31,16 +31,12 @@
#include <osmocom/hlr/hlr.h> #include <osmocom/hlr/hlr.h>
#include <osmocom/hlr/ctrl.h> #include <osmocom/hlr/ctrl.h>
#include <osmocom/hlr/db.h> #include <osmocom/hlr/db.h>
#include <osmocom/hlr/hlr_vty.h>
#define SEL_BY "by-" #define SEL_BY "by-"
#define SEL_BY_IMSI SEL_BY "imsi-" #define SEL_BY_IMSI SEL_BY "imsi-"
#define SEL_BY_MSISDN SEL_BY "msisdn-" #define SEL_BY_MSISDN SEL_BY "msisdn-"
#define SEL_BY_ID SEL_BY "id-" #define SEL_BY_ID SEL_BY "id-"
extern bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
int *minlen, int *maxlen, int *minlen_opc, int *maxlen_opc);
#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf)) #define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
static bool startswith(const char *str, const char *start) static bool startswith(const char *str, const char *start)
@@ -166,7 +162,7 @@ static void print_subscr_info(struct ctrl_cmd *cmd,
); );
} }
static void print_subscr_info_aud2g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data2 *aud) static void print_subscr_info_aud2g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data *aud)
{ {
if (aud->algo == OSMO_AUTH_ALG_NONE) if (aud->algo == OSMO_AUTH_ALG_NONE)
return; return;
@@ -178,7 +174,7 @@ static void print_subscr_info_aud2g(struct ctrl_cmd *cmd, struct osmo_sub_auth_d
hexdump_buf(aud->u.gsm.ki)); hexdump_buf(aud->u.gsm.ki));
} }
static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data2 *aud) static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data *aud)
{ {
if (aud->algo == OSMO_AUTH_ALG_NONE) if (aud->algo == OSMO_AUTH_ALG_NONE)
return; return;
@@ -187,7 +183,7 @@ static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_d
"\naud3g.k\t%s" "\naud3g.k\t%s"
, ,
osmo_auth_alg_name(aud->algo), osmo_auth_alg_name(aud->algo),
osmo_hexdump_nospc(aud->u.umts.k, aud->u.umts.k_len)); hexdump_buf(aud->u.umts.k));
/* hexdump uses a static string buffer, hence only one hexdump per /* hexdump uses a static string buffer, hence only one hexdump per
* printf(). */ * printf(). */
ctrl_cmd_reply_printf(cmd, ctrl_cmd_reply_printf(cmd,
@@ -196,82 +192,11 @@ static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_d
"\naud3g.sqn\t%" PRIu64 "\naud3g.sqn\t%" PRIu64
, ,
aud->u.umts.opc_is_op? "op" : "opc", aud->u.umts.opc_is_op? "op" : "opc",
osmo_hexdump_nospc(aud->u.umts.opc, aud->u.umts.opc_len), hexdump_buf(aud->u.umts.opc),
aud->u.umts.ind_bitlen, aud->u.umts.ind_bitlen,
aud->u.umts.sqn); aud->u.umts.sqn);
} }
CTRL_CMD_DEFINE_WO_NOVRF(subscr_create, "create");
static int set_subscr_create(struct ctrl_cmd *cmd, void *data)
{
struct hlr_subscriber subscr;
struct hlr *hlr = data;
const char *imsi = cmd->value;
int rc;
if (!osmo_imsi_str_valid(imsi)) {
cmd->reply = "Invalid IMSI value.";
return CTRL_CMD_ERROR;
}
/* Create the subscriber in the DB */
rc = db_subscr_create(g_hlr->dbc, imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS);
if (rc) {
if (rc == -EEXIST)
cmd->reply = "Subscriber already exists.";
else
cmd->reply = "Cannot create subscriber.";
return CTRL_CMD_ERROR;
}
LOGP(DCTRL, LOGL_INFO, "Created subscriber IMSI='%s'\n",
imsi);
/* Retrieve data of newly created subscriber: */
rc = db_subscr_get_by_imsi(hlr->dbc, imsi, &subscr);
if (rc < 0) {
cmd->reply = "Failed retrieving ID of newly created subscriber.";
return CTRL_CMD_ERROR;
}
cmd->reply = talloc_asprintf(cmd, "%" PRIu64, subscr.id);
return CTRL_CMD_REPLY;
}
CTRL_CMD_DEFINE_WO_NOVRF(subscr_delete, "delete");
static int set_subscr_delete(struct ctrl_cmd *cmd, void *data)
{
struct hlr_subscriber subscr;
struct hlr *hlr = data;
const char *imsi = cmd->value;
int rc;
if (!osmo_imsi_str_valid(imsi)) {
cmd->reply = "Invalid IMSI value.";
return CTRL_CMD_ERROR;
}
/* Retrieve data of newly created subscriber: */
rc = db_subscr_get_by_imsi(hlr->dbc, imsi, &subscr);
if (rc < 0) {
cmd->reply = "Subscriber doesn't exist.";
return CTRL_CMD_ERROR;
}
/* Create the subscriber in the DB */
rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
if (rc) {
cmd->reply = "Cannot delete subscriber.";
return CTRL_CMD_ERROR;
}
LOGP(DCTRL, LOGL_INFO, "Deleted subscriber IMSI='%s'\n",
imsi);
cmd->reply = talloc_asprintf(cmd, "%" PRIu64, subscr.id);
return CTRL_CMD_REPLY;
}
CTRL_CMD_DEFINE_RO(subscr_info, "info"); CTRL_CMD_DEFINE_RO(subscr_info, "info");
static int get_subscr_info(struct ctrl_cmd *cmd, void *data) static int get_subscr_info(struct ctrl_cmd *cmd, void *data)
{ {
@@ -291,8 +216,8 @@ CTRL_CMD_DEFINE_RO(subscr_info_aud, "info-aud");
static int get_subscr_info_aud(struct ctrl_cmd *cmd, void *data) static int get_subscr_info_aud(struct ctrl_cmd *cmd, void *data)
{ {
const char *imsi; const char *imsi;
struct osmo_sub_auth_data2 aud2g; struct osmo_sub_auth_data aud2g;
struct osmo_sub_auth_data2 aud3g; struct osmo_sub_auth_data aud3g;
struct hlr *hlr = data; struct hlr *hlr = data;
const char *by_selector = cmd->node; const char *by_selector = cmd->node;
int rc; int rc;
@@ -327,8 +252,8 @@ CTRL_CMD_DEFINE_RO(subscr_info_all, "info-all");
static int get_subscr_info_all(struct ctrl_cmd *cmd, void *data) static int get_subscr_info_all(struct ctrl_cmd *cmd, void *data)
{ {
struct hlr_subscriber subscr; struct hlr_subscriber subscr;
struct osmo_sub_auth_data2 aud2g; struct osmo_sub_auth_data aud2g;
struct osmo_sub_auth_data2 aud3g; struct osmo_sub_auth_data aud3g;
struct hlr *hlr = data; struct hlr *hlr = data;
const char *by_selector = cmd->node; const char *by_selector = cmd->node;
int rc; int rc;
@@ -426,302 +351,17 @@ static int set_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data)
return set_subscr_cs_ps_enabled(cmd, data, false); return set_subscr_cs_ps_enabled(cmd, data, false);
} }
CTRL_CMD_DEFINE(subscr_msisdn, "msisdn"); int hlr_ctrl_cmds_install()
static int verify_subscr_msisdn(struct ctrl_cmd *cmd, const char *value, void *data)
{ {
struct hlr_subscriber subscr; int rc = 0;
if (!value)
return 1;
if (strlen(value) > sizeof(subscr.msisdn) - 1)
return 1;
if (strcmp(value, "none") != 0 && !osmo_msisdn_str_valid(value))
return 1;
return 0;
}
static int get_subscr_msisdn(struct ctrl_cmd *cmd, void *data)
{
struct hlr_subscriber subscr;
struct hlr *hlr = data;
const char *by_selector = cmd->node;
if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd)) rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info);
return CTRL_CMD_ERROR; 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);
if (strlen(subscr.msisdn) == 0) return rc;
snprintf(subscr.msisdn, sizeof(subscr.msisdn), "none");
cmd->reply = talloc_asprintf(cmd, "%s", subscr.msisdn);
return CTRL_CMD_REPLY;
}
static int set_subscr_msisdn(struct ctrl_cmd *cmd, void *data)
{
struct hlr_subscriber subscr;
struct hlr *hlr = data;
const char *by_selector = cmd->node;
const char *msisdn;
if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
return CTRL_CMD_ERROR;
if (strcmp(cmd->value, "none") == 0)
msisdn = NULL;
else
msisdn = cmd->value;
if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
cmd->reply = "Update MSISDN failed";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
/* value format: <algo[,KI]> */
CTRL_CMD_DEFINE(subscr_aud2g, "aud2g");
static int verify_subscr_aud2g(struct ctrl_cmd *cmd, const char *value, void *data)
{
if (!value)
return 1;
if (strcasecmp(value, "none") != 0 && !strchr(value, ','))
return 1;
return 0;
}
static int get_subscr_aud2g(struct ctrl_cmd *cmd, void *data)
{
struct hlr_subscriber subscr;
struct hlr *hlr = data;
const char *by_selector = cmd->node;
struct osmo_sub_auth_data2 aud2g;
struct osmo_sub_auth_data2 aud3g_unused;
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_unused, NULL);
switch (rc) {
case 0:
break;
case -ENOENT:
case -ENOKEY:
aud2g.algo = OSMO_AUTH_ALG_NONE;
break;
default:
cmd->reply = "Error retrieving data from database.";
return CTRL_CMD_ERROR;
}
if (aud2g.algo == OSMO_AUTH_ALG_NONE) {
cmd->reply = "none";
return CTRL_CMD_REPLY;
}
cmd->reply = talloc_asprintf(cmd, "%s,%s", osmo_auth_alg_name(aud2g.algo),
hexdump_buf(aud2g.u.gsm.ki));
return CTRL_CMD_REPLY;
}
static int set_subscr_aud2g(struct ctrl_cmd *cmd, void *data)
{
struct hlr_subscriber subscr;
struct hlr *hlr = data;
const char *by_selector = cmd->node;
char *tmp = NULL, *tok, *saveptr;
int minlen = 0;
int maxlen = 0;
struct sub_auth_data_str aud2g = {
.type = OSMO_AUTH_TYPE_GSM
};
if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
return CTRL_CMD_ERROR;
tmp = talloc_strdup(cmd, cmd->value);
if (!tmp) {
cmd->reply = "OOM";
return CTRL_CMD_ERROR;
}
/* Parse alg_type: */
tok = strtok_r(tmp, ",", &saveptr);
if (!tok) {
cmd->reply = "Invalid format";
return CTRL_CMD_ERROR;
}
if (strcmp(tok, "none") == 0) {
aud2g.algo = OSMO_AUTH_ALG_NONE;
} else if (!auth_algo_parse(tok, &aud2g.algo, &minlen, &maxlen, NULL, NULL)) {
cmd->reply = "Unknown auth algorithm.";
return CTRL_CMD_ERROR;
}
if (aud2g.algo != OSMO_AUTH_ALG_NONE) {
tok = strtok_r(NULL, "\0", &saveptr);
if (!tok) {
cmd->reply = "Invalid format.";
return CTRL_CMD_ERROR;
}
aud2g.u.gsm.ki = tok;
if (!osmo_is_hexstr(aud2g.u.gsm.ki, minlen * 2, maxlen * 2, true)) {
cmd->reply = "Invalid KI.";
return CTRL_CMD_ERROR;
}
}
if (db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g)) {
cmd->reply = "Update aud2g failed.";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
/* value format: <algo[,KI,(op|opc),OP_C[,ind_bitlen]]> */
CTRL_CMD_DEFINE(subscr_aud3g, "aud3g");
static int verify_subscr_aud3g(struct ctrl_cmd *cmd, const char *value, void *data)
{
if (!value)
return 1;
if (strcasecmp(value, "none") != 0 && !strchr(value, ','))
return 1;
return 0;
}
static int get_subscr_aud3g(struct ctrl_cmd *cmd, void *data)
{
struct hlr_subscriber subscr;
struct hlr *hlr = data;
const char *by_selector = cmd->node;
struct osmo_sub_auth_data2 aud2g_unused;
struct osmo_sub_auth_data2 aud3g;
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_unused, &aud3g, NULL);
switch (rc) {
case 0:
break;
case -ENOENT:
case -ENOKEY:
aud3g.algo = OSMO_AUTH_ALG_NONE;
break;
default:
cmd->reply = "Error retrieving data from database.";
return CTRL_CMD_ERROR;
}
if (aud3g.algo == OSMO_AUTH_ALG_NONE) {
cmd->reply = "none";
return CTRL_CMD_REPLY;
}
cmd->reply = talloc_asprintf(cmd, "%s,%s,%s,%s,%u", osmo_auth_alg_name(aud3g.algo),
osmo_hexdump_nospc_c(cmd, aud3g.u.umts.k, aud3g.u.umts.k_len),
aud3g.u.umts.opc_is_op ? "OP" : "OPC",
osmo_hexdump_nospc_c(cmd, aud3g.u.umts.opc, aud3g.u.umts.opc_len),
aud3g.u.umts.ind_bitlen);
return CTRL_CMD_REPLY;
}
static int set_subscr_aud3g(struct ctrl_cmd *cmd, void *data)
{
struct hlr_subscriber subscr;
struct hlr *hlr = data;
const char *by_selector = cmd->node;
char *tmp = NULL, *tok, *saveptr;
int minlen = 0, minlen_opc = 0;
int maxlen = 0, maxlen_opc = 0;
struct sub_auth_data_str aud3g = {
.type = OSMO_AUTH_TYPE_UMTS,
.u.umts = {
.ind_bitlen = 5,
},
};
bool ind_bitlen_present;
if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
return CTRL_CMD_ERROR;
tmp = talloc_strdup(cmd, cmd->value);
if (!tmp) {
cmd->reply = "OOM";
return CTRL_CMD_ERROR;
}
/* Parse alg_type: */
tok = strtok_r(tmp, ",", &saveptr);
if (!tok) {
cmd->reply = "Invalid format.";
return CTRL_CMD_ERROR;
}
if (strcmp(tok, "none") == 0) {
aud3g.algo = OSMO_AUTH_ALG_NONE;
} else if (!auth_algo_parse(tok, &aud3g.algo, &minlen, &maxlen, &minlen_opc, &maxlen_opc)) {
cmd->reply = "Unknown auth algorithm.";
return CTRL_CMD_ERROR;
}
if (aud3g.algo != OSMO_AUTH_ALG_NONE) {
/* Parse K */
tok = strtok_r(NULL, ",", &saveptr);
if (!tok) {
cmd->reply = "Invalid format.";
return CTRL_CMD_ERROR;
}
aud3g.u.umts.k = tok;
if (!osmo_is_hexstr(aud3g.u.umts.k, minlen * 2, maxlen * 2, true)) {
cmd->reply = "Invalid KI.";
return CTRL_CMD_ERROR;
}
/* Parse OP/OPC choice */
tok = strtok_r(NULL, ",", &saveptr);
if (!tok) {
cmd->reply = "Invalid format.";
return CTRL_CMD_ERROR;
}
if (strcasecmp(tok, "op") == 0) {
aud3g.u.umts.opc_is_op = true;
} else if (strcasecmp(tok, "opc") == 0) {
aud3g.u.umts.opc_is_op = false;
} else {
cmd->reply = "Invalid format.";
return CTRL_CMD_ERROR;
}
/* Parse OP/OPC value */
ind_bitlen_present = !!strchr(saveptr, ',');
tok = strtok_r(NULL, ind_bitlen_present ? "," : "\0", &saveptr);
if (!tok) {
cmd->reply = "Invalid format.";
return CTRL_CMD_ERROR;
}
aud3g.u.umts.opc = tok;
if (!osmo_is_hexstr(aud3g.u.umts.opc, minlen_opc * 2, maxlen_opc * 2, true)) {
cmd->reply = talloc_asprintf(cmd, "Invalid OP/OPC.");
return CTRL_CMD_ERROR;
}
if (ind_bitlen_present) {
/* Parse bitlen_ind */
tok = strtok_r(NULL, "\0", &saveptr);
if (!tok || tok[0] == '\0') {
cmd->reply = "Invalid format.";
return CTRL_CMD_ERROR;
}
aud3g.u.umts.ind_bitlen = atoi(tok);
}
}
if (db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g)) {
cmd->reply = "Update aud3g failed.";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
} }
static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type, static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type,
@@ -749,30 +389,14 @@ static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type,
return 1; return 1;
} }
static int hlr_ctrl_cmds_install(void)
{
int rc = 0;
rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR, &cmd_subscr_create);
rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR, &cmd_subscr_delete);
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);
rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_msisdn);
rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_aud2g);
rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_aud3g);
return rc;
}
struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr) struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr)
{ {
int rc; int rc;
struct ctrl_handle *hdl = ctrl_interface_setup2(hlr, OSMO_CTRL_PORT_HLR, hlr_ctrl_node_lookup, struct ctrl_handle *hdl = ctrl_interface_setup_dynip2(hlr,
_LAST_CTRL_NODE_HLR); hlr->ctrl_bind_addr,
OSMO_CTRL_PORT_HLR,
hlr_ctrl_node_lookup,
_LAST_CTRL_NODE_HLR);
if (!hdl) if (!hdl)
return NULL; return NULL;

110
src/db.c
View File

@@ -1,4 +1,4 @@
/* (C) 2015-2023 by Harald Welte <laforge@gnumonks.org> /* (C) 2015 by Harald Welte <laforge@gnumonks.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@@ -22,13 +22,15 @@
#include <stdbool.h> #include <stdbool.h>
#include <sqlite3.h> #include <sqlite3.h>
#include <string.h> #include <string.h>
#include <errno.h>
#include <osmocom/hlr/logging.h> #include <osmocom/hlr/logging.h>
#include <osmocom/hlr/db.h> #include <osmocom/hlr/db.h>
#include "db_bootstrap.h" #include "db_bootstrap.h"
/* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */ /* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */
#define CURRENT_SCHEMA_VERSION 7 #define CURRENT_SCHEMA_VERSION 8
#define SEL_COLUMNS \ #define SEL_COLUMNS \
"id," \ "id," \
@@ -47,18 +49,12 @@
"ms_purged_ps," \ "ms_purged_ps," \
"last_lu_seen," \ "last_lu_seen," \
"last_lu_seen_ps," \ "last_lu_seen_ps," \
"last_lu_rat_cs," \
"last_lu_rat_ps," \
"vlr_via_proxy," \ "vlr_via_proxy," \
"sgsn_via_proxy" "sgsn_via_proxy"
static const char *stmt_sql[] = { static const char *stmt_sql[] = {
[DB_STMT_SEL_ALL] = "SELECT " SEL_COLUMNS " FROM subscriber;",
[DB_STMT_SEL_ALL_ORDER_LAST_SEEN] = "SELECT " SEL_COLUMNS " FROM subscriber "
"WHERE last_lu_seen IS NOT NULL ORDER BY last_lu_seen;",
[DB_STMT_SEL_FILTER_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn LIKE $search ORDER BY msisdn",
[DB_STMT_SEL_FILTER_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi LIKE $search ORDER BY imsi",
[DB_STMT_SEL_FILTER_IMEI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imei LIKE $search ORDER BY imei",
[DB_STMT_SEL_FILTER_CS] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE nam_cs = $search ORDER BY last_lu_seen",
[DB_STMT_SEL_FILTER_PS] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE nam_ps = $search ORDER BY last_lu_seen",
[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?", [DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
[DB_STMT_SEL_BY_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn = ?", [DB_STMT_SEL_BY_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn = ?",
[DB_STMT_SEL_BY_ID] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE id = ?", [DB_STMT_SEL_BY_ID] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE id = ?",
@@ -89,13 +85,23 @@ static const char *stmt_sql[] = {
"INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)" "INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)"
" VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)", " VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)",
[DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id", [DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id",
[DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch') WHERE id = $subscriber_id", [DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch'),"
[DB_STMT_SET_LAST_LU_SEEN_PS] = "UPDATE subscriber SET last_lu_seen_ps = datetime($val, 'unixepoch') WHERE id = $subscriber_id", " last_lu_rat_cs = $rat"
" WHERE id = $subscriber_id",
[DB_STMT_SET_LAST_LU_SEEN_PS] = "UPDATE subscriber SET last_lu_seen_ps = datetime($val, 'unixepoch'),"
" last_lu_rat_ps = $rat"
" WHERE id = $subscriber_id",
[DB_STMT_EXISTS_BY_IMSI] = "SELECT 1 FROM subscriber WHERE imsi = $imsi", [DB_STMT_EXISTS_BY_IMSI] = "SELECT 1 FROM subscriber WHERE imsi = $imsi",
[DB_STMT_EXISTS_BY_MSISDN] = "SELECT 1 FROM subscriber WHERE msisdn = $msisdn", [DB_STMT_EXISTS_BY_MSISDN] = "SELECT 1 FROM subscriber WHERE msisdn = $msisdn",
[DB_STMT_IND_ADD] = "INSERT INTO ind (vlr) VALUES ($vlr)", [DB_STMT_IND_ADD] = "INSERT INTO ind (vlr) VALUES ($vlr)",
[DB_STMT_IND_SELECT] = "SELECT ind FROM ind WHERE vlr = $vlr", [DB_STMT_IND_SELECT] = "SELECT ind FROM ind WHERE vlr = $vlr",
[DB_STMT_IND_DEL] = "DELETE FROM ind WHERE vlr = $vlr", [DB_STMT_IND_DEL] = "DELETE FROM ind WHERE vlr = $vlr",
[DB_STMT_UPD_RAT_FLAG] = "INSERT OR REPLACE INTO subscriber_rat (subscriber_id, rat, allowed)"
" VALUES ($subscriber_id, $rat, $allowed)",
[DB_STMT_RAT_BY_ID] =
"SELECT rat, allowed"
" FROM subscriber_rat"
" WHERE subscriber_id = $subscriber_id",
}; };
static void sql3_error_log_cb(void *arg, int err_code, const char *msg) static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
@@ -235,9 +241,9 @@ void db_close(struct db_context *dbc)
talloc_free(dbc); talloc_free(dbc);
} }
static int db_run_statements(struct db_context *dbc, const char * const *statements, size_t statements_count) static int db_run_statements(struct db_context *dbc, const char **statements, size_t statements_count)
{ {
int rc = 0; int rc;
int i; int i;
for (i = 0; i < statements_count; i++) { for (i = 0; i < statements_count; i++) {
const char *stmt_str = statements[i]; const char *stmt_str = statements[i];
@@ -308,7 +314,7 @@ static int
db_upgrade_v1(struct db_context *dbc) db_upgrade_v1(struct db_context *dbc)
{ {
int rc; int rc;
const char * const statements[] = { const char *statements[] = {
"ALTER TABLE subscriber ADD COLUMN last_lu_seen TIMESTAMP default NULL", "ALTER TABLE subscriber ADD COLUMN last_lu_seen TIMESTAMP default NULL",
"PRAGMA user_version = 1", "PRAGMA user_version = 1",
}; };
@@ -324,7 +330,7 @@ db_upgrade_v1(struct db_context *dbc)
static int db_upgrade_v2(struct db_context *dbc) static int db_upgrade_v2(struct db_context *dbc)
{ {
int rc; int rc;
const char * const statements[] = { const char *statements[] = {
"ALTER TABLE subscriber ADD COLUMN imei VARCHAR(14)", "ALTER TABLE subscriber ADD COLUMN imei VARCHAR(14)",
"PRAGMA user_version = 2", "PRAGMA user_version = 2",
}; };
@@ -341,7 +347,7 @@ static int db_upgrade_v3(struct db_context *dbc)
{ {
int rc; int rc;
/* A newer SQLite version would allow simply 'ATLER TABLE subscriber RENAME COLUMN hlr_number TO msc_number'. /* A newer SQLite version would allow simply 'ALTER TABLE subscriber RENAME COLUMN hlr_number TO msc_number'.
* This is a really expensive workaround for that in order to cover earlier SQLite versions as well: * This is a really expensive workaround for that in order to cover earlier SQLite versions as well:
* Create a new table with the new column name and copy the data over (https://www.sqlite.org/faq.html#q11). * Create a new table with the new column name and copy the data over (https://www.sqlite.org/faq.html#q11).
*/ */
@@ -437,7 +443,7 @@ static int db_upgrade_v3(struct db_context *dbc)
"ms_purged_ps," \ "ms_purged_ps," \
"last_lu_seen" "last_lu_seen"
const char * const statements[] = { const char *statements[] = {
"BEGIN TRANSACTION", "BEGIN TRANSACTION",
"CREATE TEMPORARY TABLE subscriber_backup" SUBSCR_V3_CREATE, "CREATE TEMPORARY TABLE subscriber_backup" SUBSCR_V3_CREATE,
"INSERT INTO subscriber_backup SELECT " SUBSCR_V2_COLUMN_NAMES " FROM subscriber", "INSERT INTO subscriber_backup SELECT " SUBSCR_V2_COLUMN_NAMES " FROM subscriber",
@@ -460,7 +466,7 @@ static int db_upgrade_v3(struct db_context *dbc)
static int db_upgrade_v4(struct db_context *dbc) static int db_upgrade_v4(struct db_context *dbc)
{ {
int rc; int rc;
const char * const statements[] = { const char *statements[] = {
"ALTER TABLE subscriber ADD COLUMN last_lu_seen_ps TIMESTAMP default NULL", "ALTER TABLE subscriber ADD COLUMN last_lu_seen_ps TIMESTAMP default NULL",
"PRAGMA user_version = 4", "PRAGMA user_version = 4",
}; };
@@ -476,7 +482,7 @@ static int db_upgrade_v4(struct db_context *dbc)
static int db_upgrade_v5(struct db_context *dbc) static int db_upgrade_v5(struct db_context *dbc)
{ {
int rc; int rc;
const char * const statements[] = { const char *statements[] = {
"ALTER TABLE subscriber ADD COLUMN vlr_via_proxy VARCHAR", "ALTER TABLE subscriber ADD COLUMN vlr_via_proxy VARCHAR",
"ALTER TABLE subscriber ADD COLUMN sgsn_via_proxy VARCHAR", "ALTER TABLE subscriber ADD COLUMN sgsn_via_proxy VARCHAR",
"PRAGMA user_version = 5", "PRAGMA user_version = 5",
@@ -493,7 +499,7 @@ static int db_upgrade_v5(struct db_context *dbc)
static int db_upgrade_v6(struct db_context *dbc) static int db_upgrade_v6(struct db_context *dbc)
{ {
int rc; int rc;
const char * const statements[] = { const char *statements[] = {
"CREATE TABLE ind (\n" "CREATE TABLE ind (\n"
" -- 3G auth IND pool to be used for this VLR\n" " -- 3G auth IND pool to be used for this VLR\n"
" ind INTEGER PRIMARY KEY,\n" " ind INTEGER PRIMARY KEY,\n"
@@ -516,33 +522,34 @@ static int db_upgrade_v6(struct db_context *dbc)
static int db_upgrade_v7(struct db_context *dbc) static int db_upgrade_v7(struct db_context *dbc)
{ {
int rc; int rc;
/* SQLite doesn't allow us to change the column type in-place, so we const char *statements[] = {
* first rename the old table, create a new table and then copy "-- Optional: add subscriber entries to allow or disallow specific RATs (2G or 3G or ...).\n"
* the data over before deleting the old table */ "-- If a subscriber has no entry, that means that all RATs are allowed (backwards compat).\n"
#define CREATE_AUC_3G_V7 \ "CREATE TABLE subscriber_rat (\n"
"CREATE TABLE auc_3g (\n" \ " subscriber_id INTEGER, -- subscriber.id\n"
" subscriber_id INTEGER PRIMARY KEY, -- subscriber.id\n" \ " rat TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu')) NOT NULL, -- Radio Access Technology, see enum ran_type\n"
" algo_id_3g INTEGER NOT NULL, -- enum osmo_auth_algo value\n" \ " allowed BOOLEAN CHECK(allowed in (0, 1)) NOT NULL DEFAULT 0,\n"
" k VARCHAR(64) NOT NULL, -- hex string: subscriber's secret key (128/256bit)\n" \ " UNIQUE (subscriber_id, rat)\n"
" op VARCHAR(64), -- hex string: operator's secret key (128/256bit)\n" \ ")",
" opc VARCHAR(64), -- hex string: derived from OP and K (128/256bit)\n" \ "CREATE UNIQUE INDEX idx_subscr_rat_flag ON subscriber_rat (subscriber_id, rat)",
" sqn INTEGER NOT NULL DEFAULT 0, -- sequence number of key usage\n" \
" -- nr of index bits at lower SQN end\n" \
" ind_bitlen INTEGER NOT NULL DEFAULT 5\n" \
");"
const char * const statements[] = {
"BEGIN TRANSACTION",
/* rename old table */
"ALTER TABLE auc_3g RENAME TO old_auc_3g",
/* create new table */
CREATE_AUC_3G_V7,
/* copy over old data */
"INSERT INTO auc_3g SELECT subscriber_id, algo_id_3g, k, op, opc,sqn, ind_bitlen FROM old_auc_3g",
/* delete old table */
"DROP TABLE old_auc_3g",
/* update user_version */
"PRAGMA user_version = 7", "PRAGMA user_version = 7",
"COMMIT", };
rc = db_run_statements(dbc, statements, ARRAY_SIZE(statements));
if (rc != SQLITE_DONE) {
LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version 7\n");
return rc;
}
return rc;
}
static int db_upgrade_v8(struct db_context *dbc)
{
int rc;
const char *statements[] = {
"ALTER TABLE subscriber ADD COLUMN last_lu_rat_cs TEXT default NULL",
"ALTER TABLE subscriber ADD COLUMN last_lu_rat_ps TEXT default NULL",
"PRAGMA user_version = 8",
}; };
rc = db_run_statements(dbc, statements, ARRAY_SIZE(statements)); rc = db_run_statements(dbc, statements, ARRAY_SIZE(statements));
@@ -562,6 +569,7 @@ static db_upgrade_func_t db_upgrade_path[] = {
db_upgrade_v5, db_upgrade_v5,
db_upgrade_v6, db_upgrade_v6,
db_upgrade_v7, db_upgrade_v7,
db_upgrade_v8,
}; };
static int db_get_user_version(struct db_context *dbc) static int db_get_user_version(struct db_context *dbc)
@@ -650,11 +658,9 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
char *err_msg; char *err_msg;
rc = sqlite3_exec(dbc->db, "PRAGMA journal_mode=WAL; PRAGMA synchonous = NORMAL;", 0, 0, &err_msg); rc = sqlite3_exec(dbc->db, "PRAGMA journal_mode=WAL; PRAGMA synchonous = NORMAL;", 0, 0, &err_msg);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK)
LOGP(DDB, LOGL_ERROR, "Unable to set Write-Ahead Logging: %s\n", LOGP(DDB, LOGL_ERROR, "Unable to set Write-Ahead Logging: %s\n",
err_msg); err_msg);
sqlite3_free(err_msg);
}
version = db_get_user_version(dbc); version = db_get_user_version(dbc);
if (version < 0) { if (version < 0) {
@@ -692,9 +698,9 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
if (version < CURRENT_SCHEMA_VERSION) { if (version < CURRENT_SCHEMA_VERSION) {
LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version); LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version);
if (!allow_upgrade) { if (!allow_upgrade) {
LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database to schema version %d; " LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database from schema version %d to %d; "
"use the --db-upgrade option to allow HLR database upgrades\n", "use the --db-upgrade option to allow HLR database upgrades\n",
CURRENT_SCHEMA_VERSION); version, CURRENT_SCHEMA_VERSION);
} }
} else } else
LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version); LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version);

View File

@@ -1,4 +1,4 @@
/* (C) 2015-2023 by Harald Welte <laforge@gnumonks.org> /* (C) 2015 by Harald Welte <laforge@gnumonks.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@@ -74,9 +74,9 @@ out:
} }
/* hexparse a specific column of a sqlite prepared statement into dst (with length check) /* hexparse a specific column of a sqlite prepared statement into dst (with length check)
* returns byte length in case of success, -EIO on error */ * returns 0 for success, -EIO on error */
static int hexparse_stmt(uint8_t *dst, size_t dst_len_min, size_t dst_len_max, sqlite3_stmt *stmt, static int hexparse_stmt(uint8_t *dst, size_t dst_len, sqlite3_stmt *stmt, int col, const char *col_name,
int col, const char *col_name, const char *imsi) const char *imsi)
{ {
const uint8_t *text; const uint8_t *text;
size_t col_len; size_t col_len;
@@ -84,15 +84,9 @@ static int hexparse_stmt(uint8_t *dst, size_t dst_len_min, size_t dst_len_max, s
/* Bytes are stored as hex strings in database, hence divide length by two */ /* Bytes are stored as hex strings in database, hence divide length by two */
col_len = sqlite3_column_bytes(stmt, col) / 2; col_len = sqlite3_column_bytes(stmt, col) / 2;
if (col_len < dst_len_min) { if (col_len != dst_len) {
LOGAUC(imsi, LOGL_ERROR, "Error reading %s, expected min length %lu but has length %lu\n", col_name, LOGAUC(imsi, LOGL_ERROR, "Error reading %s, expected length %lu but has length %lu\n", col_name,
dst_len_min, col_len); dst_len, col_len);
return -EIO;
}
if (col_len > dst_len_max) {
LOGAUC(imsi, LOGL_ERROR, "Error reading %s, expected max length %lu but has length %lu\n", col_name,
dst_len_max, col_len);
return -EIO; return -EIO;
} }
@@ -101,11 +95,8 @@ static int hexparse_stmt(uint8_t *dst, size_t dst_len_min, size_t dst_len_max, s
LOGAUC(imsi, LOGL_ERROR, "Error reading %s\n", col_name); LOGAUC(imsi, LOGL_ERROR, "Error reading %s\n", col_name);
return -EIO; return -EIO;
} }
osmo_hexparse((void *)text, dst, dst_len);
if (osmo_hexparse((void *)text, dst, dst_len_max) != col_len) return 0;
return -EINVAL;
return col_len;
} }
/* obtain the authentication data for a given imsi /* obtain the authentication data for a given imsi
@@ -113,8 +104,8 @@ static int hexparse_stmt(uint8_t *dst, size_t dst_len_min, size_t dst_len_max, s
* -ENOENT if the IMSI is not known, -ENOKEY if the IMSI is known but has no auth data, * -ENOENT if the IMSI is not known, -ENOKEY if the IMSI is known but has no auth data,
* -EIO on db failure */ * -EIO on db failure */
int db_get_auth_data(struct db_context *dbc, const char *imsi, int db_get_auth_data(struct db_context *dbc, const char *imsi,
struct osmo_sub_auth_data2 *aud2g, struct osmo_sub_auth_data *aud2g,
struct osmo_sub_auth_data2 *aud3g, struct osmo_sub_auth_data *aud3g,
int64_t *subscr_id) int64_t *subscr_id)
{ {
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_AUC_BY_IMSI]; sqlite3_stmt *stmt = dbc->stmt[DB_STMT_AUC_BY_IMSI];
@@ -148,8 +139,7 @@ int db_get_auth_data(struct db_context *dbc, const char *imsi,
/* obtain result values using sqlite3_column_*() */ /* obtain result values using sqlite3_column_*() */
if (sqlite3_column_type(stmt, 1) == SQLITE_INTEGER) { if (sqlite3_column_type(stmt, 1) == SQLITE_INTEGER) {
/* we do have some 2G authentication data */ /* we do have some 2G authentication data */
if (hexparse_stmt(aud2g->u.gsm.ki, sizeof(aud2g->u.gsm.ki), sizeof(aud2g->u.gsm.ki), if (hexparse_stmt(aud2g->u.gsm.ki, sizeof(aud2g->u.gsm.ki), stmt, 2, "Ki", imsi))
stmt, 2, "Ki", imsi) < 0)
goto end_2g; goto end_2g;
aud2g->algo = sqlite3_column_int(stmt, 1); aud2g->algo = sqlite3_column_int(stmt, 1);
aud2g->type = OSMO_AUTH_TYPE_GSM; aud2g->type = OSMO_AUTH_TYPE_GSM;
@@ -158,30 +148,24 @@ int db_get_auth_data(struct db_context *dbc, const char *imsi,
end_2g: end_2g:
if (sqlite3_column_type(stmt, 3) == SQLITE_INTEGER) { if (sqlite3_column_type(stmt, 3) == SQLITE_INTEGER) {
/* we do have some 3G authentication data */ /* we do have some 3G authentication data */
rc = hexparse_stmt(aud3g->u.umts.k, 16, sizeof(aud3g->u.umts.k), stmt, 4, "K", imsi); if (hexparse_stmt(aud3g->u.umts.k, sizeof(aud3g->u.umts.k), stmt, 4, "K", imsi)) {
if (rc < 0) {
ret = -EIO; ret = -EIO;
goto out; goto out;
} }
aud3g->u.umts.k_len = rc;
aud3g->algo = sqlite3_column_int(stmt, 3); aud3g->algo = sqlite3_column_int(stmt, 3);
/* UMTS Subscribers can have either OP or OPC */ /* UMTS Subscribers can have either OP or OPC */
if (sqlite3_column_text(stmt, 5)) { if (sqlite3_column_text(stmt, 5)) {
rc = hexparse_stmt(aud3g->u.umts.opc, 16, sizeof(aud3g->u.umts.opc), stmt, 5, "OP", imsi); if (hexparse_stmt(aud3g->u.umts.opc, sizeof(aud3g->u.umts.opc), stmt, 5, "OP", imsi)) {
if (rc < 0) {
ret = -EIO; ret = -EIO;
goto out; goto out;
} }
aud3g->u.umts.opc_len = rc;
aud3g->u.umts.opc_is_op = 1; aud3g->u.umts.opc_is_op = 1;
} else { } else {
rc = hexparse_stmt(aud3g->u.umts.opc, 16, sizeof(aud3g->u.umts.opc), stmt, 6, "OPC", imsi); if (hexparse_stmt(aud3g->u.umts.opc, sizeof(aud3g->u.umts.opc), stmt, 6, "OPC", imsi)) {
if (rc < 0) {
ret = -EIO; ret = -EIO;
goto out; goto out;
} }
aud3g->u.umts.opc_len = rc;
aud3g->u.umts.opc_is_op = 0; aud3g->u.umts.opc_is_op = 0;
} }
aud3g->u.umts.sqn = sqlite3_column_int64(stmt, 7); aud3g->u.umts.sqn = sqlite3_column_int64(stmt, 7);
@@ -207,7 +191,7 @@ int db_get_auc(struct db_context *dbc, const char *imsi,
unsigned int num_vec, const uint8_t *rand_auts, unsigned int num_vec, const uint8_t *rand_auts,
const uint8_t *auts, bool separation_bit) const uint8_t *auts, bool separation_bit)
{ {
struct osmo_sub_auth_data2 aud2g, aud3g; struct osmo_sub_auth_data aud2g, aud3g;
int64_t subscr_id; int64_t subscr_id;
int ret = 0; int ret = 0;
int rc; int rc;

View File

@@ -1,4 +1,4 @@
/* (C) 2015-2023 by Harald Welte <laforge@gnumonks.org> /* (C) 2015 by Harald Welte <laforge@gnumonks.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@@ -37,7 +37,7 @@
#include <osmocom/hlr/logging.h> #include <osmocom/hlr/logging.h>
#include <osmocom/hlr/hlr.h> #include <osmocom/hlr/hlr.h>
#include <osmocom/hlr/db.h> #include <osmocom/hlr/db.h>
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
#define LOGHLR(imsi, level, fmt, args ...) LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args) #define LOGHLR(imsi, level, fmt, args ...) LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)
@@ -45,8 +45,7 @@
* \param[in,out] dbc database context. * \param[in,out] dbc database context.
* \param[in] imsi ASCII string of IMSI digits, is validated. * \param[in] imsi ASCII string of IMSI digits, is validated.
* \param[in] flags Bitmask of DB_SUBSCR_FLAG_*. * \param[in] flags Bitmask of DB_SUBSCR_FLAG_*.
* \returns 0 on success, -EINVAL on invalid IMSI, -EEXIST if subscriber with * \returns 0 on success, -EINVAL on invalid IMSI, -EIO on database error.
* provided imsi already exists, -EIO on other database errors.
*/ */
int db_subscr_create(struct db_context *dbc, const char *imsi, uint8_t flags) int db_subscr_create(struct db_context *dbc, const char *imsi, uint8_t flags)
{ {
@@ -74,8 +73,6 @@ int db_subscr_create(struct db_context *dbc, const char *imsi, uint8_t flags)
if (rc != SQLITE_DONE) { if (rc != SQLITE_DONE) {
LOGHLR(imsi, LOGL_ERROR, "Cannot create subscriber: SQL error: (%d) %s\n", LOGHLR(imsi, LOGL_ERROR, "Cannot create subscriber: SQL error: (%d) %s\n",
rc, sqlite3_errmsg(dbc->db)); rc, sqlite3_errmsg(dbc->db));
if (rc == SQLITE_CONSTRAINT_UNIQUE)
return -EEXIST;
return -EIO; return -EIO;
} }
@@ -238,9 +235,8 @@ int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
case OSMO_AUTH_ALG_COMP128v1: case OSMO_AUTH_ALG_COMP128v1:
case OSMO_AUTH_ALG_COMP128v2: case OSMO_AUTH_ALG_COMP128v2:
case OSMO_AUTH_ALG_COMP128v3: case OSMO_AUTH_ALG_COMP128v3:
case OSMO_AUTH_ALG_XOR_2G: case OSMO_AUTH_ALG_XOR:
break; break;
case OSMO_AUTH_ALG_XOR_3G:
case OSMO_AUTH_ALG_MILENAGE: case OSMO_AUTH_ALG_MILENAGE:
LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:" LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
" auth algo not suited for 2G: %s\n", " auth algo not suited for 2G: %s\n",
@@ -268,12 +264,11 @@ int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
switch (aud->algo) { switch (aud->algo) {
case OSMO_AUTH_ALG_NONE: case OSMO_AUTH_ALG_NONE:
case OSMO_AUTH_ALG_MILENAGE: case OSMO_AUTH_ALG_MILENAGE:
case OSMO_AUTH_ALG_XOR_3G:
break; break;
case OSMO_AUTH_ALG_COMP128v1: case OSMO_AUTH_ALG_COMP128v1:
case OSMO_AUTH_ALG_COMP128v2: case OSMO_AUTH_ALG_COMP128v2:
case OSMO_AUTH_ALG_COMP128v3: case OSMO_AUTH_ALG_COMP128v3:
case OSMO_AUTH_ALG_XOR_2G: case OSMO_AUTH_ALG_XOR:
LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:" LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
" auth algo not suited for 3G: %s\n", " auth algo not suited for 3G: %s\n",
osmo_auth_alg_name(aud->algo)); osmo_auth_alg_name(aud->algo));
@@ -286,12 +281,12 @@ int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
if (aud->algo == OSMO_AUTH_ALG_NONE) if (aud->algo == OSMO_AUTH_ALG_NONE)
break; break;
if (!osmo_is_hexstr(aud->u.umts.k, 32, 64, true)) { if (!osmo_is_hexstr(aud->u.umts.k, 32, 32, true)) {
LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:" LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
" Invalid K: '%s'\n", aud->u.umts.k); " Invalid K: '%s'\n", aud->u.umts.k);
return -EINVAL; return -EINVAL;
} }
if (!osmo_is_hexstr(aud->u.umts.opc, 32, 64, true)) { if (!osmo_is_hexstr(aud->u.umts.opc, 32, 32, true)) {
LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:" LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
" Invalid OP/OPC: '%s'\n", aud->u.umts.opc); " Invalid OP/OPC: '%s'\n", aud->u.umts.opc);
return -EINVAL; return -EINVAL;
@@ -488,7 +483,7 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
if (!subscr) if (!subscr)
goto out; goto out;
*subscr = (struct hlr_subscriber){}; *subscr = hlr_subscriber_empty;
/* obtain the various columns */ /* obtain the various columns */
subscr->id = sqlite3_column_int64(stmt, 0); subscr->id = sqlite3_column_int64(stmt, 0);
@@ -510,12 +505,17 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
subscr->imsi, "CS"); subscr->imsi, "CS");
parse_last_lu_seen(&subscr->last_lu_seen_ps, (const char *)sqlite3_column_text(stmt, 15), parse_last_lu_seen(&subscr->last_lu_seen_ps, (const char *)sqlite3_column_text(stmt, 15),
subscr->imsi, "PS"); subscr->imsi, "PS");
copy_sqlite3_text_to_ipa_name(&subscr->vlr_via_proxy, stmt, 16); copy_sqlite3_text_to_buf(subscr->last_lu_rat_cs, stmt, 16);
copy_sqlite3_text_to_ipa_name(&subscr->sgsn_via_proxy, stmt, 17); copy_sqlite3_text_to_buf(subscr->last_lu_rat_ps, stmt, 17);
copy_sqlite3_text_to_ipa_name(&subscr->vlr_via_proxy, stmt, 18);
copy_sqlite3_text_to_ipa_name(&subscr->sgsn_via_proxy, stmt, 19);
out: out:
db_remove_reset(stmt); db_remove_reset(stmt);
if (ret == 0)
db_subscr_get_rat_types(dbc, subscr);
switch (ret) { switch (ret) {
case 0: case 0:
*err = NULL; *err = NULL;
@@ -630,94 +630,6 @@ int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
return rc; return rc;
} }
/*! Retrieve subscriber data from the HLR database.
* \param[in,out] dbc database context.
* \param[in] filter_type ASCII string of identifier type to search.
* \param[in] filter ASCII string to search.
* \param[in] get_cb pointer to call back function for data.
* \param[in,out] data pointer to pass to callback function.
* \param[in,out] count counter for number of matched subscribers.
* \param[in,our] err
* \returns 0 on success, -ENOENT if no subscriber was found, -EIO on
* database error.
*/
int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,
void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,
int *count, const char **err)
{
sqlite3_stmt *stmt;
char search[256];
int rc;
struct hlr_subscriber subscr;
bool show_ls = false;
if (!filter_type) {
stmt = dbc->stmt[DB_STMT_SEL_ALL];
} else if (strcmp(filter_type, "imei") == 0) {
stmt = dbc->stmt[DB_STMT_SEL_FILTER_IMEI];
} else if (strcmp(filter_type, "imsi") == 0) {
stmt = dbc->stmt[DB_STMT_SEL_FILTER_IMSI];
} else if (strcmp(filter_type, "msisdn") == 0) {
stmt = dbc->stmt[DB_STMT_SEL_FILTER_MSISDN];
} else if (strcmp(filter_type, "cs") == 0) {
stmt = dbc->stmt[DB_STMT_SEL_FILTER_CS];
} else if (strcmp(filter_type, "ps") == 0) {
stmt = dbc->stmt[DB_STMT_SEL_FILTER_PS];
} else if (strcmp(filter_type, "last_lu_seen") == 0) {
show_ls = true;
stmt = dbc->stmt[DB_STMT_SEL_ALL_ORDER_LAST_SEEN];
} else {
return -EIO;
}
if (filter_type && filter && strcmp(filter_type, "last_lu_seen") != 0) {
if (strcmp(filter, "on") == 0) {
sprintf(search, "%s", "1");
} else if (strcmp(filter, "off") == 0) {
sprintf(search, "%s", "0");
} else {
sprintf(search, "%%%s%%", filter);
}
if (!db_bind_text(stmt, "$search", search)) {
*err = sqlite3_errmsg(dbc->db);
return -EIO;
}
}
rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE) {
db_remove_reset(stmt);
*err = "No matching subscriber(s)";
return -ENOENT;
}
while (rc == SQLITE_ROW) {
subscr = (struct hlr_subscriber){
.id = sqlite3_column_int64(stmt, 0),};
copy_sqlite3_text_to_buf(subscr.imsi, stmt, 1);
copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
copy_sqlite3_text_to_buf(subscr.imei, stmt, 3);
subscr.nam_cs = sqlite3_column_int(stmt, 9);
subscr.nam_ps = sqlite3_column_int(stmt, 10);
if (show_ls)
parse_last_lu_seen(&subscr.last_lu_seen, (const char *)sqlite3_column_text(stmt, 14),
subscr.imsi, "CS");
get_cb(&subscr, data);
rc = sqlite3_step(stmt);
(*count)++;
}
db_remove_reset(stmt);
if (rc != SQLITE_DONE) {
*err = sqlite3_errmsg(dbc->db);
LOGP(DAUC, LOGL_ERROR, "Cannot read subscribers from db:: %s\n", *err);
return rc;
}
*err = NULL;
return 0;
}
/*! Retrieve subscriber data from the HLR database. /*! Retrieve subscriber data from the HLR database.
* \param[in,out] dbc database context. * \param[in,out] dbc database context.
* \param[in] id ID of the subscriber in the HLR db. * \param[in] id ID of the subscriber in the HLR db.
@@ -830,11 +742,14 @@ out:
*/ */
int db_subscr_lu(struct db_context *dbc, int64_t subscr_id, int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
const struct osmo_ipa_name *vlr_name, bool is_ps, const struct osmo_ipa_name *vlr_name, bool is_ps,
const struct osmo_ipa_name *via_proxy) const struct osmo_ipa_name *via_proxy,
const enum osmo_rat_type rat_types[], size_t rat_types_len)
{ {
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
int rc, ret = 0; int rc, ret = 0;
struct timespec localtime; struct timespec localtime;
char rat_types_str[128] = "";
int i;
stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
: DB_STMT_UPD_VLR_BY_ID]; : DB_STMT_UPD_VLR_BY_ID];
@@ -896,6 +811,21 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
goto out; goto out;
} }
for (i = 0; i < rat_types_len; i++) {
char *pos = rat_types_str + strnlen(rat_types_str, sizeof(rat_types_str));
int len = sizeof(rat_types_str) - (pos - rat_types_str);
rc = snprintf(pos, len, "%s%s", pos == rat_types_str ? "" : ",", osmo_rat_type_name(rat_types[i]));
if (rc > len) {
osmo_strlcpy(rat_types_str + sizeof(rat_types_str) - 4, "...", 4);
break;
}
}
if (!db_bind_text(stmt, "$rat", rat_types_str)) {
ret = -EIO;
goto out;
}
rc = sqlite3_step(stmt); rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) { if (rc != SQLITE_DONE) {
LOGP(DAUC, LOGL_ERROR, LOGP(DAUC, LOGL_ERROR,
@@ -1032,14 +962,14 @@ out:
return ret; return ret;
} }
int _db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr, int _db_ind(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr,
unsigned int *ind, bool del) unsigned int *ind, bool del)
{ {
const char *vlr_name = NULL; const char *vlr_name = NULL;
int rc; int rc;
switch (vlr->type) { switch (vlr->type) {
case OSMO_CNI_PEER_ID_IPA_NAME: case OSMO_GSUP_PEER_ID_IPA_NAME:
if (vlr->ipa_name.len < 2 || vlr->ipa_name.val[vlr->ipa_name.len - 1] != '\0') { if (vlr->ipa_name.len < 2 || vlr->ipa_name.val[vlr->ipa_name.len - 1] != '\0') {
LOGP(DDB, LOGL_ERROR, "Expecting VLR ipa_name to be zero terminated; found %s\n", LOGP(DDB, LOGL_ERROR, "Expecting VLR ipa_name to be zero terminated; found %s\n",
osmo_ipa_name_to_str(&vlr->ipa_name)); osmo_ipa_name_to_str(&vlr->ipa_name));
@@ -1048,8 +978,8 @@ int _db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr,
vlr_name = (const char*)vlr->ipa_name.val; vlr_name = (const char*)vlr->ipa_name.val;
break; break;
default: default:
LOGP(DDB, LOGL_ERROR, "Unsupported osmo_cni_peer_id type: %s\n", LOGP(DDB, LOGL_ERROR, "Unsupported osmo_gsup_peer_id type: %s\n",
osmo_cni_peer_id_type_name(vlr->type)); osmo_gsup_peer_id_type_name(vlr->type));
return -ENOTSUP; return -ENOTSUP;
} }
@@ -1071,12 +1001,128 @@ int _db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr,
return _db_ind_get(dbc, vlr_name, ind); return _db_ind_get(dbc, vlr_name, ind);
} }
int db_ind(struct db_context *dbc, const struct osmo_cni_peer_id *vlr, unsigned int *ind) int db_ind(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr, unsigned int *ind)
{ {
return _db_ind(dbc, vlr, ind, false); return _db_ind(dbc, vlr, ind, false);
} }
int db_ind_del(struct db_context *dbc, const struct osmo_cni_peer_id *vlr) int db_ind_del(struct db_context *dbc, const struct osmo_gsup_peer_id *vlr)
{ {
return _db_ind(dbc, vlr, NULL, true); return _db_ind(dbc, vlr, NULL, true);
} }
int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed)
{
int rc;
int ret = 0;
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_RAT_FLAG];
if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
return -EIO;
OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
if (!db_bind_text(stmt, "$rat", osmo_rat_type_name(rat)))
return -EIO;
if (!db_bind_int(stmt, "$allowed", allowed ? 1 : 0))
return -EIO;
/* execute the statement */
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
LOGP(DDB, LOGL_ERROR, "%s %s: SQL error: %s\n",
allowed ? "enable" : "disable", osmo_rat_type_name(rat),
sqlite3_errmsg(dbc->db));
ret = -EIO;
goto out;
}
/* verify execution result */
rc = sqlite3_changes(dbc->db);
if (!rc) {
LOGP(DDB, LOGL_ERROR, "Cannot %s %s: no such subscriber: ID=%" PRIu64 "\n",
allowed ? "enable" : "disable", osmo_rat_type_name(rat),
subscr_id);
ret = -ENOENT;
goto out;
} else if (rc != 1) {
LOGP(DDB, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
allowed ? "enable" : "disable", osmo_rat_type_name(rat),
rc);
ret = -EIO;
}
out:
db_remove_reset(stmt);
return ret;
}
int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr)
{
int rc;
int ret = 0;
int i;
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_RAT_BY_ID];
if (!db_bind_int64(stmt, "$subscriber_id", subscr->id))
return -EIO;
for (i = 0; i < OSMO_RAT_COUNT; i++)
subscr->rat_types[i] = true;
/* execute the statement */
while (1) {
enum osmo_rat_type rat;
bool allowed;
rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE)
break;
if (rc != SQLITE_ROW)
return -rc;
rc = get_string_value(osmo_rat_type_names, (const char*)sqlite3_column_text(stmt, 0));
if (rc == -EINVAL) {
ret = -EINVAL;
goto out;
}
if (rc <= 0 || rc >= OSMO_RAT_COUNT) {
ret = -EINVAL;
goto out;
}
rat = rc;
allowed = sqlite3_column_int(stmt, 1);
subscr->rat_types[rat] = allowed;
LOGP(DAUC, LOGL_DEBUG, "db: imsi='%s' %s %s\n",
subscr->imsi, osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden");
}
out:
db_remove_reset(stmt);
return ret;
}
int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed)
{
int rc;
OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
db_subscr_get_rat_types(hlr->dbc, subscr);
if (subscr->rat_types[rat] == allowed) {
LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",
allowed ? "enable" : "disable", osmo_rat_type_name(rat));
return -ENOEXEC;
}
rc = db_subscr_set_rat_type_flag(hlr->dbc, subscr->id, rat, allowed);
if (rc)
return rc > 0? -rc : rc;
/* FIXME: If we're disabling, send message to VLR to detach subscriber */
return 0;
}

View File

@@ -18,6 +18,10 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. * 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 $ * $Id: dbd_helper.c,v 1.44 2011/08/09 11:14:14 mhoenicka Exp $
*/ */

View File

@@ -22,7 +22,7 @@
#include <osmocom/mslookup/mslookup_client.h> #include <osmocom/mslookup/mslookup_client.h>
#include <osmocom/mslookup/mslookup_client_mdns.h> #include <osmocom/mslookup/mslookup_client_mdns.h>
#include <osmocom/gsupclient/gsup_client.h> #include <osmocom/gsupclient/gsup_client.h>
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
#include <osmocom/hlr/logging.h> #include <osmocom/hlr/logging.h>
#include <osmocom/hlr/hlr.h> #include <osmocom/hlr/hlr.h>
#include <osmocom/hlr/db.h> #include <osmocom/hlr/db.h>
@@ -170,7 +170,7 @@ void dgsm_init(void *ctx)
g_hlr->mslookup.server.local_attach_max_age = 60 * 60; g_hlr->mslookup.server.local_attach_max_age = 60 * 60;
g_hlr->mslookup.client.result_timeout_milliseconds = OSMO_DGSM_DEFAULT_RESULT_TIMEOUT_MS; g_hlr->mslookup.client.result_timeout_milliseconds = 2000;
g_hlr->gsup_unit_name.unit_name = "HLR"; g_hlr->gsup_unit_name.unit_name = "HLR";
g_hlr->gsup_unit_name.serno = "unnamed-HLR"; g_hlr->gsup_unit_name.serno = "unnamed-HLR";
@@ -191,7 +191,7 @@ void dgsm_start(void *ctx)
dgsm_mdns_client_config_apply(); dgsm_mdns_client_config_apply();
} }
void dgsm_stop(void) void dgsm_stop()
{ {
g_hlr->mslookup.allow_startup = false; g_hlr->mslookup.allow_startup = false;
mslookup_server_mdns_config_apply(); mslookup_server_mdns_config_apply();

View File

@@ -24,7 +24,7 @@
#include <osmocom/hlr/hlr_vty.h> #include <osmocom/hlr/hlr_vty.h>
#include <osmocom/hlr/mslookup_server.h> #include <osmocom/hlr/mslookup_server.h>
#include <osmocom/hlr/mslookup_server_mdns.h> #include <osmocom/hlr/mslookup_server_mdns.h>
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
struct cmd_node mslookup_node = { struct cmd_node mslookup_node = {
MSLOOKUP_NODE, MSLOOKUP_NODE,
@@ -119,7 +119,7 @@ DEFUN(cfg_mslookup_mdns_domain_suffix,
DEFUN(cfg_mslookup_no_mdns, DEFUN(cfg_mslookup_no_mdns,
cfg_mslookup_no_mdns_cmd, cfg_mslookup_no_mdns_cmd,
"no mdns bind", "no mdns",
NO_STR "Disable both server and client for mDNS mslookup\n") NO_STR "Disable both server and client for mDNS mslookup\n")
{ {
g_hlr->mslookup.server.mdns.enable = false; g_hlr->mslookup.server.mdns.enable = false;
@@ -178,9 +178,9 @@ DEFUN(cfg_mslookup_server_mdns_domain_suffix,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
DEFUN(cfg_mslookup_server_no_mdns_bind, DEFUN(cfg_mslookup_server_no_mdns,
cfg_mslookup_server_no_mdns_bind_cmd, cfg_mslookup_server_no_mdns_cmd,
"no mdns bind", "no mdns",
NO_STR "Disable server for mDNS mslookup (do not answer remote requests)\n") NO_STR "Disable server for mDNS mslookup (do not answer remote requests)\n")
{ {
g_hlr->mslookup.server.mdns.enable = false; g_hlr->mslookup.server.mdns.enable = false;
@@ -196,9 +196,8 @@ struct cmd_node mslookup_server_msc_node = {
DEFUN(cfg_mslookup_server_msc, DEFUN(cfg_mslookup_server_msc,
cfg_mslookup_server_msc_cmd, cfg_mslookup_server_msc_cmd,
"msc ipa-name .IPA_NAME", "msc .UNIT_NAME",
"Configure services for individual local MSCs\n" "Configure services for individual local MSCs\n"
"Identify locally connected MSC by IPA Unit Name\n"
"IPA Unit Name of the local MSC to configure\n") "IPA Unit Name of the local MSC to configure\n")
{ {
struct osmo_ipa_name msc_name; struct osmo_ipa_name msc_name;
@@ -366,8 +365,8 @@ DEFUN(cfg_mslookup_client_timeout,
vty_out(vty, "%% 'exit' this node to apply changes%s", VTY_NEWLINE) vty_out(vty, "%% 'exit' this node to apply changes%s", VTY_NEWLINE)
DEFUN(cfg_mslookup_client_mdns_bind, DEFUN(cfg_mslookup_client_mdns,
cfg_mslookup_client_mdns_bind_cmd, cfg_mslookup_client_mdns_cmd,
"mdns bind [IP] [<1-65535>]", "mdns bind [IP] [<1-65535>]",
MDNS_STR MDNS_STR
"Enable mDNS client, and configure multicast address to send mDNS mslookup requests to\n" "Enable mDNS client, and configure multicast address to send mDNS mslookup requests to\n"
@@ -388,9 +387,9 @@ DEFUN(cfg_mslookup_client_mdns_domain_suffix,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
DEFUN(cfg_mslookup_client_no_mdns_bind, DEFUN(cfg_mslookup_client_no_mdns,
cfg_mslookup_client_no_mdns_bind_cmd, cfg_mslookup_client_no_mdns_cmd,
"no mdns bind", "no mdns",
NO_STR "Disable mDNS client, do not query remote services by mDNS\n") NO_STR "Disable mDNS client, do not query remote services by mDNS\n")
{ {
g_hlr->mslookup.client.mdns.enable = false; g_hlr->mslookup.client.mdns.enable = false;
@@ -442,13 +441,13 @@ int config_write_mslookup(struct vty *vty)
msc = mslookup_server_msc_get(&mslookup_server_msc_wildcard, false); msc = mslookup_server_msc_get(&mslookup_server_msc_wildcard, false);
if (msc) if (msc)
config_write_msc_services(vty, " ", msc); config_write_msc_services(vty, " ", msc);
llist_for_each_entry(msc, &g_hlr->mslookup.server.local_site_services, entry) { llist_for_each_entry(msc, &g_hlr->mslookup.server.local_site_services, entry) {
if (!osmo_ipa_name_cmp(&mslookup_server_msc_wildcard, &msc->name)) if (!osmo_ipa_name_cmp(&mslookup_server_msc_wildcard, &msc->name))
continue; continue;
vty_out(vty, " msc ipa-name %s%s", osmo_ipa_name_to_str(&msc->name), VTY_NEWLINE); vty_out(vty, " msc %s%s", osmo_ipa_name_to_str(&msc->name), VTY_NEWLINE);
config_write_msc_services(vty, " ", msc); config_write_msc_services(vty, " ", msc);
} }
/* If the server is disabled, still output the above to not lose the service config. */ /* If the server is disabled, still output the above to not lose the service config. */
@@ -467,7 +466,7 @@ int config_write_mslookup(struct vty *vty)
if (g_hlr->mslookup.client.mdns.enable if (g_hlr->mslookup.client.mdns.enable
&& osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.mdns.query_addr)) && osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.mdns.query_addr))
vty_out(vty, " mdns bind %s %u%s", vty_out(vty, " mdns to %s %u%s",
g_hlr->mslookup.client.mdns.query_addr.ip, g_hlr->mslookup.client.mdns.query_addr.ip,
g_hlr->mslookup.client.mdns.query_addr.port, g_hlr->mslookup.client.mdns.query_addr.port,
VTY_NEWLINE); VTY_NEWLINE);
@@ -475,10 +474,6 @@ int config_write_mslookup(struct vty *vty)
vty_out(vty, " mdns domain-suffix %s%s", vty_out(vty, " mdns domain-suffix %s%s",
g_hlr->mslookup.client.mdns.domain_suffix, g_hlr->mslookup.client.mdns.domain_suffix,
VTY_NEWLINE); VTY_NEWLINE);
if (g_hlr->mslookup.client.result_timeout_milliseconds != OSMO_DGSM_DEFAULT_RESULT_TIMEOUT_MS)
vty_out(vty, " timeout %u%s",
g_hlr->mslookup.client.result_timeout_milliseconds,
VTY_NEWLINE);
} }
return CMD_SUCCESS; return CMD_SUCCESS;
@@ -539,7 +534,7 @@ DEFUN(do_mslookup_show_services,
llist_for_each_entry(msc, &g_hlr->mslookup.server.local_site_services, entry) { llist_for_each_entry(msc, &g_hlr->mslookup.server.local_site_services, entry) {
if (!osmo_ipa_name_cmp(&mslookup_server_msc_wildcard, &msc->name)) if (!osmo_ipa_name_cmp(&mslookup_server_msc_wildcard, &msc->name))
continue; continue;
vty_out(vty, "msc ipa-name %s%s", osmo_ipa_name_to_str(&msc->name), VTY_NEWLINE); vty_out(vty, "msc %s%s", osmo_ipa_name_to_str(&msc->name), VTY_NEWLINE);
config_write_msc_services(vty, " ", msc); config_write_msc_services(vty, " ", msc);
} }
return CMD_SUCCESS; return CMD_SUCCESS;
@@ -559,7 +554,7 @@ void dgsm_vty_init(void)
install_node(&mslookup_server_node, NULL); install_node(&mslookup_server_node, NULL);
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_mdns_bind_cmd); install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_mdns_bind_cmd);
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_mdns_domain_suffix_cmd); install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_mdns_domain_suffix_cmd);
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_no_mdns_bind_cmd); install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_no_mdns_cmd);
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_service_cmd); install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_service_cmd);
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_no_service_cmd); install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_no_service_cmd);
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_no_service_addr_cmd); install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_no_service_addr_cmd);
@@ -574,9 +569,9 @@ void dgsm_vty_init(void)
install_element(MSLOOKUP_NODE, &cfg_mslookup_no_client_cmd); install_element(MSLOOKUP_NODE, &cfg_mslookup_no_client_cmd);
install_node(&mslookup_client_node, NULL); install_node(&mslookup_client_node, NULL);
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_timeout_cmd); install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_timeout_cmd);
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_mdns_bind_cmd); install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_mdns_cmd);
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_mdns_domain_suffix_cmd); install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_mdns_domain_suffix_cmd);
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_mdns_bind_cmd); install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_mdns_cmd);
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_gateway_proxy_cmd); install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_gateway_proxy_cmd);
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_gateway_proxy_cmd); install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_gateway_proxy_cmd);

View File

@@ -18,8 +18,6 @@
*/ */
#include <errno.h> #include <errno.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <osmocom/core/msgb.h> #include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h> #include <osmocom/core/logging.h>
@@ -32,7 +30,6 @@
#include <osmocom/hlr/gsup_server.h> #include <osmocom/hlr/gsup_server.h>
#include <osmocom/hlr/gsup_router.h> #include <osmocom/hlr/gsup_router.h>
#include <osmocom/hlr/hlr.h>
#define LOG_GSUP_CONN(conn, level, fmt, args...) \ #define LOG_GSUP_CONN(conn, level, fmt, args...) \
LOGP(DLGSUP, level, "GSUP peer %s: " fmt, \ LOGP(DLGSUP, level, "GSUP peer %s: " fmt, \
@@ -68,13 +65,13 @@ int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg)
static void gsup_server_send_req_response(struct osmo_gsup_req *req, struct osmo_gsup_message *response) static void gsup_server_send_req_response(struct osmo_gsup_req *req, struct osmo_gsup_message *response)
{ {
struct osmo_gsup_server *server = req->cb_data; struct osmo_gsup_server *server = req->cb_data;
struct osmo_cni_peer_id *routing; struct osmo_gsup_peer_id *routing;
struct osmo_gsup_conn *conn = NULL; struct osmo_gsup_conn *conn = NULL;
struct msgb *msg = osmo_gsup_msgb_alloc("GSUP Tx"); struct msgb *msg = osmo_gsup_msgb_alloc("GSUP Tx");
int rc; int rc;
if (response->message_type == OSMO_GSUP_MSGT_ROUTING_ERROR if (response->message_type == OSMO_GSUP_MSGT_ROUTING_ERROR
&& !osmo_cni_peer_id_is_empty(&req->via_proxy)) { && !osmo_gsup_peer_id_is_empty(&req->via_proxy)) {
/* If a routing error occured, we need to route back via the immediate sending peer, not via the /* If a routing error occured, we need to route back via the immediate sending peer, not via the
* intended final recipient -- because one source of routing errors is a duplicate name for a recipient. * intended final recipient -- because one source of routing errors is a duplicate name for a recipient.
* If we resolve to req->source_name, we may send to a completely unrelated recipient. */ * If we resolve to req->source_name, we may send to a completely unrelated recipient. */
@@ -83,12 +80,12 @@ static void gsup_server_send_req_response(struct osmo_gsup_req *req, struct osmo
routing = &req->source_name; routing = &req->source_name;
} }
switch (routing->type) { switch (routing->type) {
case OSMO_CNI_PEER_ID_IPA_NAME: case OSMO_GSUP_PEER_ID_IPA_NAME:
conn = gsup_route_find_by_ipa_name(server, &routing->ipa_name); conn = gsup_route_find_by_ipa_name(server, &routing->ipa_name);
break; break;
default: default:
LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP peer id kind not supported: %s\n", LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP peer id kind not supported: %s\n",
osmo_cni_peer_id_type_name(routing->type)); osmo_gsup_peer_id_type_name(routing->type));
break; break;
} }
@@ -114,22 +111,22 @@ static void gsup_server_send_req_response(struct osmo_gsup_req *req, struct osmo
struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb *msg) struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb *msg)
{ {
struct osmo_gsup_req *req; struct osmo_gsup_req *req;
struct osmo_cni_peer_id cpi = { struct osmo_gsup_peer_id gpi = {
.type = OSMO_CNI_PEER_ID_IPA_NAME, .type = OSMO_GSUP_PEER_ID_IPA_NAME,
.ipa_name = conn->peer_name, .ipa_name = conn->peer_name,
}; };
req = osmo_gsup_req_new(conn->server, &cpi, msg, gsup_server_send_req_response, conn->server, NULL); req = osmo_gsup_req_new(conn->server, &gpi, msg, gsup_server_send_req_response, conn->server, NULL);
if (!req) if (!req)
return NULL; return NULL;
if (!osmo_cni_peer_id_is_empty(&req->via_proxy)) { if (!osmo_gsup_peer_id_is_empty(&req->via_proxy)) {
switch (req->via_proxy.type) { switch (req->via_proxy.type) {
case OSMO_CNI_PEER_ID_IPA_NAME: case OSMO_GSUP_PEER_ID_IPA_NAME:
break; break;
default: default:
LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP peer id kind not supported: %s\n", LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP peer id kind not supported: %s\n",
osmo_cni_peer_id_type_name(req->source_name.type)); osmo_gsup_peer_id_type_name(req->source_name.type));
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true); osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
return NULL; return NULL;
} }
@@ -140,7 +137,7 @@ struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb
LOG_GSUP_REQ(req, LOGL_ERROR, LOG_GSUP_REQ(req, LOGL_ERROR,
"GSUP message received from %s via peer %s, but there already exists a" "GSUP message received from %s via peer %s, but there already exists a"
" different route to this source, message is not routable\n", " different route to this source, message is not routable\n",
osmo_cni_peer_id_to_str(&req->source_name), osmo_gsup_peer_id_to_str(&req->source_name),
osmo_ipa_name_to_str(&conn->peer_name)); osmo_ipa_name_to_str(&conn->peer_name));
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true); osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
return NULL; return NULL;
@@ -180,9 +177,11 @@ static int osmo_gsup_server_read_cb(struct ipa_server_conn *conn,
if (hh->proto == IPAC_PROTO_IPACCESS) { if (hh->proto == IPAC_PROTO_IPACCESS) {
rc = ipa_server_conn_ccm(conn, msg); rc = ipa_server_conn_ccm(conn, msg);
msgb_free(msg); if (rc < 0) {
if (rc < 0) /* conn is already invalid here! */ /* conn is already invalid here! */
return -1; return -1;
}
msgb_free(msg);
return 0; return 0;
} }
@@ -275,7 +274,7 @@ static int osmo_gsup_server_ccm_cb(struct ipa_server_conn *conn,
{ {
struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data; struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data;
uint8_t *addr = NULL; uint8_t *addr = NULL;
int addr_len; size_t addr_len;
LOGP(DLGSUP, LOGL_INFO, "CCM Callback\n"); LOGP(DLGSUP, LOGL_INFO, "CCM Callback\n");
@@ -316,17 +315,41 @@ static int osmo_gsup_server_closed_cb(struct ipa_server_conn *conn)
return 0; return 0;
} }
static void update_fd_settings(int fd) /* Add conn to the clients list in a way that conn->auc_3g_ind takes the lowest
* unused integer and the list of clients remains sorted by auc_3g_ind.
* Keep this function non-static to allow linking in a unit test. */
void osmo_gsup_server_add_conn(struct llist_head *clients,
struct osmo_gsup_conn *conn)
{ {
int ret; struct osmo_gsup_conn *c;
int val; struct osmo_gsup_conn *prev_conn;
/*TODO: Set keepalive settings here. See OS#4312 */ c = llist_first_entry_or_null(clients, struct osmo_gsup_conn, list);
val = 1; /* Is the first index, 0, unused? */
ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); if (!c || c->auc_3g_ind > 0) {
if (ret < 0) conn->auc_3g_ind = 0;
LOGP(DLGSUP, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); llist_add(&conn->list, clients);
return;
}
/* Look for a gap later on */
prev_conn = NULL;
llist_for_each_entry(c, clients, list) {
/* skip first item, we know it has auc_3g_ind == 0. */
if (!prev_conn) {
prev_conn = c;
continue;
}
if (c->auc_3g_ind > prev_conn->auc_3g_ind + 1)
break;
prev_conn = c;
}
OSMO_ASSERT(prev_conn);
conn->auc_3g_ind = prev_conn->auc_3g_ind + 1;
llist_add(&conn->list, &prev_conn->list);
} }
/* a client has connected to the server socket and we have accept()ed it */ /* a client has connected to the server socket and we have accept()ed it */
@@ -348,11 +371,10 @@ static int osmo_gsup_server_accept_cb(struct ipa_server_link *link, int fd)
/* link data structure with server structure */ /* link data structure with server structure */
conn->server = gsups; conn->server = gsups;
llist_add_tail(&conn->list, &gsups->clients); osmo_gsup_server_add_conn(&gsups->clients, conn);
LOGP(DLGSUP, LOGL_INFO, "New GSUP client %s:%d\n", conn->conn->addr, conn->conn->port); LOGP(DLGSUP, LOGL_INFO, "New GSUP client %s:%d (IND=%u)\n",
conn->conn->addr, conn->conn->port, conn->auc_3g_ind);
update_fd_settings(fd);
/* request the identity of the client */ /* request the identity of the client */
rc = ipa_ccm_send_id_req(fd); rc = ipa_ccm_send_id_req(fd);
@@ -437,61 +459,63 @@ int osmo_gsup_configure_wildcard_apn(struct osmo_gsup_message *gsup,
return 0; return 0;
} }
/** /**
* Populate a gsup message structure with an Insert Subscriber Data Message. * Populate a gsup message structure with an Insert Subscriber Data Message.
* All required memory buffers for data pointed to by pointers in struct osmo_gsup_message * All required memory buffers for data pointed to by pointers in struct omso_gsup_message
* must be allocated by the caller and should have the same lifetime as the gsup parameter. * 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[out] gsup The gsup message to populate.
* \param[in] imsi The subscriber's IMSI. * \param[in] imsi The subscriber's IMSI.
* \param[in] msisdn The subscriber's MSISDN. * \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. * \param[in] cn_domain The CN Domain of the subscriber connection.
* \param[in] talloc_ctx To allocation memory for dynamic fields (msisdn, apn) in the gsup field
* \returns 0 on success, and negative on error. * \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, int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, const char *imsi, const char *msisdn,
enum osmo_gsup_cn_domain cn_domain, uint8_t *msisdn_enc, size_t msisdn_enc_size,
void *talloc_ctx) uint8_t *apn_buf, size_t apn_buf_size,
enum osmo_gsup_cn_domain cn_domain)
{ {
int len; int len;
uint8_t *msisdn_buf = talloc_size(talloc_ctx, OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN);
OSMO_ASSERT(gsup); OSMO_ASSERT(gsup);
*gsup = (struct osmo_gsup_message){ *gsup = (struct osmo_gsup_message){
.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST, .message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST,
}; };
osmo_strlcpy(gsup->imsi, imsi, sizeof(gsup->imsi)); osmo_strlcpy(gsup->imsi, imsi, sizeof(gsup->imsi));
len = gsm48_encode_bcd_number(msisdn_buf, OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN, 0, msisdn); if (msisdn_enc_size < OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN)
if (len < 1) { return -ENOSPC;
LOGP(DLGSUP, LOGL_ERROR, "%s: Error: cannot encode MSISDN '%s'\n", imsi, msisdn);
return -ENOSPC;
}
gsup->msisdn_enc = msisdn_buf;
gsup->msisdn_enc_len = len;
#pragma message "FIXME: deal with encoding the following data: gsup.hlr_enc" 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;
gsup->cn_domain = cn_domain; #pragma message "FIXME: deal with encoding the following data: gsup.hlr_enc"
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) {
if (g_hlr->ps.pdp_profile.enabled) {
OSMO_ASSERT(g_hlr->ps.pdp_profile.num_pdp_infos <= ARRAY_SIZE(g_hlr->ps.pdp_profile.pdp_infos));
OSMO_ASSERT(g_hlr->ps.pdp_profile.num_pdp_infos <= ARRAY_SIZE(gsup->pdp_infos));
memcpy(gsup->pdp_infos,
g_hlr->ps.pdp_profile.pdp_infos,
sizeof(struct osmo_gsup_pdp_info) * g_hlr->ps.pdp_profile.num_pdp_infos);
gsup->num_pdp_infos = g_hlr->ps.pdp_profile.num_pdp_infos;
} else {
uint8_t *apn_buf = talloc_size(talloc_ctx, APN_MAXLEN);
osmo_gsup_configure_wildcard_apn(gsup, apn_buf, APN_MAXLEN);
}
}
return 0; gsup->cn_domain = cn_domain;
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) {
OSMO_ASSERT(apn_buf_size >= APN_MAXLEN);
OSMO_ASSERT(apn_buf);
/* FIXME: PDP infos - use more fine-grained access control
instead of wildcard APN */
osmo_gsup_configure_wildcard_apn(gsup, apn_buf, apn_buf_size);
}
return 0;
} }
int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_cni_peer_id *to_peer, int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struct osmo_gsup_peer_id *to_peer,
struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup) struct osmo_gsup_req *req, struct osmo_gsup_message *modified_gsup)
{ {
int rc; int rc;
@@ -500,22 +524,22 @@ int osmo_gsup_forward_to_local_peer(struct osmo_gsup_server *server, const struc
* is required to return this as gsup->destination_name so that the reply gets routed to the original sender. */ * is required to return this as gsup->destination_name so that the reply gets routed to the original sender. */
struct osmo_gsup_message forward = *(modified_gsup? : &req->gsup); struct osmo_gsup_message forward = *(modified_gsup? : &req->gsup);
if (req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) { if (req->source_name.type != OSMO_GSUP_PEER_ID_IPA_NAME) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s", osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s",
osmo_cni_peer_id_type_name(req->source_name.type)); osmo_gsup_peer_id_type_name(req->source_name.type));
rc = -ENOTSUP; rc = -ENOTSUP;
goto routing_error; goto routing_error;
} }
forward.source_name = req->source_name.ipa_name.val; forward.source_name = req->source_name.ipa_name.val;
forward.source_name_len = req->source_name.ipa_name.len; forward.source_name_len = req->source_name.ipa_name.len;
if (to_peer->type != OSMO_CNI_PEER_ID_IPA_NAME) { if (to_peer->type != OSMO_GSUP_PEER_ID_IPA_NAME) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s", osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s",
osmo_cni_peer_id_type_name(to_peer->type)); osmo_gsup_peer_id_type_name(to_peer->type));
rc = -ENOTSUP; rc = -ENOTSUP;
goto routing_error; goto routing_error;
} }
LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to %s\n", osmo_cni_peer_id_to_str(to_peer)); LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to %s\n", osmo_gsup_peer_id_to_str(to_peer));
rc = osmo_gsup_enc_send_to_ipa_name(server, &to_peer->ipa_name, &forward); rc = osmo_gsup_enc_send_to_ipa_name(server, &to_peer->ipa_name, &forward);
if (rc) if (rc)
goto routing_error; goto routing_error;

View File

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

View File

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

View File

@@ -19,9 +19,9 @@
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <osmocom/core/utils.h> #include <osmocom/core/utils.h>
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
bool osmo_ipa_name_is_empty(const struct osmo_ipa_name *ipa_name) bool osmo_ipa_name_is_empty(struct osmo_ipa_name *ipa_name)
{ {
return (!ipa_name) || (!ipa_name->len); return (!ipa_name) || (!ipa_name->len);
} }
@@ -90,61 +90,55 @@ int osmo_ipa_name_cmp(const struct osmo_ipa_name *a, const struct osmo_ipa_name
} }
} }
/* Call osmo_ipa_name_to_str_c with OTC_SELECT. */
const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name)
{
return osmo_ipa_name_to_str_c(OTC_SELECT, ipa_name);
}
/* Return an unquoted string, not including the terminating zero. Used for writing VTY config. */ /* Return an unquoted string, not including the terminating zero. Used for writing VTY config. */
const char *osmo_ipa_name_to_str_c(void *ctx, const struct osmo_ipa_name *ipa_name) const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name)
{ {
size_t len = ipa_name->len; size_t len = ipa_name->len;
if (!len) if (!len)
return talloc_strdup(ctx, ""); return "";
if (ipa_name->val[len-1] == '\0') if (ipa_name->val[len-1] == '\0')
len--; len--;
return osmo_escape_str_c(ctx, (char*)ipa_name->val, len); return osmo_escape_str_c(OTC_SELECT, (char*)ipa_name->val, len);
} }
bool osmo_cni_peer_id_is_empty(const struct osmo_cni_peer_id *cni_peer_id) bool osmo_gsup_peer_id_is_empty(struct osmo_gsup_peer_id *gsup_peer_id)
{ {
if (!cni_peer_id) if (!gsup_peer_id)
return true; return true;
switch (cni_peer_id->type) { switch (gsup_peer_id->type) {
case OSMO_CNI_PEER_ID_EMPTY: case OSMO_GSUP_PEER_ID_EMPTY:
return true; return true;
case OSMO_CNI_PEER_ID_IPA_NAME: case OSMO_GSUP_PEER_ID_IPA_NAME:
return osmo_ipa_name_is_empty(&cni_peer_id->ipa_name); return osmo_ipa_name_is_empty(&gsup_peer_id->ipa_name);
default: default:
return false; return false;
} }
} }
int osmo_cni_peer_id_set(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni_peer_id_type type, int osmo_gsup_peer_id_set(struct osmo_gsup_peer_id *gsup_peer_id, enum osmo_gsup_peer_id_type type,
const uint8_t *val, size_t len) const uint8_t *val, size_t len)
{ {
cni_peer_id->type = type; gsup_peer_id->type = type;
switch (type) { switch (type) {
case OSMO_CNI_PEER_ID_IPA_NAME: case OSMO_GSUP_PEER_ID_IPA_NAME:
return osmo_ipa_name_set(&cni_peer_id->ipa_name, val, len); return osmo_ipa_name_set(&gsup_peer_id->ipa_name, val, len);
default: default:
return -EINVAL; return -EINVAL;
} }
} }
int osmo_cni_peer_id_set_str(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni_peer_id_type type, int osmo_gsup_peer_id_set_str(struct osmo_gsup_peer_id *gsup_peer_id, enum osmo_gsup_peer_id_type type,
const char *str_fmt, ...) const char *str_fmt, ...)
{ {
va_list ap; va_list ap;
int rc; int rc;
*cni_peer_id = (struct osmo_cni_peer_id){}; *gsup_peer_id = (struct osmo_gsup_peer_id){};
switch (type) { switch (type) {
case OSMO_CNI_PEER_ID_IPA_NAME: case OSMO_GSUP_PEER_ID_IPA_NAME:
cni_peer_id->type = OSMO_CNI_PEER_ID_IPA_NAME; gsup_peer_id->type = OSMO_GSUP_PEER_ID_IPA_NAME;
va_start(ap, str_fmt); va_start(ap, str_fmt);
rc = osmo_ipa_name_set_str_va(&cni_peer_id->ipa_name, str_fmt, ap); rc = osmo_ipa_name_set_str_va(&gsup_peer_id->ipa_name, str_fmt, ap);
va_end(ap); va_end(ap);
return rc; return rc;
default: default:
@@ -152,42 +146,30 @@ int osmo_cni_peer_id_set_str(struct osmo_cni_peer_id *cni_peer_id, enum osmo_cni
} }
} }
int osmo_cni_peer_id_cmp(const struct osmo_cni_peer_id *a, const struct osmo_cni_peer_id *b) int osmo_gsup_peer_id_cmp(const struct osmo_gsup_peer_id *a, const struct osmo_gsup_peer_id *b)
{ {
if (a == b)
return 0;
if (!a)
return -1;
if (!b)
return 1;
if (a->type != b->type) if (a->type != b->type)
return OSMO_CMP(a->type, b->type); return OSMO_CMP(a->type, b->type);
switch (a->type) { switch (a->type) {
case OSMO_CNI_PEER_ID_IPA_NAME: case OSMO_GSUP_PEER_ID_IPA_NAME:
return osmo_ipa_name_cmp(&a->ipa_name, &b->ipa_name); return osmo_ipa_name_cmp(&a->ipa_name, &b->ipa_name);
default: default:
return -EINVAL; return -EINVAL;
} }
} }
const struct value_string osmo_cni_peer_id_type_names[] = { const struct value_string osmo_gsup_peer_id_type_names[] = {
{ OSMO_CNI_PEER_ID_IPA_NAME, "IPA-name" }, { OSMO_GSUP_PEER_ID_IPA_NAME, "IPA-name" },
{} {}
}; };
/* Call osmo_cni_peer_id_to_str_c with OTC_SELECT */
const char *osmo_cni_peer_id_to_str(const struct osmo_cni_peer_id *cpi)
{
return osmo_cni_peer_id_to_str_c(OTC_SELECT, cpi);
}
/* Return an unquoted string, not including the terminating zero. Used for writing VTY config. */ /* Return an unquoted string, not including the terminating zero. Used for writing VTY config. */
const char *osmo_cni_peer_id_to_str_c(void *ctx, const struct osmo_cni_peer_id *cpi) const char *osmo_gsup_peer_id_to_str(const struct osmo_gsup_peer_id *gpi)
{ {
switch (cpi->type) { switch (gpi->type) {
case OSMO_CNI_PEER_ID_IPA_NAME: case OSMO_GSUP_PEER_ID_IPA_NAME:
return osmo_ipa_name_to_str_c(ctx, &cpi->ipa_name); return osmo_ipa_name_to_str(&gpi->ipa_name);
default: default:
return talloc_strdup(ctx, osmo_cni_peer_id_type_name(cpi->type)); return osmo_gsup_peer_id_type_name(gpi->type);
} }
} }

View File

@@ -25,81 +25,26 @@
#include <osmocom/gsupclient/gsup_req.h> #include <osmocom/gsupclient/gsup_req.h>
/*! Create a new osmo_gsup_req record, decode GSUP and add to a provided list of requests. /*! Create a new osmo_gsup_req record, decode GSUP and add to a provided list of requests.
*
* Rationales:
*
* - osmo_gsup_req makes it easy to handle GSUP requests asynchronously. Before this, a GSUP message struct would be
* valid only within a read callback function, and would not survive asynchronous handling, because the struct often
* points directly into the received msgb. An osmo_gsup_req takes ownership of the msgb and ensures that the data
* remains valid, so that it can easily be queued for later handling.
* - osmo_gsup_req unifies the composition of response messages to ensure that all IEs that identify it to belong to
* the initial request are preserved / derived, like the source_name, destination_name, session_id, etc (see
* osmo_gsup_make_response() for details).
* - Deallocation of an osmo_gsup_req is implicit upon sending a response. The idea is that msgb memory leaks are a
* recurring source of bugs. By enforcing a request-response relation with implicit deallocation, osmo_gsup_req aims
* to help avoid most such memory leaks implicitly.
*
* The typical GSUP message sequence is:
* -> rx request,
* <- tx response.
*
* With osmo_gsup_req we can easily expand to:
* -> rx request,
* ... wait asynchronously,
* <- tx response.
*
* Only few GSUP conversations go beyond a 1:1 request-response match. But some have a session (e.g. USSD) or more
* negotiation may happen before the initial request is completed (e.g. Update Location with interleaved Insert
* Subscriber Data), so osmo_gsup_req also allows passing non-final responses.
* The final_response flag allows for:
* -> rx request,
* ... wait async,
* <- tx intermediate message to same peer (final_response = false, req remains open),
* ... wait async,
* -> rx intermediate response,
* ... wait async,
* <- tx final response (final_response = true, req is deallocated).
*
* This function takes ownership of the msgb, which will, on success, be owned by the returned osmo_gsup_req instance * This function takes ownership of the msgb, which will, on success, be owned by the returned osmo_gsup_req instance
* until osmo_gsup_req_free(). If a decoding error occurs, send an error response immediately, and return NULL. * until osmo_gsup_req_free(). If a decoding error occurs, send an error response immediately, and return NULL.
* *
* The original CNI entity that sent the message is found in req->source_name. If the message was passed on by an * When this function returns, the original sender is found in req->source_name. If this is not the immediate peer name,
* intermediate CNI peer, then req->via_proxy is set to the immediate peer, and it is the responsibility of the caller * then req->via_proxy is set to the immediate peer, and it is the responsibility of the caller to add req->source_name
* to add req->source_name to the GSUP routes that are serviced by req->via_proxy (usually not relevant for clients with * to the GSUP routes that are serviced by req->via_proxy (usually not relevant for clients with a single GSUP conn).
* a single GSUP conn).
* Examples:
*
* "msc" ---> here
* source_name = "msc"
* via_proxy = <empty>
*
* "msc" ---> "proxy-HLR" ---> here (e.g. home HLR)
* source_name = "msc"
* via_proxy = "proxy-HLR"
*
* "msc" ---> "proxy-HLR" ---> "home-HLR" ---> here (e.g. EUSE)
* source_name = "msc"
* via_proxy = "home-HLR"
*
* An osmo_gsup_req must be concluded (and deallocated) by calling one of the osmo_gsup_req_respond* functions.
* *
* Note: osmo_gsup_req API makes use of OTC_SELECT to allocate volatile buffers for logging. Use of * Note: osmo_gsup_req API makes use of OTC_SELECT to allocate volatile buffers for logging. Use of
* osmo_select_main_ctx() is mandatory when using osmo_gsup_req. * osmo_select_main_ctx() is mandatory when using osmo_gsup_req.
* *
* \param[in] ctx Talloc context for allocation of the new request. * \param[in] ctx Talloc context for allocation of the new request.
* \param[in] from_peer The IPA unit name of the immediate GSUP peer from which this msgb was received. * \param[in] from_peer The IPA unit name of the immediate GSUP peer from which this msgb was received.
* \param[in] msg The message buffer containing the received GSUP message, where msgb_l2() shall point to the GSUP * \param[in] msg The GSUP message buffer.
* message start. The caller no longer owns the msgb when it is passed to this function: on error, the
* msgb is freed immediately, and on success, the msgb is owned by the returned osmo_gsup_req.
* \param[in] send_response_cb User specific method to send a GSUP response message, invoked upon * \param[in] send_response_cb User specific method to send a GSUP response message, invoked upon
* osmo_gsup_req_respond*() functions. Typically this invokes encoding and transmitting the * osmo_gsup_req_respond*() functions.
* GSUP message over a network socket. See for example gsup_server_send_req_response().
* \param[inout] cb_data Context data to be used freely by the caller. * \param[inout] cb_data Context data to be used freely by the caller.
* \param[inout] add_to_list List to which to append this request, or NULL for no list. * \param[inout] add_to_list List to which to append this request, or NULL for no list.
* \return a newly allocated osmo_gsup_req, or NULL on error. If NULL is returned, an error response has already been * \return a newly allocated osmo_gsup_req, or NULL on error.
* dispatched to the send_response_cb.
*/ */
struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id *from_peer, struct msgb *msg, struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_gsup_peer_id *from_peer, struct msgb *msg,
osmo_gsup_req_send_response_t send_response_cb, void *cb_data, osmo_gsup_req_send_response_t send_response_cb, void *cb_data,
struct llist_head *add_to_list) struct llist_head *add_to_list)
{ {
@@ -107,15 +52,9 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
struct osmo_gsup_req *req; struct osmo_gsup_req *req;
int rc; int rc;
if (!from_peer) {
LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from NULL peer is not allowed\n");
msgb_free(msg);
return NULL;
}
if (!msgb_l2(msg) || !msgb_l2len(msg)) { if (!msgb_l2(msg) || !msgb_l2len(msg)) {
LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: missing or empty L2 data\n", LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: missing or empty L2 data\n",
osmo_cni_peer_id_to_str(from_peer)); osmo_gsup_peer_id_to_str(from_peer));
msgb_free(msg); msgb_free(msg);
return NULL; return NULL;
} }
@@ -127,10 +66,11 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
req->msg = msg; req->msg = msg;
req->send_response_cb = send_response_cb; req->send_response_cb = send_response_cb;
req->cb_data = cb_data; req->cb_data = cb_data;
req->source_name = *from_peer; if (from_peer)
req->source_name = *from_peer;
rc = osmo_gsup_decode(msgb_l2(req->msg), msgb_l2len(req->msg), (struct osmo_gsup_message*)&req->gsup); rc = osmo_gsup_decode(msgb_l2(req->msg), msgb_l2len(req->msg), (struct osmo_gsup_message*)&req->gsup);
if (rc < 0) { if (rc < 0) {
LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: cannot decode (rc=%d)\n", osmo_cni_peer_id_to_str(from_peer), rc); LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: cannot decode (rc=%d)\n", osmo_gsup_peer_id_to_str(from_peer), rc);
osmo_gsup_req_free(req); osmo_gsup_req_free(req);
return NULL; return NULL;
} }
@@ -138,18 +78,18 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
LOG_GSUP_REQ(req, LOGL_DEBUG, "new request: {%s}\n", osmo_gsup_message_to_str_c(OTC_SELECT, &req->gsup)); LOG_GSUP_REQ(req, LOGL_DEBUG, "new request: {%s}\n", osmo_gsup_message_to_str_c(OTC_SELECT, &req->gsup));
if (req->gsup.source_name_len) { if (req->gsup.source_name_len) {
if (osmo_cni_peer_id_set(&req->source_name, OSMO_CNI_PEER_ID_IPA_NAME, if (osmo_gsup_peer_id_set(&req->source_name, OSMO_GSUP_PEER_ID_IPA_NAME,
req->gsup.source_name, req->gsup.source_name_len)) { req->gsup.source_name, req->gsup.source_name_len)) {
LOGP(DLGSUP, LOGL_ERROR, LOGP(DLGSUP, LOGL_ERROR,
"Rx GSUP from %s: failed to decode source_name, message is not routable\n", "Rx GSUP from %s: failed to decode source_name, message is not routable\n",
osmo_cni_peer_id_to_str(from_peer)); osmo_gsup_peer_id_to_str(from_peer));
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true); osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);
return NULL; return NULL;
} }
/* The source of the GSUP message is not the immediate GSUP peer; the peer is our proxy for that source. /* The source of the GSUP message is not the immediate GSUP peer; the peer is our proxy for that source.
*/ */
if (osmo_cni_peer_id_cmp(&req->source_name, from_peer)) if (osmo_gsup_peer_id_cmp(&req->source_name, from_peer))
req->via_proxy = *from_peer; req->via_proxy = *from_peer;
} }
@@ -164,8 +104,6 @@ struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_cni_peer_id
return req; return req;
} }
/*! Free an osmo_gsup_req and its msgb -- this is usually implicit in osmo_gsup_req_resond_*(), it should not be
* necessary to call this directly. */
void osmo_gsup_req_free(struct osmo_gsup_req *req) void osmo_gsup_req_free(struct osmo_gsup_req *req)
{ {
LOG_GSUP_REQ(req, LOGL_DEBUG, "free\n"); LOG_GSUP_REQ(req, LOGL_DEBUG, "free\n");
@@ -176,25 +114,6 @@ void osmo_gsup_req_free(struct osmo_gsup_req *req)
talloc_free(req); talloc_free(req);
} }
/*! Send a response to a GSUP request.
*
* Ensure that the response message contains all GSUP IEs that identify it as a response for the request req, by calling
* osmo_gsup_make_response().
*
* The final complete response message is passed to req->send_response_cb() to take care of the transmission.
*
* \param req Request as previously initialized by osmo_gsup_req_new().
* \param response Buffer to compose the response, possibly with some pre-configured IEs.
* Any missing IEs are added via osmo_gsup_make_response().
* Must not be NULL. Does not need to remain valid memory beyond the function call,
* i.e. this can just be a local variable in the calling function.
* \param error True when the response message indicates an error response (error message type).
* \param final_response True when the request is concluded by this response, which deallocates the req.
* False when the request should remain open after this response.
* For most plain request->response GSUP messages, this should be True.
* \param file Source file for logging as in __FILE__, added by osmo_gsup_req_respond() macro.
* \param line Source line for logging as in __LINE__, added by osmo_gsup_req_respond() macro.
*/
int _osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response, int _osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response,
bool error, bool final_response, const char *file, int line) bool error, bool final_response, const char *file, int line)
{ {
@@ -225,18 +144,6 @@ exit_cleanup:
return rc; return rc;
} }
/*! Shorthand for _osmo_gsup_req_respond() with no additional IEs and a fixed message type.
* Set the message type in a local osmo_gsup_message and feed it to _osmo_gsup_req_respond().
* That will ensure to add all IEs that identify it as a response to req.
*
* \param req Request as previously initialized by osmo_gsup_req_new().
* \param message_type The GSUP message type discriminator to respond with.
* \param final_response True when the request is concluded by this response, which deallocates the req.
* False when the request should remain open after this response.
* For most plain request->response GSUP messages, this should be True.
* \param file Source file for logging as in __FILE__, added by osmo_gsup_req_respond_msgt() macro.
* \param line Source line for logging as in __LINE__, added by osmo_gsup_req_respond_msgt() macro.
*/
int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type, int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type,
bool final_response, const char *file, int line) bool final_response, const char *file, int line)
{ {
@@ -247,17 +154,6 @@ int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_messag
file, line); file, line);
} }
/*! Shorthand for _osmo_gsup_req_respond() with an error cause IEs and using the req's matched error message type.
* Set the error cause in a local osmo_gsup_message and feed it to _osmo_gsup_req_respond().
* That will ensure to add all IEs that identify it as a response to req.
*
* Responding with an error always implies a final response: req is implicitly deallocated.
*
* \param req Request as previously initialized by osmo_gsup_req_new().
* \param cause The error cause to include in a OSMO_GSUP_CAUSE_IE.
* \param file Source file for logging as in __FILE__, added by osmo_gsup_req_respond_err() macro.
* \param line Source line for logging as in __LINE__, added by osmo_gsup_req_respond_err() macro.
*/
void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause, void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause,
const char *file, int line) const char *file, int line)
{ {

View File

@@ -31,7 +31,6 @@
#include <osmocom/vty/command.h> #include <osmocom/vty/command.h>
#include <osmocom/vty/telnet_interface.h> #include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/ports.h> #include <osmocom/vty/ports.h>
#include <osmocom/vty/cpu_sched_vty.h>
#include <osmocom/ctrl/control_vty.h> #include <osmocom/ctrl/control_vty.h>
#include <osmocom/gsm/apn.h> #include <osmocom/gsm/apn.h>
#include <osmocom/gsm/gsm48_ie.h> #include <osmocom/gsm/gsm48_ie.h>
@@ -39,7 +38,7 @@
#include <osmocom/gsm/gsm23003.h> #include <osmocom/gsm/gsm23003.h>
#include <osmocom/mslookup/mslookup_client.h> #include <osmocom/mslookup/mslookup_client.h>
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
#include <osmocom/hlr/db.h> #include <osmocom/hlr/db.h>
#include <osmocom/hlr/hlr.h> #include <osmocom/hlr/hlr.h>
#include <osmocom/hlr/ctrl.h> #include <osmocom/hlr/ctrl.h>
@@ -52,6 +51,7 @@
#include <osmocom/hlr/dgsm.h> #include <osmocom/hlr/dgsm.h>
#include <osmocom/hlr/proxy.h> #include <osmocom/hlr/proxy.h>
#include <osmocom/hlr/lu_fsm.h> #include <osmocom/hlr/lu_fsm.h>
#include <osmocom/hlr/sms_over_gsup.h>
#include <osmocom/mslookup/mdns.h> #include <osmocom/mslookup/mdns.h>
struct hlr *g_hlr; struct hlr *g_hlr;
@@ -85,6 +85,8 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
llist_for_each_entry(co, &g_hlr->gs->clients, list) { llist_for_each_entry(co, &g_hlr->gs->clients, list) {
struct osmo_gsup_message gsup = { }; 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; struct msgb *msg_out;
uint8_t *peer; uint8_t *peer;
int peer_len; int peer_len;
@@ -129,7 +131,8 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
subscr->imsi, cn_domain == OSMO_GSUP_CN_DOMAIN_PS ? "PS" : "CS", subscr->imsi, cn_domain == OSMO_GSUP_CN_DOMAIN_PS ? "PS" : "CS",
osmo_quote_str(peer_compare, -1)); osmo_quote_str(peer_compare, -1));
if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi, subscr->msisdn, cn_domain, OTC_SELECT) != 0) { 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(DLGSUP, LOGL_ERROR, LOGP(DLGSUP, LOGL_ERROR,
"IMSI='%s': Cannot notify GSUP client; could not create gsup message " "IMSI='%s': Cannot notify GSUP client; could not create gsup message "
"for %s:%u\n", subscr->imsi, "for %s:%u\n", subscr->imsi,
@@ -266,9 +269,9 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val,
if (nam_val) if (nam_val)
return 0; return 0;
if (subscr->vlr_number[0] && !osmo_ipa_name_set_str(&vlr_name, subscr->vlr_number)) if (subscr->vlr_number && osmo_ipa_name_set_str(&vlr_name, subscr->vlr_number))
osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &vlr_name, &gsup_del_data); osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &vlr_name, &gsup_del_data);
if (subscr->sgsn_number[0] && !osmo_ipa_name_set_str(&vlr_name, subscr->sgsn_number)) if (subscr->sgsn_number && osmo_ipa_name_set_str(&vlr_name, subscr->sgsn_number))
osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &vlr_name, &gsup_del_data); osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &vlr_name, &gsup_del_data);
return 0; return 0;
} }
@@ -284,7 +287,6 @@ static int rx_send_auth_info(struct osmo_gsup_req *req)
.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT, .message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT,
}; };
bool separation_bit = false; bool separation_bit = false;
int num_auth_vectors = OSMO_GSUP_MAX_NUM_AUTH_INFO;
unsigned int auc_3g_ind; unsigned int auc_3g_ind;
int rc; int rc;
@@ -293,21 +295,18 @@ static int rx_send_auth_info(struct osmo_gsup_req *req)
if (req->gsup.current_rat_type == OSMO_RAT_EUTRAN_SGS) if (req->gsup.current_rat_type == OSMO_RAT_EUTRAN_SGS)
separation_bit = true; separation_bit = true;
if (req->gsup.num_auth_vectors > 0 &&
req->gsup.num_auth_vectors <= OSMO_GSUP_MAX_NUM_AUTH_INFO)
num_auth_vectors = req->gsup.num_auth_vectors;
rc = db_ind(g_hlr->dbc, &req->source_name, &auc_3g_ind); rc = db_ind(g_hlr->dbc, &req->source_name, &auc_3g_ind);
if (rc) { if (rc) {
LOG_GSUP_REQ(req, LOGL_ERROR, LOG_GSUP_REQ(req, LOGL_ERROR,
"Unable to determine 3G auth IND for source %s (rc=%d)," "Unable to determine 3G auth IND for source %s (rc=%d),"
" generating tuples with IND = 0\n", " generating tuples with IND = 0\n",
osmo_cni_peer_id_to_str(&req->source_name), rc); osmo_gsup_peer_id_to_str(&req->source_name), rc);
auc_3g_ind = 0; auc_3g_ind = 0;
} }
rc = db_get_auc(g_hlr->dbc, req->gsup.imsi, auc_3g_ind, rc = db_get_auc(g_hlr->dbc, req->gsup.imsi, auc_3g_ind,
gsup_out.auth_vectors, gsup_out.auth_vectors,
num_auth_vectors, ARRAY_SIZE(gsup_out.auth_vectors),
req->gsup.rand, req->gsup.auts, separation_bit); req->gsup.rand, req->gsup.auts, separation_bit);
if (rc <= 0) { if (rc <= 0) {
@@ -321,7 +320,7 @@ static int rx_send_auth_info(struct osmo_gsup_req *req)
" Returning slightly inaccurate cause 'IMSI Unknown' via GSUP"); " Returning slightly inaccurate cause 'IMSI Unknown' via GSUP");
return rc; return rc;
case -ENOENT: case -ENOENT:
osmo_gsup_req_respond_err(req, g_hlr->reject_cause, "IMSI unknown"); osmo_gsup_req_respond_err(req, GMM_CAUSE_ROAMING_NOTALLOWED, "IMSI unknown");
return rc; return rc;
default: default:
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "failure to look up IMSI in db"); osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "failure to look up IMSI in db");
@@ -373,7 +372,7 @@ static int rx_purge_ms_req(struct osmo_gsup_req *req)
if (rc == 0) if (rc == 0)
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_PURGE_MS_RESULT, true); osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_PURGE_MS_RESULT, true);
else if (rc == -ENOENT) else if (rc == -ENOENT)
osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown"); osmo_gsup_req_respond_err(req, GMM_CAUSE_ROAMING_NOTALLOWED, "IMSI unknown");
else else
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "db error"); osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "db error");
return rc; return rc;
@@ -455,8 +454,8 @@ static int read_cb_forward(struct osmo_gsup_req *req)
struct osmo_ipa_name destination_name; struct osmo_ipa_name destination_name;
/* Check for routing IEs */ /* Check for routing IEs */
if (!req->gsup.source_name || !req->gsup.source_name_len if (!req->gsup.source_name[0] || !req->gsup.source_name_len
|| !req->gsup.destination_name || !req->gsup.destination_name_len) { || !req->gsup.destination_name[0] || !req->gsup.destination_name_len) {
LOGP_GSUP_FWD(&req->gsup, LOGL_ERROR, "missing routing IEs\n"); LOGP_GSUP_FWD(&req->gsup, LOGL_ERROR, "missing routing IEs\n");
goto routing_error; goto routing_error;
} }
@@ -510,6 +509,10 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
} }
} }
/* SMS over GSUP */
if (sms_over_gsup_check_handle_msg(req))
return 0;
/* Distributed GSM: check whether to proxy for / lookup a remote HLR. /* Distributed GSM: check whether to proxy for / lookup a remote HLR.
* It would require less database hits to do this only if a local-only operation fails with "unknown IMSI", but * It would require less database hits to do this only if a local-only operation fails with "unknown IMSI", but
* it becomes semantically easier if we do this once-off ahead of time. */ * it becomes semantically easier if we do this once-off ahead of time. */
@@ -565,12 +568,12 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
return 0; return 0;
} }
static void print_usage(void) static void print_usage()
{ {
printf("Usage: osmo-hlr\n"); printf("Usage: osmo-hlr\n");
} }
static void print_help(void) static void print_help()
{ {
printf(" -h --help This text.\n"); printf(" -h --help This text.\n");
printf(" -c --config-file filename The config file to use.\n"); printf(" -c --config-file filename The config file to use.\n");
@@ -583,10 +586,6 @@ static void print_help(void)
printf(" -U --db-upgrade Allow HLR database schema upgrades.\n"); printf(" -U --db-upgrade Allow HLR database schema upgrades.\n");
printf(" -C --db-check Quit after opening (and upgrading) the database.\n"); printf(" -C --db-check Quit after opening (and upgrading) the database.\n");
printf(" -V --version Print the version of OsmoHLR.\n"); printf(" -V --version Print the version of OsmoHLR.\n");
printf("\nVTY reference generation:\n");
printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
} }
static struct { static struct {
@@ -602,37 +601,10 @@ static struct {
.db_upgrade = false, .db_upgrade = false,
}; };
static void handle_long_options(const char *prog_name, const int long_option)
{
static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
switch (long_option) {
case 1:
vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
if (vty_ref_mode < 0) {
fprintf(stderr, "%s: Unknown VTY reference generation "
"mode '%s'\n", prog_name, optarg);
exit(2);
}
break;
case 2:
fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
exit(0);
default:
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
exit(2);
}
}
static void handle_options(int argc, char **argv) static void handle_options(int argc, char **argv)
{ {
while (1) { while (1) {
int option_index = 0, c; int option_index = 0, c;
static int long_option = 0;
static struct option long_options[] = { static struct option long_options[] = {
{"help", 0, 0, 'h'}, {"help", 0, 0, 'h'},
{"config-file", 1, 0, 'c'}, {"config-file", 1, 0, 'c'},
@@ -645,8 +617,6 @@ static void handle_options(int argc, char **argv)
{"db-upgrade", 0, 0, 'U' }, {"db-upgrade", 0, 0, 'U' },
{"db-check", 0, 0, 'C' }, {"db-check", 0, 0, 'C' },
{"version", 0, 0, 'V' }, {"version", 0, 0, 'V' },
{"vty-ref-mode", 1, &long_option, 1},
{"vty-ref-xml", 0, &long_option, 2},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
@@ -656,9 +626,6 @@ static void handle_options(int argc, char **argv)
break; break;
switch (c) { switch (c) {
case 0:
handle_long_options(argv[0], long_option);
break;
case 'h': case 'h':
print_usage(); print_usage();
print_help(); print_help();
@@ -724,7 +691,7 @@ static void signal_hdlr(int signal)
} }
static const char vlr_copyright[] = static const char vlr_copyright[] =
"Copyright (C) 2016-2023 by Harald Welte, sysmocom s.f.m.c. GmbH\r\n" "Copyright (C) 2016, 2017 by Harald Welte, sysmocom s.f.m.c. GmbH\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n" "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n" "This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n"; "There is NO WARRANTY, to the extent permitted by law.\r\n";
@@ -750,18 +717,19 @@ int main(int argc, char **argv)
g_hlr = talloc_zero(hlr_ctx, struct hlr); g_hlr = talloc_zero(hlr_ctx, struct hlr);
INIT_LLIST_HEAD(&g_hlr->euse_list); 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->ss_sessions);
INIT_LLIST_HEAD(&g_hlr->ussd_routes); INIT_LLIST_HEAD(&g_hlr->ussd_routes);
INIT_LLIST_HEAD(&g_hlr->mslookup.server.local_site_services); INIT_LLIST_HEAD(&g_hlr->mslookup.server.local_site_services);
g_hlr->db_file_path = talloc_strdup(g_hlr, HLR_DEFAULT_DB_FILE_PATH); g_hlr->db_file_path = talloc_strdup(g_hlr, HLR_DEFAULT_DB_FILE_PATH);
g_hlr->mslookup.server.mdns.domain_suffix = talloc_strdup(g_hlr, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT); g_hlr->mslookup.server.mdns.domain_suffix = talloc_strdup(g_hlr, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT);
g_hlr->mslookup.client.mdns.domain_suffix = talloc_strdup(g_hlr, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT); g_hlr->mslookup.client.mdns.domain_suffix = talloc_strdup(g_hlr, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT);
g_hlr->reject_cause = GMM_CAUSE_IMSI_UNKNOWN;
g_hlr->no_proxy_reject_cause = GMM_CAUSE_IMSI_UNKNOWN;
/* Init default (call independent) SS session guard timeout value */ /* Init default (call independent) SS session guard timeout value */
g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT; g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT;
g_hlr->sms_over_gsup.try_direct_delivery = true;
rc = osmo_init_logging2(hlr_ctx, &hlr_log_info); rc = osmo_init_logging2(hlr_ctx, &hlr_log_info);
if (rc < 0) { if (rc < 0) {
fprintf(stderr, "Error initializing logging\n"); fprintf(stderr, "Error initializing logging\n");
@@ -774,10 +742,9 @@ int main(int argc, char **argv)
osmo_stats_init(hlr_ctx); osmo_stats_init(hlr_ctx);
vty_init(&vty_info); vty_init(&vty_info);
ctrl_vty_init(hlr_ctx); ctrl_vty_init(hlr_ctx);
hlr_vty_init(hlr_ctx);
dgsm_vty_init();
osmo_cpu_sched_vty_init(hlr_ctx);
handle_options(argc, argv); handle_options(argc, argv);
hlr_vty_init();
dgsm_vty_init();
rc = vty_read_config_file(cmdline_opts.config_file, NULL); rc = vty_read_config_file(cmdline_opts.config_file, NULL);
if (rc < 0) { if (rc < 0) {
@@ -815,7 +782,8 @@ int main(int argc, char **argv)
} }
/* start telnet after reading config for vty_get_bind_addr() */ /* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_default(hlr_ctx, NULL, OSMO_VTY_PORT_HLR); rc = telnet_init_dynif(hlr_ctx, NULL, vty_get_bind_addr(),
OSMO_VTY_PORT_HLR);
if (rc < 0) if (rc < 0)
return rc; return rc;
@@ -828,6 +796,7 @@ int main(int argc, char **argv)
} }
proxy_init(g_hlr->gs); proxy_init(g_hlr->gs);
g_hlr->ctrl_bind_addr = ctrl_vty_get_bind_addr();
g_hlr->ctrl = hlr_controlif_setup(g_hlr); g_hlr->ctrl = hlr_controlif_setup(g_hlr);
dgsm_start(hlr_ctx); dgsm_start(hlr_ctx);

View File

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

View File

@@ -122,40 +122,9 @@ void ussd_route_del(struct hlr_ussd_route *rt)
talloc_free(rt); talloc_free(rt);
} }
static struct hlr_ussd_route *ussd_route_lookup_for_req(struct hlr *hlr, const struct ss_request *req) static struct hlr_ussd_route *ussd_route_lookup_7bit(struct hlr *hlr, const char *ussd_code)
{ {
const uint8_t cgroup = req->ussd_data_dcs >> 4;
const uint8_t lang = req->ussd_data_dcs & 0x0f;
char ussd_code[GSM0480_USSD_7BIT_STRING_LEN];
struct hlr_ussd_route *rt; struct hlr_ussd_route *rt;
ussd_code[0] = '\0';
/* We support only the Coding Group 0 (GSM 7-bit default alphabeet). In fact,
* the USSD request is usually limited to [*#0-9], so we don't really need to
* support other coding groups and languages. */
switch (cgroup) {
case 0:
/* The Language is usually set to '1111'B (unspecified), but some UEs
* are known to indicate '0000'B (German). */
if (lang != 0x0f) {
LOGP(DSS, LOGL_NOTICE, "USSD DataCodingScheme (0x%02x): "
"the Language is usually set to 15 (unspecified), "
"but the request indicates %u - ignoring this\n",
req->ussd_data_dcs, lang);
/* do not abort, attempt to decode as if it was '1111'B */
}
gsm_7bit_decode_n_ussd(&ussd_code[0], sizeof(ussd_code),
req->ussd_data, (req->ussd_data_len * 8) / 7);
break;
default:
LOGP(DSS, LOGL_ERROR, "USSD DataCodingScheme (0x%02x): "
"Coding Group %u is not supported, expecting Coding Group 0\n",
req->ussd_data_dcs, cgroup);
return NULL;
}
llist_for_each_entry(rt, &hlr->ussd_routes, list) { llist_for_each_entry(rt, &hlr->ussd_routes, list) {
if (!strncmp(ussd_code, rt->prefix, strlen(rt->prefix))) { if (!strncmp(ussd_code, rt->prefix, strlen(rt->prefix))) {
LOGP(DSS, LOGL_DEBUG, "Found %s '%s' (prefix '%s') for USSD " LOGP(DSS, LOGL_DEBUG, "Found %s '%s' (prefix '%s') for USSD "
@@ -283,7 +252,6 @@ static int ss_gsup_send_to_ms(struct ss_session *ss, struct osmo_gsup_server *gs
rc = osmo_gsup_encode(msg, gsup); rc = osmo_gsup_encode(msg, gsup);
if (rc) { if (rc) {
LOGPSS(ss, LOGL_ERROR, "Failed to encode GSUP message\n"); LOGPSS(ss, LOGL_ERROR, "Failed to encode GSUP message\n");
msgb_free(msg);
return rc; return rc;
} }
@@ -310,20 +278,20 @@ static int ss_gsup_send_to_ms(struct ss_session *ss, struct osmo_gsup_server *gs
} }
static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_msg_type, static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_msg_type,
struct msgb *ss_msg) bool final, struct msgb *ss_msg)
{ {
struct osmo_gsup_message resp; struct osmo_gsup_message resp = {
int rc;
resp = (struct osmo_gsup_message) {
.message_type = gsup_msg_type, .message_type = gsup_msg_type,
.session_id = ss->session_id, .session_id = ss->session_id,
.session_state = ss->state,
}; };
int rc;
OSMO_STRLCPY_ARRAY(resp.imsi, ss->imsi); OSMO_STRLCPY_ARRAY(resp.imsi, ss->imsi);
if (final)
resp.session_state = OSMO_GSUP_SESSION_STATE_END;
else
resp.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
if (ss_msg) { if (ss_msg) {
resp.ss_info = msgb_data(ss_msg); resp.ss_info = msgb_data(ss_msg);
resp.ss_info_len = msgb_length(ss_msg); resp.ss_info_len = msgb_length(ss_msg);
@@ -343,8 +311,7 @@ static int ss_tx_reject(struct ss_session *ss, int invoke_id, uint8_t problem_ta
LOGPSS(ss, LOGL_NOTICE, "Tx Reject(%u, 0x%02x, 0x%02x)\n", invoke_id, LOGPSS(ss, LOGL_NOTICE, "Tx Reject(%u, 0x%02x, 0x%02x)\n", invoke_id,
problem_tag, problem_code); problem_tag, problem_code);
OSMO_ASSERT(msg); OSMO_ASSERT(msg);
ss->state = OSMO_GSUP_SESSION_STATE_END; return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, msg);
} }
#endif #endif
@@ -353,16 +320,15 @@ static int ss_tx_to_ms_error(struct ss_session *ss, uint8_t invoke_id, uint8_t e
struct msgb *msg = gsm0480_gen_return_error(invoke_id, error_code); 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); LOGPSS(ss, LOGL_NOTICE, "Tx ReturnError(%u, 0x%02x)\n", invoke_id, error_code);
OSMO_ASSERT(msg); OSMO_ASSERT(msg);
ss->state = OSMO_GSUP_SESSION_STATE_END; return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, msg);
} }
static int ss_tx_to_ms_ussd_7bit(struct ss_session *ss, uint8_t invoke_id, const char *text) static int ss_tx_to_ms_ussd_7bit(struct ss_session *ss, bool final, uint8_t invoke_id, const char *text)
{ {
struct msgb *msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text); struct msgb *msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text);
LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text); LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text);
OSMO_ASSERT(msg); OSMO_ASSERT(msg);
return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, msg); return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, final, msg);
} }
/*********************************************************************** /***********************************************************************
@@ -378,8 +344,6 @@ static int handle_ussd_own_msisdn(struct ss_session *ss,
char buf[GSM0480_USSD_7BIT_STRING_LEN+1]; char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
int rc; int rc;
ss->state = OSMO_GSUP_SESSION_STATE_END;
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr); rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
switch (rc) { switch (rc) {
case 0: case 0:
@@ -387,7 +351,7 @@ static int handle_ussd_own_msisdn(struct ss_session *ss,
snprintf(buf, sizeof(buf), "You have no MSISDN!"); snprintf(buf, sizeof(buf), "You have no MSISDN!");
else else
snprintf(buf, sizeof(buf), "Your extension is %s", subscr.msisdn); snprintf(buf, sizeof(buf), "Your extension is %s", subscr.msisdn);
ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf); ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, buf);
break; break;
case -ENOENT: case -ENOENT:
ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER); ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
@@ -405,22 +369,208 @@ static int handle_ussd_own_imsi(struct ss_session *ss,
{ {
char buf[GSM0480_USSD_7BIT_STRING_LEN+1]; char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
snprintf(buf, sizeof(buf), "Your IMSI is %s", ss->imsi); snprintf(buf, sizeof(buf), "Your IMSI is %s", ss->imsi);
ss->state = OSMO_GSUP_SESSION_STATE_END; ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, buf);
ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
return 0; return 0;
} }
/* This handler just keeps the session idle unless the guard timer expires. */ static int handle_ussd_get_ran(struct ss_session *ss,
static int handle_ussd_test_idle(struct ss_session *ss, const struct osmo_gsup_message *gsup,
const struct osmo_gsup_message *gsup, const struct ss_request *req)
const struct ss_request *req)
{ {
char buf[GSM0480_USSD_7BIT_STRING_LEN + 1]; struct hlr_subscriber subscr;
snprintf(buf, sizeof(buf), "Keeping your session idle, it will expire " char response[512];
"at most in %u seconds.", g_hlr->ncss_guard_timeout); int rc;
ss->state = OSMO_GSUP_SESSION_STATE_CONTINUE; const char *rat;
ss_tx_to_ms_ussd_7bit(ss, req->invoke_id, buf);
return 0; rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
switch (rc) {
case 0:
if (!*subscr.last_lu_rat_cs)
rat = "nothing, you don't exist";
else if (!strcmp(subscr.last_lu_rat_cs, "GERAN-A"))
rat = "2G";
else if (!strcmp(subscr.last_lu_rat_cs, "UTRAN-Iu"))
rat = "3G";
else if (!strcmp(subscr.last_lu_rat_cs, "EUTRAN-SGs"))
rat = "4G";
else
rat = subscr.last_lu_rat_cs;
snprintf(response, sizeof(response),
"Now on %s. Available:%s%s%s.",
rat,
subscr.rat_types[OSMO_RAT_GERAN_A]? " 2G" : "",
subscr.rat_types[OSMO_RAT_UTRAN_IU]? " 3G" : "",
subscr.rat_types[OSMO_RAT_EUTRAN_SGS]? " 4G" : "");
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, response);
break;
case -ENOENT:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
break;
case -EIO:
default:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
break;
}
return rc;
}
static int handle_ussd_gsm_on(struct ss_session *ss,
const struct osmo_gsup_message *gsup,
const struct ss_request *req)
{
struct hlr_subscriber subscr;
int rc;
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
switch (rc) {
case 0:
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_GERAN_A, true);
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
"Enabled GERAN-A (2G)");
break;
case -ENOENT:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
break;
case -EIO:
default:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
break;
}
return rc;
}
static int handle_ussd_gsm_off(struct ss_session *ss,
const struct osmo_gsup_message *gsup,
const struct ss_request *req)
{
struct hlr_subscriber subscr;
int rc;
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
switch (rc) {
case 0:
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_GERAN_A, false);
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
"Disabled GERAN-A (2G)");
break;
case -ENOENT:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
break;
case -EIO:
default:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
break;
}
return rc;
}
static int handle_ussd_umts_on(struct ss_session *ss,
const struct osmo_gsup_message *gsup,
const struct ss_request *req)
{
struct hlr_subscriber subscr;
int rc;
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
switch (rc) {
case 0:
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_UTRAN_IU, true);
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
"Enabled UTRAN-Iu (3G)");
break;
case -ENOENT:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
break;
case -EIO:
default:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
break;
}
return rc;
}
static int handle_ussd_umts_off(struct ss_session *ss,
const struct osmo_gsup_message *gsup,
const struct ss_request *req)
{
struct hlr_subscriber subscr;
int rc;
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
switch (rc) {
case 0:
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_UTRAN_IU, false);
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
"Disabled UTRAN-Iu (3G)");
break;
case -ENOENT:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
break;
case -EIO:
default:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
break;
}
return rc;
}
static int handle_ussd_lte_on(struct ss_session *ss,
const struct osmo_gsup_message *gsup,
const struct ss_request *req)
{
struct hlr_subscriber subscr;
int rc;
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
switch (rc) {
case 0:
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_EUTRAN_SGS, true);
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
"Enabled EUTRAN-SGs (4G)");
break;
case -ENOENT:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
break;
case -EIO:
default:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
break;
}
return rc;
}
static int handle_ussd_lte_off(struct ss_session *ss,
const struct osmo_gsup_message *gsup,
const struct ss_request *req)
{
struct hlr_subscriber subscr;
int rc;
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
switch (rc) {
case 0:
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_EUTRAN_SGS, false);
rc = ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id,
"Disabled EUTRAN-SGs (4G)");
break;
case -ENOENT:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
break;
case -EIO:
default:
rc = ss_tx_to_ms_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
break;
}
return rc;
} }
@@ -434,8 +584,32 @@ static const struct hlr_iuse hlr_iuses[] = {
.handle_ussd = handle_ussd_own_imsi, .handle_ussd = handle_ussd_own_imsi,
}, },
{ {
.name = "test-idle", .name = "get-ran",
.handle_ussd = handle_ussd_test_idle, .handle_ussd = handle_ussd_get_ran,
},
{
.name = "gsm-on",
.handle_ussd = handle_ussd_gsm_on,
},
{
.name = "gsm-off",
.handle_ussd = handle_ussd_gsm_off,
},
{
.name = "umts-on",
.handle_ussd = handle_ussd_umts_on,
},
{
.name = "umts-off",
.handle_ussd = handle_ussd_umts_off,
},
{
.name = "lte-on",
.handle_ussd = handle_ussd_lte_on,
},
{
.name = "lte-off",
.handle_ussd = handle_ussd_lte_off,
}, },
}; };
@@ -470,22 +644,22 @@ static bool ss_op_is_ussd(uint8_t opcode)
} }
/* is this GSUP connection an EUSE (true) or not (false)? */ /* is this GSUP connection an EUSE (true) or not (false)? */
static bool peer_name_is_euse(const struct osmo_cni_peer_id *peer_name) static bool peer_name_is_euse(const struct osmo_gsup_peer_id *peer_name)
{ {
if (peer_name->type != OSMO_CNI_PEER_ID_IPA_NAME) if (peer_name->type != OSMO_GSUP_PEER_ID_IPA_NAME)
return false; return false;
if (peer_name->ipa_name.len <= 5) if (peer_name->ipa_name.len <= 5)
return false; return false;
return strncmp((char *)(peer_name->ipa_name.val), "EUSE-", 5) == 0; return strncmp((char *)(peer_name->ipa_name.val), "EUSE-", 5) == 0;
} }
static struct hlr_euse *euse_by_name(const struct osmo_cni_peer_id *peer_name) static struct hlr_euse *euse_by_name(const struct osmo_gsup_peer_id *peer_name)
{ {
if (!peer_name_is_euse(peer_name)) if (!peer_name_is_euse(peer_name))
return NULL; return NULL;
/* above peer_name_is_euse() ensures this: */ /* above peer_name_is_euse() ensures this: */
OSMO_ASSERT(peer_name->type == OSMO_CNI_PEER_ID_IPA_NAME); OSMO_ASSERT(peer_name->type == OSMO_GSUP_PEER_ID_IPA_NAME);
return euse_find(g_hlr, (const char*)(peer_name->ipa_name.val)+5); return euse_find(g_hlr, (const char*)(peer_name->ipa_name.val)+5);
} }
@@ -550,9 +724,9 @@ static int handle_ussd(struct ss_session *ss, bool is_euse_originated, const str
} else { } else {
/* Handle internally */ /* Handle internally */
ss->u.iuse->handle_ussd(ss, gsup, req); ss->u.iuse->handle_ussd(ss, gsup, req);
/* Release session if the handler has changed its state to END */ /* Release session immediately */
if (ss->state == OSMO_GSUP_SESSION_STATE_END) ss_session_free(ss);
ss_session_free(ss); return 0;
} }
} }
@@ -576,10 +750,10 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
LOGP(DSS, LOGL_DEBUG, "%s/0x%08x: Process SS (%s)\n", gsup->imsi, gsup->session_id, LOGP(DSS, LOGL_DEBUG, "%s/0x%08x: Process SS (%s)\n", gsup->imsi, gsup->session_id,
osmo_gsup_session_state_name(gsup->session_state)); osmo_gsup_session_state_name(gsup->session_state));
if (gsup_req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) { if (gsup_req->source_name.type != OSMO_GSUP_PEER_ID_IPA_NAME) {
LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Unable to process SS request: Unsupported GSUP peer id type%s\n", LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Unable to process SS request: Unsupported GSUP peer id type%s\n",
gsup->imsi, gsup->session_id, gsup->imsi, gsup->session_id,
osmo_cni_peer_id_type_name(gsup_req->source_name.type)); osmo_gsup_peer_id_type_name(gsup_req->source_name.type));
osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_PROTO_ERR_UNSPEC, "error processing SS request"); osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_PROTO_ERR_UNSPEC, "error processing SS request");
return; return;
} }
@@ -621,7 +795,7 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
if (!is_euse_originated) { if (!is_euse_originated) {
ss->initial_req_from_ms = gsup_req; ss->initial_req_from_ms = gsup_req;
free_gsup_req = NULL; free_gsup_req = NULL;
OSMO_ASSERT(gsup_req->source_name.type == OSMO_CNI_PEER_ID_IPA_NAME); /* checked above */ OSMO_ASSERT(gsup_req->source_name.type == OSMO_GSUP_PEER_ID_IPA_NAME); /* checked above */
ss->vlr_name = gsup_req->source_name.ipa_name; ss->vlr_name = gsup_req->source_name.ipa_name;
} else { } else {
ss->initial_req_from_euse = gsup_req; ss->initial_req_from_euse = gsup_req;
@@ -634,7 +808,7 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
} else { } else {
/* VLR->EUSE: MO USSD. VLR is known ('conn'), EUSE is to be resolved */ /* VLR->EUSE: MO USSD. VLR is known ('conn'), EUSE is to be resolved */
struct hlr_ussd_route *rt; struct hlr_ussd_route *rt;
rt = ussd_route_lookup_for_req(hlr, &req); rt = ussd_route_lookup_7bit(hlr, (const char *) req.ussd_text);
if (rt) { if (rt) {
if (rt->is_external) { if (rt->is_external) {
ss->is_external = true; ss->is_external = true;
@@ -662,8 +836,7 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
if (!ss) { if (!ss) {
LOGP(DSS, LOGL_ERROR, "%s/0x%08x: CONTINUE for unknown SS session\n", LOGP(DSS, LOGL_ERROR, "%s/0x%08x: CONTINUE for unknown SS session\n",
gsup->imsi, gsup->session_id); gsup->imsi, gsup->session_id);
osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_MSGT_INCOMP_P_STATE, osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "CONTINUE for unknown SS session");
"CONTINUE for unknown SS session");
return; return;
} }
@@ -684,8 +857,6 @@ void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)
if (!ss) { if (!ss) {
LOGP(DSS, LOGL_ERROR, "%s/0x%08x: END for unknown SS session\n", LOGP(DSS, LOGL_ERROR, "%s/0x%08x: END for unknown SS session\n",
gsup->imsi, gsup->session_id); gsup->imsi, gsup->session_id);
osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_MSGT_INCOMP_P_STATE,
"END for unknown SS session");
return; return;
} }
@@ -716,5 +887,4 @@ void rx_proc_ss_error(struct osmo_gsup_req *req)
{ {
LOGP(DSS, LOGL_NOTICE, "%s/0x%08x: Process SS ERROR (%s)\n", req->gsup.imsi, req->gsup.session_id, LOGP(DSS, LOGL_NOTICE, "%s/0x%08x: Process SS ERROR (%s)\n", req->gsup.imsi, req->gsup.session_id,
osmo_gsup_session_state_name(req->gsup.session_state)); osmo_gsup_session_state_name(req->gsup.session_state));
osmo_gsup_req_free(req);
} }

View File

@@ -25,13 +25,7 @@
* *
*/ */
#include <errno.h>
#include <string.h>
#include <osmocom/core/talloc.h> #include <osmocom/core/talloc.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/apn.h>
#include <osmocom/vty/vty.h> #include <osmocom/vty/vty.h>
#include <osmocom/vty/stats.h> #include <osmocom/vty/stats.h>
#include <osmocom/vty/command.h> #include <osmocom/vty/command.h>
@@ -46,36 +40,6 @@
#include <osmocom/hlr/hlr_ussd.h> #include <osmocom/hlr/hlr_ussd.h>
#include <osmocom/hlr/gsup_server.h> #include <osmocom/hlr/gsup_server.h>
static const struct value_string gsm48_gmm_cause_vty_names[] = {
{ GMM_CAUSE_IMSI_UNKNOWN, "imsi-unknown" },
{ GMM_CAUSE_ILLEGAL_MS, "illegal-ms" },
{ GMM_CAUSE_PLMN_NOTALLOWED, "plmn-not-allowed" },
{ GMM_CAUSE_LA_NOTALLOWED, "la-not-allowed" },
{ GMM_CAUSE_ROAMING_NOTALLOWED, "roaming-not-allowed" },
{ GMM_CAUSE_NO_SUIT_CELL_IN_LA, "no-suitable-cell-in-la" },
{ GMM_CAUSE_NET_FAIL, "net-fail" },
{ GMM_CAUSE_CONGESTION, "congestion" },
{ GMM_CAUSE_GSM_AUTH_UNACCEPT, "auth-unacceptable" },
{ GMM_CAUSE_PROTO_ERR_UNSPEC, "proto-error-unspec" },
{ 0, NULL },
};
/* TS 24.008 4.4.4.7 */
static const struct value_string gsm48_gmm_cause_vty_descs[] = {
{ GMM_CAUSE_IMSI_UNKNOWN, " #02: (IMSI unknown in HLR)" },
{ GMM_CAUSE_ILLEGAL_MS, " #03 (Illegal MS)" },
{ GMM_CAUSE_PLMN_NOTALLOWED, " #11: (PLMN not allowed)" },
{ GMM_CAUSE_LA_NOTALLOWED, " #12: (Location Area not allowed)" },
{ GMM_CAUSE_ROAMING_NOTALLOWED, " #13: (Roaming not allowed in this location area)" },
{ GMM_CAUSE_NO_SUIT_CELL_IN_LA, " #15: (No Suitable Cells In Location Area [continue search in PLMN])." },
{ GMM_CAUSE_NET_FAIL, " #17: (Network Failure)" },
{ GMM_CAUSE_CONGESTION, " #22: (Congestion)" },
{ GMM_CAUSE_GSM_AUTH_UNACCEPT, " #23: (GSM authentication unacceptable [UMTS])" },
{ GMM_CAUSE_PROTO_ERR_UNSPEC, "#111: (Protocol error, unspecified)" },
{ 0, NULL },
};
struct cmd_node hlr_node = { struct cmd_node hlr_node = {
HLR_NODE, HLR_NODE,
"%s(config-hlr)# ", "%s(config-hlr)# ",
@@ -106,194 +70,9 @@ DEFUN(cfg_gsup,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
struct cmd_node ps_node = {
PS_NODE,
"%s(config-hlr-ps)# ",
1,
};
DEFUN(cfg_ps,
cfg_ps_cmd,
"ps",
"Configure the PS options")
{
vty->node = PS_NODE;
return CMD_SUCCESS;
}
struct cmd_node ps_pdp_profiles_node = {
PS_PDP_PROFILES_NODE,
"%s(config-hlr-ps-pdp-profiles)# ",
1,
};
DEFUN(cfg_ps_pdp_profiles,
cfg_ps_pdp_profiles_cmd,
"pdp-profiles default",
"Define a PDP profile set.\n"
"Define the global default profile.\n")
{
g_hlr->ps.pdp_profile.enabled = true;
vty->node = PS_PDP_PROFILES_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_no_ps_pdp_profiles,
cfg_no_ps_pdp_profiles_cmd,
"no pdp-profiles default",
NO_STR
"Delete PDP profile.\n"
"Unique identifier for this PDP profile set.\n")
{
g_hlr->ps.pdp_profile.enabled = false;
return CMD_SUCCESS;
}
struct cmd_node ps_pdp_profiles_profile_node = {
PS_PDP_PROFILES_PROFILE_NODE,
"%s(config-hlr-ps-pdp-profile)# ",
1,
};
/* context_id == 0 means the slot is free */
struct osmo_gsup_pdp_info *get_pdp_profile(uint8_t context_id)
{
for (int i = 0; i < OSMO_GSUP_MAX_NUM_PDP_INFO; i++) {
struct osmo_gsup_pdp_info *info = &g_hlr->ps.pdp_profile.pdp_infos[i];
if (info->context_id == context_id)
return info;
}
return NULL;
}
struct osmo_gsup_pdp_info *create_pdp_profile(uint8_t context_id)
{
struct osmo_gsup_pdp_info *info = get_pdp_profile(0);
if (!info)
return NULL;
memset(info, 0, sizeof(*info));
info->context_id = context_id;
info->have_info = 1;
g_hlr->ps.pdp_profile.num_pdp_infos++;
return info;
}
void destroy_pdp_profile(struct osmo_gsup_pdp_info *info)
{
info->context_id = 0;
if (info->apn_enc)
talloc_free((void *) info->apn_enc);
g_hlr->ps.pdp_profile.num_pdp_infos--;
memset(info, 0, sizeof(*info));
}
DEFUN(cfg_ps_pdp_profiles_profile,
cfg_ps_pdp_profiles_profile_cmd,
"profile <1-10>",
"Configure a PDP profile\n"
"Unique PDP context identifier. The lowest profile will be used as default context.\n")
{
struct osmo_gsup_pdp_info *info;
uint8_t context_id = atoi(argv[0]);
info = get_pdp_profile(context_id);
if (!info) {
info = create_pdp_profile(context_id);
if (!info) {
vty_out(vty, "Failed to create profile %d!%s", context_id, VTY_NEWLINE);
return CMD_ERR_INCOMPLETE;
}
}
vty->node = PS_PDP_PROFILES_PROFILE_NODE;
vty->index = info;
return CMD_SUCCESS;
}
DEFUN(cfg_no_ps_pdp_profiles_profile,
cfg_no_ps_pdp_profiles_profile_cmd,
"no profile <1-10>",
NO_STR
"Delete a PDP profile\n"
"Unique PDP context identifier. The lowest profile will be used as default context.\n")
{
struct osmo_gsup_pdp_info *info;
uint8_t context_id = atoi(argv[0]);
info = get_pdp_profile(context_id);
if (info)
destroy_pdp_profile(info);
return CMD_SUCCESS;
}
DEFUN(cfg_ps_pdp_profile_apn, cfg_ps_pdp_profile_apn_cmd,
"apn ID",
"Configure the APN.\n"
"APN name or * for wildcard apn.\n")
{
struct osmo_gsup_pdp_info *info = vty->index;
const char *apn_name = argv[0];
/* apn encoded takes one more byte than strlen() */
size_t apn_enc_len = strlen(apn_name) + 1;
uint8_t *apn_enc;
int ret;
if (apn_enc_len > APN_MAXLEN) {
vty_out(vty, "APN name is too long '%s'. Max is %d!%s", apn_name, APN_MAXLEN, VTY_NEWLINE);
return CMD_ERR_INCOMPLETE;
}
info->apn_enc = apn_enc = (uint8_t *) talloc_zero_size(g_hlr, apn_enc_len);
ret = info->apn_enc_len = osmo_apn_from_str(apn_enc, apn_enc_len, apn_name);
if (ret < 0) {
talloc_free(apn_enc);
info->apn_enc = NULL;
info->apn_enc_len = 0;
vty_out(vty, "Invalid APN name %s!", apn_name);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_no_ps_pdp_profile_apn, cfg_no_ps_pdp_profile_apn_cmd,
"no apn",
NO_STR
"Delete the APN.\n")
{
struct osmo_gsup_pdp_info *info = vty->index;
if (info->apn_enc) {
talloc_free((void *) info->apn_enc);
info->apn_enc = NULL;
info->apn_enc_len = 0;
}
return CMD_SUCCESS;
}
static int config_write_hlr(struct vty *vty) static int config_write_hlr(struct vty *vty)
{ {
vty_out(vty, "hlr%s", VTY_NEWLINE); vty_out(vty, "hlr%s", VTY_NEWLINE);
if (g_hlr->reject_cause != GMM_CAUSE_IMSI_UNKNOWN)
vty_out(vty, " reject-cause not-found %s%s",
get_value_string_or_null(gsm48_gmm_cause_vty_names,
(uint32_t) g_hlr->reject_cause), VTY_NEWLINE);
if (g_hlr->no_proxy_reject_cause != GMM_CAUSE_IMSI_UNKNOWN)
vty_out(vty, " reject-cause no-proxy %s%s",
get_value_string_or_null(gsm48_gmm_cause_vty_names,
(uint32_t) g_hlr->no_proxy_reject_cause), VTY_NEWLINE);
if (g_hlr->store_imei) if (g_hlr->store_imei)
vty_out(vty, " store-imei%s", VTY_NEWLINE); vty_out(vty, " store-imei%s", VTY_NEWLINE);
if (g_hlr->db_file_path && strcmp(g_hlr->db_file_path, HLR_DEFAULT_DB_FILE_PATH)) if (g_hlr->db_file_path && strcmp(g_hlr->db_file_path, HLR_DEFAULT_DB_FILE_PATH))
@@ -323,39 +102,6 @@ static int config_write_hlr_gsup(struct vty *vty)
vty_out(vty, " gsup%s", VTY_NEWLINE); vty_out(vty, " gsup%s", VTY_NEWLINE);
if (g_hlr->gsup_bind_addr) if (g_hlr->gsup_bind_addr)
vty_out(vty, " bind ip %s%s", g_hlr->gsup_bind_addr, VTY_NEWLINE); vty_out(vty, " bind ip %s%s", g_hlr->gsup_bind_addr, VTY_NEWLINE);
if (g_hlr->gsup_unit_name.serno)
vty_out(vty, " ipa-name %s%s", g_hlr->gsup_unit_name.serno, VTY_NEWLINE);
return CMD_SUCCESS;
}
static int config_write_hlr_ps(struct vty *vty)
{
vty_out(vty, " ps%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
static int config_write_hlr_ps_pdp_profiles(struct vty *vty)
{
char apn[APN_MAXLEN + 1] = {};
if (!g_hlr->ps.pdp_profile.enabled)
return CMD_SUCCESS;
vty_out(vty, " pdp-profiles default%s", VTY_NEWLINE);
for (int i = 0; i < g_hlr->ps.pdp_profile.num_pdp_infos; i++) {
struct osmo_gsup_pdp_info *pdp_info = &g_hlr->ps.pdp_profile.pdp_infos[i];
if (!pdp_info->context_id)
continue;
vty_out(vty, " profile %d%s", pdp_info->context_id, VTY_NEWLINE);
if (!pdp_info->have_info)
continue;
if (pdp_info->apn_enc && pdp_info->apn_enc_len) {
osmo_apn_to_str(apn, pdp_info->apn_enc, pdp_info->apn_enc_len);
vty_out(vty, " apn %s%s", apn, VTY_NEWLINE);
}
}
return CMD_SUCCESS; return CMD_SUCCESS;
} }
@@ -368,8 +114,8 @@ static void show_one_conn(struct vty *vty, const struct osmo_gsup_conn *conn)
rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &name, IPAC_IDTAG_SERNR); rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &name, IPAC_IDTAG_SERNR);
OSMO_ASSERT(rc); OSMO_ASSERT(rc);
vty_out(vty, " '%s' from %s:%5u, CS=%u, PS=%u%s", 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, name, isc->addr, isc->port, conn->supports_cs, conn->supports_ps, conn->auc_3g_ind,
VTY_NEWLINE); VTY_NEWLINE);
} }
@@ -410,7 +156,7 @@ DEFUN(cfg_hlr_gsup_ipa_name,
{ {
if (vty->type != VTY_FILE) { if (vty->type != VTY_FILE) {
vty_out(vty, "gsup/ipa-name: The GSUP IPA name cannot be changed at run-time; " vty_out(vty, "gsup/ipa-name: The GSUP IPA name cannot be changed at run-time; "
"It can only be set in the configuration file.%s", VTY_NEWLINE); "It can only be set in the configuraton file.%s", VTY_NEWLINE);
return CMD_WARNING; return CMD_WARNING;
} }
@@ -428,11 +174,17 @@ DEFUN(cfg_hlr_gsup_ipa_name,
#define UROUTE_STR "Routing Configuration\n" #define UROUTE_STR "Routing Configuration\n"
#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n" #define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
#define INT_CHOICE "(own-msisdn|own-imsi|test-idle)" #define INT_CHOICE "(own-msisdn|own-imsi|get-ran|gsm-on|gsm-off|umts-on|umts-off|lte-on|lte-off)"
#define INT_STR "Internal USSD Handler\n" \ #define INT_STR "Internal USSD Handler\n" \
"Respond with subscribers' own MSISDN\n" \ "Respond with subscribers' own MSISDN\n" \
"Respond with subscribers' own IMSI\n" \ "Respond with subscribers' own IMSI\n" \
"Keep the session idle (useful for testing)\n" "Respond with available RAN types\n" \
"Enable GSM service\n" \
"Disable GSM service\n" \
"Enable UMTS service\n" \
"Disable UMTS service\n" \
"Enable LTE service\n" \
"Disable LTE service\n"
#define EXT_STR "External USSD Handler\n" \ #define EXT_STR "External USSD Handler\n" \
"Name of External USSD Handler (IPA CCM ID)\n" "Name of External USSD Handler (IPA CCM ID)\n"
@@ -560,7 +312,7 @@ DEFUN(cfg_no_euse, cfg_no_euse_cmd,
{ {
struct hlr_euse *euse = euse_find(g_hlr, argv[0]); struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
if (!euse) { if (!euse) {
vty_out(vty, "%% Cannot remove non-existent EUSE %s%s", argv[0], VTY_NEWLINE); vty_out(vty, "%% Cannot remove non-existant EUSE %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING; return CMD_WARNING;
} }
if (g_hlr->euse_default == euse) { if (g_hlr->euse_default == euse) {
@@ -610,21 +362,6 @@ DEFUN(cfg_ncss_guard_timeout, cfg_ncss_guard_timeout_cmd,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
DEFUN(cfg_reject_cause, cfg_reject_cause_cmd,
"reject-cause TYPE CAUSE", "") /* Dynamically Generated */
{
int cause_code = get_string_value(gsm48_gmm_cause_vty_names, argv[1]);
OSMO_ASSERT(cause_code >= 0);
if (strcmp(argv[0], "not-found") == 0)
g_hlr->reject_cause = (enum gsm48_gmm_cause) cause_code;
if (strcmp(argv[0], "no-proxy") == 0)
g_hlr->no_proxy_reject_cause = (enum gsm48_gmm_cause) cause_code;
return CMD_SUCCESS;
}
DEFUN(cfg_store_imei, cfg_store_imei_cmd, DEFUN(cfg_store_imei, cfg_store_imei_cmd,
"store-imei", "store-imei",
"Save the IMEI in the database when receiving Check IMEI requests. Note that an MSC does not necessarily send" "Save the IMEI in the database when receiving Check IMEI requests. Note that an MSC does not necessarily send"
@@ -717,22 +454,8 @@ int hlr_vty_is_config_node(struct vty *vty, int node)
} }
} }
void hlr_vty_init(void *hlr_ctx) void hlr_vty_init(void)
{ {
cfg_reject_cause_cmd.string =
vty_cmd_string_from_valstr(hlr_ctx,
gsm48_gmm_cause_vty_names,
"reject-cause (not-found|no-proxy) (", "|", ")",
VTY_DO_LOWER);
cfg_reject_cause_cmd.doc =
vty_cmd_string_from_valstr(hlr_ctx,
gsm48_gmm_cause_vty_descs,
"GSUP/GMM cause to be sent\n"
"in the case the IMSI could not be found in the database\n"
"in the case no remote HLR reponded to mslookup GSUP request\n",
"\n", "", 0);
logging_vty_add_cmds(); logging_vty_add_cmds();
osmo_talloc_vty_add_cmds(); osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds(); osmo_stats_vty_add_cmds();
@@ -748,20 +471,6 @@ void hlr_vty_init(void *hlr_ctx)
install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd); install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
install_element(GSUP_NODE, &cfg_hlr_gsup_ipa_name_cmd); install_element(GSUP_NODE, &cfg_hlr_gsup_ipa_name_cmd);
/* PS */
install_node(&ps_node, config_write_hlr_ps);
install_element(HLR_NODE, &cfg_ps_cmd);
install_node(&ps_pdp_profiles_node, config_write_hlr_ps_pdp_profiles);
install_element(PS_NODE, &cfg_ps_pdp_profiles_cmd);
install_element(PS_NODE, &cfg_no_ps_pdp_profiles_cmd);
install_node(&ps_pdp_profiles_profile_node, NULL);
install_element(PS_PDP_PROFILES_NODE, &cfg_ps_pdp_profiles_profile_cmd);
install_element(PS_PDP_PROFILES_NODE, &cfg_no_ps_pdp_profiles_profile_cmd);
install_element(PS_PDP_PROFILES_PROFILE_NODE, &cfg_ps_pdp_profile_apn_cmd);
install_element(PS_PDP_PROFILES_PROFILE_NODE, &cfg_no_ps_pdp_profile_apn_cmd);
install_element(HLR_NODE, &cfg_database_cmd); install_element(HLR_NODE, &cfg_database_cmd);
install_element(HLR_NODE, &cfg_euse_cmd); install_element(HLR_NODE, &cfg_euse_cmd);
@@ -773,7 +482,6 @@ void hlr_vty_init(void *hlr_ctx)
install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd); install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd);
install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd); install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd);
install_element(HLR_NODE, &cfg_ncss_guard_timeout_cmd); install_element(HLR_NODE, &cfg_ncss_guard_timeout_cmd);
install_element(HLR_NODE, &cfg_reject_cause_cmd);
install_element(HLR_NODE, &cfg_store_imei_cmd); install_element(HLR_NODE, &cfg_store_imei_cmd);
install_element(HLR_NODE, &cfg_no_store_imei_cmd); install_element(HLR_NODE, &cfg_no_store_imei_cmd);
install_element(HLR_NODE, &cfg_subscr_create_on_demand_cmd); install_element(HLR_NODE, &cfg_subscr_create_on_demand_cmd);

View File

@@ -1,5 +1,5 @@
/* OsmoHLR subscriber management VTY implementation */ /* OsmoHLR subscriber management VTY implementation */
/* (C) 2017-2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> /* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved * All Rights Reserved
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@@ -27,11 +27,11 @@
#include <osmocom/vty/vty.h> #include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h> #include <osmocom/vty/command.h>
#include <osmocom/core/utils.h> #include <osmocom/core/utils.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/hlr/hlr.h> #include <osmocom/hlr/hlr.h>
#include <osmocom/hlr/db.h> #include <osmocom/hlr/db.h>
#include <osmocom/hlr/timestamp.h> #include <osmocom/hlr/timestamp.h>
#include <osmocom/hlr/hlr_vty.h>
struct vty; struct vty;
@@ -39,22 +39,25 @@ struct vty;
static char *get_datestr(const time_t *t, char *buf, size_t bufsize) static char *get_datestr(const time_t *t, char *buf, size_t bufsize)
{ {
struct tm tm; struct tm *tm;
gmtime_r(t, &tm);
strftime(buf, bufsize, "%FT%T+00:00", &tm); tm = gmtime(t);
if (!tm)
return "UNKNOWN";
strftime(buf, bufsize, "%FT%T+00:00", tm);
return buf; return buf;
} }
static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen, bool only_age) static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen, const char *last_lu_rat)
{ {
uint32_t age; uint32_t age;
char datebuf[32]; char datebuf[32];
if (!last_lu_seen) if (!last_lu_seen)
return; return;
if (!only_age) vty_out(vty, " last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));
vty_out(vty, " last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));
if (!timestamp_age(&last_lu_seen, &age)) if (!timestamp_age(&last_lu_seen, &age))
vty_out(vty, " (invalid timestamp)%s", VTY_NEWLINE); vty_out(vty, " (invalid timestamp)");
else { else {
vty_out(vty, " ("); vty_out(vty, " (");
#define UNIT_AGO(UNITNAME, UNITVAL) \ #define UNIT_AGO(UNITNAME, UNITVAL) \
@@ -66,19 +69,20 @@ static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t
UNIT_AGO("h", 60*60); UNIT_AGO("h", 60*60);
UNIT_AGO("m", 60); UNIT_AGO("m", 60);
UNIT_AGO("s", 1); UNIT_AGO("s", 1);
if (!only_age) vty_out(vty, " ago)");
vty_out(vty, " ago)%s", VTY_NEWLINE);
else
vty_out(vty, " ago)");
#undef UNIT_AGO #undef UNIT_AGO
} }
if (last_lu_rat && *last_lu_rat != '\0')
vty_out(vty, " on %s", last_lu_rat);
vty_out(vty, "%s", VTY_NEWLINE);
} }
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr) static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
{ {
int rc; int rc;
struct osmo_sub_auth_data2 aud2g; int i;
struct osmo_sub_auth_data2 aud3g; struct osmo_sub_auth_data aud2g;
struct osmo_sub_auth_data aud3g;
vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE); vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
@@ -113,8 +117,12 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
vty_out(vty, " PS disabled%s", VTY_NEWLINE); vty_out(vty, " PS disabled%s", VTY_NEWLINE);
if (subscr->ms_purged_ps) if (subscr->ms_purged_ps)
vty_out(vty, " PS purged%s", VTY_NEWLINE); vty_out(vty, " PS purged%s", VTY_NEWLINE);
dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, false); dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, subscr->last_lu_rat_cs);
dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps, false); dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps, subscr->last_lu_rat_ps);
for (i = OSMO_RAT_UNKNOWN + 1; i < ARRAY_SIZE(subscr->rat_types); i++) {
vty_out(vty, " %s: %s%s", osmo_rat_type_name(i), subscr->rat_types[i] ? "allowed" : "forbidden",
VTY_NEWLINE);
}
if (!*subscr->imsi) if (!*subscr->imsi)
return; return;
@@ -137,12 +145,12 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) { if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE); vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE);
aud2g = (struct osmo_sub_auth_data2){}; aud2g = (struct osmo_sub_auth_data){};
} }
if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) { if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) {
vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE); vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE);
aud3g = (struct osmo_sub_auth_data2){}; aud3g = (struct osmo_sub_auth_data){};
} }
if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) { if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) {
@@ -154,10 +162,9 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) { if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) {
vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE); vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE);
vty_out(vty, " K=%s%s", vty_out(vty, " K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
osmo_hexdump_nospc(aud3g.u.umts.k, aud3g.u.umts.k_len), VTY_NEWLINE);
vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC", vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
osmo_hexdump_nospc(aud3g.u.umts.opc, aud3g.u.umts.opc_len), VTY_NEWLINE); hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE);
vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen); vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen);
if (aud3g.u.umts.sqn) if (aud3g.u.umts.sqn)
vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn); vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn);
@@ -165,28 +172,6 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
} }
} }
static void subscr_dump_summary_vty(struct hlr_subscriber *subscr, void *data)
{
struct vty *vty = data;
vty_out(vty, "%-5"PRIu64" %-12s %-16s", subscr->id,
*subscr->msisdn ? subscr->msisdn : "none",
*subscr->imsi ? subscr->imsi : "none");
if (*subscr->imei) {
char checksum = osmo_luhn(subscr->imei, 14);
if (checksum == -EINVAL)
vty_out(vty, " %-14s (INVALID LENGTH!)", subscr->imei);
else
vty_out(vty, " %-14s%c", subscr->imei, checksum);
} else {
vty_out(vty," ------------- ");
}
vty_out(vty, " %-2s%-2s ", subscr->nam_cs ? "CS" : "", subscr->nam_ps ? "PS" : "");
if (subscr->last_lu_seen)
dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, true);
vty_out_newline(vty);
}
static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr) static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
{ {
char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1]; char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
@@ -214,52 +199,10 @@ static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id,
return rc; return rc;
} }
static void dump_summary_table_vty(struct vty *vty, bool header, bool show_ls)
{
const char *texts = "ID MSISDN IMSI IMEI NAM";
const char *lines = "----- ------------ ---------------- ---------------- -----";
const char *ls_text = " LAST SEEN";
const char *ls_line = " ------------";
if (header) {
if (!show_ls)
vty_out(vty, "%s%s%s%s", texts, VTY_NEWLINE, lines, VTY_NEWLINE);
else
vty_out(vty, "%s%s%s%s%s%s", texts, ls_text, VTY_NEWLINE, lines, ls_line, VTY_NEWLINE);
} else {
if (!show_ls)
vty_out(vty, "%s%s%s%s", lines, VTY_NEWLINE, texts, VTY_NEWLINE);
else
vty_out(vty, "%s%s%s%s%s%s", lines, ls_line, VTY_NEWLINE, texts, ls_text, VTY_NEWLINE);
}
}
static int get_subscrs(struct vty *vty, const char *filter_type, const char *filter)
{
int rc = -1;
int count = 0;
const char *err;
bool show_ls = (filter_type && strcmp(filter_type, "last_lu_seen") == 0);
dump_summary_table_vty(vty, true, show_ls);
rc = db_subscrs_get(g_hlr->dbc, filter_type, filter, subscr_dump_summary_vty, vty, &count, &err);
if (count > 40) {
dump_summary_table_vty(vty, false, show_ls);
}
if (count > 0)
vty_out(vty, " Subscribers Shown: %d%s", count, VTY_NEWLINE);
if (rc)
vty_out(vty, "%% %s%s", err, VTY_NEWLINE);
return rc;
}
#define SUBSCR_CMD "subscriber " #define SUBSCR_CMD "subscriber "
#define SUBSCR_CMD_HELP "Subscriber management commands\n" #define SUBSCR_CMD_HELP "Subscriber management commands\n"
#define SUBSCR_SHOW_HELP "Show subscriber information\n"
#define SUBSCRS_SHOW_HELP "Show all subscribers (with filter possibility)\n"
#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT" #define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
#define SUBSCR_FILTER "(imei|imsi|msisdn) FILTER"
#define SUBSCR_ID_HELP \ #define SUBSCR_ID_HELP \
"Identify subscriber by IMSI\n" \ "Identify subscriber by IMSI\n" \
"Identify subscriber by MSISDN (phone number)\n" \ "Identify subscriber by MSISDN (phone number)\n" \
@@ -277,7 +220,7 @@ static int get_subscrs(struct vty *vty, const char *filter_type, const char *fil
DEFUN(subscriber_show, DEFUN(subscriber_show,
subscriber_show_cmd, subscriber_show_cmd,
SUBSCR "show", SUBSCR "show",
SUBSCR_HELP SUBSCR_SHOW_HELP) SUBSCR_HELP "Show subscriber information\n")
{ {
struct hlr_subscriber subscr; struct hlr_subscriber subscr;
const char *id_type = argv[0]; const char *id_type = argv[0];
@@ -292,50 +235,7 @@ DEFUN(subscriber_show,
ALIAS(subscriber_show, show_subscriber_cmd, ALIAS(subscriber_show, show_subscriber_cmd,
"show " SUBSCR_CMD SUBSCR_ID, "show " SUBSCR_CMD SUBSCR_ID,
SHOW_STR SUBSCR_SHOW_HELP SUBSCR_ID_HELP); SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
DEFUN(show_subscriber_all,
show_subscriber_all_cmd,
"show subscribers all",
SHOW_STR SUBSCRS_SHOW_HELP "Show summary of all subscribers\n")
{
if (get_subscrs(vty, NULL, NULL))
return CMD_WARNING;
return CMD_SUCCESS;
}
DEFUN(show_subscriber_filtered,
show_subscriber_filtered_cmd,
"show subscribers " SUBSCR_FILTER,
SHOW_STR SUBSCRS_SHOW_HELP
"Filter Subscribers by IMEI\n" "Filter Subscribers by IMSI\n" "Filter Subscribers by MSISDN\n"
"String to match in imei, imsi or msisdn\n")
{
const char *filter_type = argv[0];
const char *filter = argv[1];
if (get_subscrs(vty, filter_type, filter))
return CMD_WARNING;
return CMD_SUCCESS;
}
ALIAS(show_subscriber_filtered, show_subscriber_filtered_cmd2,
"show subscribers (cs|ps) (on|off)",
SHOW_STR SUBSCR_SHOW_HELP
"Filter Subscribers by CS Network Access Mode\n" "Filter Subscribers by PS Network Access Mode\n"
"Authorised\n" "Not Authorised\n");
DEFUN(show_subscriber_order_last_seen, show_subscriber_order_last_seen_cmd,
"show subscribers last-seen",
SHOW_STR SUBSCR_SHOW_HELP "Show Subscribers Ordered by Last Seen Time\n")
{
if (get_subscrs(vty, "last_lu_seen", NULL))
return CMD_WARNING;
return CMD_SUCCESS;
}
DEFUN(subscriber_create, DEFUN(subscriber_create,
subscriber_create_cmd, subscriber_create_cmd,
@@ -348,7 +248,7 @@ DEFUN(subscriber_create,
int rc; int rc;
struct hlr_subscriber subscr; struct hlr_subscriber subscr;
const char *imsi = argv[0]; const char *imsi = argv[0];
if (!osmo_imsi_str_valid(imsi)) { if (!osmo_imsi_str_valid(imsi)) {
vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE); vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
return CMD_WARNING; return CMD_WARNING;
@@ -461,27 +361,26 @@ static bool is_hexkey_valid(struct vty *vty, const char *label,
return false; return false;
} }
#define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor-2g)" #define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)"
#define AUTH_ALG_TYPES_2G_HELP \ #define AUTH_ALG_TYPES_2G_HELP \
"Use COMP128v1 algorithm\n" \ "Use COMP128v1 algorithm\n" \
"Use COMP128v2 algorithm\n" \ "Use COMP128v2 algorithm\n" \
"Use COMP128v3 algorithm\n" \ "Use COMP128v3 algorithm\n" \
"Use XOR-2G algorithm\n" "Use XOR algorithm\n"
#define AUTH_ALG_TYPES_3G "(milenage|tuak)" #define AUTH_ALG_TYPES_3G "milenage"
#define AUTH_ALG_TYPES_3G_HELP \ #define AUTH_ALG_TYPES_3G_HELP \
"Use Milenage algorithm\n" \ "Use Milenage algorithm\n"
"Use TUAK algorithm\n"
bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo, #define A38_XOR_MIN_KEY_LEN 12
int *minlen, int *maxlen, int *minlen_opc, int *maxlen_opc) #define A38_XOR_MAX_KEY_LEN 16
#define A38_COMP128_KEY_LEN 16
#define MILENAGE_KEY_LEN 16
static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
int *minlen, int *maxlen)
{ {
/* Default: no OP[c]. True for all 2G algorithms, and 3G-XOR. Overridden below for real 3G AKA algorithms. */
if (minlen_opc)
*minlen_opc = 0;
if (maxlen_opc)
*maxlen_opc = 0;
if (!strcasecmp(alg_str, "none")) { if (!strcasecmp(alg_str, "none")) {
*algo = OSMO_AUTH_ALG_NONE; *algo = OSMO_AUTH_ALG_NONE;
*minlen = *maxlen = 0; *minlen = *maxlen = 0;
@@ -494,28 +393,13 @@ bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
} else if (!strcasecmp(alg_str, "comp128v3")) { } else if (!strcasecmp(alg_str, "comp128v3")) {
*algo = OSMO_AUTH_ALG_COMP128v3; *algo = OSMO_AUTH_ALG_COMP128v3;
*minlen = *maxlen = A38_COMP128_KEY_LEN; *minlen = *maxlen = A38_COMP128_KEY_LEN;
} else if (!strcasecmp(alg_str, "xor-3g")) { } else if (!strcasecmp(alg_str, "xor")) {
*algo = OSMO_AUTH_ALG_XOR_3G; *algo = OSMO_AUTH_ALG_XOR;
*minlen = A38_XOR_MIN_KEY_LEN; *minlen = A38_XOR_MIN_KEY_LEN;
*maxlen = A38_XOR_MAX_KEY_LEN; *maxlen = A38_XOR_MAX_KEY_LEN;
} else if (!strcasecmp(alg_str, "xor-2g")) {
*algo = OSMO_AUTH_ALG_XOR_2G;
*minlen = *maxlen = A38_XOR2G_KEY_LEN;
} else if (!strcasecmp(alg_str, "milenage")) { } else if (!strcasecmp(alg_str, "milenage")) {
*algo = OSMO_AUTH_ALG_MILENAGE; *algo = OSMO_AUTH_ALG_MILENAGE;
*minlen = *maxlen = MILENAGE_KEY_LEN; *minlen = *maxlen = MILENAGE_KEY_LEN;
if (minlen_opc)
*minlen_opc = MILENAGE_KEY_LEN;
if (maxlen_opc)
*maxlen_opc = MILENAGE_KEY_LEN;
} else if (!strcasecmp(alg_str, "tuak")) {
*algo = OSMO_AUTH_ALG_TUAK;
*minlen = 16;
*maxlen = 32;
if (minlen_opc)
*minlen_opc = 32;
if (maxlen_opc)
*maxlen_opc = 32;
} else } else
return false; return false;
return true; return true;
@@ -571,7 +455,7 @@ DEFUN(subscriber_aud2g,
.u.gsm.ki = ki, .u.gsm.ki = ki,
}; };
if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen, NULL, NULL)) { if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) {
vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE); vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
return CMD_WARNING; return CMD_WARNING;
} }
@@ -630,21 +514,21 @@ DEFUN(subscriber_aud3g,
SUBSCR_UPDATE_HELP SUBSCR_UPDATE_HELP
"Set UMTS authentication data (3G, and 2G with UMTS AKA)\n" "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
AUTH_ALG_TYPES_3G_HELP AUTH_ALG_TYPES_3G_HELP
"Set Encryption Key K\n" "K as 32/64 hexadecimal characters\n" "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
"Set OP key\n" "Set OPC key\n" "OP or OPC as 32/64 hexadecimal characters\n" "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n"
"Set IND bit length\n" "IND bit length value (default: 5)\n") "Set IND bit length\n" "IND bit length value (default: 5)\n")
{ {
struct hlr_subscriber subscr; struct hlr_subscriber subscr;
int minlen = 0, minlen_opc = 0; int minlen = 0;
int maxlen = 0, maxlen_opc = 0; int maxlen = 0;
int rc; int rc;
const char *id_type = argv[0]; const char *id_type = argv[0];
const char *id = argv[1]; const char *id = argv[1];
const char *alg_type = argv[2]; const char *alg_type = AUTH_ALG_TYPES_3G;
const char *k = argv[3]; const char *k = argv[2];
bool opc_is_op = (strcasecmp("op", argv[4]) == 0); bool opc_is_op = (strcasecmp("op", argv[3]) == 0);
const char *op_opc = argv[5]; const char *op_opc = argv[4];
int ind_bitlen = argc > 7 ? atoi(argv[7]) : 5; int ind_bitlen = argc > 6? atoi(argv[6]) : 5;
struct sub_auth_data_str aud3g = { struct sub_auth_data_str aud3g = {
.type = OSMO_AUTH_TYPE_UMTS, .type = OSMO_AUTH_TYPE_UMTS,
.u.umts = { .u.umts = {
@@ -654,8 +538,8 @@ DEFUN(subscriber_aud3g,
.ind_bitlen = ind_bitlen, .ind_bitlen = ind_bitlen,
}, },
}; };
if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen, &minlen_opc, &maxlen_opc)) { if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE); vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
return CMD_WARNING; return CMD_WARNING;
} }
@@ -663,56 +547,8 @@ DEFUN(subscriber_aud3g,
if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen)) if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
return CMD_WARNING; return CMD_WARNING;
if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc, minlen_opc, maxlen_opc)) if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc,
return CMD_WARNING; MILENAGE_KEY_LEN, MILENAGE_KEY_LEN))
if (get_subscr_by_argv(vty, id_type, id, &subscr))
return CMD_WARNING;
rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
if (rc) {
vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
subscr.imsi, VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(subscriber_aud3g_xor,
subscriber_aud3g_xor_cmd,
SUBSCR_UPDATE "aud3g xor-3g k K"
" [ind-bitlen] [<0-28>]",
SUBSCR_UPDATE_HELP
"Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
"Use XOR-3G algorithm\n"
"Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
"Set IND bit length\n" "IND bit length value (default: 5)\n")
{
struct hlr_subscriber subscr;
int minlen = 0;
int maxlen = 0;
int rc;
const char *id_type = argv[0];
const char *id = argv[1];
const char *k = argv[2];
int ind_bitlen = argc > 4? atoi(argv[4]) : 5;
struct sub_auth_data_str aud3g = {
.type = OSMO_AUTH_TYPE_UMTS,
.u.umts = {
.k = k,
.opc_is_op = 0,
.opc = "00000000000000000000000000000000",
.ind_bitlen = ind_bitlen,
},
};
if (!auth_algo_parse("xor-3g", &aud3g.algo, &minlen, &maxlen, NULL, NULL)) {
vty_out(vty, "%% Unknown auth algorithm: '%s'%s", "xor-3g", VTY_NEWLINE);
return CMD_WARNING;
}
if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
return CMD_WARNING; return CMD_WARNING;
if (get_subscr_by_argv(vty, id_type, id, &subscr)) if (get_subscr_by_argv(vty, id_type, id, &subscr))
@@ -803,12 +639,50 @@ DEFUN(subscriber_nam,
} }
DEFUN(subscriber_rat,
subscriber_rat_cmd,
SUBSCR_UPDATE "rat (geran-a|utran-iu|eutran-sgs) (allowed|forbidden)",
SUBSCR_UPDATE_HELP
"Allow or forbid specific Radio Access Types\n"
"Set access to GERAN-A\n"
"Set access to UTRAN-Iu\n"
"Set access to EUTRAN-SGs\n"
"Allow access\n"
"Forbid access\n")
{
struct hlr_subscriber subscr;
const char *id_type = argv[0];
const char *id = argv[1];
const char *rat_str = argv[2];
const char *allowed_forbidden = argv[3];
enum osmo_rat_type rat = OSMO_RAT_UNKNOWN;
bool allowed;
int rc;
if (strcmp(rat_str, "geran-a") == 0)
rat = OSMO_RAT_GERAN_A;
else if (strcmp(rat_str, "utran-iu") == 0)
rat = OSMO_RAT_UTRAN_IU;
else if (strcmp(rat_str, "eutran-sgs") == 0)
rat = OSMO_RAT_EUTRAN_SGS;
allowed = (strcmp(allowed_forbidden, "allowed") == 0);
if (get_subscr_by_argv(vty, id_type, id, &subscr))
return CMD_WARNING;
rc = hlr_subscr_rat_flag(g_hlr, &subscr, rat, allowed);
if (rc && rc != -ENOEXEC) {
vty_out(vty, "%% Error: cannot set %s to %s%s",
osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden", VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
void hlr_vty_subscriber_init(void) void hlr_vty_subscriber_init(void)
{ {
install_element_ve(&show_subscriber_all_cmd);
install_element_ve(&show_subscriber_filtered_cmd);
install_element_ve(&show_subscriber_filtered_cmd2);
install_element_ve(&show_subscriber_order_last_seen_cmd);
install_element_ve(&subscriber_show_cmd); install_element_ve(&subscriber_show_cmd);
install_element_ve(&show_subscriber_cmd); install_element_ve(&show_subscriber_cmd);
install_element(ENABLE_NODE, &subscriber_create_cmd); install_element(ENABLE_NODE, &subscriber_create_cmd);
@@ -818,7 +692,7 @@ void hlr_vty_subscriber_init(void)
install_element(ENABLE_NODE, &subscriber_aud2g_cmd); install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd); install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
install_element(ENABLE_NODE, &subscriber_aud3g_cmd); install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
install_element(ENABLE_NODE, &subscriber_aud3g_xor_cmd);
install_element(ENABLE_NODE, &subscriber_imei_cmd); install_element(ENABLE_NODE, &subscriber_imei_cmd);
install_element(ENABLE_NODE, &subscriber_nam_cmd); install_element(ENABLE_NODE, &subscriber_nam_cmd);
install_element(ENABLE_NODE, &subscriber_rat_cmd);
} }

View File

@@ -43,12 +43,6 @@ const struct log_info_cat hlr_log_info_cat[] = {
.color = "\033[1;35m", .color = "\033[1;35m",
.enabled = 1, .loglevel = LOGL_NOTICE, .enabled = 1, .loglevel = LOGL_NOTICE,
}, },
[DCTRL] = {
.name = "DCTRL",
.description = "Osmocom CTRL interface",
.color = "\033[1;30m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
}; };
const struct log_info hlr_log_info = { const struct log_info hlr_log_info = {

View File

@@ -26,7 +26,7 @@
#include <osmocom/gsm/apn.h> #include <osmocom/gsm/apn.h>
#include <osmocom/gsm/gsm48_ie.h> #include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
#include <osmocom/gsupclient/gsup_req.h> #include <osmocom/gsupclient/gsup_req.h>
#include <osmocom/hlr/logging.h> #include <osmocom/hlr/logging.h>
#include <osmocom/hlr/hlr.h> #include <osmocom/hlr/hlr.h>
@@ -52,11 +52,11 @@ struct lu {
bool is_ps; bool is_ps;
/* VLR requesting the LU. */ /* VLR requesting the LU. */
struct osmo_cni_peer_id vlr_name; struct osmo_gsup_peer_id vlr_name;
/* If the LU request was received via a proxy and not immediately from a local VLR, this indicates the closest /* If the LU request was received via a proxy and not immediately from a local VLR, this indicates the closest
* peer that forwarded the GSUP message. */ * peer that forwarded the GSUP message. */
struct osmo_cni_peer_id via_proxy; struct osmo_gsup_peer_id via_proxy;
}; };
LLIST_HEAD(g_all_lu); LLIST_HEAD(g_all_lu);
@@ -108,6 +108,8 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
{ {
struct osmo_fsm_inst *fi; struct osmo_fsm_inst *fi;
struct lu *lu; struct lu *lu;
bool any_rat_allowed;
int i;
OSMO_ASSERT(update_location_req); OSMO_ASSERT(update_location_req);
OSMO_ASSERT(update_location_req->gsup.message_type == OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST); OSMO_ASSERT(update_location_req->gsup.message_type == OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST);
@@ -130,13 +132,13 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
osmo_fsm_inst_update_id_f_sanitize(fi, '_', "%s:IMSI-%s", lu->is_ps ? "PS" : "CS", update_location_req->gsup.imsi); osmo_fsm_inst_update_id_f_sanitize(fi, '_', "%s:IMSI-%s", lu->is_ps ? "PS" : "CS", update_location_req->gsup.imsi);
if (osmo_cni_peer_id_is_empty(&lu->vlr_name)) { if (osmo_gsup_peer_id_is_empty(&lu->vlr_name)) {
lu_failure(lu, GMM_CAUSE_NET_FAIL, "LU without a VLR"); lu_failure(lu, GMM_CAUSE_NET_FAIL, "LU without a VLR");
return; return;
} }
if (db_subscr_get_by_imsi(g_hlr->dbc, update_location_req->gsup.imsi, &lu->subscr) < 0) { if (db_subscr_get_by_imsi(g_hlr->dbc, update_location_req->gsup.imsi, &lu->subscr) < 0) {
lu_failure(lu, g_hlr->reject_cause, "Subscriber does not exist"); lu_failure(lu, GMM_CAUSE_ROAMING_NOTALLOWED, "Subscriber does not exist");
return; return;
} }
@@ -151,10 +153,32 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
return; return;
} }
/* Check if any available RAT type is allowed. See 3GPP TS 29.010 3.2 'Routeing area updating' and 3.8 'Location
* update' for the "No Suitable cells in location area" error code. */
any_rat_allowed = false;
for (i = 0; i < update_location_req->gsup.supported_rat_types_len; i++) {
enum osmo_rat_type rat = update_location_req->gsup.supported_rat_types[i];
if (rat <= 0 || rat >= OSMO_RAT_COUNT) {
lu_failure(lu, GMM_CAUSE_COND_IE_ERR, "Invalid RAT type in GSUP request: %s",
osmo_rat_type_name(rat));
return;
}
if (lu->subscr.rat_types[rat]) {
any_rat_allowed = true;
LOG_LU(lu, LOGL_DEBUG, "subscriber allowed on %s\n", osmo_rat_type_name(rat));
} else {
LOG_LU(lu, LOGL_DEBUG, "subscriber not allowed on %s\n", osmo_rat_type_name(rat));
}
}
if (!any_rat_allowed && update_location_req->gsup.supported_rat_types_len > 0) {
lu_failure(lu, GMM_CAUSE_NO_SUIT_CELL_IN_LA, "subscriber not allowed on any available RAT type");
return;
}
/* TODO: Set subscriber tracing = deactive in VLR/SGSN */ /* TODO: Set subscriber tracing = deactive in VLR/SGSN */
#if 0 #if 0
/* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old (FIXME: OS#4491) */ /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
if (!lu->is_ps && strcmp(subscr->vlr_number, vlr_number)) { if (!lu->is_ps && strcmp(subscr->vlr_number, vlr_number)) {
lu_op_tx_cancel_old(lu); lu_op_tx_cancel_old(lu);
} else if (lu->is_ps && strcmp(subscr->sgsn_number, sgsn_number)) { } else if (lu->is_ps && strcmp(subscr->sgsn_number, sgsn_number)) {
@@ -163,30 +187,31 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
#endif #endif
/* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */ /* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */
if (!osmo_cni_peer_id_is_empty(&lu->via_proxy)) { if (!osmo_gsup_peer_id_is_empty(&lu->via_proxy)) {
LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s, via proxy %s\n", LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s, via proxy %s\n",
lu->is_ps ? "SGSN number" : "VLR number", lu->is_ps ? "SGSN number" : "VLR number",
osmo_cni_peer_id_to_str(&lu->vlr_name), osmo_gsup_peer_id_to_str(&lu->vlr_name),
osmo_cni_peer_id_to_str(&lu->via_proxy)); osmo_gsup_peer_id_to_str(&lu->via_proxy));
} else { } else {
LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s\n", LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s\n",
lu->is_ps ? "SGSN number" : "VLR number", lu->is_ps ? "SGSN number" : "VLR number",
osmo_cni_peer_id_to_str(&lu->vlr_name)); osmo_gsup_peer_id_to_str(&lu->vlr_name));
} }
if (osmo_cni_peer_id_is_empty(&lu->vlr_name) if (osmo_gsup_peer_id_is_empty(&lu->vlr_name)
|| (lu->vlr_name.type != OSMO_CNI_PEER_ID_IPA_NAME)) { || (lu->vlr_name.type != OSMO_GSUP_PEER_ID_IPA_NAME)) {
lu_failure(lu, GMM_CAUSE_PROTO_ERR_UNSPEC, "Unsupported GSUP peer id type for vlr_name: %s", lu_failure(lu, GMM_CAUSE_PROTO_ERR_UNSPEC, "Unsupported GSUP peer id type for vlr_name: %s",
osmo_cni_peer_id_type_name(lu->vlr_name.type)); osmo_gsup_peer_id_type_name(lu->vlr_name.type));
return; return;
} }
if (!osmo_cni_peer_id_is_empty(&lu->via_proxy) && (lu->via_proxy.type != OSMO_CNI_PEER_ID_IPA_NAME)) { if (!osmo_gsup_peer_id_is_empty(&lu->via_proxy) && (lu->via_proxy.type != OSMO_GSUP_PEER_ID_IPA_NAME)) {
lu_failure(lu, GMM_CAUSE_PROTO_ERR_UNSPEC, "Unsupported GSUP peer id type for via_proxy: %s", lu_failure(lu, GMM_CAUSE_PROTO_ERR_UNSPEC, "Unsupported GSUP peer id type for via_proxy: %s",
osmo_cni_peer_id_type_name(lu->via_proxy.type)); osmo_gsup_peer_id_type_name(lu->via_proxy.type));
return; return;
} }
if (db_subscr_lu(g_hlr->dbc, lu->subscr.id, &lu->vlr_name.ipa_name, lu->is_ps, if (db_subscr_lu(g_hlr->dbc, lu->subscr.id, &lu->vlr_name.ipa_name, lu->is_ps,
osmo_cni_peer_id_is_empty(&lu->via_proxy)? NULL : &lu->via_proxy.ipa_name)) { osmo_gsup_peer_id_is_empty(&lu->via_proxy)? NULL : &lu->via_proxy.ipa_name,
update_location_req->gsup.supported_rat_types, update_location_req->gsup.supported_rat_types_len)) {
lu_failure(lu, GMM_CAUSE_NET_FAIL, "Cannot update %s in the database", lu_failure(lu, GMM_CAUSE_NET_FAIL, "Cannot update %s in the database",
lu->is_ps ? "SGSN number" : "VLR number"); lu->is_ps ? "SGSN number" : "VLR number");
return; return;
@@ -241,11 +266,13 @@ static void lu_fsm_wait_insert_data_result_onenter(struct osmo_fsm_inst *fi, uin
struct lu *lu = fi->priv; struct lu *lu = fi->priv;
struct hlr_subscriber *subscr = &lu->subscr; struct hlr_subscriber *subscr = &lu->subscr;
struct osmo_gsup_message gsup; struct osmo_gsup_message gsup;
uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
uint8_t apn[APN_MAXLEN];
if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi, if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi,
subscr->msisdn, subscr->msisdn, msisdn_enc, sizeof(msisdn_enc),
lu->is_ps ? OSMO_GSUP_CN_DOMAIN_PS : OSMO_GSUP_CN_DOMAIN_CS, apn, sizeof(apn),
OTC_SELECT)) { lu->is_ps? OSMO_GSUP_CN_DOMAIN_PS : OSMO_GSUP_CN_DOMAIN_CS)) {
lu_failure(lu, GMM_CAUSE_NET_FAIL, "cannot encode Insert Subscriber Data message"); lu_failure(lu, GMM_CAUSE_NET_FAIL, "cannot encode Insert Subscriber Data message");
return; return;
} }
@@ -275,7 +302,6 @@ void lu_fsm_wait_insert_data_result(struct osmo_fsm_inst *fi, uint32_t event, vo
case OSMO_GSUP_MSGT_INSERT_DATA_ERROR: case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
lu_failure(lu, GMM_CAUSE_NET_FAIL, "Rx %s", osmo_gsup_message_type_name(req->gsup.message_type)); lu_failure(lu, GMM_CAUSE_NET_FAIL, "Rx %s", osmo_gsup_message_type_name(req->gsup.message_type));
osmo_gsup_req_free(req);
break; break;
default: default:
@@ -313,7 +339,7 @@ static struct osmo_fsm lu_fsm = {
.cleanup = lu_fsm_cleanup, .cleanup = lu_fsm_cleanup,
}; };
static __attribute__((constructor)) void lu_fsm_init(void) static __attribute__((constructor)) void lu_fsm_init()
{ {
OSMO_ASSERT(osmo_fsm_register(&lu_fsm) == 0); OSMO_ASSERT(osmo_fsm_register(&lu_fsm) == 0);
} }

View File

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

View File

@@ -40,7 +40,7 @@ int osmo_mdns_msg_request_encode(void *ctx, struct msgb *msg, const struct osmo_
qst.domain = req->domain; qst.domain = req->domain;
qst.qtype = req->type; qst.qtype = req->type;
qst.qclass = OSMO_MDNS_RFC_CLASS_IN; qst.qclass = OSMO_MDNS_RFC_CLASS_IN;
if (osmo_mdns_rfc_question_encode(msg, &qst) != 0) if (osmo_mdns_rfc_question_encode(ctx, msg, &qst) != 0)
return -EINVAL; return -EINVAL;
return 0; return 0;
@@ -106,7 +106,7 @@ int osmo_mdns_msg_answer_encode(void *ctx, struct msgb *msg, const struct osmo_m
rec.rdlength = ans_record->length; rec.rdlength = ans_record->length;
rec.rdata = ans_record->data; rec.rdata = ans_record->data;
if (osmo_mdns_rfc_record_encode(msg, &rec) != 0) if (osmo_mdns_rfc_record_encode(ctx, msg, &rec) != 0)
return -EINVAL; return -EINVAL;
} }
@@ -213,9 +213,9 @@ struct osmo_mdns_record *osmo_mdns_record_txt_keyval_encode(void *ctx, const cha
va_start(ap, value_fmt); va_start(ap, value_fmt);
value = talloc_vasprintf(ctx, value_fmt, ap); value = talloc_vasprintf(ctx, value_fmt, ap);
va_end(ap);
if (!value) if (!value)
return NULL; return NULL;
va_end(ap);
r = _osmo_mdns_record_txt_encode(ctx, key, value); r = _osmo_mdns_record_txt_encode(ctx, key, value);
talloc_free(value); talloc_free(value);
return r; return r;

View File

@@ -27,9 +27,86 @@
#include <osmocom/core/msgb.h> #include <osmocom/core/msgb.h>
#include <osmocom/core/bitvec.h> #include <osmocom/core/bitvec.h>
#include <osmocom/core/logging.h> #include <osmocom/core/logging.h>
#include <osmocom/gsm/apn.h>
#include <osmocom/mslookup/mdns_rfc.h> #include <osmocom/mslookup/mdns_rfc.h>
/*
* Encode/decode IEs
*/
/*! Encode a domain string as qname (RFC 1035 4.1.2).
* \param[in] domain multiple labels separated by dots, e.g. "sip.voice.1234.msisdn".
* \returns allocated buffer with length-value pairs for each label (e.g. 0x03 "sip" 0x05 "voice" ...), NULL on error.
*/
char *osmo_mdns_rfc_qname_encode(void *ctx, const char *domain)
{
char *domain_dup;
char *domain_iter;
char buf[OSMO_MDNS_RFC_MAX_NAME_LEN + 2] = ""; /* len(qname) is len(domain) +1 */
struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
char *label;
if (strlen(domain) > OSMO_MDNS_RFC_MAX_NAME_LEN)
return NULL;
domain_iter = domain_dup = talloc_strdup(ctx, domain);
while ((label = strsep(&domain_iter, "."))) {
size_t len = strlen(label);
/* Empty domain, dot at start, two dots in a row, or ending with a dot */
if (!len)
goto error;
OSMO_STRBUF_PRINTF(sb, "%c%s", (char)len, label);
}
talloc_free(domain_dup);
return talloc_strdup(ctx, buf);
error:
talloc_free(domain_dup);
return NULL;
}
/*! Decode a domain string from a qname (RFC 1035 4.1.2).
* \param[in] qname buffer with length-value pairs for each label (e.g. 0x03 "sip" 0x05 "voice" ...)
* \param[in] qname_max_len amount of bytes that can be read at most from the memory location that qname points to.
* \returns allocated buffer with domain string, multiple labels separated by dots (e.g. "sip.voice.1234.msisdn"),
* NULL on error.
*/
char *osmo_mdns_rfc_qname_decode(void *ctx, const char *qname, size_t qname_max_len)
{
const char *next_label, *qname_end = qname + qname_max_len;
char buf[OSMO_MDNS_RFC_MAX_NAME_LEN + 1];
int i = 0;
if (qname_max_len < 1)
return NULL;
while (*qname) {
size_t len = *qname;
next_label = qname + len + 1;
if (next_label >= qname_end || i + len > OSMO_MDNS_RFC_MAX_NAME_LEN)
return NULL;
if (i) {
/* Two dots in a row is not allowed */
if (buf[i - 1] == '.')
return NULL;
buf[i] = '.';
i++;
}
memcpy(buf + i, qname + 1, len);
i += len;
qname = next_label;
}
buf[i] = '\0';
return talloc_strdup(ctx, buf);
}
/* /*
* Encode/decode message sections * Encode/decode message sections
*/ */
@@ -69,17 +146,20 @@ int osmo_mdns_rfc_header_decode(const uint8_t *data, size_t data_len, struct osm
/*! Encode question section (RFC 1035 4.1.2). /*! Encode question section (RFC 1035 4.1.2).
* \param[in] msgb mesage buffer to which the encoded data will be appended. * \param[in] msgb mesage buffer to which the encoded data will be appended.
*/ */
int osmo_mdns_rfc_question_encode(struct msgb *msg, const struct osmo_mdns_rfc_question *qst) int osmo_mdns_rfc_question_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_question *qst)
{ {
uint8_t *buf; char *qname;
size_t buf_len; size_t qname_len;
uint8_t *qname_buf;
/* qname */ /* qname */
buf_len = strlen(qst->domain) + 1; qname = osmo_mdns_rfc_qname_encode(ctx, qst->domain);
buf = msgb_put(msg, buf_len); if (!qname)
if (osmo_apn_from_str(buf, buf_len, qst->domain) < 0)
return -EINVAL; return -EINVAL;
msgb_put_u8(msg, 0x00); qname_len = strlen(qname) + 1;
qname_buf = msgb_put(msg, qname_len);
memcpy(qname_buf, qname, qname_len);
talloc_free(qname);
/* qtype and qclass */ /* qtype and qclass */
msgb_put_u16(msg, qst->qtype); msgb_put_u16(msg, qst->qtype);
@@ -97,25 +177,21 @@ struct osmo_mdns_rfc_question *osmo_mdns_rfc_question_decode(void *ctx, const ui
if (data_len < 6) if (data_len < 6)
return NULL; return NULL;
/* qname */
ret = talloc_zero(ctx, struct osmo_mdns_rfc_question); ret = talloc_zero(ctx, struct osmo_mdns_rfc_question);
if (!ret) if (!ret)
return NULL; return NULL;
ret->domain = osmo_mdns_rfc_qname_decode(ret, (const char *)data, qname_len);
/* qname */ if (!ret->domain) {
ret->domain = talloc_size(ret, qname_len - 1); talloc_free(ret);
if (!ret->domain) return NULL;
goto error; }
if (!osmo_apn_to_str(ret->domain, data, qname_len - 1))
goto error;
/* qtype and qclass */ /* qtype and qclass */
ret->qtype = osmo_load16be(data + qname_len); ret->qtype = osmo_load16be(data + qname_len);
ret->qclass = osmo_load16be(data + qname_len + 2); ret->qclass = osmo_load16be(data + qname_len + 2);
return ret; return ret;
error:
talloc_free(ret);
return NULL;
} }
/* /*
@@ -125,17 +201,20 @@ error:
/*! Encode one resource record (RFC 1035 4.1.3). /*! Encode one resource record (RFC 1035 4.1.3).
* \param[in] msgb mesage buffer to which the encoded data will be appended. * \param[in] msgb mesage buffer to which the encoded data will be appended.
*/ */
int osmo_mdns_rfc_record_encode(struct msgb *msg, const struct osmo_mdns_rfc_record *rec) int osmo_mdns_rfc_record_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_record *rec)
{ {
char *name;
size_t name_len;
uint8_t *buf; uint8_t *buf;
size_t buf_len;
/* name */ /* name */
buf_len = strlen(rec->domain) + 1; name = osmo_mdns_rfc_qname_encode(ctx, rec->domain);
buf = msgb_put(msg, buf_len); if (!name)
if (osmo_apn_from_str(buf, buf_len, rec->domain) < 0)
return -EINVAL; return -EINVAL;
msgb_put_u8(msg, 0x00); name_len = strlen(name) + 1;
buf = msgb_put(msg, name_len);
memcpy(buf, name, name_len);
talloc_free(name);
/* type, class, ttl, rdlength */ /* type, class, ttl, rdlength */
msgb_put_u16(msg, rec->type); msgb_put_u16(msg, rec->type);
@@ -153,26 +232,15 @@ int osmo_mdns_rfc_record_encode(struct msgb *msg, const struct osmo_mdns_rfc_rec
struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_t *data, size_t data_len, struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_t *data, size_t data_len,
size_t *record_len) size_t *record_len)
{ {
struct osmo_mdns_rfc_record *ret; struct osmo_mdns_rfc_record *ret = talloc_zero(ctx, struct osmo_mdns_rfc_record);
size_t name_len; size_t name_len;
/* name length: represented as a series of labels, and terminated by a /* name */
* label with zero length (RFC 1035 3.3). A label with zero length is a ret->domain = osmo_mdns_rfc_qname_decode(ret, (const char *)data, data_len - 10);
* NUL byte. */
name_len = strnlen((const char *)data, data_len - 10) + 1;
if (data[name_len])
return NULL;
/* allocate ret + ret->domain */
ret = talloc_zero(ctx, struct osmo_mdns_rfc_record);
if (!ret)
return NULL;
ret->domain = talloc_size(ctx, name_len - 1);
if (!ret->domain) if (!ret->domain)
goto error; goto error;
name_len = strlen(ret->domain) + 2;
/* name */ if (name_len + 10 > data_len)
if (!osmo_apn_to_str(ret->domain, data, name_len - 1))
goto error; goto error;
/* type, class, ttl, rdlength */ /* type, class, ttl, rdlength */
@@ -186,7 +254,7 @@ struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_
/* rdata */ /* rdata */
ret->rdata = talloc_memdup(ret, data + name_len + 10, ret->rdlength); ret->rdata = talloc_memdup(ret, data + name_len + 10, ret->rdlength);
if (!ret->rdata) if (!ret->rdata)
goto error; return NULL;
*record_len = name_len + 10 + ret->rdlength; *record_len = name_len + 10 + ret->rdlength;
return ret; return ret;

View File

@@ -84,7 +84,7 @@ struct osmo_mdns_sock *osmo_mdns_sock_init(void *ctx, const char *ip, unsigned i
rc = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char*)&iface, sizeof(iface)); rc = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char*)&iface, sizeof(iface));
if (rc == -1) { if (rc == -1) {
LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno)); LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
goto error_sock; goto error;
} }
memcpy(&multicast_req.imr_multiaddr, &((struct sockaddr_in*)(ret->ai->ai_addr))->sin_addr, memcpy(&multicast_req.imr_multiaddr, &((struct sockaddr_in*)(ret->ai->ai_addr))->sin_addr,
sizeof(multicast_req.imr_multiaddr)); sizeof(multicast_req.imr_multiaddr));
@@ -92,7 +92,7 @@ struct osmo_mdns_sock *osmo_mdns_sock_init(void *ctx, const char *ip, unsigned i
rc = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multicast_req, sizeof(multicast_req)); rc = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multicast_req, sizeof(multicast_req));
if (rc == -1) { if (rc == -1) {
LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno)); LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
goto error_sock; goto error;
} }
/* Always allow binding the same IP and port twice. This is needed in OsmoHLR (where the code becomes cleaner by /* Always allow binding the same IP and port twice. This is needed in OsmoHLR (where the code becomes cleaner by
@@ -102,22 +102,20 @@ struct osmo_mdns_sock *osmo_mdns_sock_init(void *ctx, const char *ip, unsigned i
rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&y, sizeof(y)); rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&y, sizeof(y));
if (rc == -1) { if (rc == -1) {
LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno)); LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: setsockopt: %s\n", strerror(errno));
goto error_sock; goto error;
} }
/* Bind and register osmo_fd callback */ /* Bind and register osmo_fd callback */
rc = bind(sock, ret->ai->ai_addr, ret->ai->ai_addrlen); rc = bind(sock, ret->ai->ai_addr, ret->ai->ai_addrlen);
if (rc == -1) { if (rc == -1) {
LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: bind: %s\n", strerror(errno)); LOGP(DMSLOOKUP, LOGL_ERROR, "osmo_mdns_sock_init: bind: %s\n", strerror(errno));
goto error_sock; goto error;
} }
osmo_fd_setup(&ret->osmo_fd, sock, OSMO_FD_READ, cb, data, priv_nr); osmo_fd_setup(&ret->osmo_fd, sock, OSMO_FD_READ, cb, data, priv_nr);
if (osmo_fd_register(&ret->osmo_fd) != 0) if (osmo_fd_register(&ret->osmo_fd) != 0)
goto error_sock; goto error;
return ret; return ret;
error_sock:
close(sock);
error: error:
if (ret->ai) if (ret->ai)
freeaddrinfo(ret->ai); freeaddrinfo(ret->ai);

View File

@@ -205,9 +205,9 @@ size_t osmo_mslookup_result_to_str_buf(char *buf, size_t buflen,
if (result && result->rc == OSMO_MSLOOKUP_RC_NONE) if (result && result->rc == OSMO_MSLOOKUP_RC_NONE)
result = NULL; result = NULL;
if (result) { if (result) {
if (result->rc != OSMO_MSLOOKUP_RC_RESULT) { if (result->rc != OSMO_MSLOOKUP_RC_RESULT)
OSMO_STRBUF_PRINTF(sb, " %s", osmo_mslookup_result_code_name(result->rc)); OSMO_STRBUF_PRINTF(sb, " %s", osmo_mslookup_result_code_name(result->rc));
} else { if (result->rc == OSMO_MSLOOKUP_RC_RESULT) {
if (result->host_v4.ip[0]) { if (result->host_v4.ip[0]) {
OSMO_STRBUF_PRINTF(sb, " -> ipv4: " OSMO_SOCKADDR_STR_FMT, OSMO_STRBUF_PRINTF(sb, " -> ipv4: " OSMO_SOCKADDR_STR_FMT,
OSMO_SOCKADDR_STR_FMT_ARGS(&result->host_v4)); OSMO_SOCKADDR_STR_FMT_ARGS(&result->host_v4));
@@ -260,8 +260,6 @@ static int token(char *dest, size_t dest_size, const char *start, const char *en
/*! Parse a string like "foo.moo.goo.123456789012345.msisdn" into service="foo.moo.goo", id="123456789012345" and /*! Parse a string like "foo.moo.goo.123456789012345.msisdn" into service="foo.moo.goo", id="123456789012345" and
* id_type="msisdn", placed in a struct osmo_mslookup_query. * id_type="msisdn", placed in a struct osmo_mslookup_query.
* \param q Write parsed query to this osmo_mslookup_query.
* \param domain Human readable domain string like "sip.voice.12345678.msisdn".
* \returns 0 on success, negative on error. * \returns 0 on success, negative on error.
*/ */
int osmo_mslookup_query_init_from_domain_str(struct osmo_mslookup_query *q, const char *domain) int osmo_mslookup_query_init_from_domain_str(struct osmo_mslookup_query *q, const char *domain)

View File

@@ -146,7 +146,7 @@ void osmo_mslookup_client_rx_result(struct osmo_mslookup_client *client, uint32_
if (!req) { if (!req) {
LOGP(DMSLOOKUP, LOGL_ERROR, LOGP(DMSLOOKUP, LOGL_ERROR,
"Internal error: Got mslookup result for a request that does not exist (handle %u)\n", "Internal error: Got mslookup result for a request that does not exist (handle %u)\n",
request_handle); req->request_handle);
return; return;
} }

View File

@@ -114,7 +114,6 @@ static void mdns_method_request(struct osmo_mslookup_client_method *method, cons
if (!msg) { if (!msg) {
LOGP(DMSLOOKUP, LOGL_ERROR, "Cannot encode request: %s\n", LOGP(DMSLOOKUP, LOGL_ERROR, "Cannot encode request: %s\n",
osmo_mslookup_result_name_b(buf, sizeof(buf), query, NULL)); osmo_mslookup_result_name_b(buf, sizeof(buf), query, NULL));
return;
} }
/* Send over the wire */ /* Send over the wire */

View File

@@ -53,7 +53,7 @@ static void print_version(void)
"\n"); "\n");
} }
static void print_help(void) static void print_help()
{ {
print_version(); print_version();
printf( printf(
@@ -315,18 +315,17 @@ int do_send(int argc, char ** argv)
struct msgb *msg = osmo_mdns_result_encode(ctx, 0, &q, &r, cmdline_opts.mdns_domain_suffix); struct msgb *msg = osmo_mdns_result_encode(ctx, 0, &q, &r, cmdline_opts.mdns_domain_suffix);
if (!msg) { if (!msg) {
print_error("unable to encode mDNS response\n"); print_error("unable to encode mDNS response\n");
goto exit_cleanup_sock; goto exit_cleanup;
} }
if (osmo_mdns_sock_send(sock, msg)) { if (osmo_mdns_sock_send(sock, msg)) {
print_error("unable to send mDNS message\n"); print_error("unable to send mDNS message\n");
goto exit_cleanup_sock; goto exit_cleanup;
} }
rc = 0; rc = 0;
exit_cleanup_sock:
osmo_mdns_sock_cleanup(sock);
exit_cleanup: exit_cleanup:
osmo_mdns_sock_cleanup(sock);
talloc_free(ctx); talloc_free(ctx);
return rc; return rc;
} }
@@ -419,9 +418,9 @@ static void socket_client_close(struct socket_client *c)
{ {
struct osmo_fd *ofd = &c->ofd; struct osmo_fd *ofd = &c->ofd;
osmo_fd_unregister(ofd);
close(ofd->fd); close(ofd->fd);
ofd->fd = -1; ofd->fd = -1;
osmo_fd_unregister(ofd);
llist_del(&c->entry); llist_del(&c->entry);
talloc_free(c); talloc_free(c);
@@ -456,11 +455,6 @@ static int socket_read_cb(struct osmo_fd *ofd)
rxbuf[rc] = '\0'; rxbuf[rc] = '\0';
query_with_timeout = strtok(rxbuf, "\r\n"); query_with_timeout = strtok(rxbuf, "\r\n");
if (!query_with_timeout) {
print_error("ERROR: failed to read line from socket\n");
goto close;
}
at = strchr(query_with_timeout, '@'); at = strchr(query_with_timeout, '@');
query_str = at ? at + 1 : query_with_timeout; query_str = at ? at + 1 : query_with_timeout;
@@ -487,7 +481,7 @@ static int socket_cb(struct osmo_fd *ofd, unsigned int flags)
{ {
int rc = 0; int rc = 0;
if (flags & OSMO_FD_READ) if (flags & BSC_FD_READ)
rc = socket_read_cb(ofd); rc = socket_read_cb(ofd);
if (rc < 0) if (rc < 0)
return rc; return rc;
@@ -512,7 +506,7 @@ int socket_accept(struct osmo_fd *ofd, unsigned int flags)
c = talloc_zero(globals.ctx, struct socket_client); c = talloc_zero(globals.ctx, struct socket_client);
OSMO_ASSERT(c); OSMO_ASSERT(c);
c->ofd.fd = rc; c->ofd.fd = rc;
c->ofd.when = OSMO_FD_READ; c->ofd.when = BSC_FD_READ;
c->ofd.cb = socket_cb; c->ofd.cb = socket_cb;
c->ofd.data = c; c->ofd.data = c;
@@ -543,7 +537,7 @@ int socket_init(const char *sock_path)
return -1; return -1;
} }
ofd->when = OSMO_FD_READ; ofd->when = BSC_FD_READ;
ofd->cb = socket_accept; ofd->cb = socket_accept;
rc = osmo_fd_register(ofd); rc = osmo_fd_register(ofd);
@@ -555,15 +549,15 @@ int socket_init(const char *sock_path)
return 0; return 0;
} }
void socket_close(void) void socket_close()
{ {
struct socket_client *c, *n; struct socket_client *c, *n;
llist_for_each_entry_safe(c, n, &globals.socket_clients, entry) llist_for_each_entry_safe(c, n, &globals.socket_clients, entry)
socket_client_close(c); socket_client_close(c);
if (osmo_fd_is_registered(&globals.socket_ofd)) { if (osmo_fd_is_registered(&globals.socket_ofd)) {
osmo_fd_unregister(&globals.socket_ofd);
close(globals.socket_ofd.fd); close(globals.socket_ofd.fd);
globals.socket_ofd.fd = -1; globals.socket_ofd.fd = -1;
osmo_fd_unregister(&globals.socket_ofd);
} }
} }
@@ -584,11 +578,11 @@ void respond_result(const char *query_str, const struct osmo_mslookup_result *r)
llist_for_each_entry_safe(c, n, &globals.socket_clients, entry) { llist_for_each_entry_safe(c, n, &globals.socket_clients, entry) {
if (!strcmp(query_str, c->query_str)) { if (!strcmp(query_str, c->query_str)) {
socket_client_respond_result(c, g_buf); socket_client_respond_result(c, g_buf);
if (!r || r->last) if (r->last)
socket_client_close(c); socket_client_close(c);
} }
} }
if (!r || r->last) if (r->last)
globals.requests_handled++; globals.requests_handled++;
} }

View File

@@ -49,7 +49,7 @@ static void set_result(struct osmo_mslookup_result *result,
result->age = age; result->age = age;
} }
const struct mslookup_service_host *mslookup_server_get_local_gsup_addr(void) const struct mslookup_service_host *mslookup_server_get_local_gsup_addr()
{ {
static struct mslookup_service_host gsup_bind = {}; static struct mslookup_service_host gsup_bind = {};
struct mslookup_service_host *host; struct mslookup_service_host *host;
@@ -261,7 +261,6 @@ static bool subscriber_has_done_lu_here_hlr(const struct osmo_mslookup_query *qu
if (!subscr->vlr_number[0]) { if (!subscr->vlr_number[0]) {
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: not attached (vlr_number unset)\n", LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: not attached (vlr_number unset)\n",
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
return false;
} }
if (subscr->vlr_via_proxy.len) { if (subscr->vlr_via_proxy.len) {

View File

@@ -71,7 +71,7 @@ static int osmo_mslookup_server_mdns_rx(struct osmo_fd *osmo_fd, unsigned int wh
/* Parse the message and print it */ /* Parse the message and print it */
n = read(osmo_fd->fd, buffer, sizeof(buffer)); n = read(osmo_fd->fd, buffer, sizeof(buffer));
if (n <= 0) if (n < 0)
return n; return n;
ctx = talloc_named_const(server, 0, __func__); ctx = talloc_named_const(server, 0, __func__);
@@ -121,7 +121,7 @@ void osmo_mslookup_server_mdns_stop(struct osmo_mslookup_server_mdns *server)
talloc_free(server); talloc_free(server);
} }
void mslookup_server_mdns_config_apply(void) void mslookup_server_mdns_config_apply()
{ {
/* Check whether to start/stop/restart mDNS server */ /* Check whether to start/stop/restart mDNS server */
bool should_run; bool should_run;
@@ -148,7 +148,7 @@ void mslookup_server_mdns_config_apply(void)
g_hlr->mslookup.server.mdns.domain_suffix); g_hlr->mslookup.server.mdns.domain_suffix);
if (!g_hlr->mslookup.server.mdns.running) if (!g_hlr->mslookup.server.mdns.running)
LOGP(DMSLOOKUP, LOGL_ERROR, "Failed to start mslookup mDNS server on " OSMO_SOCKADDR_STR_FMT "\n", LOGP(DMSLOOKUP, LOGL_ERROR, "Failed to start mslookup mDNS server on " OSMO_SOCKADDR_STR_FMT "\n",
OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.server.mdns.bind_addr)); OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.server.mdns.running->bind_addr));
else else
LOGP(DMSLOOKUP, LOGL_NOTICE, "Started mslookup mDNS server, receiving mDNS requests at multicast " LOGP(DMSLOOKUP, LOGL_NOTICE, "Started mslookup mDNS server, receiving mDNS requests at multicast "
OSMO_SOCKADDR_STR_FMT "\n", OSMO_SOCKADDR_STR_FMT "\n",

View File

@@ -29,7 +29,6 @@
#include <osmocom/gsupclient/gsup_client.h> #include <osmocom/gsupclient/gsup_client.h>
#include <osmocom/gsupclient/gsup_req.h> #include <osmocom/gsupclient/gsup_req.h>
#include <osmocom/hlr/hlr.h>
#include <osmocom/hlr/logging.h> #include <osmocom/hlr/logging.h>
#include <osmocom/hlr/proxy.h> #include <osmocom/hlr/proxy.h>
#include <osmocom/hlr/remote_hlr.h> #include <osmocom/hlr/remote_hlr.h>
@@ -81,19 +80,7 @@ static void proxy_deferred_gsup_req_add(struct proxy *proxy, struct osmo_gsup_re
static void proxy_pending_req_remote_hlr_connect_result(struct osmo_gsup_req *req, struct remote_hlr *remote_hlr) static void proxy_pending_req_remote_hlr_connect_result(struct osmo_gsup_req *req, struct remote_hlr *remote_hlr)
{ {
if (!remote_hlr || !remote_hlr_is_up(remote_hlr)) { if (!remote_hlr || !remote_hlr_is_up(remote_hlr)) {
/* Do not respond with an error to a CHECK_IMEI_REQUEST as osmo-msc will send a LU Reject Cause #6 osmo_gsup_req_respond_err(req, GMM_CAUSE_ROAMING_NOTALLOWED, "Proxy: Failed to connect to home HLR");
* Just respond ACK and deal with the IMSI check that comes next. */
if (req->gsup.message_type == OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST) {
/* Accept all IMEIs */
struct osmo_gsup_message gsup_reply = (struct osmo_gsup_message){
.message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT,
.imei_result = OSMO_GSUP_IMEI_RESULT_ACK,
};
osmo_gsup_req_respond(req, &gsup_reply, false, true);
return;
}
osmo_gsup_req_respond_err(req, g_hlr->no_proxy_reject_cause,
"Proxy: Failed to connect to home HLR");
return; return;
} }
@@ -203,7 +190,6 @@ int proxy_subscr_create_or_update(struct proxy *proxy, const struct proxy_subscr
int _proxy_subscr_del(struct proxy_subscr_listentry *e) int _proxy_subscr_del(struct proxy_subscr_listentry *e)
{ {
llist_del(&e->entry); llist_del(&e->entry);
talloc_free(e);
return 0; return 0;
} }
@@ -277,10 +263,10 @@ static int proxy_acknowledge_gsup_to_remote_hlr(struct proxy *proxy, const struc
bool cs; bool cs;
int rc; int rc;
if (req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) { if (req->source_name.type != OSMO_GSUP_PEER_ID_IPA_NAME) {
LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_ERROR, LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_ERROR,
"Unsupported GSUP peer id type: %s\n", "Unsupported GSUP peer id type: %s\n",
osmo_cni_peer_id_type_name(req->source_name.type)); osmo_gsup_peer_id_type_name(req->source_name.type));
return -ENOTSUP; return -ENOTSUP;
} }
@@ -317,7 +303,7 @@ static int proxy_acknowledge_gsup_to_remote_hlr(struct proxy *proxy, const struc
"%s: preliminary VLR name for%s%s to %s\n", "%s: preliminary VLR name for%s%s to %s\n",
rc ? "failed to update" : "updated", rc ? "failed to update" : "updated",
cs ? " CS" : "", ps ? " PS" : "", cs ? " CS" : "", ps ? " PS" : "",
osmo_cni_peer_id_to_str(&req->source_name)); osmo_gsup_peer_id_to_str(&req->source_name));
break; break;
/* TODO: delete proxy entry in case of a Purge Request? */ /* TODO: delete proxy entry in case of a Purge Request? */
default: default:
@@ -497,13 +483,13 @@ int proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_s
return 0; return 0;
} }
if (!osmo_cni_peer_id_is_empty(&req->via_proxy)) { if (!osmo_gsup_peer_id_is_empty(&req->via_proxy)) {
LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_INFO, "VLR->HLR: forwarding from %s via proxy %s\n", LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_INFO, "VLR->HLR: forwarding from %s via proxy %s\n",
osmo_cni_peer_id_to_str(&req->source_name), osmo_gsup_peer_id_to_str(&req->source_name),
osmo_cni_peer_id_to_str(&req->via_proxy)); osmo_gsup_peer_id_to_str(&req->via_proxy));
} else { } else {
LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_INFO, "VLR->HLR: forwarding from %s\n", LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_INFO, "VLR->HLR: forwarding from %s\n",
osmo_cni_peer_id_to_str(&req->source_name)); osmo_gsup_peer_id_to_str(&req->source_name));
} }
/* We could always store in the defer queue and empty the queue if the connection is already up. /* We could always store in the defer queue and empty the queue if the connection is already up.

View File

@@ -233,9 +233,9 @@ void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct
else else
forward = req->gsup; forward = req->gsup;
if (req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) { if (req->source_name.type != OSMO_GSUP_PEER_ID_IPA_NAME) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s", osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s",
osmo_cni_peer_id_type_name(req->source_name.type)); osmo_gsup_peer_id_type_name(req->source_name.type));
return; return;
} }
forward.source_name = req->source_name.ipa_name.val; forward.source_name = req->source_name.ipa_name.val;

423
src/sms_over_gsup.c Normal file
View File

@@ -0,0 +1,423 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/gsm0411_utils.h>
#include <osmocom/gsm/protocol/gsm_04_11.h>
#include <osmocom/mslookup/mslookup_client.h>
#include <osmocom/hlr/hlr.h>
#include <osmocom/hlr/remote_hlr.h>
#include <osmocom/hlr/mslookup_server.h>
#include <osmocom/hlr/db.h>
#include <osmocom/hlr/sms_over_gsup.h>
static int sms_extract_destination_msisdn(char *msisdn, size_t msisdn_size, struct osmo_gsup_req *req)
{
int rc;
if (req->gsup.sm_rp_da_type == OSMO_GSUP_SMS_SM_RP_ODA_MSISDN
&& req->gsup.sm_rp_da_len > 0) {
LOG_GSUP_REQ(req, LOGL_INFO, "Extracting destination MSISDN from GSUP SM_RP_DA IE: %s\n",
osmo_hexdump(req->gsup.sm_rp_da, req->gsup.sm_rp_da_len));
rc = gsm48_decode_bcd_number2(msisdn, msisdn_size, req->gsup.sm_rp_da, req->gsup.sm_rp_da_len, 0);
if (!rc)
LOG_GSUP_REQ(req, LOGL_INFO, "success -> %s\n", msisdn);
else
LOG_GSUP_REQ(req, LOGL_ERROR, "fail %d\n", rc);
return rc;
}
/* The DA was not an MSISDN -- get from inside the SMS PDU */
if (req->gsup.sm_rp_ui_len > 3) {
const uint8_t *da = req->gsup.sm_rp_ui + 2;
uint8_t da_len = *da;
uint8_t da_len_bytes;
uint8_t address_lv[12] = {};
da_len_bytes = 2 + da_len/2 + da_len%2;
if (da_len_bytes < 4 || da_len_bytes > 12
|| da_len_bytes > req->gsup.sm_rp_ui_len - 2) {
LOG_GSUP_REQ(req, LOGL_ERROR, "Invalid da_len_bytes %u\n", da_len_bytes);
return -EINVAL;
}
memcpy(address_lv, da, da_len_bytes);
address_lv[0] = da_len_bytes - 1;
LOG_GSUP_REQ(req, LOGL_INFO, "Extracting destination MSISDN from SMS PDU DA: %s\n",
osmo_hexdump(address_lv, da_len_bytes));
rc = gsm48_decode_bcd_number2(msisdn, msisdn_size, address_lv, da_len_bytes, 1);
if (!rc)
LOG_GSUP_REQ(req, LOGL_INFO, "success -> %s\n", msisdn);
else
LOG_GSUP_REQ(req, LOGL_ERROR, "fail %d\n", rc);
return rc;
}
LOG_GSUP_REQ(req, LOGL_ERROR, "fail: no SM_RP_DA nor SMS PDU (sm_rp_ui_len > 3)\n");
return -ENOTSUP;
}
static int sms_extract_sender_msisdn(char *msisdn, size_t msisdn_size, struct osmo_gsup_req *req)
{
int rc;
if (req->gsup.sm_rp_oa_type == OSMO_GSUP_SMS_SM_RP_ODA_MSISDN
&& req->gsup.sm_rp_oa_len > 0) {
LOG_GSUP_REQ(req, LOGL_INFO, "Extracting sender MSISDN from GSUP SM_RP_OA IE: %s\n",
osmo_hexdump(req->gsup.sm_rp_oa, req->gsup.sm_rp_oa_len));
rc = gsm48_decode_bcd_number2(msisdn, msisdn_size, req->gsup.sm_rp_oa, req->gsup.sm_rp_oa_len, 0);
if (!rc)
LOG_GSUP_REQ(req, LOGL_INFO, "success -> %s\n", msisdn);
else
LOG_GSUP_REQ(req, LOGL_ERROR, "fail %d\n", rc);
return rc;
}
LOG_GSUP_REQ(req, LOGL_ERROR, "fail: no MSISDN obtained from SM_RP_OA\n");
return -ENOTSUP;
}
static struct msgb *sms_mo_pdu_to_mt_pdu(const uint8_t *mo_pdu, size_t mo_pdu_len, const char *sender_msisdn)
{
/* Hacky shortened copy-paste of osmo-msc's gsm340_rx_tpdu() */
uint8_t protocol_id;
uint8_t data_coding_scheme;
uint8_t user_data_len;
uint8_t user_data_octet_len;
const uint8_t *user_data;
uint8_t status_rep_req;
uint8_t ud_hdr_ind;
{
const uint8_t *smsp = mo_pdu;
enum sms_alphabet sms_alphabet;
uint8_t sms_vpf;
uint8_t da_len_bytes;
sms_vpf = (*smsp & 0x18) >> 3;
status_rep_req = (*smsp & 0x20) >> 5;
ud_hdr_ind = (*smsp & 0x40);
smsp += 2;
/* length in bytes of the destination address */
da_len_bytes = 2 + *smsp/2 + *smsp%2;
if (da_len_bytes < 4 || da_len_bytes > 12)
return NULL;
smsp += da_len_bytes;
protocol_id = *smsp++;
data_coding_scheme = *smsp++;
sms_alphabet = gsm338_get_sms_alphabet(data_coding_scheme);
if (sms_alphabet == 0xffffffff)
return NULL;
switch (sms_vpf) {
case GSM340_TP_VPF_RELATIVE:
smsp++;
break;
case GSM340_TP_VPF_ABSOLUTE:
case GSM340_TP_VPF_ENHANCED:
/* the additional functionality indicator... */
if (sms_vpf == GSM340_TP_VPF_ENHANCED && *smsp & (1<<7))
smsp++;
smsp += 7;
break;
case GSM340_TP_VPF_NONE:
break;
default:
return NULL;
}
/* As per 3GPP TS 03.40, section 9.2.3.16, TP-User-Data-Length (TP-UDL)
* may indicate either the number of septets, or the number of octets,
* depending on Data Coding Scheme. We store TP-UDL value as-is,
* so this should be kept in mind to avoid buffer overruns. */
user_data_len = *smsp++;
user_data = smsp;
if (user_data_len > 0) {
if (sms_alphabet == DCS_7BIT_DEFAULT) {
/* TP-UDL is indicated in septets (up to 160) */
if (user_data_len > GSM340_UDL_SPT_MAX) {
user_data_len = GSM340_UDL_SPT_MAX;
}
user_data_octet_len = gsm_get_octet_len(user_data_len);
} else {
/* TP-UDL is indicated in octets (up to 140) */
if (user_data_len > GSM340_UDL_OCT_MAX) {
user_data_len = GSM340_UDL_OCT_MAX;
}
user_data_octet_len = user_data_len;
}
}
}
{
/* The following is a hacky copy pasted and shortened version of osmo-msc's gsm340_gen_sms_deliver_tpdu() */
struct msgb *msg = gsm411_msgb_alloc();
uint8_t *smsp;
uint8_t oa[12]; /* max len per 03.40 */
int oa_len;
if (!msg)
return NULL;
/* generate first octet with masked bits */
smsp = msgb_put(msg, 1);
/* TP-MTI (message type indicator) */
*smsp = GSM340_SMS_DELIVER_SC2MS;
/* TP-MMS (more messages to send) */
if (0 /* FIXME */)
*smsp |= 0x04;
/* TP-SRI(deliver)/SRR(submit) */
if (status_rep_req)
*smsp |= 0x20;
/* TP-UDHI (indicating TP-UD contains a header) */
if (ud_hdr_ind)
*smsp |= 0x40;
/* generate originator address */
oa_len = gsm340_gen_oa(oa, sizeof(oa), 0, 0, sender_msisdn);
if (oa_len < 0) {
msgb_free(msg);
return NULL;
}
smsp = msgb_put(msg, oa_len);
memcpy(smsp, oa, oa_len);
/* generate TP-PID */
smsp = msgb_put(msg, 1);
*smsp = protocol_id;
/* generate TP-DCS */
smsp = msgb_put(msg, 1);
*smsp = data_coding_scheme;
/* generate TP-SCTS */
smsp = msgb_put(msg, 7);
gsm340_gen_scts(smsp, time(NULL));
/* generate TP-UDL */
smsp = msgb_put(msg, 1);
*smsp = user_data_len;
smsp = msgb_put(msg, user_data_octet_len);
memcpy(smsp, user_data, user_data_octet_len);
return msg;
}
}
static void sms_recipient_up_cb(const struct osmo_sockaddr_str *addr, struct remote_hlr *remote_hlr, void *data)
{
struct osmo_gsup_req *req = data;
struct osmo_gsup_message modified_gsup = req->gsup;
// struct msgb *mt_pdu = NULL;
if (!remote_hlr) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_MSC_TEMP_NOTREACH,
"Failed to connect to SMS recipient: " OSMO_SOCKADDR_STR_FMT,
OSMO_SOCKADDR_STR_FMT_ARGS(addr));
return;
}
/* We must not send out another MO request, to make sure we don't send the request in an infinite loop. */
#if 0
if (req->gsup.message_type == OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST) {
char sender_msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
if (sms_extract_sender_msisdn(sender_msisdn, sizeof(sender_msisdn), req)) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "Cannot find sender MSISDN");
return;
}
mt_pdu = sms_mo_pdu_to_mt_pdu(req->gsup.sm_rp_ui, req->gsup.sm_rp_ui_len, sender_msisdn);
if (!mt_pdu) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
"Cannot translate PDU to a DELIVER PDU");
return;
}
modified_gsup.message_type = OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST;
modified_gsup.sm_rp_ui = mt_pdu->data;
modified_gsup.sm_rp_ui_len = mt_pdu->len;
}
#endif
LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to remote HLR " OSMO_SOCKADDR_STR_FMT "\n",
OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
remote_hlr_gsup_forward_to_remote_hlr(remote_hlr, req, &modified_gsup);
}
static void sms_over_gsup_mt(struct osmo_gsup_req *req)
{
/* Find a locally connected MSC that knows this MSISDN. */
uint32_t lu_age;
struct osmo_gsup_peer_id local_msc_id;
struct osmo_mslookup_query query = {
.service = OSMO_MSLOOKUP_SERVICE_SMS_GSUP,
.id = {
.type = OSMO_MSLOOKUP_ID_MSISDN,
},
};
struct osmo_gsup_message modified_gsup = req->gsup;
struct msgb *mt_pdu = NULL;
if (sms_extract_destination_msisdn(query.id.msisdn, sizeof(query.id.msisdn), req)) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "invalid MSISDN");
return;
}
LOG_GSUP_REQ(req, LOGL_NOTICE, "SMS to MSISDN: %s\n", query.id.msisdn);
/* If a local attach is found, write the subscriber's IMSI to the modified_gsup buffer */
if (!subscriber_has_done_lu_here(&query, &lu_age, &local_msc_id.ipa_name,
modified_gsup.imsi, sizeof(modified_gsup.imsi))) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_MSC_TEMP_NOTREACH,
"SMS recipient not reachable: %s\n",
osmo_mslookup_result_name_c(OTC_SELECT, &query, NULL));
return;
}
local_msc_id.type = OSMO_GSUP_PEER_ID_IPA_NAME;
/* A local MSC indeed has an active subscription for the recipient. Deliver there. */
if (modified_gsup.message_type == OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST) {
/* This is a direct local delivery, and sms_over_gsup_mo_directly_to_mt() just passed the MO request
* altough here we are on the MT side. We must not send out another MO request, to make sure we don't
* send the request in an infinite loop.
* Also patch in the recipient's IMSI.
*/
char sender_msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
if (sms_extract_sender_msisdn(sender_msisdn, sizeof(sender_msisdn), req)) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "Cannot find sender MSISDN");
return;
}
mt_pdu = sms_mo_pdu_to_mt_pdu(req->gsup.sm_rp_ui, req->gsup.sm_rp_ui_len, sender_msisdn);
if (!mt_pdu) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
"Cannot translate PDU to a DELIVER PDU");
return;
}
modified_gsup.message_type = OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST;
modified_gsup.sm_rp_ui = mt_pdu->data;
modified_gsup.sm_rp_ui_len = mt_pdu->len;
}
osmo_gsup_forward_to_local_peer(g_hlr->gs, &local_msc_id, req, &modified_gsup);
if (mt_pdu)
msgb_free(mt_pdu);
}
static void resolve_sms_recipient_cb(struct osmo_mslookup_client *client,
uint32_t request_handle,
const struct osmo_mslookup_query *query,
const struct osmo_mslookup_result *result)
{
struct osmo_gsup_req *req = query->priv;
const struct osmo_sockaddr_str *remote_hlr_addr = NULL;
const struct mslookup_service_host *local_gsup;
if (result->rc == OSMO_MSLOOKUP_RC_RESULT) {
if (osmo_sockaddr_str_is_nonzero(&result->host_v4))
remote_hlr_addr = &result->host_v4;
else if (osmo_sockaddr_str_is_nonzero(&result->host_v6))
remote_hlr_addr = &result->host_v6;
}
if (!remote_hlr_addr) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_MSC_TEMP_NOTREACH,
"Failed to resolve SMS recipient: %s\n",
osmo_mslookup_result_name_c(OTC_SELECT, query, result));
return;
}
/* Possibly, this HLR here has responded to itself via mslookup. Don't make a GSUP connection to ourselves,
* instead go directly to the MT path. */
local_gsup = mslookup_server_get_local_gsup_addr();
LOG_GSUP_REQ(req, LOGL_NOTICE, "local_gsup " OSMO_SOCKADDR_STR_FMT " " OSMO_SOCKADDR_STR_FMT
" remote_hlr_addr " OSMO_SOCKADDR_STR_FMT "\n",
OSMO_SOCKADDR_STR_FMT_ARGS(&local_gsup->host_v4),
OSMO_SOCKADDR_STR_FMT_ARGS(&local_gsup->host_v6),
OSMO_SOCKADDR_STR_FMT_ARGS(remote_hlr_addr));
if (local_gsup
&& (!osmo_sockaddr_str_cmp(&local_gsup->host_v4, remote_hlr_addr)
|| !osmo_sockaddr_str_cmp(&local_gsup->host_v6, remote_hlr_addr))) {
sms_over_gsup_mt(req);
return;
}
remote_hlr_get_or_connect(remote_hlr_addr, true, sms_recipient_up_cb, req);
}
static void sms_over_gsup_mo_directly_to_mt(struct osmo_gsup_req *req)
{
/* Figure out the location of the SMS recipient by mslookup */
if (osmo_mslookup_client_active(g_hlr->mslookup.client.client)) {
/* D-GSM is active. Kick off an mslookup for the current location of the MSISDN. */
uint32_t request_handle;
struct osmo_mslookup_query_handling handling = {
.min_wait_milliseconds = g_hlr->mslookup.client.result_timeout_milliseconds,
.result_cb = resolve_sms_recipient_cb,
};
struct osmo_mslookup_query query = {
.id = {
.type = OSMO_MSLOOKUP_ID_MSISDN,
},
.priv = req,
};
if (sms_extract_destination_msisdn(query.id.msisdn, sizeof(query.id.msisdn), req)
|| !osmo_msisdn_str_valid(query.id.msisdn)) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "invalid MSISDN");
return;
}
OSMO_STRLCPY_ARRAY(query.service, OSMO_MSLOOKUP_SERVICE_SMS_GSUP);
request_handle = osmo_mslookup_client_request(g_hlr->mslookup.client.client, &query, &handling);
if (request_handle) {
/* Querying succeeded. Wait for resolve_sms_recipient_cb() to be called. */
return;
}
/* Querying failed. Try whether delivering to a locally connected MSC works out. */
LOG_DGSM(req->gsup.imsi, LOGL_ERROR,
"Error dispatching mslookup query for SMS: %s -- trying local delivery\n",
osmo_mslookup_result_name_c(OTC_SELECT, &query, NULL));
}
/* Attempt direct delivery */
sms_over_gsup_mt(req);
}
static void sms_over_gsup_mo(struct osmo_gsup_req *req)
{
if (!osmo_gsup_peer_id_is_empty(&g_hlr->sms_over_gsup.smsc)) {
/* Forward to SMSC */
/* FIXME actually use branch fixeria/sms for this */
osmo_gsup_forward_to_local_peer(g_hlr->gs, &g_hlr->sms_over_gsup.smsc, req, NULL);
return;
}
if (!g_hlr->sms_over_gsup.try_direct_delivery) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_PROTO_ERR_UNSPEC,
"cannot deliver SMS over GSUP: No SMSC (and direct delivery disabled)");
return;
}
sms_over_gsup_mo_directly_to_mt(req);
}
bool sms_over_gsup_check_handle_msg(struct osmo_gsup_req *req)
{
switch (req->gsup.message_type) {
case OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST:
sms_over_gsup_mo(req);
return true;
case OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST:
sms_over_gsup_mt(req);
return true;
default:
return false;
}
}

View File

@@ -1,5 +1,6 @@
SUBDIRS = \ SUBDIRS = \
auc \ auc \
gsup_server \
db \ db \
gsup \ gsup \
db_upgrade \ db_upgrade \
@@ -69,9 +70,6 @@ vty-test:
CTRL_TEST_DB = hlr_ctrl_test.db CTRL_TEST_DB = hlr_ctrl_test.db
# Run a specific test with: 'make ctrl-test CTRL_TEST=test_subscriber.ctrl'
CTRL_TEST ?= *.ctrl
# To update the CTRL script from current application behavior, # To update the CTRL script from current application behavior,
# pass -u to ctrl_script_runner.py by doing: # pass -u to ctrl_script_runner.py by doing:
# make ctrl-test U=-u # make ctrl-test U=-u
@@ -82,7 +80,7 @@ ctrl-test:
osmo_verify_transcript_ctrl.py -v \ osmo_verify_transcript_ctrl.py -v \
-p 4259 \ -p 4259 \
-r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l $(CTRL_TEST_DB)" \ -r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l $(CTRL_TEST_DB)" \
$(U) $(srcdir)/$(CTRL_TEST) $(U) $(srcdir)/*.ctrl
-rm -f $(CTRL_TEST_DB) -rm -f $(CTRL_TEST_DB)
-rm $(CTRL_TEST_DB)-* -rm $(CTRL_TEST_DB)-*

View File

@@ -2,12 +2,12 @@ SUBDIRS = gen_ts_55_205_test_sets
AM_CPPFLAGS = \ AM_CPPFLAGS = \
$(all_includes) \ $(all_includes) \
-I$(top_srcdir)/include \
$(NULL) $(NULL)
AM_CFLAGS = \ AM_CFLAGS = \
-Wall \ -Wall \
-ggdb3 \ -ggdb3 \
-I$(top_srcdir)/include \
$(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \
$(NULL) $(NULL)
@@ -23,15 +23,17 @@ EXTRA_DIST = \
auc_ts_55_205_test_sets.err \ auc_ts_55_205_test_sets.err \
$(NULL) $(NULL)
check_PROGRAMS = auc_test auc_ts_55_205_test_sets check_PROGRAMS = auc_ts_55_205_test_sets
noinst_PROGRAMS = auc_test
auc_test_SOURCES = \ auc_test_SOURCES = \
auc_test.c \ auc_test.c \
$(NULL) $(NULL)
auc_test_LDADD = \ auc_test_LDADD = \
$(top_builddir)/src/auc.o \ $(top_srcdir)/src/auc.c \
$(top_builddir)/src/logging.o \ $(top_srcdir)/src/logging.c \
$(LIBOSMOCORE_LIBS) \ $(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \ $(LIBOSMOGSM_LIBS) \
$(NULL) $(NULL)
@@ -41,8 +43,8 @@ auc_ts_55_205_test_sets_SOURCES = \
$(NULL) $(NULL)
auc_ts_55_205_test_sets_LDADD = \ auc_ts_55_205_test_sets_LDADD = \
$(top_builddir)/src/auc.o \ $(top_srcdir)/src/auc.c \
$(top_builddir)/src/logging.o \ $(top_srcdir)/src/logging.c \
$(LIBOSMOCORE_LIBS) \ $(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \ $(LIBOSMOGSM_LIBS) \
$(NULL) $(NULL)

View File

@@ -113,17 +113,16 @@ int rand_get(uint8_t *rand, unsigned int len)
return len; return len;
} }
/* Subscriber with 2G-only (COMP128v1) authentication data */
static void test_gen_vectors_2g_only(void) static void test_gen_vectors_2g_only(void)
{ {
struct osmo_sub_auth_data2 aud2g; struct osmo_sub_auth_data aud2g;
struct osmo_sub_auth_data2 aud3g; struct osmo_sub_auth_data aud3g;
struct osmo_auth_vector vec; struct osmo_auth_vector vec;
int rc; int rc;
comment_start(); comment_start();
aud2g = (struct osmo_sub_auth_data2){ aud2g = (struct osmo_sub_auth_data){
.type = OSMO_AUTH_TYPE_GSM, .type = OSMO_AUTH_TYPE_GSM,
.algo = OSMO_AUTH_ALG_COMP128v1, .algo = OSMO_AUTH_ALG_COMP128v1,
}; };
@@ -131,7 +130,7 @@ static void test_gen_vectors_2g_only(void)
osmo_hexparse("EB215756028D60E3275E613320AEC880", osmo_hexparse("EB215756028D60E3275E613320AEC880",
aud2g.u.gsm.ki, sizeof(aud2g.u.gsm.ki)); aud2g.u.gsm.ki, sizeof(aud2g.u.gsm.ki));
aud3g = (struct osmo_sub_auth_data2){ 0 }; aud3g = (struct osmo_sub_auth_data){ 0 };
next_rand("39fa2f4e3d523d8619a73b4f65c3e14d", true); next_rand("39fa2f4e3d523d8619a73b4f65c3e14d", true);
@@ -175,18 +174,16 @@ static void test_gen_vectors_2g_only(void)
comment_end(); comment_end();
} }
/* Subscriber with separate 2G (COMP128v1) and 3G (MILENAGE) authentication data,
* reflects the default configuration of sysmoUSIM-SJS1 */
static void test_gen_vectors_2g_plus_3g(void) static void test_gen_vectors_2g_plus_3g(void)
{ {
struct osmo_sub_auth_data2 aud2g; struct osmo_sub_auth_data aud2g;
struct osmo_sub_auth_data2 aud3g; struct osmo_sub_auth_data aud3g;
struct osmo_auth_vector vec; struct osmo_auth_vector vec;
int rc; int rc;
comment_start(); comment_start();
aud2g = (struct osmo_sub_auth_data2){ aud2g = (struct osmo_sub_auth_data){
.type = OSMO_AUTH_TYPE_GSM, .type = OSMO_AUTH_TYPE_GSM,
.algo = OSMO_AUTH_ALG_COMP128v1, .algo = OSMO_AUTH_ALG_COMP128v1,
}; };
@@ -194,11 +191,9 @@ static void test_gen_vectors_2g_plus_3g(void)
osmo_hexparse("EB215756028D60E3275E613320AEC880", osmo_hexparse("EB215756028D60E3275E613320AEC880",
aud2g.u.gsm.ki, sizeof(aud2g.u.gsm.ki)); aud2g.u.gsm.ki, sizeof(aud2g.u.gsm.ki));
aud3g = (struct osmo_sub_auth_data2){ aud3g = (struct osmo_sub_auth_data){
.type = OSMO_AUTH_TYPE_UMTS, .type = OSMO_AUTH_TYPE_UMTS,
.algo = OSMO_AUTH_ALG_MILENAGE, .algo = OSMO_AUTH_ALG_MILENAGE,
.u.umts.k_len = 16,
.u.umts.opc_len = 16,
.u.umts.sqn = 31, .u.umts.sqn = 31,
}; };
@@ -289,13 +284,10 @@ void _test_gen_vectors_3g_only__expect_vecs(struct osmo_auth_vector vecs[3])
); );
} }
/* Subscriber with only 3G (MILENAGE) authentication data,
* reflects the default configuration of sysmoISIM-SJA2. Resulting
* tuples are suitable for both 2G and 3G authentication */
static void test_gen_vectors_3g_only(void) static void test_gen_vectors_3g_only(void)
{ {
struct osmo_sub_auth_data2 aud2g; struct osmo_sub_auth_data aud2g;
struct osmo_sub_auth_data2 aud3g; struct osmo_sub_auth_data aud3g;
struct osmo_auth_vector vec; struct osmo_auth_vector vec;
struct osmo_auth_vector vecs[3]; struct osmo_auth_vector vecs[3];
uint8_t auts[14]; uint8_t auts[14];
@@ -304,13 +296,11 @@ static void test_gen_vectors_3g_only(void)
comment_start(); comment_start();
aud2g = (struct osmo_sub_auth_data2){ 0 }; aud2g = (struct osmo_sub_auth_data){ 0 };
aud3g = (struct osmo_sub_auth_data2){ aud3g = (struct osmo_sub_auth_data){
.type = OSMO_AUTH_TYPE_UMTS, .type = OSMO_AUTH_TYPE_UMTS,
.algo = OSMO_AUTH_ALG_MILENAGE, .algo = OSMO_AUTH_ALG_MILENAGE,
.u.umts.k_len = 16,
.u.umts.opc_len = 16,
.u.umts.sqn = 31, .u.umts.sqn = 31,
}; };
@@ -464,58 +454,7 @@ static void test_gen_vectors_3g_only(void)
comment_end(); comment_end();
} }
/* Subscriber with only 3G (XOR) authentication data, void test_gen_vectors_bad_args()
* reflects the default configuration of sysmoTSIM-SJAx as well
* as many "Test USIM" cards. Resulting tuples are suitable for both
* 2G and 3G authentication */
static void test_gen_vectors_3g_xor(void)
{
struct osmo_sub_auth_data2 aud2g;
struct osmo_sub_auth_data2 aud3g;
struct osmo_auth_vector vec;
int rc;
comment_start();
aud2g = (struct osmo_sub_auth_data2){ 0 };
aud3g = (struct osmo_sub_auth_data2){
.type = OSMO_AUTH_TYPE_UMTS,
.algo = OSMO_AUTH_ALG_XOR_3G,
.u.umts.k_len = 16,
.u.umts.opc_len = 16,
.u.umts.sqn = 0,
};
osmo_hexparse("000102030405060708090a0b0c0d0e0f",
aud3g.u.umts.k, sizeof(aud3g.u.umts.k));
osmo_hexparse("00000000000000000000000000000000",
aud3g.u.umts.opc, sizeof(aud3g.u.umts.opc));
next_rand("b5039c57e4a75051551d1a390a71ce48", true);
vec = (struct osmo_auth_vector){ {0} };
VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64);
rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL);
VERBOSE_ASSERT(rc, == 1, "%d");
VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64);
VEC_IS(&vec,
" rand: b5039c57e4a75051551d1a390a71ce48\n"
" autn: 54e0a256565d0000b5029e54e0a25656\n"
" ck: 029e54e0a256565d141032067cc047b5\n"
" ik: 9e54e0a256565d141032067cc047b502\n"
" res: b5029e54e0a256565d141032067cc047\n"
" res_len: 10\n"
" kc: 98e880384887f9fe\n"
" sres: 0ec81877\n"
" auth_types: 03000000\n"
);
comment_end();
}
/* Test a variety of invalid authentication data combinations */
void test_gen_vectors_bad_args(void)
{ {
struct osmo_auth_vector vec; struct osmo_auth_vector vec;
uint8_t auts[14]; uint8_t auts[14];
@@ -523,43 +462,39 @@ void test_gen_vectors_bad_args(void)
int rc; int rc;
int i; int i;
struct osmo_sub_auth_data2 aud2g = { struct osmo_sub_auth_data aud2g = {
.type = OSMO_AUTH_TYPE_GSM, .type = OSMO_AUTH_TYPE_GSM,
.algo = OSMO_AUTH_ALG_COMP128v1, .algo = OSMO_AUTH_ALG_COMP128v1,
}; };
struct osmo_sub_auth_data2 aud3g = { struct osmo_sub_auth_data aud3g = {
.type = OSMO_AUTH_TYPE_UMTS, .type = OSMO_AUTH_TYPE_UMTS,
.algo = OSMO_AUTH_ALG_MILENAGE, .algo = OSMO_AUTH_ALG_MILENAGE,
.u.umts.k_len = 16,
.u.umts.opc_len = 16,
}; };
struct osmo_sub_auth_data2 aud2g_noalg = { struct osmo_sub_auth_data aud2g_noalg = {
.type = OSMO_AUTH_TYPE_GSM, .type = OSMO_AUTH_TYPE_GSM,
.algo = OSMO_AUTH_ALG_NONE, .algo = OSMO_AUTH_ALG_NONE,
}; };
struct osmo_sub_auth_data2 aud3g_noalg = { struct osmo_sub_auth_data aud3g_noalg = {
.type = OSMO_AUTH_TYPE_UMTS, .type = OSMO_AUTH_TYPE_UMTS,
.algo = OSMO_AUTH_ALG_NONE, .algo = OSMO_AUTH_ALG_NONE,
.u.umts.k_len = 16,
.u.umts.opc_len = 16,
}; };
struct osmo_sub_auth_data2 aud_notype = { struct osmo_sub_auth_data aud_notype = {
.type = OSMO_AUTH_TYPE_NONE, .type = OSMO_AUTH_TYPE_NONE,
.algo = OSMO_AUTH_ALG_MILENAGE, .algo = OSMO_AUTH_ALG_MILENAGE,
}; };
struct osmo_sub_auth_data2 no_aud = { struct osmo_sub_auth_data no_aud = {
.type = OSMO_AUTH_TYPE_NONE, .type = OSMO_AUTH_TYPE_NONE,
.algo = OSMO_AUTH_ALG_NONE, .algo = OSMO_AUTH_ALG_NONE,
}; };
struct { struct {
struct osmo_sub_auth_data2 *aud2g; struct osmo_sub_auth_data *aud2g;
struct osmo_sub_auth_data2 *aud3g; struct osmo_sub_auth_data *aud3g;
uint8_t *rand_auts; uint8_t *rand_auts;
uint8_t *auts; uint8_t *auts;
const char *label; const char *label;
@@ -678,20 +613,15 @@ int main(int argc, char **argv)
void *tall_ctx = talloc_named_const(NULL, 1, "auc_test"); void *tall_ctx = talloc_named_const(NULL, 1, "auc_test");
osmo_init_logging2(tall_ctx, &hlr_log_info); osmo_init_logging2(tall_ctx, &hlr_log_info);
log_set_print_filename2(osmo_stderr_target, log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose);
cmdline_opts.verbose ?
LOG_FILENAME_BASENAME :
LOG_FILENAME_NONE);
log_set_print_timestamp(osmo_stderr_target, 0); log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0); log_set_use_color(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1); log_set_print_category(osmo_stderr_target, 1);
log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1"); log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");
test_gen_vectors_2g_only(); test_gen_vectors_2g_only();
test_gen_vectors_2g_plus_3g(); test_gen_vectors_2g_plus_3g();
test_gen_vectors_3g_only(); test_gen_vectors_3g_only();
test_gen_vectors_3g_xor();
test_gen_vectors_bad_args(); test_gen_vectors_bad_args();
printf("Done\n"); printf("Done\n");

View File

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

View File

@@ -24,46 +24,43 @@
static void {func_name}(void) static void {func_name}(void)
{{ {{
struct osmo_sub_auth_data2 aud2g; struct osmo_sub_auth_data aud2g;
struct osmo_sub_auth_data2 aud3g; struct osmo_sub_auth_data aud3g;
struct osmo_auth_vector vec; struct osmo_auth_vector vec;
int rc; int rc;
comment_start(); comment_start();
aud2g = (struct osmo_sub_auth_data2){{ 0 }}; aud2g = (struct osmo_sub_auth_data){{ 0 }};
aud3g = (struct osmo_sub_auth_data2){{ aud3g = (struct osmo_sub_auth_data){{
.type = OSMO_AUTH_TYPE_UMTS, .type = OSMO_AUTH_TYPE_UMTS,
.algo = OSMO_AUTH_ALG_MILENAGE, .algo = OSMO_AUTH_ALG_MILENAGE,
.u.umts.k_len = 16,
.u.umts.opc_len = 16,
.u.umts.sqn = 31, .u.umts.sqn = 31,
}}; }};
osmo_hexparse("{Ki}", osmo_hexparse("{Ki}",
aud3g.u.umts.k, sizeof(aud3g.u.umts.k)); aud3g.u.umts.k, sizeof(aud3g.u.umts.k));
osmo_hexparse("{OPc}", osmo_hexparse("{OPc}",
aud3g.u.umts.opc, sizeof(aud3g.u.umts.opc)); aud3g.u.umts.opc, sizeof(aud3g.u.umts.opc));
osmo_hexparse("{RAND}", osmo_hexparse("{RAND}",
fake_rand, sizeof(fake_rand)); fake_rand, sizeof(fake_rand));
vec = (struct osmo_auth_vector){{ {{0}} }}; vec = (struct osmo_auth_vector){{ {{0}} }};
vec.res_len = 8;
VERBOSE_ASSERT(aud3g.u.umts.sqn, == 31, "%"PRIu64); VERBOSE_ASSERT(aud3g.u.umts.sqn, == 31, "%"PRIu64);
rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL);
VERBOSE_ASSERT(rc, == 1, "%d"); VERBOSE_ASSERT(rc, == 1, "%d");
VERBOSE_ASSERT(aud3g.u.umts.sqn, == 32, "%"PRIu64); VERBOSE_ASSERT(aud3g.u.umts.sqn, == 32, "%"PRIu64);
VEC_IS(&vec, VEC_IS(&vec,
" rand: {RAND}\n" " rand: {RAND}\n"
" ck: {MIL3G-CK}\n" " ck: {MIL3G-CK}\n"
" ik: {MIL3G-IK}\n" " ik: {MIL3G-IK}\n"
" res: {MIL3G-RES}0000000000000000\n" " res: {MIL3G-RES}0000000000000000\n"
" kc: {Kc}\n" " kc: {Kc}\n"
" sres: {SRES#1}\n" " sres: {SRES#1}\n"
); );
comment_end(); comment_end();
}} }}

View File

@@ -55,7 +55,7 @@ char *vec_str(const struct osmo_auth_vector *vec)
if (pos >= end) \ if (pos >= end) \
return buf; \ return buf; \
pos += snprintf(pos, sizeof(buf) - (pos - buf), \ pos += snprintf(pos, sizeof(buf) - (pos - buf), \
" " #what ": %s\n", \ " " #what ": %s\n", \
osmo_hexdump_nospc((void*)&vec->what, sizeof(vec->what))) osmo_hexdump_nospc((void*)&vec->what, sizeof(vec->what)))
append(rand); append(rand);
@@ -71,7 +71,7 @@ char *vec_str(const struct osmo_auth_vector *vec)
#define VEC_IS(vec, expect) do { \ #define VEC_IS(vec, expect) do { \
char *_is = vec_str(vec); \ char *_is = vec_str(vec); \
if (strcmp(_is, expect)) { \ if (strcmp(_is, expect)) { \
fprintf(stderr, "MISMATCH! expected ==\n%s\n", \ fprintf(stderr, "MISMATCH! expected ==\n%s\n", \
expect); \ expect); \
char *a = _is; \ char *a = _is; \
@@ -100,16 +100,15 @@ int rand_get(uint8_t *rand, unsigned int len)
FUNCTIONS FUNCTIONS
int main(int argc, char **argv) int main()
{ {
printf("3GPP TS 55.205 Test Sets\n"); printf("3GPP TS 55.205 Test Sets\n");
void *tall_ctx = talloc_named_const(NULL, 1, "test"); void *tall_ctx = talloc_named_const(NULL, 1, "test");
msgb_talloc_ctx_init(tall_ctx, 0); msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &hlr_log_info); osmo_init_logging2(tall_ctx, &hlr_log_info);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE); log_set_print_filename(osmo_stderr_target, 0);
log_set_print_timestamp(osmo_stderr_target, 0); log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0); log_set_use_color(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1); log_set_print_category(osmo_stderr_target, 1);
log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1"); log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");

View File

@@ -1,10 +1,7 @@
AM_CPPFLAGS = \ AM_CFLAGS = \
$(all_includes) \ $(all_includes) \
-I$(top_srcdir)/include \ -I$(top_srcdir)/include \
-I$(top_builddir)/include \ -I$(top_builddir)/include \
$(NULL)
AM_CFLAGS = \
-Wall \ -Wall \
-ggdb3 \ -ggdb3 \
$(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOCORE_CFLAGS) \
@@ -33,7 +30,7 @@ db_test_LDADD = \
$(top_builddir)/src/db_auc.o \ $(top_builddir)/src/db_auc.o \
$(top_builddir)/src/db_hlr.o \ $(top_builddir)/src/db_hlr.o \
$(top_builddir)/src/db.o \ $(top_builddir)/src/db.o \
$(top_builddir)/src/cni_peer_id.o \ $(top_builddir)/src/gsup_peer_id.o \
$(LIBOSMOCORE_LIBS) \ $(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \ $(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \ $(LIBOSMOABIS_LIBS) \

View File

@@ -27,7 +27,7 @@
#include <osmocom/core/utils.h> #include <osmocom/core/utils.h>
#include <osmocom/core/logging.h> #include <osmocom/core/logging.h>
#include <osmocom/gsupclient/cni_peer_id.h> #include <osmocom/gsupclient/gsup_peer_id.h>
#include <osmocom/hlr/db.h> #include <osmocom/hlr/db.h>
#include <osmocom/hlr/logging.h> #include <osmocom/hlr/logging.h>
@@ -122,16 +122,16 @@ static void _fill_invalid(void *dest, size_t size)
/* Not linking the real auc_compute_vectors(), just returning num_vec. /* 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. */ * 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, int auc_compute_vectors(struct osmo_auth_vector *vec, unsigned int num_vec,
struct osmo_sub_auth_data2 *aud2g, struct osmo_sub_auth_data *aud2g,
struct osmo_sub_auth_data2 *aud3g, struct osmo_sub_auth_data *aud3g,
const uint8_t *rand_auts, const uint8_t *auts) const uint8_t *rand_auts, const uint8_t *auts)
{ return num_vec; } { return num_vec; }
static struct db_context *dbc = NULL; static struct db_context *dbc = NULL;
static void *ctx = NULL; static void *ctx = NULL;
static struct hlr_subscriber g_subscr; static struct hlr_subscriber g_subscr;
static struct osmo_sub_auth_data2 g_aud2g; static struct osmo_sub_auth_data g_aud2g;
static struct osmo_sub_auth_data2 g_aud3g; static struct osmo_sub_auth_data g_aud3g;
static int g_rc; static int g_rc;
static int64_t g_id; static int64_t g_id;
@@ -173,6 +173,8 @@ void dump_subscr(struct hlr_subscriber *subscr)
Pfo(lmsi, "0x%x", subscr); Pfo(lmsi, "0x%x", subscr);
Pb(true, ms_purged_cs); Pb(true, ms_purged_cs);
Pb(true, ms_purged_ps); Pb(true, ms_purged_ps);
Ps(last_lu_rat_cs);
Ps(last_lu_rat_ps);
fprintf(stderr, "}\n"); fprintf(stderr, "}\n");
#undef Ps #undef Ps
#undef Pd #undef Pd
@@ -180,21 +182,18 @@ void dump_subscr(struct hlr_subscriber *subscr)
#undef Pb #undef Pb
} }
void dump_aud(const char *label, struct osmo_sub_auth_data2 *aud) void dump_aud(const char *label, struct osmo_sub_auth_data *aud)
{ {
if (aud->type == OSMO_AUTH_TYPE_NONE) { if (aud->type == OSMO_AUTH_TYPE_NONE) {
fprintf(stderr, "%s: none\n", label); fprintf(stderr, "%s: none\n", label);
return; return;
} }
fprintf(stderr, "%s: struct osmo_sub_auth_data2 {\n", label); fprintf(stderr, "%s: struct osmo_sub_auth_data {\n", label);
#define Pf(name, fmt) \ #define Pf(name, fmt) \
Pfo(name, fmt, aud) Pfo(name, fmt, aud)
#define Phex(name) \ #define Phex(name) \
Pfv(name, "'%s'", osmo_hexdump_nospc(aud->name, sizeof(aud->name))) Pfv(name, "'%s'", osmo_hexdump_nospc(aud->name, sizeof(aud->name)))
#define Phexl(name, len) \
Pfv(name, "'%s'", osmo_hexdump_nospc(aud->name, aud->len))
Pfv(type, "%s", osmo_sub_auth_type_name(aud->type)); Pfv(type, "%s", osmo_sub_auth_type_name(aud->type));
Pfv(algo, "%s", osmo_auth_alg_name(aud->algo)); Pfv(algo, "%s", osmo_auth_alg_name(aud->algo));
@@ -203,9 +202,9 @@ void dump_aud(const char *label, struct osmo_sub_auth_data2 *aud)
Phex(u.gsm.ki); Phex(u.gsm.ki);
break; break;
case OSMO_AUTH_TYPE_UMTS: case OSMO_AUTH_TYPE_UMTS:
Phexl(u.umts.opc, u.umts.opc_len); Phex(u.umts.opc);
Pf(u.umts.opc_is_op, "%u"); Pf(u.umts.opc_is_op, "%u");
Phexl(u.umts.k, u.umts.k_len); Phex(u.umts.k);
Phex(u.umts.amf); Phex(u.umts.amf);
if (aud->u.umts.sqn) { if (aud->u.umts.sqn) {
Pf(u.umts.sqn, "%"PRIu64); Pf(u.umts.sqn, "%"PRIu64);
@@ -222,7 +221,6 @@ void dump_aud(const char *label, struct osmo_sub_auth_data2 *aud)
#undef Pf #undef Pf
#undef Phex #undef Phex
#undef Phexl
} }
void db_raw_sql(struct db_context *dbc, const char *sql) void db_raw_sql(struct db_context *dbc, const char *sql)
@@ -247,10 +245,10 @@ static int db_subscr_lu_str(struct db_context *dbc, int64_t subscr_id,
{ {
struct osmo_ipa_name vlr_nr; struct osmo_ipa_name vlr_nr;
osmo_ipa_name_set_str(&vlr_nr, vlr_or_sgsn_number); osmo_ipa_name_set_str(&vlr_nr, vlr_or_sgsn_number);
return db_subscr_lu(dbc, subscr_id, &vlr_nr, is_ps, NULL); return db_subscr_lu(dbc, subscr_id, &vlr_nr, is_ps, NULL, NULL, 0);
} }
static void test_subscr_create_update_sel_delete(void) static void test_subscr_create_update_sel_delete()
{ {
int64_t id0, id1, id2, id_short; int64_t id0, id1, id2, id_short;
comment_start(); comment_start();
@@ -266,13 +264,13 @@ static void test_subscr_create_update_sel_delete(void)
ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0); ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), 0);
ASSERT_SEL(imsi, imsi2, 0); ASSERT_SEL(imsi, imsi2, 0);
id2 = g_subscr.id; id2 = g_subscr.id;
ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EEXIST); ASSERT_RC(db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
ASSERT_SEL(imsi, imsi0, 0); ASSERT_SEL(imsi, imsi0, 0);
ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EEXIST); ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EEXIST); ASSERT_RC(db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
ASSERT_SEL(imsi, imsi1, 0); ASSERT_SEL(imsi, imsi1, 0);
ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EEXIST); ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EEXIST); ASSERT_RC(db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EIO);
ASSERT_SEL(imsi, imsi2, 0); ASSERT_SEL(imsi, imsi2, 0);
ASSERT_RC(db_subscr_create(dbc, "123456789 000003", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL); ASSERT_RC(db_subscr_create(dbc, "123456789 000003", DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS), -EINVAL);
@@ -545,7 +543,7 @@ static const struct sub_auth_data_str *mk_aud_3g(enum osmo_auth_algo algo,
return &aud; return &aud;
} }
static void test_subscr_aud(void) static void test_subscr_aud()
{ {
int64_t id; int64_t id;
@@ -590,7 +588,7 @@ static void test_subscr_aud(void)
ASSERT_SEL_AUD(imsi0, 0, id); ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_XOR_2G, "CededEffacedAceFacedBadFadedBeef")), mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")),
0); 0);
ASSERT_SEL_AUD(imsi0, 0, id); ASSERT_SEL_AUD(imsi0, 0, id);
@@ -608,7 +606,7 @@ static void test_subscr_aud(void)
-ENOENT); -ENOENT);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_XOR_2G, "CededEffacedAceFacedBadFadedBeef")), mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")),
0); 0);
ASSERT_SEL_AUD(imsi0, 0, id); ASSERT_SEL_AUD(imsi0, 0, id);
@@ -711,12 +709,12 @@ static void test_subscr_aud(void)
ASSERT_SEL_AUD(imsi0, 0, id); ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_XOR_2G, "f000000000000f00000000000f000000f00000000")), mk_aud_2g(OSMO_AUTH_ALG_XOR, "f000000000000f00000000000f000000f00000000")),
-EINVAL); -EINVAL);
ASSERT_SEL_AUD(imsi0, 0, id); ASSERT_SEL_AUD(imsi0, 0, id);
ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, ASSERT_RC(db_subscr_update_aud_by_id(dbc, id,
mk_aud_2g(OSMO_AUTH_ALG_XOR_2G, "f00")), mk_aud_2g(OSMO_AUTH_ALG_XOR, "f00")),
-EINVAL); -EINVAL);
ASSERT_SEL_AUD(imsi0, 0, id); ASSERT_SEL_AUD(imsi0, 0, id);
@@ -787,7 +785,7 @@ static void test_subscr_aud(void)
/* Make each key too short in this test. Note that we can't set them longer than the allowed size without changing the /* Make each key too short in this test. Note that we can't set them longer than the allowed size without changing the
* table structure. */ * table structure. */
static void test_subscr_aud_invalid_len(void) static void test_subscr_aud_invalid_len()
{ {
int64_t id; int64_t id;
@@ -849,7 +847,7 @@ static void test_subscr_aud_invalid_len(void)
comment_end(); comment_end();
} }
static void test_subscr_sqn(void) static void test_subscr_sqn()
{ {
int64_t id; int64_t id;
@@ -922,22 +920,22 @@ static void test_subscr_sqn(void)
comment_end(); comment_end();
} }
static void test_ind(void) static void test_ind()
{ {
comment_start(); comment_start();
#define ASSERT_IND(VLR, IND) do { \ #define ASSERT_IND(VLR, IND) do { \
unsigned int ind; \ unsigned int ind; \
struct osmo_cni_peer_id vlr; \ struct osmo_gsup_peer_id vlr; \
OSMO_ASSERT(!osmo_cni_peer_id_set_str(&vlr, OSMO_CNI_PEER_ID_IPA_NAME, VLR)); \ OSMO_ASSERT(!osmo_gsup_peer_id_set_str(&vlr, OSMO_GSUP_PEER_ID_IPA_NAME, VLR)); \
ASSERT_RC(db_ind(dbc, &vlr, &ind), 0); \ ASSERT_RC(db_ind(dbc, &vlr, &ind), 0); \
fprintf(stderr, "%s ind = %u\n\n", osmo_quote_str((char*)vlr.ipa_name.val, vlr.ipa_name.len), ind); \ fprintf(stderr, "%s ind = %u\n\n", osmo_quote_str((char*)vlr.ipa_name.val, vlr.ipa_name.len), ind); \
if (ind != (IND)) \ if (ind != (IND)) \
fprintf(stderr, " ERROR: expected " #IND "\n"); \ fprintf(stderr, " ERROR: expected " #IND "\n"); \
} while (0) } while (0)
#define IND_DEL(VLR) do { \ #define IND_DEL(VLR) do { \
struct osmo_cni_peer_id vlr; \ struct osmo_gsup_peer_id vlr; \
OSMO_ASSERT(!osmo_cni_peer_id_set_str(&vlr, OSMO_CNI_PEER_ID_IPA_NAME, VLR)); \ OSMO_ASSERT(!osmo_gsup_peer_id_set_str(&vlr, OSMO_GSUP_PEER_ID_IPA_NAME, VLR)); \
ASSERT_RC(db_ind_del(dbc, &vlr), 0); \ ASSERT_RC(db_ind_del(dbc, &vlr), 0); \
fprintf(stderr, "%s ind deleted\n\n", osmo_quote_str((char*)vlr.ipa_name.val, vlr.ipa_name.len)); \ fprintf(stderr, "%s ind deleted\n\n", osmo_quote_str((char*)vlr.ipa_name.val, vlr.ipa_name.len)); \
} while (0) } while (0)
@@ -1028,13 +1026,9 @@ int main(int argc, char **argv)
handle_options(argc, argv); handle_options(argc, argv);
osmo_init_logging2(ctx, &hlr_log_info); osmo_init_logging2(ctx, &hlr_log_info);
log_set_print_filename2(osmo_stderr_target, log_set_print_filename(osmo_stderr_target, cmdline_opts.verbose);
cmdline_opts.verbose ?
LOG_FILENAME_BASENAME :
LOG_FILENAME_NONE);
log_set_print_timestamp(osmo_stderr_target, 0); log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0); log_set_use_color(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1); log_set_print_category(osmo_stderr_target, 1);
log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1"); log_parse_category_mask(osmo_stderr_target, "DMAIN,1:DDB,1:DAUC,1");

View File

@@ -27,7 +27,7 @@ struct hlr_subscriber {
.imsi = '123456789000002', .imsi = '123456789000002',
} }
db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EEXIST db_subscr_create(dbc, imsi0, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
DAUC IMSI='123456789000000': Cannot create subscriber: SQL error: (2067) 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 db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0
@@ -36,10 +36,10 @@ struct hlr_subscriber {
.imsi = '123456789000000', .imsi = '123456789000000',
} }
db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EEXIST db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EEXIST db_subscr_create(dbc, imsi1, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
DAUC IMSI='123456789000001': Cannot create subscriber: SQL error: (2067) 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 db_subscr_get_by_imsi(dbc, imsi1, &g_subscr) --> 0
@@ -48,10 +48,10 @@ struct hlr_subscriber {
.imsi = '123456789000001', .imsi = '123456789000001',
} }
db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EEXIST db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) UNIQUE constraint failed: subscriber.imsi
db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EEXIST db_subscr_create(dbc, imsi2, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS) --> -EIO
DAUC IMSI='123456789000002': Cannot create subscriber: SQL error: (2067) 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 db_subscr_get_by_imsi(dbc, imsi2, &g_subscr) --> 0
@@ -824,7 +824,7 @@ db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "01234567
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 3G Auth Data DAUC IMSI='123456789000000': No 3G Auth Data
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v1, .algo = COMP128v1,
.u.gsm.ki = '0123456789abcdef0123456789abcdef', .u.gsm.ki = '0123456789abcdef0123456789abcdef',
@@ -841,7 +841,7 @@ db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "01234567
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 3G Auth Data DAUC IMSI='123456789000000': No 3G Auth Data
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v1, .algo = COMP128v1,
.u.gsm.ki = '0123456789abcdef0123456789abcdef', .u.gsm.ki = '0123456789abcdef0123456789abcdef',
@@ -853,7 +853,7 @@ db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_COMP128v2, "BeadedBe
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 3G Auth Data DAUC IMSI='123456789000000': No 3G Auth Data
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v2, .algo = COMP128v2,
.u.gsm.ki = 'beadedbeeaced1ebbeddefacedfacade', .u.gsm.ki = 'beadedbeeaced1ebbeddefacedfacade',
@@ -865,21 +865,21 @@ db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_COMP128v3, "DeafBedd
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 3G Auth Data DAUC IMSI='123456789000000': No 3G Auth Data
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v3, .algo = COMP128v3,
.u.gsm.ki = 'deafbeddedbabeacceededfadeddecaf', .u.gsm.ki = 'deafbeddedbabeacceededfadeddecaf',
} }
3G: none 3G: none
db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR_2G, "CededEffacedAceFacedBadFadedBeef")) --> 0 db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")) --> 0
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 3G Auth Data DAUC IMSI='123456789000000': No 3G Auth Data
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = XOR-2G, .algo = XOR,
.u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef',
} }
3G: none 3G: none
@@ -900,14 +900,14 @@ 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_NONE, NULL)) --> -ENOENT
db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR_2G, "CededEffacedAceFacedBadFadedBeef")) --> 0 db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")) --> 0
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 3G Auth Data DAUC IMSI='123456789000000': No 3G Auth Data
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = XOR-2G, .algo = XOR,
.u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef',
} }
3G: none 3G: none
@@ -932,7 +932,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -954,7 +954,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -970,7 +970,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'deaf0ff1ced0d0dabbedd1ced1cef00d', .u.umts.opc = 'deaf0ff1ced0d0dabbedd1ced1cef00d',
@@ -985,7 +985,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1001,7 +1001,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'cededeffacedacefacedbadfadedbeef', .u.umts.opc = 'cededeffacedacefacedbadfadedbeef',
@@ -1033,7 +1033,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'cededeffacedacefacedbadfadedbeef', .u.umts.opc = 'cededeffacedacefacedbadfadedbeef',
@@ -1069,12 +1069,12 @@ db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "BeefedCaf
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v3, .algo = COMP128v3,
.u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef',
} }
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1097,12 +1097,12 @@ DAUC Cannot update auth tokens: Unknown auth algo: 99999
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v3, .algo = COMP128v3,
.u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef',
} }
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1112,17 +1112,17 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
.u.umts.ind_bitlen = 5, .u.umts.ind_bitlen = 5,
} }
db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR_2G, "f000000000000f00000000000f000000f00000000")) --> -EINVAL db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR, "f000000000000f00000000000f000000f00000000")) --> -EINVAL
DAUC Cannot update auth tokens: Invalid KI: 'f000000000000f00000000000f000000f00000000' DAUC Cannot update auth tokens: Invalid KI: 'f000000000000f00000000000f000000f00000000'
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v3, .algo = COMP128v3,
.u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef',
} }
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1132,17 +1132,17 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
.u.umts.ind_bitlen = 5, .u.umts.ind_bitlen = 5,
} }
db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR_2G, "f00")) --> -EINVAL db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR, "f00")) --> -EINVAL
DAUC Cannot update auth tokens: Invalid KI: 'f00' DAUC Cannot update auth tokens: Invalid KI: 'f00'
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v3, .algo = COMP128v3,
.u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef',
} }
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1157,12 +1157,12 @@ DAUC Cannot update auth tokens: auth algo not suited for 2G: MILENAGE
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v3, .algo = COMP128v3,
.u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef',
} }
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1177,12 +1177,12 @@ DAUC Cannot update auth tokens: Invalid OP/OPC: '0f000000000000f00000000000f0000
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v3, .algo = COMP128v3,
.u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef',
} }
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1197,12 +1197,12 @@ DAUC Cannot update auth tokens: Invalid K: '000000000000f00000000000f000000'
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v3, .algo = COMP128v3,
.u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef',
} }
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1217,12 +1217,12 @@ DAUC Cannot update auth tokens: Invalid ind_bitlen: 29
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v3, .algo = COMP128v3,
.u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef',
} }
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1237,12 +1237,12 @@ DAUC Cannot update auth tokens: Invalid OP/OPC: 'X000000000000f00000000000f00000
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v3, .algo = COMP128v3,
.u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef',
} }
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1257,12 +1257,12 @@ DAUC Cannot update auth tokens: Invalid K: 'f000000000000 f00000000000 f000000'
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
2G: struct osmo_sub_auth_data2 { 2G: struct osmo_sub_auth_data {
.type = GSM, .type = GSM,
.algo = COMP128v3, .algo = COMP128v3,
.u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef',
} }
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1338,7 +1338,7 @@ sqlite3_prepare_v2(dbc->db, sql, -1, &stmt, NULL) --> SQLITE_OK
sqlite3_step(stmt) --> SQLITE_DONE sqlite3_step(stmt) --> SQLITE_DONE
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -ENOKEY db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -ENOKEY
DAUC IMSI='123456789000000': Error reading Ki, expected min length 16 but has length 15 DAUC IMSI='123456789000000': Error reading Ki, expected length 16 but has length 15
DAUC IMSI='123456789000000': No 3G Auth Data DAUC IMSI='123456789000000': No 3G Auth Data
@@ -1359,7 +1359,7 @@ sqlite3_step(stmt) --> SQLITE_DONE
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -5 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -5
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': Error reading K, expected min length 16 but has length 15 DAUC IMSI='123456789000000': Error reading K, expected length 16 but has length 15
@@ -1374,7 +1374,7 @@ sqlite3_step(stmt) --> SQLITE_DONE
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -5 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -5
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': Error reading OP, expected min length 16 but has length 15 DAUC IMSI='123456789000000': Error reading OP, expected length 16 but has length 15
@@ -1389,7 +1389,7 @@ sqlite3_step(stmt) --> SQLITE_DONE
db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -5 db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -5
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
DAUC IMSI='123456789000000': Error reading OPC, expected min length 16 but has length 15 DAUC IMSI='123456789000000': Error reading OPC, expected length 16 but has length 15
@@ -1458,7 +1458,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1477,7 +1477,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1495,7 +1495,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1513,7 +1513,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1534,7 +1534,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1550,7 +1550,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1568,7 +1568,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',
@@ -1586,7 +1586,7 @@ db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 0
DAUC IMSI='123456789000000': No 2G Auth Data DAUC IMSI='123456789000000': No 2G Auth Data
2G: none 2G: none
3G: struct osmo_sub_auth_data2 { 3G: struct osmo_sub_auth_data {
.type = UMTS, .type = UMTS,
.algo = MILENAGE, .algo = MILENAGE,
.u.umts.opc = 'beefedcafefaceacedaddeddecadefee', .u.umts.opc = 'beefedcafefaceacedaddeddecadefee',

View File

@@ -4,6 +4,9 @@ OsmoHLR# subscriber imsi 123456789012345 create
ID: 1 ID: 1
IMSI: 123456789012345 IMSI: 123456789012345
MSISDN: none MSISDN: none
GERAN-A: allowed
UTRAN-Iu: allowed
EUTRAN-SGs: allowed
OsmoHLR# subscriber imsi 123456789012345 update msisdn 098765432109876 OsmoHLR# subscriber imsi 123456789012345 update msisdn 098765432109876
% Updated subscriber IMSI='123456789012345' to MSISDN='098765432109876' % Updated subscriber IMSI='123456789012345' to MSISDN='098765432109876'
OsmoHLR# subscriber imsi 123456789012345 update aud2g comp128v1 ki BeefedCafeFaceAcedAddedDecadeFee OsmoHLR# subscriber imsi 123456789012345 update aud2g comp128v1 ki BeefedCafeFaceAcedAddedDecadeFee
@@ -13,11 +16,17 @@ OsmoHLR# subscriber imsi 111111111 create
ID: 2 ID: 2
IMSI: 111111111 IMSI: 111111111
MSISDN: none MSISDN: none
GERAN-A: allowed
UTRAN-Iu: allowed
EUTRAN-SGs: allowed
OsmoHLR# subscriber imsi 222222222 create OsmoHLR# subscriber imsi 222222222 create
% Created subscriber 222222222 % Created subscriber 222222222
ID: 3 ID: 3
IMSI: 222222222 IMSI: 222222222
MSISDN: none MSISDN: none
GERAN-A: allowed
UTRAN-Iu: allowed
EUTRAN-SGs: allowed
OsmoHLR# subscriber imsi 222222222 update msisdn 22222 OsmoHLR# subscriber imsi 222222222 update msisdn 22222
% Updated subscriber IMSI='222222222' to MSISDN='22222' % Updated subscriber IMSI='222222222' to MSISDN='22222'
OsmoHLR# subscriber imsi 333333 create OsmoHLR# subscriber imsi 333333 create
@@ -25,6 +34,9 @@ OsmoHLR# subscriber imsi 333333 create
ID: 4 ID: 4
IMSI: 333333 IMSI: 333333
MSISDN: none MSISDN: none
GERAN-A: allowed
UTRAN-Iu: allowed
EUTRAN-SGs: allowed
OsmoHLR# subscriber imsi 333333 update msisdn 3 OsmoHLR# subscriber imsi 333333 update msisdn 3
% Updated subscriber IMSI='333333' to MSISDN='3' % Updated subscriber IMSI='333333' to MSISDN='3'
OsmoHLR# subscriber imsi 333333 update aud2g comp128v2 ki 33333333333333333333333333333333 OsmoHLR# subscriber imsi 333333 update aud2g comp128v2 ki 33333333333333333333333333333333
@@ -33,6 +45,9 @@ OsmoHLR# subscriber imsi 444444444444444 create
ID: 5 ID: 5
IMSI: 444444444444444 IMSI: 444444444444444
MSISDN: none MSISDN: none
GERAN-A: allowed
UTRAN-Iu: allowed
EUTRAN-SGs: allowed
OsmoHLR# subscriber imsi 444444444444444 update msisdn 4444 OsmoHLR# subscriber imsi 444444444444444 update msisdn 4444
% Updated subscriber IMSI='444444444444444' to MSISDN='4444' % Updated subscriber IMSI='444444444444444' to MSISDN='4444'
OsmoHLR# subscriber imsi 444444444444444 update aud3g milenage k 44444444444444444444444444444444 op 44444444444444444444444444444444 OsmoHLR# subscriber imsi 444444444444444 update aud3g milenage k 44444444444444444444444444444444 op 44444444444444444444444444444444
@@ -41,7 +56,10 @@ OsmoHLR# subscriber imsi 5555555 create
ID: 6 ID: 6
IMSI: 5555555 IMSI: 5555555
MSISDN: none MSISDN: none
GERAN-A: allowed
UTRAN-Iu: allowed
EUTRAN-SGs: allowed
OsmoHLR# subscriber imsi 5555555 update msisdn 55555555555555 OsmoHLR# subscriber imsi 5555555 update msisdn 55555555555555
% Updated subscriber IMSI='5555555' to MSISDN='55555555555555' % Updated subscriber IMSI='5555555' to MSISDN='55555555555555'
OsmoHLR# subscriber imsi 5555555 update aud2g xor-2g ki 55555555555555555555555555555555 OsmoHLR# subscriber imsi 5555555 update aud2g xor ki 55555555555555555555555555555555
OsmoHLR# subscriber imsi 5555555 update aud3g milenage k 55555555555555555555555555555555 opc 55555555555555555555555555555555 OsmoHLR# subscriber imsi 5555555 update aud3g milenage k 55555555555555555555555555555555 opc 55555555555555555555555555555555

View File

@@ -12,7 +12,7 @@ Table auc_2g contents:
algo_id_2g|ki|subscriber_id algo_id_2g|ki|subscriber_id
1|BeefedCafeFaceAcedAddedDecadeFee|1 1|BeefedCafeFaceAcedAddedDecadeFee|1
2|33333333333333333333333333333333|4 2|33333333333333333333333333333333|4
6|55555555555555555555555555555555|6 4|55555555555555555555555555555555|6
Table: auc_3g Table: auc_3g
name|type|notnull|dflt_value|pk name|type|notnull|dflt_value|pk
@@ -87,6 +87,7 @@ DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 4
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 5 DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 5
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 6 DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 6
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 7 DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 7
DDB Database <PATH>test.db' has been upgraded to HLR DB schema version 8
DMAIN Cmdline option --db-check: Database was opened successfully, quitting. DMAIN Cmdline option --db-check: Database was opened successfully, quitting.
Resulting db: Resulting db:
@@ -101,15 +102,15 @@ Table auc_2g contents:
algo_id_2g|ki|subscriber_id algo_id_2g|ki|subscriber_id
1|BeefedCafeFaceAcedAddedDecadeFee|1 1|BeefedCafeFaceAcedAddedDecadeFee|1
2|33333333333333333333333333333333|4 2|33333333333333333333333333333333|4
6|55555555555555555555555555555555|6 4|55555555555555555555555555555555|6
Table: auc_3g Table: auc_3g
name|type|notnull|dflt_value|pk name|type|notnull|dflt_value|pk
algo_id_3g|INTEGER|1||0 algo_id_3g|INTEGER|1||0
ind_bitlen|INTEGER|1|5|0 ind_bitlen|INTEGER|1|5|0
k|VARCHAR(64)|1||0 k|VARCHAR(32)|1||0
op|VARCHAR(64)|0||0 op|VARCHAR(32)|0||0
opc|VARCHAR(64)|0||0 opc|VARCHAR(32)|0||0
sqn|INTEGER|1|0|0 sqn|INTEGER|1|0|0
subscriber_id|INTEGER|0||1 subscriber_id|INTEGER|0||1
@@ -134,6 +135,8 @@ id|INTEGER|0||1
imei|VARCHAR(14)|0||0 imei|VARCHAR(14)|0||0
imeisv|VARCHAR|0||0 imeisv|VARCHAR|0||0
imsi|VARCHAR(15)|1||0 imsi|VARCHAR(15)|1||0
last_lu_rat_cs|TEXT|0|NULL|0
last_lu_rat_ps|TEXT|0|NULL|0
last_lu_seen|TIMESTAMP|0|NULL|0 last_lu_seen|TIMESTAMP|0|NULL|0
last_lu_seen_ps|TIMESTAMP|0|NULL|0 last_lu_seen_ps|TIMESTAMP|0|NULL|0
lmsi|INTEGER|0||0 lmsi|INTEGER|0||0
@@ -153,13 +156,13 @@ vlr_number|VARCHAR(15)|0||0
vlr_via_proxy|VARCHAR|0||0 vlr_via_proxy|VARCHAR|0||0
Table subscriber contents: Table subscriber contents:
ggsn_number|gmlc_number|id|imei|imeisv|imsi|last_lu_seen|last_lu_seen_ps|lmsi|ms_purged_cs|ms_purged_ps|msc_number|msisdn|nam_cs|nam_ps|periodic_lu_tmr|periodic_rau_tau_tmr|sgsn_address|sgsn_number|sgsn_via_proxy|smsc_number|vlr_number|vlr_via_proxy ggsn_number|gmlc_number|id|imei|imeisv|imsi|last_lu_rat_cs|last_lu_rat_ps|last_lu_seen|last_lu_seen_ps|lmsi|ms_purged_cs|ms_purged_ps|msc_number|msisdn|nam_cs|nam_ps|periodic_lu_tmr|periodic_rau_tau_tmr|sgsn_address|sgsn_number|sgsn_via_proxy|smsc_number|vlr_number|vlr_via_proxy
||1|||123456789012345||||0|0||098765432109876|1|1|||||||MSC-1| ||1|||123456789012345||||||0|0||098765432109876|1|1|||||||MSC-1|
||2|||111111111||||1|0|||1|1|||||||| ||2|||111111111||||||1|0|||1|1||||||||
||3|||222222222||||0|1||22222|1|1|||||||| ||3|||222222222||||||0|1||22222|1|1||||||||
||4|||333333||||0|0||3|0|1|||||||| ||4|||333333||||||0|0||3|0|1||||||||
||5|||444444444444444||||0|0||4444|1|0|||||||| ||5|||444444444444444||||||0|0||4444|1|0||||||||
||6|||5555555||||0|0||55555555555555|0|0|||||||| ||6|||5555555||||||0|0||55555555555555|0|0||||||||
Table: subscriber_apn Table: subscriber_apn
name|type|notnull|dflt_value|pk name|type|notnull|dflt_value|pk
@@ -175,10 +178,18 @@ subscriber_id|INTEGER|0||0
Table subscriber_multi_msisdn contents: Table subscriber_multi_msisdn contents:
Table: subscriber_rat
name|type|notnull|dflt_value|pk
allowed|BOOLEAN|1|0|0
rat|TEXT|1||0
subscriber_id|INTEGER|0||0
Table subscriber_rat contents:
Verify that osmo-hlr can open it: Verify that osmo-hlr can open it:
osmo-hlr --database $db --db-check --config-file $srcdir/osmo-hlr.cfg osmo-hlr --database $db --db-check --config-file $srcdir/osmo-hlr.cfg
rc = 0 rc = 0
DMAIN hlr starting DMAIN hlr starting
DDB using database: <PATH>test.db DDB using database: <PATH>test.db
DDB Database <PATH>test.db' has HLR DB schema version 7 DDB Database <PATH>test.db' has HLR DB schema version 8
DMAIN Cmdline option --db-check: Database was opened successfully, quitting. DMAIN Cmdline option --db-check: Database was opened successfully, quitting.

View File

@@ -61,7 +61,7 @@ CREATE TABLE auc_2g (
); );
INSERT INTO auc_2g VALUES(1,1,'BeefedCafeFaceAcedAddedDecadeFee'); INSERT INTO auc_2g VALUES(1,1,'BeefedCafeFaceAcedAddedDecadeFee');
INSERT INTO auc_2g VALUES(4,2,'33333333333333333333333333333333'); INSERT INTO auc_2g VALUES(4,2,'33333333333333333333333333333333');
INSERT INTO auc_2g VALUES(6,6,'55555555555555555555555555555555'); INSERT INTO auc_2g VALUES(6,4,'55555555555555555555555555555555');
CREATE TABLE auc_3g ( CREATE TABLE auc_3g (
subscriber_id INTEGER PRIMARY KEY, -- subscriber.id subscriber_id INTEGER PRIMARY KEY, -- subscriber.id
algo_id_3g INTEGER NOT NULL, -- enum osmo_auth_algo value algo_id_3g INTEGER NOT NULL, -- enum osmo_auth_algo value

View File

@@ -1,9 +1,9 @@
AM_CPPFLAGS = \ AM_CPPFLAGS = \
$(all_includes) \ $(all_includes) \
-I$(top_srcdir)/include \
$(NULL) $(NULL)
AM_CFLAGS = \ AM_CFLAGS = \
-I$(top_srcdir)/include \
$(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \
$(NULL) $(NULL)
@@ -17,7 +17,7 @@ EXTRA_DIST = \
gsup_test.err \ gsup_test.err \
$(NULL) $(NULL)
check_PROGRAMS = \ noinst_PROGRAMS = \
gsup_test \ gsup_test \
$(NULL) $(NULL)

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More