D-GSM 1/n: add mslookup server in osmo-hlr
Implement the mslookup server to service remote mslookup requests. This patch merely adds the logic to answer incoming mslookup requests, an actual method to receive requests (mDNS) follows in a subsequent patch. - API to configure service names and addresses for the local site (per MSC). - determine whether a subscriber is on a local MSC (checking the local proxy will be added in subsequent patch that adds proxy capability). - VTY config follows in a subsequent patch. For a detailed overview of the D-GSM and mslookup related files, please see the elaborate comment at the top of mslookup.c (already added in an earlier patch). Change-Id: Ife4a61d71926d08f310a1aeed9d9f1974f64178b
This commit is contained in:
parent
86b507b6ea
commit
ab7dc40f16
|
@ -10,5 +10,7 @@ noinst_HEADERS = \
|
||||||
hlr_vty_subscr.h \
|
hlr_vty_subscr.h \
|
||||||
logging.h \
|
logging.h \
|
||||||
lu_fsm.h \
|
lu_fsm.h \
|
||||||
|
mslookup_server.h \
|
||||||
rand.h \
|
rand.h \
|
||||||
|
timestamp.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -67,6 +67,13 @@ struct hlr {
|
||||||
/* Bitmask of DB_SUBSCR_FLAG_* */
|
/* Bitmask of DB_SUBSCR_FLAG_* */
|
||||||
uint8_t subscr_create_on_demand_flags;
|
uint8_t subscr_create_on_demand_flags;
|
||||||
unsigned int subscr_create_on_demand_rand_msisdn_len;
|
unsigned int subscr_create_on_demand_rand_msisdn_len;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
uint32_t local_attach_max_age;
|
||||||
|
struct llist_head local_site_services;
|
||||||
|
} server;
|
||||||
|
} mslookup;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct hlr *g_hlr;
|
extern struct hlr *g_hlr;
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct osmo_mslookup_query;
|
||||||
|
struct osmo_mslookup_result;
|
||||||
|
|
||||||
|
/*! mslookup service name used for roaming/proxying between osmo-hlr instances. */
|
||||||
|
#define OSMO_MSLOOKUP_SERVICE_HLR_GSUP "gsup.hlr"
|
||||||
|
|
||||||
|
/*! What addresses to return to mslookup queries when a subscriber is attached at the local site.
|
||||||
|
* Mapping of service name to IP address and port. This corresponds to the VTY config for
|
||||||
|
* 'mslookup' / 'server' [/ 'msc MSC-1-2-3'] / 'service sip.voice at 1.2.3.4 1234'.
|
||||||
|
*/
|
||||||
|
struct mslookup_service_host {
|
||||||
|
struct llist_head entry;
|
||||||
|
char service[OSMO_MSLOOKUP_SERVICE_MAXLEN+1];
|
||||||
|
struct osmo_sockaddr_str host_v4;
|
||||||
|
struct osmo_sockaddr_str host_v6;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! Sets of mslookup_service_host per connected MSC.
|
||||||
|
* When there are more than one MSC connected to this osmo-hlr, this allows keeping separate sets of service addresses
|
||||||
|
* for each MSC. The entry with mslookup_server_msc_wildcard as MSC name is used for all MSCs (if no match for that
|
||||||
|
* particular MSC is found). This corresponds to the VTY config for
|
||||||
|
* 'mslookup' / 'server' / 'msc MSC-1-2-3'.
|
||||||
|
*/
|
||||||
|
struct mslookup_server_msc_cfg {
|
||||||
|
struct llist_head entry;
|
||||||
|
struct osmo_ipa_name name;
|
||||||
|
struct llist_head service_hosts;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mslookup_service_host *mslookup_server_service_get(const struct osmo_ipa_name *msc_name, const char *service);
|
||||||
|
|
||||||
|
struct mslookup_service_host *mslookup_server_msc_service_get(struct mslookup_server_msc_cfg *msc, const char *service,
|
||||||
|
bool create);
|
||||||
|
int mslookup_server_msc_service_set(struct mslookup_server_msc_cfg *msc, const char *service,
|
||||||
|
const struct osmo_sockaddr_str *addr);
|
||||||
|
int mslookup_server_msc_service_del(struct mslookup_server_msc_cfg *msc, const char *service,
|
||||||
|
const struct osmo_sockaddr_str *addr);
|
||||||
|
|
||||||
|
extern const struct osmo_ipa_name mslookup_server_msc_wildcard;
|
||||||
|
struct mslookup_server_msc_cfg *mslookup_server_msc_get(const struct osmo_ipa_name *msc_name, bool create);
|
||||||
|
|
||||||
|
const struct mslookup_service_host *mslookup_server_get_local_gsup_addr();
|
||||||
|
void mslookup_server_rx(const struct osmo_mslookup_query *query,
|
||||||
|
struct osmo_mslookup_result *result);
|
|
@ -0,0 +1,28 @@
|
||||||
|
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef time_t timestamp_t;
|
||||||
|
void timestamp_update(timestamp_t *timestamp);
|
||||||
|
bool timestamp_age(const timestamp_t *timestamp, uint32_t *age);
|
|
@ -9,6 +9,7 @@ AM_CFLAGS = \
|
||||||
$(LIBOSMOGSM_CFLAGS) \
|
$(LIBOSMOGSM_CFLAGS) \
|
||||||
$(LIBOSMOVTY_CFLAGS) \
|
$(LIBOSMOVTY_CFLAGS) \
|
||||||
$(LIBOSMOCTRL_CFLAGS) \
|
$(LIBOSMOCTRL_CFLAGS) \
|
||||||
|
$(LIBOSMOMSLOOKUP_CFLAGS) \
|
||||||
$(LIBOSMOABIS_CFLAGS) \
|
$(LIBOSMOABIS_CFLAGS) \
|
||||||
$(SQLITE3_CFLAGS) \
|
$(SQLITE3_CFLAGS) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
@ -53,14 +54,18 @@ osmo_hlr_SOURCES = \
|
||||||
gsup_send.c \
|
gsup_send.c \
|
||||||
hlr_ussd.c \
|
hlr_ussd.c \
|
||||||
lu_fsm.c \
|
lu_fsm.c \
|
||||||
|
mslookup_server.c \
|
||||||
|
timestamp.c \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
osmo_hlr_LDADD = \
|
osmo_hlr_LDADD = \
|
||||||
$(top_builddir)/src/gsupclient/libosmo-gsup-client.la \
|
$(top_builddir)/src/gsupclient/libosmo-gsup-client.la \
|
||||||
|
$(top_builddir)/src/mslookup/libosmo-mslookup.la \
|
||||||
$(LIBOSMOCORE_LIBS) \
|
$(LIBOSMOCORE_LIBS) \
|
||||||
$(LIBOSMOGSM_LIBS) \
|
$(LIBOSMOGSM_LIBS) \
|
||||||
$(LIBOSMOVTY_LIBS) \
|
$(LIBOSMOVTY_LIBS) \
|
||||||
$(LIBOSMOCTRL_LIBS) \
|
$(LIBOSMOCTRL_LIBS) \
|
||||||
|
$(LIBOSMOMSLOOKUP_LIBS) \
|
||||||
$(LIBOSMOABIS_LIBS) \
|
$(LIBOSMOABIS_LIBS) \
|
||||||
$(SQLITE3_LIBS) \
|
$(SQLITE3_LIBS) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -695,6 +695,7 @@ int main(int argc, char **argv)
|
||||||
INIT_LLIST_HEAD(&g_hlr->euse_list);
|
INIT_LLIST_HEAD(&g_hlr->euse_list);
|
||||||
INIT_LLIST_HEAD(&g_hlr->ss_sessions);
|
INIT_LLIST_HEAD(&g_hlr->ss_sessions);
|
||||||
INIT_LLIST_HEAD(&g_hlr->ussd_routes);
|
INIT_LLIST_HEAD(&g_hlr->ussd_routes);
|
||||||
|
INIT_LLIST_HEAD(&g_hlr->mslookup.server.local_site_services);
|
||||||
g_hlr->db_file_path = talloc_strdup(g_hlr, HLR_DEFAULT_DB_FILE_PATH);
|
g_hlr->db_file_path = talloc_strdup(g_hlr, HLR_DEFAULT_DB_FILE_PATH);
|
||||||
|
|
||||||
/* Init default (call independent) SS session guard timeout value */
|
/* Init default (call independent) SS session guard timeout value */
|
||||||
|
|
|
@ -0,0 +1,376 @@
|
||||||
|
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <osmocom/core/sockaddr_str.h>
|
||||||
|
#include <osmocom/gsm/gsup.h>
|
||||||
|
#include <osmocom/mslookup/mslookup.h>
|
||||||
|
#include <osmocom/hlr/logging.h>
|
||||||
|
#include <osmocom/hlr/hlr.h>
|
||||||
|
#include <osmocom/hlr/db.h>
|
||||||
|
#include <osmocom/hlr/timestamp.h>
|
||||||
|
#include <osmocom/hlr/mslookup_server.h>
|
||||||
|
|
||||||
|
static const struct osmo_mslookup_result not_found = {
|
||||||
|
.rc = OSMO_MSLOOKUP_RC_NOT_FOUND,
|
||||||
|
};
|
||||||
|
const struct osmo_ipa_name mslookup_server_msc_wildcard = {};
|
||||||
|
|
||||||
|
static void set_result(struct osmo_mslookup_result *result,
|
||||||
|
const struct mslookup_service_host *service_host,
|
||||||
|
uint32_t age)
|
||||||
|
{
|
||||||
|
if (!osmo_sockaddr_str_is_nonzero(&service_host->host_v4)
|
||||||
|
&& !osmo_sockaddr_str_is_nonzero(&service_host->host_v6)) {
|
||||||
|
*result = not_found;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result->rc = OSMO_MSLOOKUP_RC_RESULT;
|
||||||
|
result->host_v4 = service_host->host_v4;
|
||||||
|
result->host_v6 = service_host->host_v6;
|
||||||
|
result->age = age;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct mslookup_service_host *mslookup_server_get_local_gsup_addr()
|
||||||
|
{
|
||||||
|
static struct mslookup_service_host gsup_bind = {};
|
||||||
|
struct mslookup_service_host *host;
|
||||||
|
|
||||||
|
/* Find a HLR/GSUP service set for the server (no VLR unit name) */
|
||||||
|
host = mslookup_server_service_get(&mslookup_server_msc_wildcard, OSMO_MSLOOKUP_SERVICE_HLR_GSUP);
|
||||||
|
if (host)
|
||||||
|
return host;
|
||||||
|
|
||||||
|
/* Try to use the locally configured GSUP bind address */
|
||||||
|
osmo_sockaddr_str_from_str(&gsup_bind.host_v4, g_hlr->gsup_bind_addr, OSMO_GSUP_PORT);
|
||||||
|
if (gsup_bind.host_v4.af == AF_INET6) {
|
||||||
|
gsup_bind.host_v6 = gsup_bind.host_v4;
|
||||||
|
gsup_bind.host_v4 = (struct osmo_sockaddr_str){};
|
||||||
|
}
|
||||||
|
return &gsup_bind;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mslookup_server_msc_cfg *mslookup_server_msc_get(const struct osmo_ipa_name *msc_name, bool create)
|
||||||
|
{
|
||||||
|
struct llist_head *c = &g_hlr->mslookup.server.local_site_services;
|
||||||
|
struct mslookup_server_msc_cfg *msc;
|
||||||
|
|
||||||
|
if (!msc_name)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
llist_for_each_entry(msc, c, entry) {
|
||||||
|
if (osmo_ipa_name_cmp(&msc->name, msc_name))
|
||||||
|
continue;
|
||||||
|
return msc;
|
||||||
|
}
|
||||||
|
if (!create)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
msc = talloc_zero(g_hlr, struct mslookup_server_msc_cfg);
|
||||||
|
OSMO_ASSERT(msc);
|
||||||
|
INIT_LLIST_HEAD(&msc->service_hosts);
|
||||||
|
msc->name = *msc_name;
|
||||||
|
llist_add_tail(&msc->entry, c);
|
||||||
|
return msc;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mslookup_service_host *mslookup_server_msc_service_get(struct mslookup_server_msc_cfg *msc, const char *service,
|
||||||
|
bool create)
|
||||||
|
{
|
||||||
|
struct mslookup_service_host *e;
|
||||||
|
if (!msc)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
llist_for_each_entry(e, &msc->service_hosts, entry) {
|
||||||
|
if (!strcmp(e->service, service))
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!create)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
e = talloc_zero(msc, struct mslookup_service_host);
|
||||||
|
OSMO_ASSERT(e);
|
||||||
|
OSMO_STRLCPY_ARRAY(e->service, service);
|
||||||
|
llist_add_tail(&e->entry, &msc->service_hosts);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mslookup_service_host *mslookup_server_service_get(const struct osmo_ipa_name *msc_name, const char *service)
|
||||||
|
{
|
||||||
|
struct mslookup_server_msc_cfg *msc = mslookup_server_msc_get(msc_name, false);
|
||||||
|
if (!msc)
|
||||||
|
return NULL;
|
||||||
|
return mslookup_server_msc_service_get(msc, service, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mslookup_server_msc_service_set(struct mslookup_server_msc_cfg *msc, const char *service,
|
||||||
|
const struct osmo_sockaddr_str *addr)
|
||||||
|
{
|
||||||
|
struct mslookup_service_host *e;
|
||||||
|
|
||||||
|
if (!service || !service[0]
|
||||||
|
|| strlen(service) > OSMO_MSLOOKUP_SERVICE_MAXLEN)
|
||||||
|
return -EINVAL;
|
||||||
|
if (!addr || !osmo_sockaddr_str_is_nonzero(addr))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
e = mslookup_server_msc_service_get(msc, service, true);
|
||||||
|
if (!e)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
switch (addr->af) {
|
||||||
|
case AF_INET:
|
||||||
|
e->host_v4 = *addr;
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
e->host_v6 = *addr;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mslookup_server_msc_service_del(struct mslookup_server_msc_cfg *msc, const char *service,
|
||||||
|
const struct osmo_sockaddr_str *addr)
|
||||||
|
{
|
||||||
|
struct mslookup_service_host *e, *n;
|
||||||
|
int deleted = 0;
|
||||||
|
|
||||||
|
if (!msc)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
llist_for_each_entry_safe(e, n, &msc->service_hosts, entry) {
|
||||||
|
if (service && strcmp(service, e->service))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (addr) {
|
||||||
|
if (!osmo_sockaddr_str_cmp(addr, &e->host_v4)) {
|
||||||
|
e->host_v4 = (struct osmo_sockaddr_str){};
|
||||||
|
/* Removed one addr. If the other is still there, keep the entry. */
|
||||||
|
if (osmo_sockaddr_str_is_nonzero(&e->host_v6))
|
||||||
|
continue;
|
||||||
|
} else if (!osmo_sockaddr_str_cmp(addr, &e->host_v6)) {
|
||||||
|
e->host_v6 = (struct osmo_sockaddr_str){};
|
||||||
|
/* Removed one addr. If the other is still there, keep the entry. */
|
||||||
|
if (osmo_sockaddr_str_is_nonzero(&e->host_v4))
|
||||||
|
continue;
|
||||||
|
} else
|
||||||
|
/* No addr match, keep the entry. */
|
||||||
|
continue;
|
||||||
|
/* Addr matched and none is left. Delete. */
|
||||||
|
}
|
||||||
|
llist_del(&e->entry);
|
||||||
|
talloc_free(e);
|
||||||
|
deleted++;
|
||||||
|
}
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A remote entity is asking us whether we are the home HLR of the given subscriber. */
|
||||||
|
static void mslookup_server_rx_hlr_gsup(const struct osmo_mslookup_query *query,
|
||||||
|
struct osmo_mslookup_result *result)
|
||||||
|
{
|
||||||
|
const struct mslookup_service_host *host;
|
||||||
|
int rc;
|
||||||
|
switch (query->id.type) {
|
||||||
|
case OSMO_MSLOOKUP_ID_IMSI:
|
||||||
|
rc = db_subscr_exists_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);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGP(DMSLOOKUP, LOGL_ERROR, "Unknown mslookup ID type: %d\n", query->id.type);
|
||||||
|
*result = not_found;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: does not exist in local HLR\n",
|
||||||
|
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
|
||||||
|
*result = not_found;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: found in local HLR\n",
|
||||||
|
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
|
||||||
|
|
||||||
|
host = mslookup_server_get_local_gsup_addr();
|
||||||
|
|
||||||
|
set_result(result, host, 0);
|
||||||
|
if (result->rc != OSMO_MSLOOKUP_RC_RESULT) {
|
||||||
|
LOGP(DMSLOOKUP, LOGL_ERROR,
|
||||||
|
"Subscriber found, but error in service '" OSMO_MSLOOKUP_SERVICE_HLR_GSUP "' config:"
|
||||||
|
" v4: " OSMO_SOCKADDR_STR_FMT " v6: " OSMO_SOCKADDR_STR_FMT "\n",
|
||||||
|
OSMO_SOCKADDR_STR_FMT_ARGS(&host->host_v4),
|
||||||
|
OSMO_SOCKADDR_STR_FMT_ARGS(&host->host_v6));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Look in the local HLR record: If the subscriber is "at home" in this HLR and is also currently located at a local
|
||||||
|
* VLR, we will find a valid location updating with vlr_number, and no vlr_via_proxy entry. */
|
||||||
|
static bool subscriber_has_done_lu_here_hlr(const struct osmo_mslookup_query *query,
|
||||||
|
uint32_t *lu_age,
|
||||||
|
struct osmo_ipa_name *local_msc_name,
|
||||||
|
struct hlr_subscriber *ret_subscr)
|
||||||
|
{
|
||||||
|
struct hlr_subscriber _subscr;
|
||||||
|
int rc;
|
||||||
|
uint32_t age;
|
||||||
|
|
||||||
|
struct hlr_subscriber *subscr = ret_subscr ? : &_subscr;
|
||||||
|
|
||||||
|
switch (query->id.type) {
|
||||||
|
case OSMO_MSLOOKUP_ID_IMSI:
|
||||||
|
rc = db_subscr_get_by_imsi(g_hlr->dbc, query->id.imsi, subscr);
|
||||||
|
break;
|
||||||
|
case OSMO_MSLOOKUP_ID_MSISDN:
|
||||||
|
rc = db_subscr_get_by_msisdn(g_hlr->dbc, query->id.msisdn, subscr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGP(DMSLOOKUP, LOGL_ERROR, "Unknown mslookup ID type: %d\n", query->id.type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: does not exist in local HLR\n",
|
||||||
|
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!subscr->vlr_number[0]) {
|
||||||
|
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: not attached (vlr_number unset)\n",
|
||||||
|
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subscr->vlr_via_proxy.len) {
|
||||||
|
/* The VLR is behind a proxy, the subscriber is not attached to a local VLR but a remote one. That
|
||||||
|
* remote proxy should instead respond to the service lookup request. */
|
||||||
|
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: last attach is not at local VLR, but at VLR '%s' via proxy %s\n",
|
||||||
|
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
|
||||||
|
subscr->vlr_number,
|
||||||
|
osmo_ipa_name_to_str(&subscr->vlr_via_proxy));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!timestamp_age(&subscr->last_lu_seen, &age)) {
|
||||||
|
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: Invalid last_lu_seen timestamp for subscriber\n",
|
||||||
|
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (age > g_hlr->mslookup.server.local_attach_max_age) {
|
||||||
|
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: last attach was here, but too long ago: %us > %us\n",
|
||||||
|
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
|
||||||
|
age, g_hlr->mslookup.server.local_attach_max_age);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*lu_age = age;
|
||||||
|
osmo_ipa_name_set_str(local_msc_name, subscr->vlr_number);
|
||||||
|
LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: attached %u seconds ago at local VLR %s\n",
|
||||||
|
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
|
||||||
|
age, osmo_ipa_name_to_str(local_msc_name));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query,
|
||||||
|
uint32_t *lu_age_p,
|
||||||
|
struct osmo_ipa_name *local_msc_name)
|
||||||
|
{
|
||||||
|
bool attached_here;
|
||||||
|
uint32_t lu_age = 0;
|
||||||
|
struct osmo_ipa_name msc_name = {};
|
||||||
|
|
||||||
|
/* First ask the local HLR db, but if the local proxy record indicates a more recent LU, use that instead.
|
||||||
|
* For all usual cases, only one of these will reflect a LU, even if a subscriber had more than one home HLR:
|
||||||
|
* - if the subscriber is known here, we will never proxy.
|
||||||
|
* - if the subscriber is not known here, this local HLR db will never record a LU.
|
||||||
|
* However, if a subscriber was being proxied to a remote home HLR, and if then the subscriber was also added to
|
||||||
|
* the local HLR database, there might occur a situation where both reflect a LU. So, to be safe against all
|
||||||
|
* situations, compare the two entries.
|
||||||
|
*/
|
||||||
|
attached_here = subscriber_has_done_lu_here_hlr(query, &lu_age, &msc_name, NULL);
|
||||||
|
|
||||||
|
/* Future: If proxy has a younger lu, replace. */
|
||||||
|
|
||||||
|
if (attached_here && !msc_name.len) {
|
||||||
|
LOGP(DMSLOOKUP, LOGL_ERROR, "%s: attached here, but no VLR name known\n",
|
||||||
|
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attached_here) {
|
||||||
|
/* Already logged "not attached" for both local-db and proxy attach */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP(DMSLOOKUP, LOGL_INFO, "%s: attached here, at VLR %s\n",
|
||||||
|
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
|
||||||
|
osmo_ipa_name_to_str(&msc_name));
|
||||||
|
*lu_age_p = lu_age;
|
||||||
|
*local_msc_name = msc_name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A remote entity is asking us whether we are providing the given service for the given subscriber. */
|
||||||
|
void mslookup_server_rx(const struct osmo_mslookup_query *query,
|
||||||
|
struct osmo_mslookup_result *result)
|
||||||
|
{
|
||||||
|
const struct mslookup_service_host *service_host;
|
||||||
|
uint32_t age;
|
||||||
|
struct osmo_ipa_name msc_name;
|
||||||
|
|
||||||
|
/* A request for a home HLR: answer exactly if this is the subscriber's home HLR, i.e. the IMSI is listed in the
|
||||||
|
* HLR database. */
|
||||||
|
if (strcmp(query->service, OSMO_MSLOOKUP_SERVICE_HLR_GSUP) == 0)
|
||||||
|
return mslookup_server_rx_hlr_gsup(query, result);
|
||||||
|
|
||||||
|
/* All other service types: answer when the subscriber has done a LU that is either listed in the local HLR or
|
||||||
|
* in the GSUP proxy database: i.e. if the subscriber has done a Location Updating at an VLR belonging to this
|
||||||
|
* HLR. Respond with whichever services are configured in the osmo-hlr.cfg. */
|
||||||
|
if (!subscriber_has_done_lu_here(query, &age, &msc_name)) {
|
||||||
|
*result = not_found;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We've detected a LU here. The VLR where the LU happened is stored in msc_unit_name, and the LU age is stored
|
||||||
|
* in 'age'. Figure out the address configured for that VLR and service name. */
|
||||||
|
service_host = mslookup_server_service_get(&msc_name, query->service);
|
||||||
|
|
||||||
|
if (!service_host) {
|
||||||
|
/* Find such service set globally (no VLR unit name) */
|
||||||
|
service_host = mslookup_server_service_get(&mslookup_server_msc_wildcard, query->service);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!service_host) {
|
||||||
|
LOGP(DMSLOOKUP, LOGL_ERROR,
|
||||||
|
"%s: subscriber found, but no service %s configured, cannot service lookup request\n",
|
||||||
|
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
|
||||||
|
osmo_quote_str_c(OTC_SELECT, query->service, -1));
|
||||||
|
*result = not_found;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_result(result, service_host, age);
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <osmocom/core/timer.h>
|
||||||
|
#include <osmocom/hlr/timestamp.h>
|
||||||
|
|
||||||
|
/* Central implementation to set a timestamp to the current time, in case we want to modify this in the future. */
|
||||||
|
void timestamp_update(timestamp_t *timestamp)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
time_t raw;
|
||||||
|
struct tm utc;
|
||||||
|
/* The simpler way would be just time(&raw), but by using osmo_gettimeofday() we can also use
|
||||||
|
* osmo_gettimeofday_override for unit tests independent from real time. */
|
||||||
|
osmo_gettimeofday(&tv, NULL);
|
||||||
|
raw = tv.tv_sec;
|
||||||
|
gmtime_r(&raw, &utc);
|
||||||
|
*timestamp = mktime(&utc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate seconds since a given timestamp was taken. Return true for a valid age returned in age_p, return false if
|
||||||
|
* the timestamp is either in the future or the age surpasses uint32_t range. When false is returned, *age_p is set to
|
||||||
|
* UINT32_MAX. */
|
||||||
|
bool timestamp_age(const timestamp_t *timestamp, uint32_t *age_p)
|
||||||
|
{
|
||||||
|
int64_t age64;
|
||||||
|
timestamp_t now;
|
||||||
|
timestamp_update(&now);
|
||||||
|
age64 = (int64_t)now - (int64_t)(*timestamp);
|
||||||
|
if (age64 < 0 || age64 > UINT32_MAX) {
|
||||||
|
*age_p = UINT32_MAX;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*age_p = (uint32_t)age64;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue