Compare commits

...

28 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
41 changed files with 857 additions and 73 deletions

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

@@ -41,11 +41,11 @@ PKG_PROG_PKG_CONFIG([0.20])
PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.6.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)

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

37
debian/changelog vendored
View File

@@ -1,3 +1,40 @@
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 ]

6
debian/control vendored
View File

@@ -7,9 +7,9 @@ Build-Depends: debhelper (>= 10),
dh-autoreconf,
autotools-dev,
python3-minimal,
libosmocore-dev (>= 1.10.0),
libosmo-abis-dev (>= 1.6.0),
libosmo-netif-dev (>= 1.5.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.6.0)

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/>.

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

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

@@ -63,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 {
@@ -132,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;
};
@@ -142,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

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

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

@@ -333,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");
@@ -391,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};
@@ -445,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];
@@ -566,7 +570,7 @@ 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);
@@ -586,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");
@@ -754,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,
};
@@ -779,8 +788,10 @@ int main(int argc, char **argv)
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;

View File

@@ -320,14 +320,22 @@ 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))
@@ -784,17 +792,27 @@ static int config_write_smsc(struct vty *vty)
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"
@@ -883,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 =
@@ -908,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();

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;
@@ -637,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,
@@ -739,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' },
@@ -764,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

@@ -113,6 +113,8 @@ 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