Compare commits

...

48 Commits

Author SHA1 Message Date
Keith Whyte
945cc067b3 Split REJect cause by CN DOMAIN
Allow using a distinct Reject Cause for CS and PS Domain.

This breaks any existing config that defines custom reject-cause

TODO: Add Alias or Hidden function or whatever to handle old config.
2025-08-27 22:23:58 +01:00
Keith
ff058aac73 Add the VLR to the subscribers last-seen vty command
It's useful to see WHERE the subcriber was last seen in a
distributed GSM configuration

Change-Id: I658e42c965a95b23675d4333d262522206fd24c4
2025-08-27 22:18:53 +01:00
Keith
caee387b7e Add a time limit to show subscribers last-seen
There was a comment in gerrit I7f0573381a6d0d13841ac6d42d50f0e8389decf4
mentioning concern that the show subscriber commands I added had no limits
I see now my mistake.

Change-Id: I170e1025d534881281379efee0f5f8f51b88fc06
2025-08-27 22:18:53 +01:00
Keith
7dbe2b1fa4 Make local attach max age VTY configurable
Change-Id: I14d1e6489001529ac2c96ddbf41605c33b05283b
2025-08-27 22:18:53 +01:00
Keith
081990e4ff VTY: Add proxy inspection and delete
Change-Id: I5d990b1c765e2bfb762012f0924e68c2856804bc
2025-08-27 22:18:53 +01:00
Keith
b8fbb709fa Implement subscriber-create-on-demand fallback
If the mslookup client gets no response with the configured
timeout, then go ahead and create a local subscriber if
subscriber-create-on-demand is so configured.

Change-Id: I3755baa393f6cc6b766abf90ba0f3a062e4c0f5f
2025-08-27 22:18:53 +01:00
Keith
b3f6229953 DGSM: Add ignore-created-on-demand option
When a subscriber is create(d)-on-demand (assuming configuration
does not grant CS/PS access by default), then the following will
be true:

* The subscriber will never have connected, and therefore
have no vlr_number entry.
* The msisdn length will be the length configured in create-on-demand.
* The subscriber will have no CS/PS access.

Let's use these three conditions to 'detect' subscribers than
have beeen created on demand, and ignore them in both the case
of an incoming mslookup, and a local GSUP request.

Change-Id: I40d40467316c360bcbd50d50cb2e52a38e718eac
2025-08-27 22:18:53 +01:00
Keith
9e2beaffc2 mslookup: Add a flag to not stop on receive age: zero
This allows using the DGSM system to search for duplicate IMSI entries across all HLRs

Change-Id: Ic69d6bc9834099ba3a7f3b6f8334b5eac90f2897
2025-08-27 22:18:53 +01:00
Keith
1c84633350 D-GSM: Add a query type for authorized imsi.
Adds the possibility to use the DGSM/mDNS system to
search all HLRs for only IMSIs that have either CS or PS service

Change-Id: I54c469d34c3cd1bfd9a72a6c751ae183606d75ec
2025-08-27 22:18:53 +01:00
Keith
5225de1474 DGSM: add option to respond only for authorized IMSIs
Adds a vty configuration option to the mslookup server
which instructs the server to only reposnd to gsup.hlr
queries for IMSIs that are both known AND have either
Circuit Switched or Packet Switched access enabled.

This helps in the case of so-called 'evil twin' IMSI
entries, where, possibly due to subscriber-create-on-demand,
as a result of a Location Update at a time when the home
HLR was not reachable, the local hlr has an entry for an
IMSI that in reality belongs to another HLR.

NOTE: Network-wide concurrent use of subscriber-create-on-demand
and the mslookup client is not currently possible, as the
mslookup client has no fallback option to create-on-demand.
A future commit, however will implement this fallback,
thereby completing support for D-GSM with create-on-demand.

Change-Id: I2643d6c93289ec0835fa1c00e275d627914775bc
2025-08-27 22:18:53 +01:00
Oliver Smith
d4b4548589 Generate version.h files
Related: OS#6626
Change-Id: I716821b2426c28fc2500fa8cdf44b7b8cb339d65
2025-06-23 14:16:06 +02:00
Oliver Smith
bb7d5b2a61 Bump version: 1.9.0.2-1a71e → 1.9.1
Change-Id: Ib8bdcee7a6fa4ae9821d9748a8a7ea4aecbea62e
2025-04-02 15:12:36 +02:00
Oliver Smith
1a71e73fdc db: flush after changing schema version
Flush the cache after changing the version, to make the scenario less
likely that after an unclean shutdown the DB gets restored with the
right table layout but wrong version.

When that happens, osmo-hlr tries to upgrade the database layout again
and fails with errors like "duplicate column name".

Related: SYS#7394
Change-Id: I5943a1cb3447c038c6e7972f68f9656b73a9248b
2025-03-17 14:43:12 +01:00
Pau Espin Pedrol
bb8591dfab Drop use of deprecated vty is_config_node() cb
This callback was drepecated and is not ever called since
libosmocore.git 70ce871532ab21955e0955d7e230eae65438f047 (release 1.3.0).

See also libosmocore.git d31de237582f6fe3315d61bb9a488d4cda92654e.

Change-Id: I0a77ff4c0a880230116a361651c78c77328db989
2025-03-04 18:21:43 +01:00
Oliver Smith
521fe02d41 Bump version: 1.8.0.15-74e4-dirty → 1.9.0
Change-Id: I1acb428ed42dd2217a734bedd286bbc60d0cc526
2025-02-12 12:17:53 +01:00
Pau Espin Pedrol
74e413c014 jenkins.sh: Use --disable-doxygen configure param
Change-Id: If5213e1080e22ad1fe87c61582a9cc4c2c164174
2024-12-10 22:32:01 +00:00
Pau Espin Pedrol
b08796d279 Drop unneeded use of abis/ipa.h header
Direct include of abis/ipa.h is not needed there, remove it to ease
later deprecation of such header.

Change-Id: I72d0787d4f5641a6a2fbe51427ac490120cbc616
2024-12-10 14:45:45 +01:00
Pau Espin Pedrol
35d4c5b376 gsup_client: Avoid double memset 0
Memory is already zeroed during talloc_zero, no need to reset it to
zero.

Change-Id: I66515fc893ffc64a36abedc01a75b57aed7e932d
2024-12-04 14:39:29 +01:00
Pau Espin Pedrol
558160be4b gsup_client: Add new APIs to avoid users accessing struct fields
This is a first step towards changing gsup_client implementation to use
an osmo_stream_cli instead of libosmo-abis' ipa_client_conn.
The libosmo-abis' ipa_client_conn will eventually be deprecated together
with all IPA related code in libosmo-abis.

In order to be able to make the implementation change, we first need to
make sure no users are using the struct fields of gsup_client (this
patch); 2nd step will be making the struct private by moving it to a
private header; 3rd step will be changing the implementation to use
osmo_stream.

Related: OS#5896
Change-Id: I401af83232022f1c141eef1f428cbe206a8aaaa2
2024-12-04 14:32:19 +01:00
Pau Espin Pedrol
e9dc7eaeda Drop use of libosmo-abis osmocom/abis/ipaccess.h
That header is only really used to provide an old hack for
ipaccess-proxy tool in openbsc.h/osmo-bsc.h, and will be deprecated
soon.

Take the chance to organize a bit better the includes based on dependency
chain.

Change-Id: I393cd23a09af6d1eefa68dc955415f4fe334d71b
2024-11-29 18:52:06 +01:00
Pau Espin Pedrol
19929d873b jenkins.sh: libosmo-netif no longer depends on libosmo-abis
Change-Id: I98b233e2ebb209803be5b6523bb1154ca694041c
Depends: libosmo-abis.git Change-Id I079dc3999de508301dd37ed03e399356a58d3cab
Depends: libosmo-netif.git Change-Id I13d6e88158f6d9ce017986283183ee9c2cc68cae
2024-11-21 14:49:03 +01:00
Mychaela N. Falconia
ada985a0ea change default no-proxy reject cause to net-fail
If LU has to be rejected because DGSM mechanism cannot reach the
needed HLR, then returning reject cause #17 (Network Failure)
is the most accurate and truthful description of what is actually
happening.

Reject cause #2 (IMSI unknown in HLR) is unlikely to be a good
choice: in areas where regular commercial cell services are present
alongside with experimental/adventurous Osmocom-based GSM networks,
this reject cause is acutely dangerous as described in the previous
commit, and even in areas where no regular commercial cell services
are present (the presumed intended use case for DGSM), the behavior
of commanding LU-failing phones "please don't ever again try to
connect to ANY network until the user power-cycles you" is unlikely
to be correct/desirable.  If that behavior _is_ desired, it should
be configured explicitly, not by default.

Change-Id: I557ae1d3291066a87228b5db4f2e207ea721329c
2024-11-21 03:33:38 +00:00
Mychaela N. Falconia
6b5616fd36 change default reject cause to plmn-not-allowed
Unless the Osmocom-based network operator has very carefully
considered what they are doing, returning MM/GMM reject cause #2
(IMSI unknown in HLR) to LU requests from bystander phones is a
very bad idea.  Here is what typically happens when someone sets
up an Osmocom-based GSM network for development, testing or
research, without roaming interconnection with any commercial
operators:

* Even when the private network operates its BTS at very low power
  levels, bystander phones in close proximity (e.g., in directly
  adjacent neighbor apartments or office suites) will receive a much
  stronger signal from the private/test network BTS than from any
  commercial operator, i.e., the private/test network will be
  the strongest signal.

* Many phones will attempt to "jump ship" to this strongest signal
  even if it broadcasts a completely unknown PLMN ID that is not in
  the preferred operator list, and even when they were previously
  not roaming at all, registered to their most preferred home operator
  with perfectly good reception quality.

* If these wayward phones receive reject cause #2 in response to their
  LU attempt, the spec effectively requires them to go into a "black
  hole" where they no longer attempt to register to any operator,
  including their own legitimate one.

Returning reject cause #11 instead (PLMN not allowed) solves this
problem: wayward phones that erroneously attempted to register to the
private/test network go back to their own legitimate commercial
operator, and everyone is happy.

This bug should be considered critical: when the reject cause is set
to #2 by default, any private Osmocom-based network, no matter how
low-power, will effectively cause service disruption to all nearby
commercially-served phones even when there is no clash in terms of
used radio frequencies.

Change-Id: Icff1d19670c398b119ec68b1d5f0fad87b605702
2024-11-21 03:33:38 +00:00
Mychaela N. Falconia
96dab5f886 vty: always emit reject-cause lines in saved config
When reject-cause (not-found|no-proxy) setting was added, it was
implemented such that the new setting is not written into saved
config files if it equals the default of 'imsi-unknown'.  Change
this behavior to always write out the current setting for each of
not-found and no-proxy, whether or not they match compiled-in
defaults.

Change-Id: I18708fc1c08a85e98582c97ec59dd3822a0767fb
2024-11-21 03:33:38 +00:00
Pau Espin Pedrol
db2f1f65da gsup: Replace deprecated ipa_msg_push_header()
Use the libosmocore API, which does the same except setting the l2h
pointer, which we don't really need here.

Change-Id: I7a4d05a0b09032d8133d8775fb4707a4da8f0cf3
2024-11-19 14:47:44 +01:00
Alexander Couzens
0451b13e0d debian/copyright: add gsup_client under GPLv2+
Change-Id: Idddcd6356af76987bf57ecd21096299cb2ba896f
2024-10-13 23:43:57 +02:00
Alexander Couzens
26c82ba2be gsupclient: add missing SPDX line
SPDX is a spec to describe the license of a file
in a machine readable format.

Change-Id: Iaded691cc0eb2f5dd85efd1915ddef1a747fe518
2024-10-13 23:39:26 +02:00
Alexander Couzens
e1aed27178 gsupclient: Introduce gsup_client_mux
GSUP supports different message classes (e.g. SMS, USSD, Subscriber management),
within a daemon, the messages are usually handled in different parts of it.
The gsup client mux allow to place callbacks for the different message class.

This code original comes from the osmo-msc code and is here
re-licensed under the GPLv2.

Change-Id: I916c1bb6cfc48f92fc5db2b5c914eb1b4e25fa7f
2024-10-13 23:31:45 +02:00
Vadim Yanitskiy
7c06eea5b2 subscriber-create-on-demand: add mode for MSISDN=IMSI
In may be desirable for some users to assign deterministic MSISDNs
when subscriber-create-on-demand is enabled.  This can be achieved
by assigning MSISDN=IMSI.  This commit adds a new mode for that.

Change-Id: I3470492f5e46de7246d9a74e80c37f80f455d851
2024-08-22 02:32:07 +07:00
Vadim Yanitskiy
d4693f652a subscriber-create-on-demand: rework configuration
This commit prepares for adding a new subscriber-create-on-demand
mode (MSISDN=IMSI), which is implemented in a follow-up patch.

* add an enumerated type for subscriber-create-on-demand mode;
* store the related parameters in an anonymous structure.

Change-Id: Ib553172655f83dad1ac0e0254615c8c207d79ca9
2024-08-07 16:20:14 +07:00
Oliver Smith
c819234a42 Bump version: 1.7.0.17-bae13 → 1.8.0
Change-Id: I3b05d5ac5417f5c995e7057474adba72326ebf11
2024-07-24 16:22:50 +02:00
Oliver Smith
bae137d9eb mslookup: don't ignore return value of write()
Fix GCC 13.2.0 refuses to build this with -Werror:

src/mslookup/osmo-mslookup-client.c: In function 'socket_client_respond_result':
src/osmo-hlr/src/mslookup/osmo-mslookup-client.c:432:9: error: ignoring return value of 'write' declared with attribute 'warn_unused_result' [-Werror=unused-result]
  432 |         write(c->ofd.fd, response, strlen(response));
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/mslookup/osmo-mslookup-client.c: In function 'socket_accept':
src/osmo-hlr/src/mslookup/osmo-mslookup-client.c:530:17: error: ignoring return value of 'write' declared with attribute 'warn_unused_result' [-Werror=unused-result]
  530 |                 write(c->ofd.fd, CSV_HEADERS, strlen(CSV_HEADERS));
      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Change-Id: I4b2e5cabe5306a999197f7c98362d872a3739078
2024-07-03 10:45:25 +02:00
Vadim Yanitskiy
413f5e3670 README.md: cosmetic: fix a typo
Change-Id: Idd4270a74c2a9921943606c1157343f4756b1643
2024-06-05 18:34:07 +07:00
Oliver Smith
6b771e34bd debian/postinst: add checks, be verbose
Do not attempt to change permissions/ownership if the package gets
upgraded from a version higher than the next release.

Do not fail if the user deleted the config file.

Be verbose when changing permissions.

Related: OS#4107
Change-Id: I1bcbe414fd18101e4d875a16539deab7baf9cb5f
2024-05-15 11:47:40 +00:00
Oliver Smith
a005ad6495 contrib: remove rpm spec file
Related: https://osmocom.org/news/255
Related: OS#6446
Change-Id: Idd67d52ca736c4e145387ea8d4030f9cf4b9596d
2024-05-08 14:40:58 +02:00
Oliver Smith
e391c4c58d .deb/.rpm: various fixes related to non-root
* Explicitly chown /var/lib/osmocom to osmocom:osmocom, instead of
  relying on systemd to do it when the service starts up. This does not
  work with the systemd versions in debian 10 and almalinux 8.
* deb: Use "useradd" instead of the interactive "adduser" perl script
  from Debian. This makes it consistent with how we do it in rpm, and
  avoids the dependency on "adduser".
* deb: Remove support for the "dpkg-statoverride --list" logic. This
  seems to be a rather obscure feature to override permissions for
  certain files or directories. Let's rather remove this complexity to
  make the postinst script more maintainable and more similar to the
  rpm spec file. If users need this, they can achieve something similar
  by using their own Osmocom config file in a different path with
  different permissions.
* deb: Consistently use tabs throughout postinst, instead of mixing
  tabs and spaces.

Related: OS#4107
Change-Id: Ib20406dd253f5e8720552e92e9002e45591218fa
2024-04-26 15:07:32 +02:00
Max
a26abc6aa8 .deb/.rpm: add osmocom user during package install
Created osmocom user & group during package installation.
Fix the configuration dir/files permission to match.

Related: OS#4107
Tweaked-By: Oliver Smith <osmith@sysmocom.de>
Change-Id: I625c993ab03dfe32976c651acca9c35c33a768e7
2024-04-24 11:52:09 +02:00
Harald Welte
da4fc0eab9 README.md: Add Forum and Issue Tracker sections
Change-Id: Ic88d30fc8952762565af6115c8bb7d67fa7d7866
2024-03-23 17:28:12 +01:00
Harald Welte
507315eed8 README.md: Improve mark-down formatting
Change-Id: Ib608ec568572af672934d1e2960e1dabc3a2a45c
2024-03-23 17:26:19 +01:00
Harald Welte
72f92e28c9 Add funding link to github mirror
see https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository

Change-Id: I935ec923362fd8d3f03a8db8e93e033f86f0a5ac
2024-03-23 17:22:45 +01:00
Vadim Yanitskiy
ac1365fddf build: include {README.md,git-version-gen} into the release tarball
Change-Id: I072b5899ac77326f619d351f17aa78490946f452
2024-01-26 23:33:48 +07:00
Mychaela N. Falconia
be8bcd30eb ctrl: add subscriber.by-*.imsi GET-able variable
There may be a need in various OsmoCNI-attached entities (for example,
external SMSC implementations) to perform a mapping from known MSISDN
to unknown IMSI, querying OsmoHLR subscriber db for it.  Querying for
subscriber.by-msisdn-*.imsi will be much more efficient (and easier on
client-side implementors) than querying for subscriber.by-msisdn-*.info
and fishing the IMSI out of the long multiline response, discarding all
other irrelevant info.

Related: OS#6312
Change-Id: Icea1a74d0c664047f46758ab4ad75508782f3d12
2023-12-17 21:47:21 +00:00
Andreas Eversberg
947e137918 Use uniform log format for default config files
Related: OS#6272
Change-Id: I4319b688286845d2ffbd944e51e9cc2e5159563c
2023-12-01 12:41:19 +01:00
Mychaela N. Falconia
e513c43857 SMS over GSUP: handle READY-FOR-SM.req from MSCs
When an MS indicates that it is ready to receive MT SMS, the MSC will
send us a READY-FOR-SM.req message.  Handle it by sending copies of
the same message to all connected SMSCs and returning OK result
to the MS that indicates its ready status.

Related: OS#6135
Change-Id: I731545a3a0d0804289e24a7769e13bfd3f645132
2023-09-21 02:06:58 +00:00
Mychaela N. Falconia
f6a303c669 SMS over GSUP: implement forwarding of MT SMS
When an SMSC tries to deliver an SM to a subscriber, it will send us
an MT-forwardSM.req GSUP message.  We look up the subscriber by IMSI
and see if they are attached to a VLR.  If the subscriber is attached,
we forward the message to the MSC/VLR, otherwise return an error
to the SMSC.

Related: OS#6135
Change-Id: Ib3551bf7839690606c677461758c5cfef5f0aa7b
2023-09-21 02:06:49 +00:00
Mychaela N. Falconia
786ec5b91c SMS over GSUP: implement forwarding of MO SMS
MO-forwardSM.req messages are now forwarded to a connected SMSC
based on the SMSC address (SM-RP-DA) in the MO SM and the vty-defined
mapping from SMSC numeric addresses to IPA names.

Related: OS#6135
Depends: Iea5c29909c5be80f81dbbc2873656ff5cf590a5d (libosmocore)
Change-Id: Iaad4531922c41583d261c79f42561a1bdbe03521
2023-09-21 02:05:15 +00:00
Mychaela N. Falconia
ff7c7ea085 SMS over GSUP: implement vty config of SMSC routing
At the user-visible level (advanced settings menus on phones,
GSM 07.05 AT commands, SIM programming) each SMSC is identified
by a numeric address that looks like a phone number, originally
meant to be a Global Title.  OsmoMSC passes these SMSC addresses
through as-is to MO-forwardSM.req GSUP message - however, SMSCs
that connect to OsmoHLR via GSUP identify themselves by their
IPA names instead.  Hence we need a mapping mechanism in OsmoHLR
config.

To accommodate different styles of network design ranging from
strict recreation of classic GSM architecture to guest roaming
arrangements, a two-level configuration is implemented, modeled
after EUSE/USSD configuration: first one defines which SMSCs exist
as entities, identified only by their IPA names, and then one
defines which numeric SMSC address (in SM-RP-DA) should go to which
configured SMSC, with the additional possibility of a default route.

Related: OS#6135
Change-Id: I1624dcd9d22b4efca965ccdd1c74f0063a94a33c
2023-09-21 01:56:21 +00:00
Vadim Yanitskiy
fa6af8872f hlr_vty.c: drop redundant include of hlr_ussd.h
Change-Id: Ia873c3ea7fb76cb83628811f159e9d5f2de8dcbd
2023-09-17 02:04:05 +00:00
56 changed files with 1590 additions and 329 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
open_collective: osmocom

2
.gitignore vendored
View File

@@ -86,3 +86,5 @@ contrib/osmo-hlr.spec
/debian/osmo-mslookup-utils/
/debian/*.log
/debian/*.substvars
include/osmocom/*/version.h

View File

@@ -11,8 +11,9 @@ SUBDIRS = \
EXTRA_DIST = \
.version \
contrib/osmo-hlr.spec.in \
README.md \
debian \
git-version-gen \
$(NULL)
AM_DISTCHECK_CONFIGURE_FLAGS = \

View File

@@ -1,8 +1,8 @@
osmo-hlr - Osmocom HLR Implementation
=====================================
This repository contains a C-language implementation of a GSM Home
Location Register (HLR). It is part of the
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.
@@ -10,13 +10,14 @@ 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.
it with [OsmoMSC](https://osmocom.org/projects/osmomsc/wiki),
[OsmoSGSN](https://osmocom.org/projects/osmosgsn/wiki) etc. -
but not directly with third party components.
Homepage
--------
The official homepage of the project is
https://osmocom.org/projects/osmo-hlr/wiki
The official homepage of the project is <https://osmocom.org/projects/osmo-hlr/wiki>.
GIT Repository
--------------
@@ -33,11 +34,18 @@ 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
Pre-rendered PDF versions of the current `master` can be found at
* [User Manual](https://ftp.osmocom.org/docs/latest/osmohlr-usermanual.pdf)
* [VTY Reference Manual for osmo-hlr](https://ftp.osmocom.org/docs/latest/osmohlr-vty-reference.pdf)
Forum
-----
We welcome any osmo-hlr related discussions in the
[Cellular Network Infrastructure -> 2G/3G Core Network](https://discourse.osmocom.org/c/cni/2g-3g-cn)
section of the osmocom discourse (web based Forum).
Mailing List
------------
@@ -50,16 +58,23 @@ Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
Issue Tracker
-------------
We use the [issue tracker of the osmo-hlr project on osmocom.org](https://osmocom.org/projects/osmo-hlr/issues) for
tracking the state of bug reports and feature requests. Feel free to submit any issues you may find, or help
us out by resolving existing issues.
Contributing
------------
Our coding standards are described at
https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
We us a gerrit based patch submission/review process for managing
We use a Gerrit based patch submission/review process for managing
contributions. Please see
https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for
<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
<https://gerrit.osmocom.org/#/q/project:osmo-hlr+status:open>

View File

@@ -1,9 +1,9 @@
# 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:
# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# 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, removed, or changed since the last update: c + 1:0:a.
# 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
#library what description / commit summary line

View File

@@ -41,11 +41,11 @@ PKG_PROG_PKG_CONFIG([0.20])
PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 2.0.0)
PKG_CHECK_MODULES(SQLITE3, sqlite3)
@@ -203,7 +203,6 @@ AC_OUTPUT(
contrib/Makefile
contrib/systemd/Makefile
contrib/dgsm/Makefile
contrib/osmo-hlr.spec
tests/Makefile
tests/auc/Makefile
tests/auc/gen_ts_55_205_test_sets/Makefile

View File

@@ -29,7 +29,8 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
export PATH="$inst/bin:$PATH"
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
osmo-build-dep.sh libosmocore "" --disable-doxygen
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
osmo-build-dep.sh libosmo-abis
# Additional configure options and depends

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

@@ -9,6 +9,8 @@ Type=simple
Restart=always
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-hlr -c /etc/osmocom/osmo-hlr.cfg -l /var/lib/osmocom/hlr.db
RestartSec=2
ProtectHome=true

70
debian/changelog vendored
View File

@@ -1,3 +1,73 @@
osmo-hlr (1.9.1) unstable; urgency=medium
[ Pau Espin Pedrol ]
* Drop use of deprecated vty is_config_node() cb
[ Oliver Smith ]
* db: flush after changing schema version
-- Oliver Smith <osmith@sysmocom.de> Wed, 02 Apr 2025 15:12:35 +0200
osmo-hlr (1.9.0) unstable; urgency=medium
[ Vadim Yanitskiy ]
* subscriber-create-on-demand: rework configuration
* subscriber-create-on-demand: add mode for MSISDN=IMSI
[ Alexander Couzens ]
* gsupclient: Introduce gsup_client_mux
* gsupclient: add missing SPDX line
* debian/copyright: add gsup_client under GPLv2+
[ Pau Espin Pedrol ]
* gsup: Replace deprecated ipa_msg_push_header()
* jenkins.sh: libosmo-netif no longer depends on libosmo-abis
* Drop use of libosmo-abis osmocom/abis/ipaccess.h
* gsup_client: Add new APIs to avoid users accessing struct fields
* gsup_client: Avoid double memset 0
* Drop unneeded use of abis/ipa.h header
* jenkins.sh: Use --disable-doxygen configure param
[ Mychaela N. Falconia ]
* vty: always emit reject-cause lines in saved config
* change default reject cause to plmn-not-allowed
* change default no-proxy reject cause to net-fail
-- Oliver Smith <osmith@sysmocom.de> Wed, 12 Feb 2025 12:17:52 +0100
osmo-hlr (1.8.0) unstable; urgency=medium
[ Vadim Yanitskiy ]
* hlr_vty.c: drop redundant include of hlr_ussd.h
* build: include {README.md,git-version-gen} into the release tarball
* README.md: cosmetic: fix a typo
[ Mychaela N. Falconia ]
* SMS over GSUP: implement vty config of SMSC routing
* SMS over GSUP: implement forwarding of MO SMS
* SMS over GSUP: implement forwarding of MT SMS
* SMS over GSUP: handle READY-FOR-SM.req from MSCs
* ctrl: add subscriber.by-*.imsi GET-able variable
[ Andreas Eversberg ]
* Use uniform log format for default config files
[ Harald Welte ]
* Add funding link to github mirror
* README.md: Improve mark-down formatting
* README.md: Add Forum and Issue Tracker sections
[ Max ]
* .deb/.rpm: add osmocom user during package install
[ Oliver Smith ]
* .deb/.rpm: various fixes related to non-root
* contrib: remove rpm spec file
* debian/postinst: add checks, be verbose
* mslookup: don't ignore return value of write()
-- Oliver Smith <osmith@sysmocom.de> Wed, 24 Jul 2024 15:29:12 +0200
osmo-hlr (1.7.0) unstable; urgency=medium
[ Oliver Smith ]

8
debian/control vendored
View File

@@ -7,12 +7,12 @@ Build-Depends: debhelper (>= 10),
dh-autoreconf,
autotools-dev,
python3-minimal,
libosmocore-dev (>= 1.9.0),
libosmo-abis-dev (>= 1.5.0),
libosmo-netif-dev (>= 1.4.0),
libosmocore-dev (>= 1.11.0),
libosmo-abis-dev (>= 2.0.0),
libosmo-netif-dev (>= 1.6.0),
libsqlite3-dev,
sqlite3,
osmo-gsm-manuals-dev (>= 1.5.0)
osmo-gsm-manuals-dev (>= 1.6.0)
Standards-Version: 3.9.6
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr

18
debian/copyright vendored
View File

@@ -6,6 +6,10 @@ Files: *
Copyright: 2016-2022 Sysmocom s. f. m. c. GmbH <info@sysmocom.de>
License: AGPL-3+
Files: src/gsupclient/*
Copyright: 2014-2016,2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
License: GPL-2+
License: AGPL-3+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@@ -19,3 +23,17 @@ License: AGPL-3+
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
License: GPL-2+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

38
debian/postinst vendored
View File

@@ -3,3 +3,41 @@
# 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
case "$1" in
configure)
# Create the osmocom group and user (if it doesn't exist yet)
if ! getent group osmocom >/dev/null; then
groupadd --system osmocom
fi
if ! getent passwd osmocom >/dev/null; then
useradd \
--system \
--gid osmocom \
--home-dir /var/lib/osmocom \
--shell /sbin/nologin \
--comment "Open Source Mobile Communications" \
osmocom
fi
# Fix permissions of previous (root-owned) install (OS#4107)
if dpkg --compare-versions "$2" le "1.8.0"; then
if [ -e /etc/osmocom/osmo-hlr.cfg ]; then
chown -v osmocom:osmocom /etc/osmocom/osmo-hlr.cfg
chmod -v 0660 /etc/osmocom/osmo-hlr.cfg
fi
if [ -d /etc/osmocom ]; then
chown -v root:osmocom /etc/osmocom
chmod -v 2775 /etc/osmocom
fi
mkdir -p /var/lib/osmocom
chown -R -v osmocom:osmocom /var/lib/osmocom
fi
;;
esac
# dh_installdeb(1) will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#

View File

@@ -1,4 +1,12 @@
# OsmoHLR example configuration for Distributed GSM (mslookup)
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
hlr
gsup
# For D-GSM roaming, osmo-hlr's GSUP must listen on a public IP:

View File

@@ -4,11 +4,11 @@
log stderr
logging filter all 1
logging color 1
logging print category 1
logging print category-hex 0
logging print level 1
logging print category 1
logging timestamp 0
logging print file basename last
logging print extended-timestamp 1
logging print level 1
logging level main notice
logging level db notice
logging level auc notice

View File

@@ -1,13 +1,25 @@
SUBDIRS = osmocom
osmocom/%/version.h: osmocom/%/version.h.tpl
$(AM_V_GEN)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)sed \
-e "s/{{VERSION}}/$$(echo '@VERSION@' | cut -d. -f1-3)/g" \
-e "s/{{VERSION_MAJOR}}/$$(echo '@VERSION@' | cut -d. -f1)/g" \
-e "s/{{VERSION_MINOR}}/$$(echo '@VERSION@' | cut -d. -f2)/g" \
-e "s/{{VERSION_PATCH}}/$$(echo '@VERSION@' | cut -d. -f3)/g" \
$< > $@
nobase_include_HEADERS = \
osmocom/gsupclient/cni_peer_id.h \
osmocom/gsupclient/gsup_client.h \
osmocom/gsupclient/gsup_client_mux.h \
osmocom/gsupclient/gsup_req.h \
osmocom/gsupclient/version.h \
osmocom/mslookup/mdns.h \
osmocom/mslookup/mdns_sock.h \
osmocom/mslookup/mslookup_client_fake.h \
osmocom/mslookup/mslookup_client.h \
osmocom/mslookup/mslookup_client_mdns.h \
osmocom/mslookup/mslookup.h \
osmocom/mslookup/version.h \
$(NULL)

View File

@@ -20,6 +20,9 @@
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/oap_client.h>
#include <osmocom/gsm/ipa.h>
@@ -40,6 +43,7 @@ typedef int (*osmo_gsup_client_read_cb_t)(struct osmo_gsup_client *gsupc, struct
typedef bool (*osmo_gsup_client_up_down_cb_t)(struct osmo_gsup_client *gsupc, bool up);
/* NOTE: THIS STRUCT IS CONSIDERED PRIVATE, AVOID ACCESSING ITS FIELDS! */
struct osmo_gsup_client {
const char *unit_name; /* same as ipa_dev->unit_name, for backwards compat */
@@ -99,3 +103,12 @@ int osmo_gsup_client_enc_send(struct osmo_gsup_client *gsupc,
const struct osmo_gsup_message *gsup_msg);
struct msgb *osmo_gsup_client_msgb_alloc(void);
void *osmo_gsup_client_get_data(const struct osmo_gsup_client *gsupc);
void osmo_gsup_client_set_data(struct osmo_gsup_client *gsupc, void *data);
const char *osmo_gsup_client_get_rem_addr(const struct osmo_gsup_client *gsupc);
uint16_t osmo_gsup_client_get_rem_port(const struct osmo_gsup_client *gsupc);
bool osmo_gsup_client_is_connected(const struct osmo_gsup_client *gsupc);
const struct ipaccess_unit *osmo_gsup_client_get_ipaccess_unit(const struct osmo_gsup_client *gsupc);

View File

@@ -0,0 +1,56 @@
/*
* (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <osmocom/gsm/gsup.h>
struct gsup_client_mux;
struct ipaccess_unit;
struct gsup_client_mux_rx_cb {
int (*func)(struct gsup_client_mux *gcm, void *data, const struct osmo_gsup_message *gsup_msg);
void *data;
};
/* A GSUP client shared between code paths for various GSUP Message Classes.
* The main task is to dispatch GSUP messages to code paths corresponding to the respective Message Class, i.e.
* subscriber management, SMS, SS/USSD and inter-MSC messaging.
* If a GSUP Message Class IE is present in the message, the received message is dispatched directly to the rx_cb entry
* for that Message Class. Otherwise, the Message Class is determined by a switch() on the Message Type.*/
struct gsup_client_mux {
struct osmo_gsup_client *gsup_client;
/* Target clients by enum osmo_gsup_message_class */
struct gsup_client_mux_rx_cb rx_cb[OSMO_GSUP_MESSAGE_CLASS_ARRAYSIZE];
};
struct gsup_client_mux *gsup_client_mux_alloc(void *talloc_ctx);
int gsup_client_mux_start(struct gsup_client_mux *gcm, const char *gsup_server_addr_str, uint16_t gsup_server_port,
struct ipaccess_unit *ipa_dev);
int gsup_client_mux_tx(struct gsup_client_mux *gcm, const struct osmo_gsup_message *gsup_msg);
void gsup_client_mux_tx_set_source(const struct gsup_client_mux *gcm, struct osmo_gsup_message *gsup_msg);
void gsup_client_mux_tx_error_reply(struct gsup_client_mux *gcm, const struct osmo_gsup_message *gsup_orig,
enum gsm48_gmm_cause cause);
int gsup_client_mux_rx(struct osmo_gsup_client *gsup_client, struct msgb *msg);

View File

@@ -0,0 +1,16 @@
#pragma once
#define LIBOSMO_GSUP_CLIENT_VERSION {{VERSION}}
#define LIBOSMO_GSUP_CLIENT_VERSION_STR "{{VERSION}}"
#define LIBOSMO_GSUP_CLIENT_VERSION_MAJOR {{VERSION_MAJOR}}
#define LIBOSMO_GSUP_CLIENT_VERSION_MINOR {{VERSION_MINOR}}
#define LIBOSMO_GSUP_CLIENT_VERSION_PATCH {{VERSION_PATCH}}
#define LIBOSMO_GSUP_CLIENT_VERSION_GREATER_EQUAL(major, minor, patch) \
(LIBOSMO_GSUP_CLIENT_VERSION_MAJOR > (major) || \
(LIBOSMO_GSUP_CLIENT_VERSION_MAJOR == (major) && \
LIBOSMO_GSUP_CLIENT_VERSION_MINOR > (minor)) || \
(LIBOSMO_GSUP_CLIENT_VERSION_MAJOR == (major) && \
LIBOSMO_GSUP_CLIENT_VERSION_MINOR == (minor) && \
LIBOSMO_GSUP_CLIENT_VERSION_PATCH >= (patch)))

View File

@@ -6,6 +6,7 @@ noinst_HEADERS = \
gsup_router.h \
gsup_server.h \
hlr.h \
hlr_sms.h \
hlr_ussd.h \
hlr_vty.h \
hlr_vty_subscr.h \

View File

@@ -40,6 +40,8 @@ enum stmt_idx {
DB_STMT_SET_LAST_LU_SEEN,
DB_STMT_SET_LAST_LU_SEEN_PS,
DB_STMT_EXISTS_BY_IMSI,
DB_STMT_EXISTS_AUTHORIZED_BY_IMSI,
DB_STMT_IS_CREATED_ON_DEMAND_BY_IMSI,
DB_STMT_EXISTS_BY_MSISDN,
DB_STMT_IND_ADD,
DB_STMT_IND_SELECT,
@@ -157,6 +159,8 @@ int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
int db_subscr_update_imei_by_imsi(struct db_context *dbc, const char* imsi, const char *imei);
int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi);
int db_subscr_authorized_by_imsi(struct db_context *dbc, const char *imsi);
int db_subscr_is_created_on_demand_by_imsi(struct db_context *dbc, const char *imsi, unsigned int msisdn_len);
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,

View File

@@ -25,6 +25,7 @@
#include <osmocom/gsupclient/cni_peer_id.h>
#include <osmocom/gsupclient/gsup_req.h>
#define OSMO_DGSM_DEFAULT_LOCAL_ATTACH_MAX_AGE 60 * 60
#define OSMO_DGSM_DEFAULT_RESULT_TIMEOUT_MS 2000
#define LOG_DGSM(imsi, level, fmt, args...) \
LOGP(DDGSM, level, "(IMSI-%s) " fmt, imsi, ##args)

View File

@@ -2,9 +2,9 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/msgb.h>
#include <osmocom/abis/ipa.h>
#include <osmocom/abis/ipaccess.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/abis/ipa.h>
#include <osmocom/gsupclient/cni_peer_id.h>
#include <osmocom/gsupclient/gsup_req.h>

View File

@@ -39,6 +39,13 @@ enum osmo_gsup_message_type;
extern struct osmo_tdef g_hlr_tdefs[];
enum subscr_create_on_demand_mode {
SUBSCR_COD_MODE_DISABLED = 0,
SUBSCR_COD_MODE_NO_MSISDN,
SUBSCR_COD_MODE_RAND_MSISDN,
SUBSCR_COD_MODE_MSISDN_FROM_IMSI,
};
struct hlr {
/* GSUP server pointer */
struct osmo_gsup_server *gs;
@@ -56,8 +63,16 @@ struct hlr {
struct llist_head euse_list;
struct hlr_euse *euse_default;
enum gsm48_gmm_cause reject_cause;
enum gsm48_gmm_cause no_proxy_reject_cause;
struct {
enum gsm48_gmm_cause cs;
enum gsm48_gmm_cause ps;
} reject_cause;
struct {
enum gsm48_gmm_cause cs;
enum gsm48_gmm_cause ps;
} no_proxy_reject_cause;
/* PS: APN default configuration used by Subscription Data on ISR */
struct {
struct {
@@ -74,12 +89,18 @@ struct hlr {
struct llist_head ss_sessions;
struct llist_head smsc_list;
struct llist_head smsc_routes;
struct hlr_smsc *smsc_default;
bool store_imei;
bool subscr_create_on_demand;
/* Bitmask of DB_SUBSCR_FLAG_* */
uint8_t subscr_create_on_demand_flags;
unsigned int subscr_create_on_demand_rand_msisdn_len;
struct {
enum subscr_create_on_demand_mode mode;
unsigned int rand_msisdn_len;
/* Bitmask of DB_SUBSCR_FLAG_* */
uint8_t flags;
} subscr_create_on_demand;
struct {
bool allow_startup;
@@ -119,7 +140,10 @@ struct hlr {
char *domain_suffix;
struct osmo_mslookup_client_method *running;
} mdns;
bool subscr_create_on_demand_fallback;
} client;
bool auth_imsi_only;
bool ignore_created_on_demand;
} mslookup;
};
@@ -129,3 +153,4 @@ struct hlr_subscriber;
void osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr);
int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
void dgsm_fallback_to_hlr();

View File

@@ -0,0 +1,33 @@
#pragma once
#include <osmocom/core/linuxlist.h>
struct hlr_smsc {
/* g_hlr->smsc_list */
struct llist_head list;
struct hlr *hlr;
/* name (must match the IPA ID tag) */
const char *name;
/* human-readable description */
const char *description;
};
struct hlr_smsc *smsc_find(struct hlr *hlr, const char *name);
struct hlr_smsc *smsc_alloc(struct hlr *hlr, const char *name);
void smsc_free(struct hlr_smsc *smsc);
struct hlr_smsc_route {
/* g_hlr->smsc_routes */
struct llist_head list;
const char *num_addr;
struct hlr_smsc *smsc;
};
struct hlr_smsc_route *smsc_route_find(struct hlr *hlr, const char *num_addr);
struct hlr_smsc_route *smsc_route_alloc(struct hlr *hlr, const char *num_addr,
struct hlr_smsc *smsc);
void smsc_route_free(struct hlr_smsc_route *rt);
void forward_mo_sms(struct osmo_gsup_req *req);
void forward_mt_sms(struct osmo_gsup_req *req);
void rx_ready_for_sm_req(struct osmo_gsup_req *req);

View File

@@ -31,6 +31,7 @@ enum hlr_vty_node {
HLR_NODE = _LAST_OSMOVTY_NODE + 1,
GSUP_NODE,
EUSE_NODE,
SMSC_NODE,
MSLOOKUP_NODE,
MSLOOKUP_SERVER_NODE,
MSLOOKUP_SERVER_MSC_NODE,
@@ -47,7 +48,6 @@ enum hlr_vty_node {
#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_go_parent(struct vty *vty);
void hlr_vty_init(void *hlr_ctx);
void dgsm_vty_init(void);

View File

@@ -71,6 +71,8 @@ void proxy_init(struct osmo_gsup_server *gsup_server_to_vlr);
void proxy_del(struct proxy *proxy);
void proxy_set_gc_period(struct proxy *proxy, uint32_t gc_period);
struct osmo_gsup_req *proxy_deferred_gsup_req_get_by_imsi(struct proxy *proxy, const char *imsi);
/* The API to access / modify proxy entries keeps the implementation opaque, to make sure that we can easily move proxy
* storage to SQLite db. */
int proxy_subscr_get_by_imsi(struct proxy_subscr *dst, struct proxy *proxy, const char *imsi);

View File

@@ -35,6 +35,7 @@ enum osmo_mslookup_id_type {
OSMO_MSLOOKUP_ID_NONE = 0,
OSMO_MSLOOKUP_ID_IMSI,
OSMO_MSLOOKUP_ID_MSISDN,
OSMO_MSLOOKUP_ID_IMSI_AUTHORIZED,
};
extern const struct value_string osmo_mslookup_id_type_names[];

View File

@@ -37,6 +37,8 @@ typedef void (*osmo_mslookup_cb_t)(struct osmo_mslookup_client *client,
* This query handling info is not seen by the individual method implementations, to clarify that it is the
* osmo_mslookup_client layer that takes care of these details. */
struct osmo_mslookup_query_handling {
bool search_all;
/*! Wait at least this long before returning any results.
*
* If nonzero, result_cb will be called as soon as this delay has elapsed, either with the so far youngest age

View File

@@ -0,0 +1,16 @@
#pragma once
#define LIBOSMO_MSLOOKUP_VERSION {{VERSION}}
#define LIBOSMO_MSLOOKUP_VERSION_STR "{{VERSION}}"
#define LIBOSMO_MSLOOKUP_VERSION_MAJOR {{VERSION_MAJOR}}
#define LIBOSMO_MSLOOKUP_VERSION_MINOR {{VERSION_MINOR}}
#define LIBOSMO_MSLOOKUP_VERSION_PATCH {{VERSION_PATCH}}
#define LIBOSMO_MSLOOKUP_VERSION_GREATER_EQUAL(major, minor, patch) \
(LIBOSMO_MSLOOKUP_VERSION_MAJOR > (major) || \
(LIBOSMO_MSLOOKUP_VERSION_MAJOR == (major) && \
LIBOSMO_MSLOOKUP_VERSION_MINOR > (minor)) || \
(LIBOSMO_MSLOOKUP_VERSION_MAJOR == (major) && \
LIBOSMO_MSLOOKUP_VERSION_MINOR == (minor) && \
LIBOSMO_MSLOOKUP_VERSION_PATCH >= (patch)))

View File

@@ -52,6 +52,7 @@ osmo_hlr_SOURCES = \
hlr_vty.c \
hlr_vty_subscr.c \
gsup_send.c \
hlr_sms.c \
hlr_ussd.c \
proxy.c \
dgsm.c \

View File

@@ -426,6 +426,20 @@ static int set_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data)
return set_subscr_cs_ps_enabled(cmd, data, false);
}
CTRL_CMD_DEFINE_RO(subscr_imsi, "imsi");
static int get_subscr_imsi(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))
return CTRL_CMD_ERROR;
cmd->reply = talloc_strdup(cmd, subscr.imsi);
return CTRL_CMD_REPLY;
}
CTRL_CMD_DEFINE(subscr_msisdn, "msisdn");
static int verify_subscr_msisdn(struct ctrl_cmd *cmd, const char *value, void *data)
{
@@ -761,6 +775,7 @@ static int hlr_ctrl_cmds_install(void)
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_imsi);
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);

View File

@@ -53,7 +53,7 @@
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;",
"WHERE last_lu_seen IS NOT NULL AND last_lu_seen > datetime('now','-1 month') 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",
@@ -92,6 +92,10 @@ static const char *stmt_sql[] = {
[DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch') WHERE id = $subscriber_id",
[DB_STMT_SET_LAST_LU_SEEN_PS] = "UPDATE subscriber SET last_lu_seen_ps = datetime($val, 'unixepoch') WHERE id = $subscriber_id",
[DB_STMT_EXISTS_BY_IMSI] = "SELECT 1 FROM subscriber WHERE imsi = $imsi",
[DB_STMT_EXISTS_AUTHORIZED_BY_IMSI] = "SELECT 1 FROM subscriber WHERE imsi = $imsi AND (nam_cs = 1 OR nam_ps = 1)",
[DB_STMT_IS_CREATED_ON_DEMAND_BY_IMSI] =
"SELECT 1 FROM subscriber WHERE imsi = $imsi AND length(msisdn) = $msisdn_len"
" AND nam_cs = 0 AND nam_ps = 0 AND vlr_number IS NULL",
[DB_STMT_EXISTS_BY_MSISDN] = "SELECT 1 FROM subscriber WHERE msisdn = $msisdn",
[DB_STMT_IND_ADD] = "INSERT INTO ind (vlr) VALUES ($vlr)",
[DB_STMT_IND_SELECT] = "SELECT ind FROM ind WHERE vlr = $vlr",
@@ -595,6 +599,7 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
int rc;
bool has_sqlite_config_sqllog = false;
int version;
bool version_changed = false;
LOGP(DDB, LOGL_NOTICE, "using database: %s\n", fname);
LOGP(DDB, LOGL_INFO, "Compiled against SQLite3 lib version %s\n", SQLITE_VERSION);
@@ -672,6 +677,7 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
goto out_free;
}
version = CURRENT_SCHEMA_VERSION;
version_changed = true;
}
LOGP(DDB, LOGL_NOTICE, "Database '%s' has HLR DB schema version %d\n", dbc->fname, version);
@@ -686,6 +692,7 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
}
LOGP(DDB, LOGL_NOTICE, "Database '%s' has been upgraded to HLR DB schema version %d\n",
dbc->fname, version+1);
version_changed = true;
}
if (version != CURRENT_SCHEMA_VERSION) {
@@ -702,6 +709,12 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
goto out_free;
}
/* Flush the cache after changing the version, to make the scenario
* less likely that after an unclean shutdown the DB gets restored
* with the right table layout but wrong version (SYS#7394). */
if (version_changed)
sqlite3_db_cacheflush(dbc->db);
/* prepare all SQL statements */
for (i = 0; i < ARRAY_SIZE(dbc->stmt); i++) {
rc = sqlite3_prepare_v2(dbc->db, stmt_sql[i], -1,

View File

@@ -555,6 +555,59 @@ int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi) {
return rc;
}
/*! Check if a subscriber exists and has CS or PS service in the HLR database.
* \param[in, out] dbc database context.
* \param[in] imsi ASCII string of IMSI digits.
* \returns 0 if exists & authorized, -ENOENT if not, -EIO on database error.
*/
int db_subscr_authorized_by_imsi(struct db_context *dbc, const char *imsi) {
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_EXISTS_AUTHORIZED_BY_IMSI];
const char *err;
int rc;
if (!db_bind_text(stmt, NULL, imsi))
return -EIO;
rc = sqlite3_step(stmt);
db_remove_reset(stmt);
if (rc == SQLITE_ROW)
return 0; /* exists */
if (rc == SQLITE_DONE)
return -ENOENT; /* does not exist */
err = sqlite3_errmsg(dbc->db);
LOGP(DAUC, LOGL_ERROR, "Failed to check for authorized subscriber by IMSI='%s': %s\n", imsi, err);
return rc;
}
/*! Check if a subscriber exists and has ever been attached
* \param[in, out] dbc database context.
* \param[in] imsi ASCII string of IMSI digits.
* \returns 0 if has vlr_number, -ENOENT if not, -EIO on database error.
*/
int db_subscr_is_created_on_demand_by_imsi(struct db_context *dbc, const char *imsi, unsigned int msisdn_len) {
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_IS_CREATED_ON_DEMAND_BY_IMSI];
const char *err;
int rc;
if (!db_bind_text(stmt, "$imsi", imsi))
return -EIO;
if (!db_bind_int(stmt, "$msisdn_len", msisdn_len))
return -EIO;
rc = sqlite3_step(stmt);
db_remove_reset(stmt);
if (rc == SQLITE_ROW)
return 0; /* exists */
if (rc == SQLITE_DONE)
return -ENOENT; /* does not exist */
err = sqlite3_errmsg(dbc->db);
LOGP(DAUC, LOGL_ERROR, "Failed to check for on demand subscriber by IMSI='%s': %s\n", imsi, err);
return rc;
}
/*! Retrieve subscriber data from the HLR database.
* \param[in,out] dbc database context.
* \param[in] imsi ASCII string of IMSI digits.
@@ -700,9 +753,12 @@ int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *
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)
if (show_ls) {
parse_last_lu_seen(&subscr.last_lu_seen, (const char *)sqlite3_column_text(stmt, 14),
subscr.imsi, "CS");
copy_sqlite3_text_to_buf(subscr.vlr_number, stmt, 4);
}
get_cb(&subscr, data);
rc = sqlite3_step(stmt);
(*count)++;

View File

@@ -58,6 +58,12 @@ static void resolve_hlr_result_cb(struct osmo_mslookup_client *client,
if (result->rc != OSMO_MSLOOKUP_RC_RESULT) {
LOG_DGSM(query->id.imsi, LOGL_ERROR, "Failed to resolve remote HLR: %s\n",
osmo_mslookup_result_name_c(OTC_SELECT, query, result));
if (g_hlr->mslookup.client.subscr_create_on_demand_fallback &&
db_subscr_exists_by_imsi(g_hlr->dbc, query->id.imsi) != 0) {
struct osmo_gsup_req *req = proxy_deferred_gsup_req_get_by_imsi(proxy, query->id.imsi);
if (req && req->gsup.message_type == OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST)
dgsm_fallback_to_hlr(req);
}
proxy_subscr_del(proxy, query->id.imsi);
return;
}
@@ -91,8 +97,17 @@ bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req)
struct osmo_mslookup_query_handling handling;
uint32_t request_handle;
/* If the IMSI is known in the local HLR, then we won't proxy. */
if (db_subscr_exists_by_imsi(g_hlr->dbc, req->gsup.imsi) == 0)
/* If the IMSI is authorized in the local HLR, then we won't proxy */
if (db_subscr_authorized_by_imsi(g_hlr->dbc, req->gsup.imsi) == 0)
return false;
/* unless configuration tells us to do otherwise. */
if (!g_hlr->mslookup.ignore_created_on_demand && !g_hlr->mslookup.auth_imsi_only &&
db_subscr_exists_by_imsi(g_hlr->dbc, req->gsup.imsi) == 0)
return false;
if (!g_hlr->mslookup.auth_imsi_only && !(g_hlr->mslookup.ignore_created_on_demand &&
db_subscr_is_created_on_demand_by_imsi(g_hlr->dbc, req->gsup.imsi,
g_hlr->subscr_create_on_demand.rand_msisdn_len) == 0))
return false;
/* Are we already forwarding this IMSI to a remote HLR? */
@@ -168,7 +183,7 @@ void dgsm_init(void *ctx)
dgsm_ctx = talloc_named_const(ctx, 0, "dgsm");
INIT_LLIST_HEAD(&g_hlr->mslookup.server.local_site_services);
g_hlr->mslookup.server.local_attach_max_age = 60 * 60;
g_hlr->mslookup.server.local_attach_max_age = OSMO_DGSM_DEFAULT_LOCAL_ATTACH_MAX_AGE;
g_hlr->mslookup.client.result_timeout_milliseconds = OSMO_DGSM_DEFAULT_RESULT_TIMEOUT_MS;

View File

@@ -22,9 +22,11 @@
#include <osmocom/mslookup/mslookup_client_mdns.h>
#include <osmocom/mslookup/mdns.h>
#include <osmocom/hlr/hlr_vty.h>
#include <osmocom/hlr/proxy.h>
#include <osmocom/hlr/mslookup_server.h>
#include <osmocom/hlr/mslookup_server_mdns.h>
#include <osmocom/gsupclient/cni_peer_id.h>
#include <osmocom/gsm/gsm23003.h>
struct cmd_node mslookup_node = {
MSLOOKUP_NODE,
@@ -188,6 +190,53 @@ DEFUN(cfg_mslookup_server_no_mdns_bind,
return CMD_SUCCESS;
}
DEFUN(cfg_mslookup_server_max_age,
cfg_mslookup_server_max_age_cmd,
"max-age <1-21600>",
"How old can the Last Location Update be for the mslookup server to respond\n"
"max age in seconds\n")
{
uint32_t val = atol(argv[0]);
g_hlr->mslookup.server.local_attach_max_age = val;
return CMD_SUCCESS;
}
DEFUN(cfg_mslookup_auth_imsi_only,
cfg_mslookup_auth_imsi_only_cmd,
"authorized-imsi-only",
"On local GSUP, use mslookup ignoring local HLR + don't answer queries for IMSIs without PS or CS network access mode")
{
g_hlr->mslookup.auth_imsi_only = true;
return CMD_SUCCESS;
}
DEFUN(cfg_mslookup_no_auth_imsi_only,
cfg_mslookup_no_auth_imsi_only_cmd,
"no authorized-imsi-only",
NO_STR "Answer Local GSUP/mDNS queries for any IMSI in the local HLR database")
{
g_hlr->mslookup.auth_imsi_only = false;
return CMD_SUCCESS;
}
DEFUN(cfg_mslookup_cod,
cfg_mslookup_cod_cmd,
"ignore-created-on-demand",
"Ignore IMSIs that were created-on-demand")
{
g_hlr->mslookup.ignore_created_on_demand = true;
return CMD_SUCCESS;
}
DEFUN(cfg_mslookup_no_cod,
cfg_mslookup_no_cod_cmd,
"no ignore-created-on-demand",
NO_STR "Answer mslookup and local GSUP for created on demand IMSIs")
{
g_hlr->mslookup.ignore_created_on_demand = false;
return CMD_SUCCESS;
}
struct cmd_node mslookup_server_msc_node = {
MSLOOKUP_SERVER_MSC_NODE,
"%s(config-mslookup-server-msc)# ",
@@ -350,6 +399,24 @@ DEFUN(cfg_mslookup_no_client,
return CMD_SUCCESS;
}
DEFUN(cfg_mslookup_client_subscr_cod_fallback,
cfg_mslookup_client_subscr_cod_fallback_cmd,
"create-on-demand-fallback",
"If the msclient does not get a response from mDNS, proceed according to this HLR subscriber-create-on-demand config")
{
g_hlr->mslookup.client.subscr_create_on_demand_fallback = true;
return CMD_SUCCESS;
}
DEFUN(cfg_mslookup_client_no_subscr_cod_fallback,
cfg_mslookup_client_no_subscr_cod_fallback_cmd,
"no create-on-demand-fallback",
NO_STR "Return IMSI UNKNOWN if the mslookup client does not receive a response from mDNS")
{
g_hlr->mslookup.client.subscr_create_on_demand_fallback = false;
return CMD_SUCCESS;
}
DEFUN(cfg_mslookup_client_timeout,
cfg_mslookup_client_timeout_cmd,
"timeout <1-100000>",
@@ -421,6 +488,11 @@ int config_write_mslookup(struct vty *vty)
vty_out(vty, "mslookup%s", VTY_NEWLINE);
if (g_hlr->mslookup.auth_imsi_only)
vty_out(vty, " authorized-imsi-only%s", VTY_NEWLINE);
if (g_hlr->mslookup.ignore_created_on_demand)
vty_out(vty, " ignore-created-on-demand%s", VTY_NEWLINE);
if (g_hlr->mslookup.server.enable || !llist_empty(&g_hlr->mslookup.server.local_site_services)) {
struct mslookup_server_msc_cfg *msc;
@@ -450,6 +522,9 @@ int config_write_mslookup(struct vty *vty)
vty_out(vty, " msc ipa-name %s%s", osmo_ipa_name_to_str(&msc->name), VTY_NEWLINE);
config_write_msc_services(vty, " ", msc);
}
if (g_hlr->mslookup.server.local_attach_max_age != OSMO_DGSM_DEFAULT_LOCAL_ATTACH_MAX_AGE)
vty_out(vty, " max-age %u%s",
g_hlr->mslookup.server.local_attach_max_age, VTY_NEWLINE);
/* If the server is disabled, still output the above to not lose the service config. */
if (!g_hlr->mslookup.server.enable)
@@ -479,6 +554,8 @@ int config_write_mslookup(struct vty *vty)
vty_out(vty, " timeout %u%s",
g_hlr->mslookup.client.result_timeout_milliseconds,
VTY_NEWLINE);
if (g_hlr->mslookup.client.subscr_create_on_demand_fallback)
vty_out(vty, " create-on-demand-fallback%s", VTY_NEWLINE);
}
return CMD_SUCCESS;
@@ -545,11 +622,110 @@ DEFUN(do_mslookup_show_services,
return CMD_SUCCESS;
}
struct proxy_subscr_listentry {
struct llist_head entry;
timestamp_t last_update;
struct proxy_subscr data;
};
struct proxy_pending_gsup_req {
struct llist_head entry;
struct osmo_gsup_req *req;
timestamp_t received_at;
};
static void write_one_proxy(struct vty *vty, struct proxy_subscr_listentry *e)
{
struct proxy_subscr p = e->data;
uint32_t age;
vty_out(vty, "%-12s %-16s %-12s:%-4u ",
strlen(p.msisdn) == 0 ? "Unknown" : p.msisdn,
strlen(p.imsi) == 0 ? "Unknown" : p.imsi,
p.remote_hlr_addr.ip ? p.remote_hlr_addr.ip : "Unknown",
p.remote_hlr_addr.port);
if (!timestamp_age(&e->last_update, &age)) {
vty_out(vty, "Invalid%s", VTY_NEWLINE);
return;
}
#define UNIT_AGO(UNITNAME, UNITVAL) \
if (age >= (UNITVAL)) { \
vty_out(vty, "%u%s", age / (UNITVAL), UNITNAME); \
age = age % (UNITVAL); \
}
UNIT_AGO("d", 60*60*24);
UNIT_AGO("h", 60*60);
UNIT_AGO("m", 60);
UNIT_AGO("s", 1);
vty_out(vty, "%s", VTY_NEWLINE);
#undef UNIT_AGO
}
static void write_one_proxy_request(struct vty *vty, struct osmo_gsup_req *r)
{
vty_out(vty, "IMSI: %s TYPE: %s%s",
r->gsup.imsi,
osmo_gsup_message_type_name(r->gsup.message_type),
VTY_NEWLINE);
}
DEFUN(do_proxy_del_sub,
do_proxy_del_sub_cmd,
"proxy subscriber-delete [IMSI]",
"Subscriber Proxy \n"
"Delete by IMSI\n"
"IMSI of subscriber to delete from the Proxy"
)
{
const char *imsi = argv[0];
if (!osmo_imsi_str_valid(imsi)) {
vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
return CMD_WARNING;
}
if (proxy_subscr_del(g_hlr->gs->proxy, imsi) == 0)
return CMD_SUCCESS;
vty_out(vty, "%% Unable to delete a Proxy for: %s%s", imsi, VTY_NEWLINE);
return CMD_WARNING;
}
DEFUN(do_proxy_show,
do_proxy_show_cmd,
"show proxy",
SHOW_STR "Proxy Entries\n")
{
struct proxy_subscr_listentry *e;
struct proxy_pending_gsup_req *p;
unsigned int count = 0;
vty_out(vty, "MSISDN IMSI HLR AGE%s", VTY_NEWLINE);
vty_out(vty, "------------ ---------------- -------------------- ------%s", VTY_NEWLINE);
llist_for_each_entry(e, &g_hlr->gs->proxy->subscr_list, entry) {
count++;
write_one_proxy(vty, e);
}
vty_out(vty, "%s%s",
(count == 0) ? "% No proxy subscribers" : "", VTY_NEWLINE);
if (!llist_count(&g_hlr->gs->proxy->pending_gsup_reqs))
return CMD_SUCCESS;
vty_out(vty, "In-flight Proxy Subscribers Requests:%s", VTY_NEWLINE);
llist_for_each_entry(p, &g_hlr->gs->proxy->pending_gsup_reqs, entry) {
write_one_proxy_request(vty, p->req);
}
return CMD_SUCCESS;
}
void dgsm_vty_init(void)
{
install_element(CONFIG_NODE, &cfg_mslookup_cmd);
install_node(&mslookup_node, config_write_mslookup);
install_element(MSLOOKUP_NODE, &cfg_mslookup_auth_imsi_only_cmd);
install_element(MSLOOKUP_NODE, &cfg_mslookup_no_auth_imsi_only_cmd);
install_element(MSLOOKUP_NODE, &cfg_mslookup_cod_cmd);
install_element(MSLOOKUP_NODE, &cfg_mslookup_no_cod_cmd);
install_element(MSLOOKUP_NODE, &cfg_mslookup_mdns_cmd);
install_element(MSLOOKUP_NODE, &cfg_mslookup_mdns_domain_suffix_cmd);
install_element(MSLOOKUP_NODE, &cfg_mslookup_no_mdns_cmd);
@@ -563,6 +739,7 @@ void dgsm_vty_init(void)
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_service_cmd);
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_no_service_cmd);
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_no_service_addr_cmd);
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_max_age_cmd);
install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_cmd);
install_node(&mslookup_server_msc_node, NULL);
@@ -573,6 +750,8 @@ void dgsm_vty_init(void)
install_element(MSLOOKUP_NODE, &cfg_mslookup_client_cmd);
install_element(MSLOOKUP_NODE, &cfg_mslookup_no_client_cmd);
install_node(&mslookup_client_node, NULL);
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_subscr_cod_fallback_cmd);
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_subscr_cod_fallback_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_domain_suffix_cmd);
@@ -581,4 +760,6 @@ void dgsm_vty_init(void)
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_gateway_proxy_cmd);
install_element_ve(&do_mslookup_show_services_cmd);
install_element_ve(&do_proxy_show_cmd);
install_element_ve(&do_proxy_del_sub_cmd);
}

View File

@@ -24,11 +24,12 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/abis/ipa.h>
#include <osmocom/abis/ipaccess.h>
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/gsm/apn.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/gsm/ipa.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/abis/ipa.h>
#include <osmocom/hlr/gsup_server.h>
#include <osmocom/hlr/gsup_router.h>
@@ -49,7 +50,7 @@ static void osmo_gsup_server_send(struct osmo_gsup_conn *conn,
int proto_ext, struct msgb *msg_tx)
{
ipa_prepend_header_ext(msg_tx, proto_ext);
ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO);
ipa_prepend_header(msg_tx, IPAC_PROTO_OSMO);
ipa_server_conn_send(conn->conn, msg_tx);
}

View File

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

View File

@@ -2,6 +2,8 @@
*
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or

View File

@@ -6,6 +6,8 @@
* Author: Jacob Erlbeck
* Author: Neels Hofmeyr
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -24,6 +26,7 @@
#include <osmocom/gsupclient/gsup_client.h>
#include <osmocom/abis/ipa.h>
#include <osmocom/gsm/ipa.h>
#include <osmocom/gsm/oap_client.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/core/msgb.h>
@@ -42,7 +45,7 @@ static void gsup_client_send_ping(struct osmo_gsup_client *gsupc)
msg->l2h = msgb_put(msg, 1);
msg->l2h[0] = IPAC_MSGT_PING;
ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS);
ipa_prepend_header(msg, IPAC_PROTO_IPACCESS);
ipa_client_conn_send(gsupc->link, msg);
}
@@ -112,7 +115,7 @@ static void client_send(struct osmo_gsup_client *gsupc, int proto_ext,
struct msgb *msg_tx)
{
ipa_prepend_header_ext(msg_tx, proto_ext);
ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO);
ipa_prepend_header(msg_tx, IPAC_PROTO_OSMO);
ipa_client_conn_send(gsupc->link, msg_tx);
/* msg_tx is now queued and will be freed. */
}
@@ -304,7 +307,7 @@ struct osmo_gsup_client *osmo_gsup_client_create3(void *talloc_ctx, struct osmo_
OSMO_ASSERT(config->ipa_dev->unit_name);
gsupc = talloc_zero(talloc_ctx, struct osmo_gsup_client);
gsupc = talloc(talloc_ctx, struct osmo_gsup_client);
OSMO_ASSERT(gsupc);
*gsupc = (struct osmo_gsup_client){
.unit_name = (const char *)config->ipa_dev->unit_name, /* API backwards compat */
@@ -447,3 +450,36 @@ struct msgb *osmo_gsup_client_msgb_alloc(void)
{
return msgb_alloc_headroom(4000, 64, __func__);
}
void *osmo_gsup_client_get_data(const struct osmo_gsup_client *gsupc)
{
return gsupc->data;
}
void osmo_gsup_client_set_data(struct osmo_gsup_client *gsupc, void *data)
{
gsupc->data = data;
}
const char *osmo_gsup_client_get_rem_addr(const struct osmo_gsup_client *gsupc)
{
if (!gsupc->link)
return NULL;
return gsupc->link->addr;
}
uint16_t osmo_gsup_client_get_rem_port(const struct osmo_gsup_client *gsupc)
{
if (!gsupc->link)
return 0;
return gsupc->link->port;
}
bool osmo_gsup_client_is_connected(const struct osmo_gsup_client *gsupc)
{
return gsupc->is_connected;
}
const struct ipaccess_unit *osmo_gsup_client_get_ipaccess_unit(const struct osmo_gsup_client *gsupc)
{
return gsupc->ipa_dev;
}

View File

@@ -0,0 +1,196 @@
/* Directing individual GSUP messages to their respective handlers. */
/*
* (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <osmocom/core/logging.h>
#include <osmocom/gsupclient/gsup_client.h>
#include <osmocom/gsupclient/gsup_client_mux.h>
static enum osmo_gsup_message_class gsup_client_mux_classify(struct gsup_client_mux *gcm,
const struct osmo_gsup_message *gsup_msg)
{
if (gsup_msg->message_class)
return gsup_msg->message_class;
LOGP(DLGSUP, LOGL_DEBUG, "No explicit GSUP Message Class, trying to guess from message type %s\n",
osmo_gsup_message_type_name(gsup_msg->message_type));
switch (gsup_msg->message_type) {
case OSMO_GSUP_MSGT_PROC_SS_REQUEST:
case OSMO_GSUP_MSGT_PROC_SS_RESULT:
case OSMO_GSUP_MSGT_PROC_SS_ERROR:
return OSMO_GSUP_MESSAGE_CLASS_USSD;
/* GSM 04.11 code implementing MO SMS */
case OSMO_GSUP_MSGT_MO_FORWARD_SM_ERROR:
case OSMO_GSUP_MSGT_MO_FORWARD_SM_RESULT:
case OSMO_GSUP_MSGT_READY_FOR_SM_ERROR:
case OSMO_GSUP_MSGT_READY_FOR_SM_RESULT:
case OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST:
return OSMO_GSUP_MESSAGE_CLASS_SMS;
default:
return OSMO_GSUP_MESSAGE_CLASS_SUBSCRIBER_MANAGEMENT;
}
}
/* Non-static for unit tests */
int gsup_client_mux_rx(struct osmo_gsup_client *gsup_client, struct msgb *msg)
{
struct gsup_client_mux *gcm = osmo_gsup_client_get_data(gsup_client);
struct osmo_gsup_message gsup;
enum osmo_gsup_message_class message_class;
int rc;
rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
if (rc < 0) {
LOGP(DLGSUP, LOGL_ERROR, "Failed to decode GSUP message: '%s' (%d) [ %s]\n",
get_value_string(gsm48_gmm_cause_names, -rc), -rc, osmo_hexdump(msg->data, msg->len));
goto msgb_free_and_return;
}
if (!gsup.imsi[0]) {
LOGP(DLGSUP, LOGL_ERROR, "Failed to decode GSUP message: missing IMSI\n");
if (OSMO_GSUP_IS_MSGT_REQUEST(gsup.message_type))
gsup_client_mux_tx_error_reply(gcm, &gsup, GMM_CAUSE_INV_MAND_INFO);
rc = -GMM_CAUSE_INV_MAND_INFO;
goto msgb_free_and_return;
}
message_class = gsup_client_mux_classify(gcm, &gsup);
if (message_class <= OSMO_GSUP_MESSAGE_CLASS_UNSET || message_class >= ARRAY_SIZE(gcm->rx_cb)) {
LOGP(DLGSUP, LOGL_ERROR, "Failed to classify GSUP message target\n");
rc = -EINVAL;
goto msgb_free_and_return;
}
if (!gcm->rx_cb[message_class].func) {
LOGP(DLGSUP, LOGL_ERROR, "No receiver set up for GSUP Message Class %s\n", osmo_gsup_message_class_name(message_class));
rc = -ENOTSUP;
goto msgb_free_and_return;
}
rc = gcm->rx_cb[message_class].func(gcm, gcm->rx_cb[message_class].data, &gsup);
msgb_free_and_return:
msgb_free(msg);
return rc;
}
/* Make it clear that struct gsup_client_mux should be talloc allocated, so that it can be used as talloc parent. */
struct gsup_client_mux *gsup_client_mux_alloc(void *talloc_ctx)
{
return talloc_zero(talloc_ctx, struct gsup_client_mux);
}
/* Start a GSUP client to serve this gsup_client_mux. */
int gsup_client_mux_start(struct gsup_client_mux *gcm, const char *gsup_server_addr_str, uint16_t gsup_server_port,
struct ipaccess_unit *ipa_dev)
{
gcm->gsup_client = osmo_gsup_client_create2(gcm, ipa_dev,
gsup_server_addr_str,
gsup_server_port,
&gsup_client_mux_rx, NULL);
if (!gcm->gsup_client)
return -ENOMEM;
osmo_gsup_client_set_data(gcm->gsup_client, gcm);
return 0;
}
int gsup_client_mux_tx(struct gsup_client_mux *gcm, const struct osmo_gsup_message *gsup_msg)
{
struct msgb *msg;
int rc;
if (!gcm || !gcm->gsup_client) {
LOGP(DLGSUP, LOGL_ERROR, "GSUP link is down, cannot send GSUP message\n");
return -ENOTSUP;
}
msg = osmo_gsup_client_msgb_alloc();
rc = osmo_gsup_encode(msg, gsup_msg);
if (rc < 0) {
LOGP(DLGSUP, LOGL_ERROR, "Failed to encode GSUP message: '%s'\n", strerror(-rc));
return rc;
}
return osmo_gsup_client_send(gcm->gsup_client, msg);
}
/* Set GSUP source_name to our local IPA name */
void gsup_client_mux_tx_set_source(const struct gsup_client_mux *gcm,
struct osmo_gsup_message *gsup_msg)
{
const char *local_msc_name;
const struct ipaccess_unit *ipa_dev;
if (!gcm)
return;
if (!gcm->gsup_client)
return;
ipa_dev = osmo_gsup_client_get_ipaccess_unit(gcm->gsup_client);
if (!ipa_dev)
return;
local_msc_name = ipa_dev->serno;
if (!local_msc_name)
return;
gsup_msg->source_name = (const uint8_t *) local_msc_name;
gsup_msg->source_name_len = strlen(local_msc_name) + 1;
}
/* Transmit GSUP error in response to original message */
void gsup_client_mux_tx_error_reply(struct gsup_client_mux *gcm, const struct osmo_gsup_message *gsup_orig,
enum gsm48_gmm_cause cause)
{
struct osmo_gsup_message gsup_reply;
/* No need to answer if we couldn't parse an ERROR message type, only REQUESTs need an error reply. */
if (!OSMO_GSUP_IS_MSGT_REQUEST(gsup_orig->message_type))
return;
gsup_reply = (struct osmo_gsup_message){
.cause = cause,
.message_type = OSMO_GSUP_TO_MSGT_ERROR(gsup_orig->message_type),
.message_class = gsup_orig->message_class,
.destination_name = gsup_orig->source_name,
.destination_name_len = gsup_orig->source_name_len,
/* RP-Message-Reference is mandatory for SM Service */
.sm_rp_mr = gsup_orig->sm_rp_mr,
};
OSMO_STRLCPY_ARRAY(gsup_reply.imsi, gsup_orig->imsi);
gsup_client_mux_tx_set_source(gcm, &gsup_reply);
/* For SS/USSD, it's important to keep both session state and ID IEs */
if (gsup_orig->session_state != OSMO_GSUP_SESSION_STATE_NONE) {
gsup_reply.session_state = OSMO_GSUP_SESSION_STATE_END;
gsup_reply.session_id = gsup_orig->session_id;
}
if (osmo_gsup_client_enc_send(gcm->gsup_client, &gsup_reply))
LOGP(DLGSUP, LOGL_ERROR, "Failed to send Error reply (imsi=%s)\n",
osmo_quote_str(gsup_orig->imsi, -1));
}

View File

@@ -2,6 +2,8 @@
*
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or

View File

@@ -49,6 +49,7 @@
#include <osmocom/hlr/rand.h>
#include <osmocom/hlr/hlr_vty.h>
#include <osmocom/hlr/hlr_ussd.h>
#include <osmocom/hlr/hlr_sms.h>
#include <osmocom/hlr/dgsm.h>
#include <osmocom/hlr/proxy.h>
#include <osmocom/hlr/lu_fsm.h>
@@ -203,23 +204,34 @@ static int subscr_create_on_demand(const char *imsi)
{
char msisdn[GSM23003_MSISDN_MAX_DIGITS + 1];
int rc;
unsigned int rand_msisdn_len = g_hlr->subscr_create_on_demand_rand_msisdn_len;
if (!g_hlr->subscr_create_on_demand)
return -1;
if (db_subscr_exists_by_imsi(g_hlr->dbc, imsi) == 0)
return -1;
if (rand_msisdn_len && generate_new_msisdn(msisdn, imsi, rand_msisdn_len) != 0)
switch (g_hlr->subscr_create_on_demand.mode) {
case SUBSCR_COD_MODE_MSISDN_FROM_IMSI:
OSMO_STRLCPY_ARRAY(msisdn, imsi);
break;
case SUBSCR_COD_MODE_RAND_MSISDN:
if (generate_new_msisdn(msisdn, imsi, g_hlr->subscr_create_on_demand.rand_msisdn_len) != 0)
return -1;
break;
case SUBSCR_COD_MODE_NO_MSISDN:
msisdn[0] = '\0';
break;
case SUBSCR_COD_MODE_DISABLED:
default:
return -1;
}
LOGP(DMAIN, LOGL_INFO, "IMSI='%s': Creating subscriber on demand\n", imsi);
rc = db_subscr_create(g_hlr->dbc, imsi, g_hlr->subscr_create_on_demand_flags);
rc = db_subscr_create(g_hlr->dbc, imsi, g_hlr->subscr_create_on_demand.flags);
if (rc) {
LOGP(DMAIN, LOGL_ERROR, "Failed to create subscriber on demand (rc=%d): IMSI='%s'\n", rc, imsi);
return rc;
}
if (!rand_msisdn_len)
if (msisdn[0] == '\0')
return 0;
/* Update MSISDN of the new (just allocated) subscriber */
@@ -321,7 +333,11 @@ static int rx_send_auth_info(struct osmo_gsup_req *req)
" Returning slightly inaccurate cause 'IMSI Unknown' via GSUP");
return rc;
case -ENOENT:
osmo_gsup_req_respond_err(req, g_hlr->reject_cause, "IMSI unknown");
osmo_gsup_req_respond_err(req,
(req->gsup.cn_domain == OSMO_GSUP_CN_DOMAIN_CS) ?
g_hlr->no_proxy_reject_cause.cs :
g_hlr->no_proxy_reject_cause.ps,
"IMSI unknown");
return rc;
default:
osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "failure to look up IMSI in db");
@@ -379,7 +395,7 @@ static int rx_purge_ms_req(struct osmo_gsup_req *req)
return rc;
}
static int rx_check_imei_req(struct osmo_gsup_req *req)
static int rx_check_imei_req(struct osmo_gsup_req *req, bool final)
{
struct osmo_gsup_message gsup_reply;
char imei[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1] = {0};
@@ -433,7 +449,7 @@ static int rx_check_imei_req(struct osmo_gsup_req *req)
.message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT,
.imei_result = OSMO_GSUP_IMEI_RESULT_ACK,
};
return osmo_gsup_req_respond(req, &gsup_reply, false, true);
return osmo_gsup_req_respond(req, &gsup_reply, false, final);
}
static char namebuf[255];
@@ -554,7 +570,16 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
lu_rx_gsup(req);
break;
case OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST:
rx_check_imei_req(req);
rx_check_imei_req(req, true);
break;
case OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST:
forward_mo_sms(req);
break;
case OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST:
forward_mt_sms(req);
break;
case OSMO_GSUP_MSGT_READY_FOR_SM_REQUEST:
rx_ready_for_sm_req(req);
break;
default:
LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
@@ -565,6 +590,12 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
return 0;
}
void dgsm_fallback_to_hlr(struct osmo_gsup_req *req) {
LOGP(DDGSM, LOGL_DEBUG, "Fall back to HLR from DGSM for [%s]\n",
osmo_gsup_message_type_name(req->gsup.message_type));
rx_check_imei_req(req, false);
}
static void print_usage(void)
{
printf("Usage: osmo-hlr\n");
@@ -733,7 +764,6 @@ static struct vty_app_info vty_info = {
.name = "OsmoHLR",
.version = PACKAGE_VERSION,
.copyright = vlr_copyright,
.is_config_node = hlr_vty_is_config_node,
.go_parent_cb = hlr_vty_go_parent,
};
@@ -750,14 +780,18 @@ int main(int argc, char **argv)
g_hlr = talloc_zero(hlr_ctx, struct hlr);
INIT_LLIST_HEAD(&g_hlr->euse_list);
INIT_LLIST_HEAD(&g_hlr->smsc_list);
INIT_LLIST_HEAD(&g_hlr->ss_sessions);
INIT_LLIST_HEAD(&g_hlr->ussd_routes);
INIT_LLIST_HEAD(&g_hlr->smsc_routes);
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->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->reject_cause = GMM_CAUSE_IMSI_UNKNOWN;
g_hlr->no_proxy_reject_cause = GMM_CAUSE_IMSI_UNKNOWN;
g_hlr->reject_cause.cs = GMM_CAUSE_PLMN_NOTALLOWED;
g_hlr->no_proxy_reject_cause.cs = GMM_CAUSE_PLMN_NOTALLOWED;
g_hlr->reject_cause.ps = GMM_CAUSE_NET_FAIL;
g_hlr->no_proxy_reject_cause.ps = GMM_CAUSE_NET_FAIL;
/* Init default (call independent) SS session guard timeout value */
g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT;

276
src/hlr_sms.c Normal file
View File

@@ -0,0 +1,276 @@
/* OsmoHLR SMS-over-GSUP routing implementation */
/* Author: Mychaela N. Falconia <falcon@freecalypso.org>, 2023 - however,
* Mother Mychaela's contributions are NOT subject to copyright.
* No rights reserved, all rights relinquished.
*
* Based on earlier unmerged work by Vadim Yanitskiy, 2019.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/gsm/protocol/gsm_04_11.h>
#include <osmocom/hlr/hlr.h>
#include <osmocom/hlr/hlr_sms.h>
#include <osmocom/hlr/gsup_server.h>
#include <osmocom/hlr/gsup_router.h>
#include <osmocom/hlr/logging.h>
#include <osmocom/hlr/db.h>
/***********************************************************************
* core data structures expressing config from VTY
***********************************************************************/
struct hlr_smsc *smsc_find(struct hlr *hlr, const char *name)
{
struct hlr_smsc *smsc;
llist_for_each_entry(smsc, &hlr->smsc_list, list) {
if (!strcmp(smsc->name, name))
return smsc;
}
return NULL;
}
struct hlr_smsc *smsc_alloc(struct hlr *hlr, const char *name)
{
struct hlr_smsc *smsc = smsc_find(hlr, name);
if (smsc)
return NULL;
smsc = talloc_zero(hlr, struct hlr_smsc);
smsc->name = talloc_strdup(smsc, name);
smsc->hlr = hlr;
llist_add_tail(&smsc->list, &hlr->smsc_list);
return smsc;
}
void smsc_free(struct hlr_smsc *smsc)
{
llist_del(&smsc->list);
talloc_free(smsc);
}
struct hlr_smsc_route *smsc_route_find(struct hlr *hlr, const char *num_addr)
{
struct hlr_smsc_route *rt;
llist_for_each_entry(rt, &hlr->smsc_routes, list) {
if (!strcmp(rt->num_addr, num_addr))
return rt;
}
return NULL;
}
struct hlr_smsc_route *smsc_route_alloc(struct hlr *hlr, const char *num_addr,
struct hlr_smsc *smsc)
{
struct hlr_smsc_route *rt;
if (smsc_route_find(hlr, num_addr))
return NULL;
rt = talloc_zero(hlr, struct hlr_smsc_route);
rt->num_addr = talloc_strdup(rt, num_addr);
rt->smsc = smsc;
llist_add_tail(&rt->list, &hlr->smsc_routes);
return rt;
}
void smsc_route_free(struct hlr_smsc_route *rt)
{
llist_del(&rt->list);
talloc_free(rt);
}
/***********************************************************************
* forwarding of MO SMS to SMSCs based on SM-RP-DA
***********************************************************************/
static const struct hlr_smsc *find_smsc_route(const char *smsc_addr)
{
const struct hlr_smsc_route *rt;
rt = smsc_route_find(g_hlr, smsc_addr);
if (rt)
return rt->smsc;
return g_hlr->smsc_default;
}
static void respond_with_sm_rp_cause(struct osmo_gsup_req *req,
uint8_t sm_rp_cause)
{
struct osmo_gsup_message rsp_msg = { };
rsp_msg.sm_rp_cause = &sm_rp_cause;
osmo_gsup_req_respond(req, &rsp_msg, true, true);
}
/* Short Message from MSC/VLR towards SMSC */
void forward_mo_sms(struct osmo_gsup_req *req)
{
uint8_t gsm48_decode_buffer[GSM411_SMSC_ADDR_MAX_OCTETS];
char smsc_addr[GSM411_SMSC_ADDR_MAX_DIGITS+1];
const struct hlr_smsc *smsc;
struct osmo_cni_peer_id dest_peer;
/* Make sure SM-RP-DA (SMSC address) is present */
if (req->gsup.sm_rp_da == NULL || !req->gsup.sm_rp_da_len) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
"missing SM-RP-DA");
return;
}
if (req->gsup.sm_rp_da_type != OSMO_GSUP_SMS_SM_RP_ODA_SMSC_ADDR) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
"SM-RP-DA type is not SMSC");
return;
}
/* Enforce the length constrainst on SM-RP-DA, as specified in
* GSM 04.11 section 8.2.5.2. Also enforce absence of ToN/NPI
* extension octets at the same time. */
if (req->gsup.sm_rp_da_len < GSM411_SMSC_ADDR_MIN_OCTETS ||
req->gsup.sm_rp_da_len > GSM411_SMSC_ADDR_MAX_OCTETS ||
!(req->gsup.sm_rp_da[0] & 0x80)) {
/* This form of bogosity originates from the MS,
* not from OsmoMSC or any other Osmocom network elements! */
LOGP(DLSMS, LOGL_NOTICE,
"Rx '%s' (IMSI-%s) contains invalid SM-RP-DA from MS\n",
osmo_gsup_message_type_name(req->gsup.message_type),
req->gsup.imsi);
respond_with_sm_rp_cause(req, GSM411_RP_CAUSE_SEMANT_INC_MSG);
return;
}
/* Decode SMSC address from SM-RP-DA */
gsm48_decode_buffer[0] = req->gsup.sm_rp_da_len - 1;
memcpy(gsm48_decode_buffer + 1, req->gsup.sm_rp_da + 1,
req->gsup.sm_rp_da_len - 1);
gsm48_decode_bcd_number2(smsc_addr, sizeof(smsc_addr),
gsm48_decode_buffer,
req->gsup.sm_rp_da_len, 0);
/* Look for a route to this SMSC */
smsc = find_smsc_route(smsc_addr);
if (smsc == NULL) {
LOGP(DLSMS, LOGL_NOTICE,
"Failed to find a route for '%s' (IMSI-%s, SMSC-Addr-%s)\n",
osmo_gsup_message_type_name(req->gsup.message_type),
req->gsup.imsi, smsc_addr);
respond_with_sm_rp_cause(req,
GSM411_RP_CAUSE_MO_NUM_UNASSIGNED);
return;
}
/* We got the IPA name of our SMSC - forward the message */
osmo_cni_peer_id_set(&dest_peer, OSMO_CNI_PEER_ID_IPA_NAME,
(const uint8_t *) smsc->name,
strlen(smsc->name) + 1);
osmo_gsup_forward_to_local_peer(req->cb_data, &dest_peer, req, NULL);
}
/***********************************************************************
* forwarding of MT SMS from SMSCs to MSC/VLR based on IMSI
***********************************************************************/
void forward_mt_sms(struct osmo_gsup_req *req)
{
struct hlr_subscriber subscr;
struct osmo_cni_peer_id dest_peer;
int rc;
rc = db_subscr_get_by_imsi(g_hlr->dbc, req->gsup.imsi, &subscr);
if (rc < 0) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN,
"IMSI unknown");
return;
}
/* is this subscriber currently attached to a VLR? */
if (!subscr.vlr_number[0]) {
osmo_gsup_req_respond_err(req, GMM_CAUSE_IMPL_DETACHED,
"subscriber not attached to a VLR");
return;
}
osmo_cni_peer_id_set(&dest_peer, OSMO_CNI_PEER_ID_IPA_NAME,
(const uint8_t *) subscr.vlr_number,
strlen(subscr.vlr_number) + 1);
osmo_gsup_forward_to_local_peer(req->cb_data, &dest_peer, req, NULL);
}
/***********************************************************************
* READY-FOR-SM handling
*
* An MSC indicates that an MS is ready to receive messages. If one
* or more SMSCs have previously tried to send MT SMS to this MS and
* failed, we should pass this READY-FOR-SM message to them so they
* can resend their queued SMS right away. But which SMSC do we
* forward the message to? 3GPP specs call for a complicated system
* where the HLR remembers which SMSCs have tried and failed to deliver
* MT SMS, and those SMSCs then get notified - but that design is too
* much complexity for the current state of Osmocom. So we keep it
* simple: we iterate over all configured SMSCs and forward a copy
* of the READY-FOR-SM.req message to each.
*
* Routing of responses is another problem: the MSC that sent
* READY-FOR-SM.req expects only one response, and one can even argue
* that the operation is a "success" from the perspective of the MS
* irrespective of whether each given SMSC handled the notification
* successfully or not. Hence our approach: we always return success
* to the MS, and when we forward copies of READY-FOR-SM.req to SMSCs,
* we list the HLR as the message source - this way SMSC responses
* will terminate at this HLR and won't be forwarded to the MSC.
***********************************************************************/
static void forward_req_copy_to_smsc(const struct osmo_gsup_req *req,
const struct hlr_smsc *smsc)
{
const char *my_ipa_name = g_hlr->gsup_unit_name.serno;
struct osmo_gsup_message forward = req->gsup;
struct osmo_ipa_name smsc_ipa_name;
/* set the source to this HLR */
forward.source_name = (const uint8_t *) my_ipa_name;
forward.source_name_len = strlen(my_ipa_name) + 1;
/* send it off */
LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding source-reset copy to %s\n",
smsc->name);
osmo_ipa_name_set(&smsc_ipa_name, (const uint8_t *) smsc->name,
strlen(smsc->name) + 1);
osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &smsc_ipa_name, &forward);
}
void rx_ready_for_sm_req(struct osmo_gsup_req *req)
{
struct hlr_smsc *smsc;
/* fan request msg out to all SMSCs */
llist_for_each_entry(smsc, &g_hlr->smsc_list, list)
forward_req_copy_to_smsc(req, smsc);
/* send OK response to the MSC and the MS */
osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_READY_FOR_SM_RESULT,
true);
}

View File

@@ -44,6 +44,7 @@
#include <osmocom/hlr/hlr_vty.h>
#include <osmocom/hlr/hlr_vty_subscr.h>
#include <osmocom/hlr/hlr_ussd.h>
#include <osmocom/hlr/hlr_sms.h>
#include <osmocom/hlr/gsup_server.h>
static const struct value_string gsm48_gmm_cause_vty_names[] = {
@@ -281,40 +282,65 @@ DEFUN(cfg_no_ps_pdp_profile_apn, cfg_no_ps_pdp_profile_apn_cmd,
return CMD_SUCCESS;
}
static void config_write_subscr_create_on_demand(struct vty *vty)
{
const uint8_t flags = g_hlr->subscr_create_on_demand.flags;
const char *flags_str;
switch (g_hlr->subscr_create_on_demand.mode) {
case SUBSCR_COD_MODE_MSISDN_FROM_IMSI:
vty_out(vty, " subscriber-create-on-demand msisdn-from-imsi");
break;
case SUBSCR_COD_MODE_RAND_MSISDN:
vty_out(vty, " subscriber-create-on-demand %u",
g_hlr->subscr_create_on_demand.rand_msisdn_len);
break;
case SUBSCR_COD_MODE_NO_MSISDN:
vty_out(vty, " subscriber-create-on-demand no-msisdn");
break;
case SUBSCR_COD_MODE_DISABLED:
default:
vty_out(vty, " no subscriber-create-on-demand%s", VTY_NEWLINE);
return;
}
if ((flags & DB_SUBSCR_FLAG_NAM_CS) && (flags & DB_SUBSCR_FLAG_NAM_PS))
flags_str = "cs+ps";
else if (flags & DB_SUBSCR_FLAG_NAM_CS)
flags_str = "cs";
else if (flags & DB_SUBSCR_FLAG_NAM_PS)
flags_str = "ps";
else
flags_str = "none";
vty_out(vty, " %s%s", flags_str, VTY_NEWLINE);
}
static int config_write_hlr(struct vty *vty)
{
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",
if (g_hlr->reject_cause.cs != GMM_CAUSE_PLMN_NOTALLOWED)
vty_out(vty, " reject-cause not-found cs %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",
(uint32_t) g_hlr->reject_cause.cs), VTY_NEWLINE);
if (g_hlr->reject_cause.ps != GMM_CAUSE_PLMN_NOTALLOWED)
vty_out(vty, " reject-cause not-found ps %s%s",
get_value_string_or_null(gsm48_gmm_cause_vty_names,
(uint32_t) g_hlr->no_proxy_reject_cause), VTY_NEWLINE);
(uint32_t) g_hlr->reject_cause.ps), VTY_NEWLINE);
if (g_hlr->no_proxy_reject_cause.cs != GMM_CAUSE_NET_FAIL)
vty_out(vty, " reject-cause no-proxy cs %s%s",
get_value_string_or_null(gsm48_gmm_cause_vty_names,
(uint32_t) g_hlr->no_proxy_reject_cause.cs), VTY_NEWLINE);
if (g_hlr->no_proxy_reject_cause.ps != GMM_CAUSE_NET_FAIL)
vty_out(vty, " reject-cause no-proxy ps %s%s",
get_value_string_or_null(gsm48_gmm_cause_vty_names,
(uint32_t) g_hlr->no_proxy_reject_cause.ps), VTY_NEWLINE);
if (g_hlr->store_imei)
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))
vty_out(vty, " database %s%s", g_hlr->db_file_path, VTY_NEWLINE);
if (g_hlr->subscr_create_on_demand) {
const char *flags_str = "none";
uint8_t flags = g_hlr->subscr_create_on_demand_flags;
unsigned int rand_msisdn_len = g_hlr->subscr_create_on_demand_rand_msisdn_len;
if ((flags & DB_SUBSCR_FLAG_NAM_CS) && (flags & DB_SUBSCR_FLAG_NAM_PS))
flags_str = "cs+ps";
else if (flags & DB_SUBSCR_FLAG_NAM_CS)
flags_str = "cs";
else if (flags & DB_SUBSCR_FLAG_NAM_PS)
flags_str = "ps";
if (rand_msisdn_len)
vty_out(vty, " subscriber-create-on-demand %i %s%s", rand_msisdn_len, flags_str, VTY_NEWLINE);
else
vty_out(vty, " subscriber-create-on-demand no-msisdn %s%s", flags_str, VTY_NEWLINE);
}
config_write_subscr_create_on_demand(vty);
return CMD_SUCCESS;
}
@@ -422,8 +448,6 @@ DEFUN(cfg_hlr_gsup_ipa_name,
* USSD Entity
***********************************************************************/
#include <osmocom/hlr/hlr_ussd.h>
#define USSD_STR "USSD Configuration\n"
#define UROUTE_STR "Routing Configuration\n"
#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
@@ -610,21 +634,185 @@ DEFUN(cfg_ncss_guard_timeout, cfg_ncss_guard_timeout_cmd,
return CMD_SUCCESS;
}
/***********************************************************************
* Routing of SM-RL to GSUP-attached SMSCs
***********************************************************************/
#define SMSC_STR "Configuration of GSUP routing to SMSCs\n"
struct cmd_node smsc_node = {
SMSC_NODE,
"%s(config-hlr-smsc)# ",
1,
};
DEFUN(cfg_smsc_entity, cfg_smsc_entity_cmd,
"smsc entity NAME",
SMSC_STR
"Configure a particular external SMSC\n"
"IPA name of the external SMSC\n")
{
struct hlr_smsc *smsc;
const char *id = argv[0];
smsc = smsc_find(g_hlr, id);
if (!smsc) {
smsc = smsc_alloc(g_hlr, id);
if (!smsc)
return CMD_WARNING;
}
vty->index = smsc;
vty->index_sub = &smsc->description;
vty->node = SMSC_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_no_smsc_entity, cfg_no_smsc_entity_cmd,
"no smsc entity NAME",
NO_STR SMSC_STR "Remove a particular external SMSC\n"
"IPA name of the external SMSC\n")
{
struct hlr_smsc *smsc = smsc_find(g_hlr, argv[0]);
if (!smsc) {
vty_out(vty, "%% Cannot remove non-existent SMSC %s%s",
argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
if (g_hlr->smsc_default == smsc) {
vty_out(vty,
"%% Cannot remove SMSC %s, it is the default route%s",
argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
smsc_free(smsc);
return CMD_SUCCESS;
}
DEFUN(cfg_smsc_route, cfg_smsc_route_cmd,
"smsc route NUMBER NAME",
SMSC_STR
"Configure GSUP route to a particular SMSC\n"
"Numeric address of this SMSC, must match EF.SMSP programming in SIMs\n"
"IPA name of the external SMSC\n")
{
struct hlr_smsc *smsc = smsc_find(g_hlr, argv[1]);
struct hlr_smsc_route *rt = smsc_route_find(g_hlr, argv[0]);
if (rt) {
vty_out(vty,
"%% Cannot add [another?] route for SMSC address %s%s",
argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
if (!smsc) {
vty_out(vty, "%% Cannot find SMSC '%s'%s", argv[1],
VTY_NEWLINE);
return CMD_WARNING;
}
smsc_route_alloc(g_hlr, argv[0], smsc);
return CMD_SUCCESS;
}
DEFUN(cfg_no_smsc_route, cfg_no_smsc_route_cmd,
"no smsc route NUMBER",
NO_STR SMSC_STR "Remove GSUP route to a particular SMSC\n"
"Numeric address of the SMSC\n")
{
struct hlr_smsc_route *rt = smsc_route_find(g_hlr, argv[0]);
if (!rt) {
vty_out(vty, "%% Cannot find route for SMSC address %s%s",
argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
smsc_route_free(rt);
return CMD_SUCCESS;
}
DEFUN(cfg_smsc_defroute, cfg_smsc_defroute_cmd,
"smsc default-route NAME",
SMSC_STR
"Configure default SMSC route for unknown SMSC numeric addresses\n"
"IPA name of the external SMSC\n")
{
struct hlr_smsc *smsc;
smsc = smsc_find(g_hlr, argv[0]);
if (!smsc) {
vty_out(vty, "%% Cannot find SMSC %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
if (g_hlr->smsc_default != smsc) {
vty_out(vty, "Switching default route from %s to %s%s",
g_hlr->smsc_default ? g_hlr->smsc_default->name : "<none>",
smsc->name, VTY_NEWLINE);
g_hlr->smsc_default = smsc;
}
return CMD_SUCCESS;
}
DEFUN(cfg_no_smsc_defroute, cfg_no_smsc_defroute_cmd,
"no smsc default-route",
NO_STR SMSC_STR
"Remove default SMSC route for unknown SMSC numeric addresses\n")
{
g_hlr->smsc_default = NULL;
return CMD_SUCCESS;
}
static void dump_one_smsc(struct vty *vty, struct hlr_smsc *smsc)
{
vty_out(vty, " smsc entity %s%s", smsc->name, VTY_NEWLINE);
}
static int config_write_smsc(struct vty *vty)
{
struct hlr_smsc *smsc;
struct hlr_smsc_route *rt;
llist_for_each_entry(smsc, &g_hlr->smsc_list, list)
dump_one_smsc(vty, smsc);
llist_for_each_entry(rt, &g_hlr->smsc_routes, list) {
vty_out(vty, " smsc route %s %s%s", rt->num_addr,
rt->smsc->name, VTY_NEWLINE);
}
if (g_hlr->smsc_default)
vty_out(vty, " smsc default-route %s%s",
g_hlr->smsc_default->name, VTY_NEWLINE);
return 0;
}
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]);
int cause_code = get_string_value(gsm48_gmm_cause_vty_names, argv[2]);
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;
if (strcmp(argv[0], "not-found") == 0) {
if (strcmp(argv[1], "cs") == 0)
g_hlr->reject_cause.cs = (enum gsm48_gmm_cause) cause_code;
else
g_hlr->reject_cause.ps = (enum gsm48_gmm_cause) cause_code;
}
if (strcmp(argv[0], "no-proxy") == 0) {
if (strcmp(argv[1], "cs") == 0)
g_hlr->no_proxy_reject_cause.cs = (enum gsm48_gmm_cause) cause_code;
else
g_hlr->no_proxy_reject_cause.ps = (enum gsm48_gmm_cause) cause_code;
}
return CMD_SUCCESS;
}
DEFUN(cfg_store_imei, cfg_store_imei_cmd,
"store-imei",
"Save the IMEI in the database when receiving Check IMEI requests. Note that an MSC does not necessarily send"
@@ -643,29 +831,37 @@ DEFUN(cfg_no_store_imei, cfg_no_store_imei_cmd,
}
DEFUN(cfg_subscr_create_on_demand, cfg_subscr_create_on_demand_cmd,
"subscriber-create-on-demand (no-msisdn|<3-15>) (none|cs|ps|cs+ps)",
"subscriber-create-on-demand (no-msisdn|msisdn-from-imsi|<3-15>) (none|cs|ps|cs+ps)",
"Make a new record when a subscriber is first seen.\n"
"Do not automatically assign MSISDN.\n"
"Assign MSISDN identical to subscriber's IMSI.\n"
"Length of an automatically assigned MSISDN.\n"
"Do not allow any NAM (Network Access Mode) by default.\n"
"Allow access to circuit switched NAM by default.\n"
"Allow access to packet switched NAM by default.\n"
"Allow access to circuit and packet switched NAM by default.\n")
{
enum subscr_create_on_demand_mode mode;
unsigned int rand_msisdn_len = 0;
uint8_t flags = 0x00;
if (strcmp(argv[0], "no-msisdn") != 0)
if (strcmp(argv[0], "no-msisdn") == 0) {
mode = SUBSCR_COD_MODE_NO_MSISDN;
} else if (strcmp(argv[0], "msisdn-from-imsi") == 0) {
mode = SUBSCR_COD_MODE_MSISDN_FROM_IMSI;
} else { /* random MSISDN */
mode = SUBSCR_COD_MODE_RAND_MSISDN;
rand_msisdn_len = atoi(argv[0]);
}
if (strstr(argv[1], "cs"))
flags |= DB_SUBSCR_FLAG_NAM_CS;
if (strstr(argv[1], "ps"))
flags |= DB_SUBSCR_FLAG_NAM_PS;
g_hlr->subscr_create_on_demand = true;
g_hlr->subscr_create_on_demand_rand_msisdn_len = rand_msisdn_len;
g_hlr->subscr_create_on_demand_flags = flags;
g_hlr->subscr_create_on_demand.mode = mode;
g_hlr->subscr_create_on_demand.rand_msisdn_len = rand_msisdn_len;
g_hlr->subscr_create_on_demand.flags = flags;
return CMD_SUCCESS;
}
@@ -674,7 +870,7 @@ DEFUN(cfg_no_subscr_create_on_demand, cfg_no_subscr_create_on_demand_cmd,
"no subscriber-create-on-demand",
"Do not make a new record when a subscriber is first seen.\n")
{
g_hlr->subscr_create_on_demand = false;
g_hlr->subscr_create_on_demand.mode = SUBSCR_COD_MODE_DISABLED;
return CMD_SUCCESS;
}
@@ -705,24 +901,12 @@ int hlr_vty_go_parent(struct vty *vty)
return vty->node;
}
int hlr_vty_is_config_node(struct vty *vty, int node)
{
switch (node) {
/* add items that are not config */
case CONFIG_NODE:
return 0;
default:
return 1;
}
}
void hlr_vty_init(void *hlr_ctx)
{
cfg_reject_cause_cmd.string =
vty_cmd_string_from_valstr(hlr_ctx,
gsm48_gmm_cause_vty_names,
"reject-cause (not-found|no-proxy) (", "|", ")",
"reject-cause (not-found|no-proxy) (cs|ps) (", "|", ")",
VTY_DO_LOWER);
cfg_reject_cause_cmd.doc =
@@ -730,7 +914,9 @@ void hlr_vty_init(void *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",
"in the case no remote HLR reponded to mslookup GSUP request\n"
"for CS domain\n"
"for PS domain\n",
"\n", "", 0);
logging_vty_add_cmds();
@@ -773,6 +959,15 @@ void hlr_vty_init(void *hlr_ctx)
install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd);
install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd);
install_element(HLR_NODE, &cfg_ncss_guard_timeout_cmd);
install_node(&smsc_node, config_write_smsc);
install_element(HLR_NODE, &cfg_smsc_entity_cmd);
install_element(HLR_NODE, &cfg_no_smsc_entity_cmd);
install_element(HLR_NODE, &cfg_smsc_route_cmd);
install_element(HLR_NODE, &cfg_no_smsc_route_cmd);
install_element(HLR_NODE, &cfg_smsc_defroute_cmd);
install_element(HLR_NODE, &cfg_no_smsc_defroute_cmd);
install_element(HLR_NODE, &cfg_reject_cause_cmd);
install_element(HLR_NODE, &cfg_store_imei_cmd);
install_element(HLR_NODE, &cfg_no_store_imei_cmd);

View File

@@ -182,8 +182,11 @@ static void subscr_dump_summary_vty(struct hlr_subscriber *subscr, void *data)
vty_out(vty," ------------- ");
}
vty_out(vty, " %-2s%-2s ", subscr->nam_cs ? "CS" : "", subscr->nam_ps ? "PS" : "");
if (subscr->last_lu_seen)
if (subscr->last_lu_seen) {
/* VLR Number is max length 32, possibly truncate here */
vty_out(vty, " %.22s ", subscr->vlr_number);
dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, true);
}
vty_out_newline(vty);
}
@@ -218,8 +221,8 @@ 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 = " ------------";
const char *ls_text = " VLR_NUMBER LAST SEEN ";
const char *ls_line = " --------------------- ---------------------";
if (header) {
if (!show_ls)
vty_out(vty, "%s%s%s%s", texts, VTY_NEWLINE, lines, VTY_NEWLINE);

View File

@@ -136,7 +136,8 @@ static void lu_start(struct osmo_gsup_req *update_location_req)
}
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, (lu->is_ps) ? g_hlr->reject_cause.ps : g_hlr->reject_cause.cs,
"Subscriber does not exist");
return;
}

View File

@@ -45,6 +45,7 @@ static char *domain_from_query(void *ctx, const struct osmo_mslookup_query *quer
/* Get id from query */
switch (query->id.type) {
case OSMO_MSLOOKUP_ID_IMSI:
case OSMO_MSLOOKUP_ID_IMSI_AUTHORIZED:
id = query->id.imsi;
break;
case OSMO_MSLOOKUP_ID_MSISDN:

View File

@@ -91,6 +91,7 @@ const struct value_string osmo_mslookup_id_type_names[] = {
{ OSMO_MSLOOKUP_ID_NONE, "none" },
{ OSMO_MSLOOKUP_ID_IMSI, "imsi" },
{ OSMO_MSLOOKUP_ID_MSISDN, "msisdn" },
{ OSMO_MSLOOKUP_ID_IMSI_AUTHORIZED, "imsiauth" },
{}
};
@@ -134,6 +135,7 @@ bool osmo_mslookup_id_valid(const struct osmo_mslookup_id *id)
{
switch (id->type) {
case OSMO_MSLOOKUP_ID_IMSI:
case OSMO_MSLOOKUP_ID_IMSI_AUTHORIZED:
return osmo_imsi_str_valid(id->imsi);
case OSMO_MSLOOKUP_ID_MSISDN:
return osmo_msisdn_str_valid(id->msisdn);
@@ -157,6 +159,7 @@ size_t osmo_mslookup_id_name_buf(char *buf, size_t buflen, const struct osmo_msl
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
switch (id->type) {
case OSMO_MSLOOKUP_ID_IMSI:
case OSMO_MSLOOKUP_ID_IMSI_AUTHORIZED:
OSMO_STRBUF_PRINTF(sb, "%s", id->imsi);
break;
case OSMO_MSLOOKUP_ID_MSISDN:
@@ -298,6 +301,7 @@ int osmo_mslookup_query_init_from_domain_str(struct osmo_mslookup_query *q, cons
id = second_last_dot + 1;
switch (q->id.type) {
case OSMO_MSLOOKUP_ID_IMSI:
case OSMO_MSLOOKUP_ID_IMSI_AUTHORIZED:
rc = token(q->id.imsi, sizeof(q->id.imsi), id, last_dot);
if (rc)
return rc;

View File

@@ -154,16 +154,20 @@ void osmo_mslookup_client_rx_result(struct osmo_mslookup_client *client, uint32_
if (result->rc != OSMO_MSLOOKUP_RC_RESULT)
return;
/* If the client wants to see all results, send this result now */
if (req->handling.search_all && result->age > 0)
req->handling.result_cb(client, request_handle, &req->query, result);
/* If we already stored an earlier successful result, keep that if its age is younger. */
if (req->result.rc == OSMO_MSLOOKUP_RC_RESULT
&& result->age >= req->result.age)
&& result->age > req->result.age)
return;
req->result = *result;
/* If age == 0, it doesn't get any better, so return the result immediately. */
if (req->result.age == 0) {
osmo_mslookup_request_send_result(req, true);
osmo_mslookup_request_send_result(req, !req->handling.search_all);
return;
}

View File

@@ -133,6 +133,10 @@ CSV_HEADERS "\n"
" 1000-5000@sip.voice.123.msisdn Same, but silent for first second\n"
" 10000-@smpp.sms.567.msisdn Return 1 result after 10 seconds\n"
"\n"
"--search-all -A\n"
" Do not stop when a response with age of zero comes in.\n"
" This can be used to \"search\" the entire dGSM network if for\n"
" example there is a need to track down so-called \"evil twin\" hlr entries.\n"
"--format -f csv (default)\n"
" Format result lines in CSV format.\n"
"--no-csv-headers -H\n"
@@ -196,6 +200,7 @@ enum result_format {
};
static struct {
bool search_all;
bool daemon;
struct osmo_sockaddr_str mdns_addr;
uint32_t min_delay;
@@ -429,7 +434,11 @@ static void socket_client_close(struct socket_client *c)
void socket_client_respond_result(struct socket_client *c, const char *response)
{
write(c->ofd.fd, response, strlen(response));
size_t len = strlen(response);
int rc = write(c->ofd.fd, response, len);
if (rc != len)
print_error("%s: write() returned %d instead of %zu\n", __func__, rc, len);
}
static int socket_read_cb(struct osmo_fd *ofd)
@@ -526,8 +535,13 @@ int socket_accept(struct osmo_fd *ofd, unsigned int flags)
llist_add(&c->entry, &globals.socket_clients);
if (globals.format == FORMAT_CSV && cmdline_opts.csv_headers)
write(c->ofd.fd, CSV_HEADERS, strlen(CSV_HEADERS));
if (globals.format == FORMAT_CSV && cmdline_opts.csv_headers) {
size_t len = strlen(CSV_HEADERS);
int rc = write(c->ofd.fd, CSV_HEADERS, strlen(CSV_HEADERS));
if (rc != len)
print_error("%s: write() returned %d instead of %zu\n", __func__, rc, len);
}
return 0;
}
@@ -628,6 +642,7 @@ void start_query_str(const char *query_str)
const char *domain_str = query_str;
char *at;
struct osmo_mslookup_query_handling h = {
.search_all = cmdline_opts.search_all,
.min_wait_milliseconds = cmdline_opts.min_delay,
.result_timeout_milliseconds = cmdline_opts.timeout,
.result_cb = mslookup_result_cb,
@@ -730,6 +745,7 @@ int main(int argc, char **argv)
int option_index = 0;
static struct option long_options[] = {
{ "search-all", 0, 0, 'A' },
{ "format", 1, 0, 'f' },
{ "no-csv-headers", 0, 0, 'H' },
{ "daemon", 0, 0, 'd' },
@@ -755,12 +771,15 @@ int main(int argc, char **argv)
} \
} while (0)
c = getopt_long(argc, argv, "f:Hdm:M:D:t:T:s:SqhV", long_options, &option_index);
c = getopt_long(argc, argv, "Af:Hdm:M:D:t:T:s:SqhV", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'A':
cmdline_opts.search_all = true;
break;
case 'f':
cmdline_opts.format_str = optarg;
break;

View File

@@ -192,12 +192,37 @@ static void mslookup_server_rx_hlr_gsup(const struct osmo_mslookup_query *query,
{
const struct mslookup_service_host *host;
int rc;
bool exists = false;
bool auth_imsi_only = false;
bool created_on_demand = false;
switch (query->id.type) {
case OSMO_MSLOOKUP_ID_IMSI_AUTHORIZED:
auth_imsi_only = true;
case OSMO_MSLOOKUP_ID_IMSI:
/* Entries that have been created by subscriber create on demand
will have default msisdn length. and will not have any vlr_number entry.
We should not answer for these, unless they have CS/PS service. */
if (g_hlr->mslookup.ignore_created_on_demand) {
rc = db_subscr_is_created_on_demand_by_imsi(g_hlr->dbc, query->id.imsi,
g_hlr->subscr_create_on_demand.rand_msisdn_len);
if (!rc) {
exists = true;
created_on_demand = true;
rc = -ENOENT;
break;
}
}
rc = db_subscr_exists_by_imsi(g_hlr->dbc, query->id.imsi);
if (g_hlr->mslookup.auth_imsi_only || auth_imsi_only) {
if (!rc)
exists = true;
rc = db_subscr_authorized_by_imsi(g_hlr->dbc, query->id.imsi);
}
break;
case OSMO_MSLOOKUP_ID_MSISDN:
rc = db_subscr_exists_by_msisdn(g_hlr->dbc, query->id.msisdn);
/* FIXME: The log message below might not match */
break;
default:
LOGP(DMSLOOKUP, LOGL_ERROR, "Unknown mslookup ID type: %d\n", query->id.type);
@@ -206,8 +231,11 @@ static void mslookup_server_rx_hlr_gsup(const struct osmo_mslookup_query *query,
}
if (rc) {
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: does not exist in local HLR\n",
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: %s%s%s in local HLR\n",
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
(exists) ? "exists but" : "does not exist",
(created_on_demand) ? " is created on demand and since untouched" : "",
(exists && !created_on_demand) ? " is not authorized" : "");
*result = not_found;
return;
}

View File

@@ -92,7 +92,11 @@ static void proxy_pending_req_remote_hlr_connect_result(struct osmo_gsup_req *re
osmo_gsup_req_respond(req, &gsup_reply, false, true);
return;
}
osmo_gsup_req_respond_err(req, g_hlr->no_proxy_reject_cause,
osmo_gsup_req_respond_err(req,
(req->gsup.cn_domain == OSMO_GSUP_CN_DOMAIN_CS) ?
g_hlr->no_proxy_reject_cause.cs :
g_hlr->no_proxy_reject_cause.ps,
"Proxy: Failed to connect to home HLR");
return;
}
@@ -113,6 +117,19 @@ static bool proxy_deferred_gsup_req_waiting(struct proxy *proxy, const char *ims
return false;
}
struct osmo_gsup_req *proxy_deferred_gsup_req_get_by_imsi(struct proxy *proxy, const char *imsi)
{
struct proxy_pending_gsup_req *p;
OSMO_ASSERT(imsi);
llist_for_each_entry(p, &proxy->pending_gsup_reqs, entry) {
if (strcmp(p->req->gsup.imsi, imsi))
continue;
return p->req;
}
return NULL;
}
/* Result of looking for remote HLR. If it failed, pass remote_hlr as NULL. On failure, the remote_hlr may be passed
* NULL. */
static void proxy_deferred_gsup_req_pop(struct proxy *proxy, const char *imsi, struct remote_hlr *remote_hlr)

View File

@@ -20,7 +20,7 @@
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/abis/ipa.h>
#include <osmocom/gsm/ipa.h>
#include <osmocom/gsupclient/gsup_client.h>
#include <osmocom/hlr/logging.h>
#include <osmocom/hlr/hlr.h>
@@ -66,7 +66,7 @@ static void remote_hlr_err_reply(struct remote_hlr *rh, const struct osmo_gsup_m
* The local MSC shall be indicated by gsup.destination_name. */
static int remote_hlr_rx(struct osmo_gsup_client *gsupc, struct msgb *msg)
{
struct remote_hlr *rh = gsupc->data;
struct remote_hlr *rh = osmo_gsup_client_get_data(gsupc);
struct proxy_subscr proxy_subscr;
struct osmo_gsup_message gsup;
int rc;
@@ -108,7 +108,7 @@ struct remote_hlr_pending_up {
static bool remote_hlr_up_down(struct osmo_gsup_client *gsupc, bool up)
{
struct remote_hlr *remote_hlr = gsupc->data;
struct remote_hlr *remote_hlr = osmo_gsup_client_get_data(gsupc);
struct remote_hlr_pending_up *p, *n;
if (!up) {
LOG_REMOTE_HLR(remote_hlr, LOGL_NOTICE, "link to remote HLR is down, removing GSUP client\n");
@@ -127,7 +127,7 @@ static bool remote_hlr_up_down(struct osmo_gsup_client *gsupc, bool up)
bool remote_hlr_is_up(struct remote_hlr *remote_hlr)
{
return remote_hlr && remote_hlr->gsupc && remote_hlr->gsupc->is_connected;
return remote_hlr && remote_hlr->gsupc && osmo_gsup_client_is_connected(remote_hlr->gsupc);
}
struct remote_hlr *remote_hlr_get_or_connect(const struct osmo_sockaddr_str *addr, bool connect,
@@ -180,7 +180,7 @@ struct remote_hlr *remote_hlr_get_or_connect(const struct osmo_sockaddr_str *add
return NULL;
}
rh->gsupc->data = rh;
osmo_gsup_client_set_data(rh->gsupc, rh);
llist_add(&rh->entry, &remote_hlrs);
add_result_cb:

View File

@@ -47,6 +47,7 @@ OsmoHLR(config-hlr)# ?
no Negate a command or set its defaults
ussd USSD Configuration
ncss-guard-timeout Set guard timer for NCSS (call independent SS) session activity
smsc Configuration of GSUP routing to SMSCs
reject-cause GSUP/GMM cause to be sent
store-imei Save the IMEI in the database when receiving Check IMEI requests. Note that an MSC does not necessarily send Check IMEI requests (for OsmoMSC, you may want to set 'check-imei-rqd 1').
subscriber-create-on-demand Make a new record when a subscriber is first seen.
@@ -63,10 +64,16 @@ OsmoHLR(config-hlr)# list
ussd default-route external EUSE
no ussd default-route
ncss-guard-timeout <0-255>
smsc entity NAME
no smsc entity NAME
smsc route NUMBER NAME
no smsc route NUMBER
smsc default-route NAME
no smsc default-route
reject-cause (not-found|no-proxy) (imsi-unknown|illegal-ms|plmn-not-allowed|la-not-allowed|roaming-not-allowed|no-suitable-cell-in-la|net-fail|congestion|auth-unacceptable|proto-error-unspec)
store-imei
no store-imei
subscriber-create-on-demand (no-msisdn|<3-15>) (none|cs|ps|cs+ps)
subscriber-create-on-demand (no-msisdn|msisdn-from-imsi|<3-15>) (none|cs|ps|cs+ps)
no subscriber-create-on-demand
OsmoHLR(config-hlr)# gsup
@@ -106,8 +113,11 @@ log stderr
logging level dgsm notice
...
hlr
reject-cause not-found plmn-not-allowed
reject-cause no-proxy net-fail
store-imei
database hlr_vty_test.db
no subscriber-create-on-demand
gsup
bind ip 127.0.0.1
ipa-name unnamed-HLR

View File

@@ -10,6 +10,14 @@ hlr
subscriber-create-on-demand no-msisdn none
...
OsmoHLR(config-hlr)# subscriber-create-on-demand msisdn-from-imsi cs+ps
OsmoHLR(config-hlr)# show running-config
...
hlr
...
subscriber-create-on-demand msisdn-from-imsi cs+ps
...
OsmoHLR(config-hlr)# subscriber-create-on-demand 3 none
OsmoHLR(config-hlr)# show running-config
...