aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2019-11-07 04:22:07 +0100
committerNeels Hofmeyr <neels@hofmeyr.de>2019-11-11 05:31:59 +0100
commit2975bcbc2febb5c284b0db750e7f40b95dcf7998 (patch)
treec8086e2b6800e0a4be3ad81c6ebfa3eb53312b45
parent749e42902dc42eeeda5a056220f093329f9b51e4 (diff)
hlr mslookup and gsup proxy works for the first time
-rw-r--r--configure.ac1
-rw-r--r--include/osmocom/gsupclient/gsup_client.h12
-rw-r--r--src/db_hlr.c2
-rw-r--r--src/dgsm.c310
-rw-r--r--src/dgsm.h7
-rw-r--r--src/dgsm_vty.c4
-rw-r--r--src/gsup_router.c5
-rw-r--r--src/gsup_router.h1
-rw-r--r--src/gsup_server.c11
-rw-r--r--src/gsupclient/gsup_client.c75
-rw-r--r--src/hlr.c23
-rw-r--r--src/hlr.h1
-rw-r--r--src/hlr_ussd.c8
-rw-r--r--src/hlr_vty.c20
-rw-r--r--src/luop.c11
-rw-r--r--src/luop.h2
-rw-r--r--src/mslookup_server.c2
-rw-r--r--src/mslookup_server_mdns.c2
-rw-r--r--src/proxy.c13
-rw-r--r--src/proxy.h10
-rw-r--r--src/remote_hlr.c101
-rw-r--r--src/remote_hlr.h2
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/mslookup_manual_test/Makefile.am40
-rw-r--r--tests/mslookup_manual_test/fake_msc.c80
-rw-r--r--tests/mslookup_manual_test/osmo-hlr-1.cfg40
-rw-r--r--tests/mslookup_manual_test/osmo-hlr-2.cfg40
-rwxr-xr-xtests/mslookup_manual_test/run.sh22
28 files changed, 739 insertions, 107 deletions
diff --git a/configure.ac b/configure.ac
index 5089958..c0cf28b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -188,4 +188,5 @@ AC_OUTPUT(
tests/gsup/Makefile
tests/db/Makefile
tests/db_upgrade/Makefile
+ tests/mslookup_manual_test/Makefile
)
diff --git a/include/osmocom/gsupclient/gsup_client.h b/include/osmocom/gsupclient/gsup_client.h
index 154e3e0..deae4d4 100644
--- a/include/osmocom/gsupclient/gsup_client.h
+++ b/include/osmocom/gsupclient/gsup_client.h
@@ -39,6 +39,8 @@ struct osmo_gsup_client;
/* Expects message in msg->l2h */
typedef int (*osmo_gsup_client_read_cb_t)(struct osmo_gsup_client *gsupc, struct msgb *msg);
+typedef bool (*osmo_gsup_client_up_down_cb_t)(struct osmo_gsup_client *gsupc, bool up);
+
struct osmo_gsup_client {
const char *unit_name; /* same as ipa_dev->unit_name, for backwards compat */
@@ -54,8 +56,18 @@ struct osmo_gsup_client {
int got_ipa_pong;
struct ipaccess_unit *ipa_dev; /* identification information sent to IPA server */
+
+ osmo_gsup_client_up_down_cb_t up_down_cb;
};
+struct osmo_gsup_client *osmo_gsup_client_create3(void *talloc_ctx,
+ struct ipaccess_unit *ipa_dev,
+ const char *ip_addr,
+ unsigned int tcp_port,
+ struct osmo_oap_client_config *oapc_config,
+ osmo_gsup_client_read_cb_t read_cb,
+ osmo_gsup_client_up_down_cb_t up_down_cb,
+ void *data);
struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
struct ipaccess_unit *ipa_dev,
const char *ip_addr,
diff --git a/src/db_hlr.c b/src/db_hlr.c
index 8d606f8..161c0bd 100644
--- a/src/db_hlr.c
+++ b/src/db_hlr.c
@@ -906,7 +906,7 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val,
/* FIXME: only send to single SGSN where latest update for IMSI came from */
llist_for_each_entry(co, &hlr->gs->clients, list) {
- luop = lu_op_alloc_conn(co);
+ luop = lu_op_alloc_conn(co, NULL);
if (!luop) {
LOGHLR(subscr->imsi, LOGL_ERROR,
"Cannot notify GSUP client, cannot allocate lu_operation,"
diff --git a/src/dgsm.c b/src/dgsm.c
index ba8ca20..85b067b 100644
--- a/src/dgsm.c
+++ b/src/dgsm.c
@@ -13,9 +13,6 @@
#include "mslookup_server_mdns.h"
#include "global_title.h"
-#define LOG_DGSM(imsi, level, fmt, args...) \
- LOGP(DDGSM, level, "(IMSI-%s) " fmt, imsi, ##args)
-
void *dgsm_ctx = NULL;
const struct global_title dgsm_config_msc_wildcard = {};
@@ -174,16 +171,102 @@ static void defer_gsup_message(const struct osmo_gsup_message *gsup)
/* Since osmo_gsup_message has a lot of dangling external pointers, the only way to defer the message is to
* store it encoded. */
m->gsup = osmo_gsup_msgb_alloc("GSUP proxy defer");
- osmo_gsup_encode(m->gsup, gsup);
+ if (osmo_gsup_encode(m->gsup, gsup)) {
+ LOGP(DDGSM, LOGL_ERROR, "GSUP proxy defer: unable to encode GSUP message: %s %s\n",
+ osmo_gsup_message_type_name(gsup->message_type), osmo_quote_str(gsup->imsi, -1));
+ msgb_free(m->gsup);
+ talloc_free(m);
+ return;
+ }
llist_add_tail(&m->entry, &pending_gsup_messages);
}
-void dgsm_send_to_remote_hlr(const struct proxy_subscr *ps, const struct osmo_gsup_message *gsup)
+/* Unable to resolve remote HLR for this IMSI, Answer with error back to the sender. */
+static void defer_gsup_message_err(struct pending_gsup_message *m)
+{
+ struct osmo_gsup_message gsup;
+ struct osmo_gsup_conn *conn;
+
+ printf("whaha %p %p\n", m, m->gsup);
+ if (osmo_gsup_decode(m->gsup->data, m->gsup->len, &gsup)) {
+ LOGP(DDGSM, LOGL_ERROR, "Unable to decode deferred GSUP message for %s\n", m->imsi);
+ msgb_free(m->gsup);
+ m->gsup = NULL;
+ return;
+ }
+
+ conn = gsup_route_find(g_hlr->gs, gsup.source_name, gsup.source_name_len);
+ if (!conn) {
+ LOGP(DDGSM, LOGL_ERROR, "Unable to send error reply for %s %s, source_name is not known: %s\n",
+ osmo_gsup_message_type_name(gsup.message_type), gsup.imsi,
+ osmo_quote_str_c(OTC_SELECT, (char*)gsup.source_name, gsup.source_name_len));
+ return;
+ }
+ osmo_gsup_conn_send_err_reply(conn, &gsup, GMM_CAUSE_IMSI_UNKNOWN);
+ msgb_free(m->gsup);
+ m->gsup = NULL;
+}
+
+/* Forward spooled message for this IMSI to remote HLR. */
+static void defer_gsup_message_send(struct pending_gsup_message *m, struct remote_hlr *remote_hlr)
+{
+ /* To be able to log what we are forwarding, the spooled message needs to be decoded. Do that only if debug
+ * logging is enabled and decoding succeeds. If decoding fails, don't change anything about the code path, so
+ * that logging doesn't affect how osmo-hlr works. */
+ if (log_check_level(DDGSM, LOGL_DEBUG)) {
+ struct osmo_gsup_message gsup;
+ if (osmo_gsup_decode(m->gsup->data, m->gsup->len, &gsup))
+ LOGP(DDGSM, LOGL_ERROR, "Unable to decode deferred GSUP message for %s\n", m->imsi);
+ else
+ LOGP(DDGSM, LOGL_DEBUG,
+ "Forwarding deferred GSUP: %s %s from %s to " OSMO_SOCKADDR_STR_FMT "\n",
+ osmo_gsup_message_type_name(gsup.message_type), gsup.imsi,
+ osmo_quote_str_c(OTC_SELECT, (char*)gsup.source_name, gsup.source_name_len),
+ OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
+ }
+
+ /* If sending fails, still discard. */
+ if (!remote_hlr->gsupc || !remote_hlr->gsupc->is_connected) {
+ LOGP(DDGSM, LOGL_ERROR, "GSUP link to remote HLR is not connected: " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
+ defer_gsup_message_err(m);
+ return;
+ }
+
+ remote_hlr_msgb_send(remote_hlr, m->gsup);
+}
+
+/* Result of looking for remote HLR. If it failed, pass remote_hlr as NULL. */
+static void defer_gsup_message_pop(const char *imsi, struct remote_hlr *remote_hlr)
+{
+ struct pending_gsup_message *m, *n;
+
+ if (remote_hlr)
+ LOG_DGSM(imsi, LOGL_DEBUG, "Sending spooled GSUP messages to remote HLR at " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
+ else
+ LOG_DGSM(imsi, LOGL_ERROR, "No remote HLR found, dropping spooled GSUP messages\n");
+
+ llist_for_each_entry_safe(m, n, &pending_gsup_messages, entry) {
+ if (strcmp(m->imsi, imsi))
+ continue;
+
+ if (!remote_hlr)
+ defer_gsup_message_err(m);
+ else
+ defer_gsup_message_send(m, remote_hlr);
+
+ llist_del(&m->entry);
+ talloc_free(m);
+ }
+}
+
+void dgsm_send_to_remote_hlr(const struct proxy_subscr *proxy_subscr, const struct osmo_gsup_message *gsup)
{
struct remote_hlr *remote_hlr;
- if (!osmo_sockaddr_str_is_nonzero(&ps->remote_hlr)) {
+ if (!osmo_sockaddr_str_is_nonzero(&proxy_subscr->remote_hlr_addr)) {
/* We don't know the remote target yet. Still waiting for an MS lookup response. */
LOG_DGSM(gsup->imsi, LOGL_DEBUG, "GSUP Proxy: deferring until remote proxy is known: %s\n",
osmo_gsup_message_type_name(gsup->message_type));
@@ -192,67 +275,216 @@ void dgsm_send_to_remote_hlr(const struct proxy_subscr *ps, const struct osmo_gs
}
LOG_DGSM(gsup->imsi, LOGL_DEBUG, "GSUP Proxy: forwarding to " OSMO_SOCKADDR_STR_FMT ": %s\n",
- OSMO_SOCKADDR_STR_FMT_ARGS(&ps->remote_hlr), osmo_gsup_message_type_name(gsup->message_type));
+ OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr->remote_hlr_addr),
+ osmo_gsup_message_type_name(gsup->message_type));
- remote_hlr = remote_hlr_get(&ps->remote_hlr, true);
+ remote_hlr = remote_hlr_get(&proxy_subscr->remote_hlr_addr, true);
if (!remote_hlr) {
LOG_DGSM(gsup->imsi, LOGL_ERROR, "Failed to establish connection to remote HLR " OSMO_SOCKADDR_STR_FMT
", discarding GSUP: %s\n",
- OSMO_SOCKADDR_STR_FMT_ARGS(&ps->remote_hlr), osmo_gsup_message_type_name(gsup->message_type));
+ OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr->remote_hlr_addr),
+ osmo_gsup_message_type_name(gsup->message_type));
+ return;
+ }
+
+ if (!remote_hlr->gsupc || !remote_hlr->gsupc->is_connected) {
+ /* GSUP link is still busy establishing... */
+ LOG_DGSM(gsup->imsi, LOGL_DEBUG, "GSUP Proxy: deferring until link to remote proxy is up: %s\n",
+ osmo_gsup_message_type_name(gsup->message_type));
+ defer_gsup_message(gsup);
return;
}
remote_hlr_gsup_send(remote_hlr, gsup);
}
+static void resolve_hlr_result_cb(struct osmo_mslookup_client *client,
+ uint32_t request_handle,
+ const struct osmo_mslookup_query *query,
+ const struct osmo_mslookup_result *result)
+{
+ struct proxy *proxy = g_hlr->gsup_proxy.cs;
+ const struct proxy_subscr *proxy_subscr;
+ struct proxy_subscr proxy_subscr_new;
+ struct remote_hlr *remote_hlr;
+
+ /* A remote HLR is answering back, indicating that it is the home HLR for a given IMSI.
+ * There should be a mostly empty proxy entry for that IMSI.
+ * Add the remote address data in the proxy. */
+ if (query->id.type != OSMO_MSLOOKUP_ID_IMSI) {
+ LOGP(DDGSM, LOGL_ERROR, "Expected IMSI ID type in mslookup query+result: %s\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, result));
+ return;
+ }
+
+ proxy_subscr = proxy_subscr_get_by_imsi(proxy, query->id.imsi);
+ if (!proxy_subscr) {
+ LOG_DGSM(query->id.imsi, LOGL_ERROR, "No proxy entry for mslookup result: %s\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, result));
+ defer_gsup_message_pop(query->id.imsi, NULL);
+ return;
+ }
+
+ if (result->rc != OSMO_MSLOOKUP_RC_OK) {
+ LOG_DGSM(query->id.imsi, LOGL_ERROR, "Failed to resolve remote HLR: %s\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, result));
+ defer_gsup_message_pop(query->id.imsi, NULL);
+ proxy_subscr_del(proxy, proxy_subscr->imsi);
+ return;
+ }
+
+ /* Store the address. Make a copy to modify. */
+ proxy_subscr_new = *proxy_subscr;
+ proxy_subscr = &proxy_subscr_new;
+ if (osmo_sockaddr_str_is_nonzero(&result->host_v4))
+ proxy_subscr_new.remote_hlr_addr = result->host_v4;
+ else if (osmo_sockaddr_str_is_nonzero(&result->host_v6))
+ proxy_subscr_new.remote_hlr_addr = result->host_v6;
+ else {
+ LOG_DGSM(query->id.imsi, LOGL_ERROR, "Invalid address for remote HLR: %s\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, result));
+ defer_gsup_message_pop(query->id.imsi, NULL);
+ proxy_subscr_del(proxy, proxy_subscr->imsi);
+ return;
+ }
+
+ if (proxy_subscr_update(proxy, proxy_subscr)) {
+ LOG_DGSM(query->id.imsi, LOGL_ERROR, "Failed to store proxy entry for remote HLR: %s\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, result));
+ defer_gsup_message_pop(query->id.imsi, NULL);
+ proxy_subscr_del(proxy, proxy_subscr->imsi);
+ return;
+ }
+ LOG_DGSM(proxy_subscr->imsi, LOGL_DEBUG, "Stored remote hlr address for this IMSI: " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr->remote_hlr_addr));
+
+ remote_hlr = remote_hlr_get(&proxy_subscr->remote_hlr_addr, true);
+ if (!remote_hlr) {
+ defer_gsup_message_pop(query->id.imsi, NULL);
+ proxy_subscr_del(proxy, proxy_subscr->imsi);
+ return;
+ }
+
+ if (!remote_hlr->gsupc || !remote_hlr->gsupc->is_connected) {
+ LOG_DGSM(query->id.imsi, LOGL_DEBUG, "Resolved remote HLR, waiting for link-up: %s\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, result));
+ return;
+ }
+
+ LOG_DGSM(query->id.imsi, LOGL_DEBUG, "Resolved remote HLR, sending spooled GSUP messages: %s\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, result));
+ defer_gsup_message_pop(query->id.imsi, remote_hlr);
+}
+
+static bool remote_hlr_up_yield(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, void *data)
+{
+ struct remote_hlr *remote_hlr = data;
+ defer_gsup_message_pop(proxy_subscr->imsi, remote_hlr);
+ return true;
+}
+
+void dgsm_remote_hlr_up(struct remote_hlr *remote_hlr)
+{
+ LOGP(DDGSM, LOGL_NOTICE, "link to remote HLR is up, sending spooled GSUP messages: " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
+ /* Send all spooled GSUP messaged for IMSIs that are waiting for this link to establish. */
+ proxy_subscrs_get_by_remote_hlr(g_hlr->gsup_proxy.cs, &remote_hlr->addr, remote_hlr_up_yield, remote_hlr);
+}
+
/* Return true when the message has been handled by D-GSM. */
bool dgsm_check_forward_gsup_msg(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
{
const struct proxy_subscr *proxy_subscr;
- struct proxy_subscr ps_new;
- struct gsup_route *r;
+ struct proxy_subscr proxy_subscr_new;
+ struct gsup_route *route;
struct osmo_gsup_message gsup_copy;
- struct proxy *proxy = g_hlr->gsup_proxy.cs;
- if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
+ struct proxy *proxy;
+ struct osmo_mslookup_query query;
+ struct osmo_mslookup_query_handling handling;
+ uint32_t request_handle;
+
+ switch (gsup->cn_domain) {
+ case OSMO_GSUP_CN_DOMAIN_CS:
+ proxy = g_hlr->gsup_proxy.cs;
+ break;
+ case OSMO_GSUP_CN_DOMAIN_PS:
proxy = g_hlr->gsup_proxy.ps;
+ break;
+ default:
+ LOG_DGSM(gsup->imsi, LOGL_ERROR, "Unknown cn_domain: %d\n", gsup->cn_domain);
+ osmo_gsup_conn_send_err_reply(conn, gsup, GMM_CAUSE_INV_MAND_INFO);
+ return -GMM_CAUSE_INV_MAND_INFO;
+ }
- proxy_subscr = proxy_subscr_get_by_imsi(proxy, gsup->imsi);
- if (proxy_subscr)
- goto yes_we_are_proxying;
+ /* To forward to a remote HLR, we need to indicate the source MSC's name to make sure the reply can be routed
+ * back. Store the sender MSC in gsup->source_name -- the remote HLR is required to return this as
+ * gsup->destination_name so that the reply gets routed to the original MSC. */
+ route = gsup_route_find_by_conn(conn);
+ if (!route) {
+ /* The conn has not sent its IPA unit name yet, and hence we won't be able to proxy responses back from
+ * a remote HLR. Send GSUP error and indicate that this message has been handled. */
+ osmo_gsup_conn_send_err_reply(conn, gsup, GMM_CAUSE_NET_FAIL);
+ return true;
+ }
- /* No proxy entry exists. If the IMSI is known in the local HLR, then we won't proxy. */
+ /* If the IMSI is known in the local HLR, then we won't proxy. */
if (db_subscr_exists_by_imsi(g_hlr->dbc, gsup->imsi) == 0)
return false;
+ /* Are we already forwarding this IMSI to a remote HLR? */
+ proxy_subscr = proxy_subscr_get_by_imsi(proxy, gsup->imsi);
+ if (proxy_subscr)
+ goto yes_we_are_proxying;
+
/* The IMSI is not known locally, so we want to proxy to a remote HLR, but no proxy entry exists yet. We need to
* look up the subscriber in remote HLRs via D-GSM mslookup, forward GSUP and reply once a result is back from
* there. Defer message and kick off MS lookup. */
+ /* Kick off an mslookup for the remote HLR. */
+ if (!g_hlr->mslookup.client.client) {
+ LOGP(DDGSM, LOGL_DEBUG, "mslookup client not running, cannot query remote home HLR for %s\n",
+ gsup->imsi);
+ return false;
+ }
+
+ query = (struct osmo_mslookup_query){
+ .id = {
+ .type = OSMO_MSLOOKUP_ID_IMSI,
+ }
+ };
+ OSMO_STRLCPY_ARRAY(query.id.imsi, gsup->imsi);
+ OSMO_STRLCPY_ARRAY(query.service, OSMO_MSLOOKUP_SERVICE_HLR_GSUP);
+ handling = (struct osmo_mslookup_query_handling){
+ .result_timeout_milliseconds = g_hlr->mslookup.client.result_timeout_milliseconds,
+ .result_cb = resolve_hlr_result_cb,
+ };
+ request_handle = osmo_mslookup_client_request(g_hlr->mslookup.client.client, &query, &handling);
+ if (!request_handle) {
+ LOG_DGSM(gsup->imsi, LOGL_ERROR, "Error dispatching mslookup query for home HLR: %s\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, &query, NULL));
+ proxy_subscr_del(proxy, gsup->imsi);
+ return false;
+ }
+
/* Add a proxy entry without a remote address to indicate that we are busy querying for a remote HLR. */
- ps_new = (struct proxy_subscr){};
- OSMO_STRLCPY_ARRAY(ps_new.imsi, gsup->imsi);
- proxy_subscr_update(proxy, &ps_new);
- proxy_subscr = &ps_new;
+ proxy_subscr_new = (struct proxy_subscr){};
+ OSMO_STRLCPY_ARRAY(proxy_subscr_new.imsi, gsup->imsi);
+ global_title_set(&proxy_subscr_new.vlr_name, route->addr, talloc_total_size(route->addr));
+ proxy_subscr = &proxy_subscr_new;
+ proxy_subscr_update(proxy, proxy_subscr);
yes_we_are_proxying:
OSMO_ASSERT(proxy_subscr);
- /* To forward to a remote HLR, we need to indicate the source MSC's name to make sure the reply can be routed
- * back. Store the sender MSC in gsup->source_name -- the remote HLR is required to return this as
- * gsup->destination_name so that the reply gets routed to the original MSC. */
- r = gsup_route_find_by_conn(conn);
- if (!r) {
- /* The conn has not sent its IPA unit name yet, and hence we won't be able to proxy responses back from
- * a remote HLR. Send GSUP error and indicate that this message has been handled. */
- osmo_gsup_conn_send_err_reply(conn, gsup, GMM_CAUSE_NET_FAIL);
- return true;
- }
-
- /* Be aware that osmo_gsup_message has a lot of external pointer references, so this is not a deep copy. */
+ /* Be aware that osmo_gsup_message has a lot of external pointer references, so this is not a deep copy.
+ * dgsm_send_to_remote_hlr() may need to defer the GSUP message until the remote HLR has answered, in which case
+ * it will take care to deep copy (encode into a msgb), i.e. it is fine to pass a shallow copy. */
gsup_copy = *gsup;
- gsup_copy.source_name = r->addr;
- gsup_copy.source_name_len = talloc_total_size(r->addr);
+ gsup_copy.source_name = route->addr;
+ gsup_copy.source_name_len = talloc_total_size(route->addr);
+ /* If the remote HLR is already known, directly forward the GSUP message; otherwise, spool the GSUP message
+ * until the remote HLR will respond / until timeout aborts. */
dgsm_send_to_remote_hlr(proxy_subscr, &gsup_copy);
return true;
}
@@ -263,7 +495,15 @@ void dgsm_init(void *ctx)
dgsm_ctx = talloc_named_const(ctx, 0, "dgsm");
dgsm_pending_messages_ctx = talloc_named_const(dgsm_ctx, 0, "dgsm_pending_messages");
INIT_LLIST_HEAD(&g_hlr->mslookup.vty.server.msc_configs);
+
g_hlr->mslookup.server.max_age = 60 * 60;
+
+ g_hlr->mslookup.client.result_timeout_milliseconds = 2000;
+
+ g_hlr->gsup_proxy.gsup_client_name.unit_name = "HLR";
+ g_hlr->gsup_proxy.gsup_client_name.serno = "unnamed-HLR";
+ g_hlr->gsup_proxy.gsup_client_name.swversion = PACKAGE_NAME "-" PACKAGE_VERSION;
+
osmo_sockaddr_str_from_str(&g_hlr->mslookup.vty.server.mdns.bind_addr,
OSMO_MSLOOKUP_MDNS_IP4, OSMO_MSLOOKUP_MDNS_PORT);
osmo_sockaddr_str_from_str(&g_hlr->mslookup.vty.client.mdns.query_addr,
diff --git a/src/dgsm.h b/src/dgsm.h
index 4bbf7fa..972d598 100644
--- a/src/dgsm.h
+++ b/src/dgsm.h
@@ -4,7 +4,11 @@
#include "gsup_server.h"
#include "global_title.h"
+#define LOG_DGSM(imsi, level, fmt, args...) \
+ LOGP(DDGSM, level, "(IMSI-%s) " fmt, imsi, ##args)
+
struct vty;
+struct remote_hlr;
extern void *dgsm_ctx;
@@ -67,4 +71,5 @@ void dgsm_start(void *ctx);
bool dgsm_check_forward_gsup_msg(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
void dgsm_vty_init();
-void dgsm_vty_go_parent_action(struct vty *vty);
+
+void dgsm_remote_hlr_up(struct remote_hlr *remote_hlr);
diff --git a/src/dgsm_vty.c b/src/dgsm_vty.c
index c3da81e..4335c44 100644
--- a/src/dgsm_vty.c
+++ b/src/dgsm_vty.c
@@ -353,7 +353,3 @@ void dgsm_vty_init()
install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_mdns_cmd);
}
-
-void dgsm_vty_go_parent_action(struct vty *vty)
-{
-}
diff --git a/src/gsup_router.c b/src/gsup_router.c
index df978ba..7794320 100644
--- a/src/gsup_router.c
+++ b/src/gsup_router.c
@@ -47,6 +47,11 @@ struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
return NULL;
}
+struct osmo_gsup_conn *gsup_route_find_gt(struct osmo_gsup_server *gs, const struct global_title *gt)
+{
+ return gsup_route_find(gs, gt->val, gt->len);
+}
+
/*! Find a GSUP connection's route (to read the IPA address from the route).
* \param[in] conn GSUP connection
* \return GSUP route
diff --git a/src/gsup_router.h b/src/gsup_router.h
index 339ab66..0eb3ba9 100644
--- a/src/gsup_router.h
+++ b/src/gsup_router.h
@@ -13,6 +13,7 @@ struct gsup_route {
struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
const uint8_t *addr, size_t addrlen);
+struct osmo_gsup_conn *gsup_route_find_gt(struct osmo_gsup_server *gs, const struct global_title *gt);
struct gsup_route *gsup_route_find_by_conn(const struct osmo_gsup_conn *conn);
diff --git a/src/gsup_server.c b/src/gsup_server.c
index a22ee14..f10034b 100644
--- a/src/gsup_server.c
+++ b/src/gsup_server.c
@@ -82,14 +82,10 @@ void osmo_gsup_conn_send_err_reply(struct osmo_gsup_conn *conn, const struct osm
/* RP-Message-Reference is mandatory for SM Service */
.sm_rp_mr = gsup_orig->sm_rp_mr,
};
+ osmo_gsup_set_reply(gsup_orig, &gsup_reply);
- OSMO_STRLCPY_ARRAY(gsup_reply.imsi, gsup_orig->imsi);
-
- /* For SS/USSD, it's important to keep both session state and ID IEs */
- if (gsup_orig->session_state != OSMO_GSUP_SESSION_STATE_NONE) {
+ 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;
- }
msg_out = osmo_gsup_msgb_alloc("GSUP ERR response");
rc = osmo_gsup_encode(msg_out, &gsup_reply);
@@ -100,6 +96,9 @@ void osmo_gsup_conn_send_err_reply(struct osmo_gsup_conn *conn, const struct osm
return;
}
+ LOGP(DLGSUP, LOGL_DEBUG, "%s: GSUP tx %s\n",
+ osmo_quote_str(gsup_orig->imsi, -1), osmo_gsup_message_type_name(gsup_reply.message_type));
+
rc = osmo_gsup_conn_send(conn, msg_out);
if (rc)
LOGP(DLGSUP, LOGL_ERROR, "%s: Unable to send error response %s (rc=%d)\n",
diff --git a/src/gsupclient/gsup_client.c b/src/gsupclient/gsup_client.c
index 814d5a2..4216c2c 100644
--- a/src/gsupclient/gsup_client.c
+++ b/src/gsupclient/gsup_client.c
@@ -97,7 +97,14 @@ static void connect_timer_cb(void *gsupc_)
if (gsupc->is_connected)
return;
+ if (gsupc->up_down_cb) {
+ /* When the up_down_cb() returns false, the user asks us not to retry connecting. */
+ if (!gsupc->up_down_cb(gsupc, false))
+ return;
+ }
+
gsup_client_connect(gsupc);
+
}
static void client_send(struct osmo_gsup_client *gsupc, int proto_ext,
@@ -139,9 +146,18 @@ static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
gsup_client_oap_register(gsupc);
osmo_timer_del(&gsupc->connect_timer);
+
+ if (gsupc->up_down_cb)
+ gsupc->up_down_cb(gsupc, true);
} else {
osmo_timer_del(&gsupc->ping_timer);
+ if (gsupc->up_down_cb) {
+ /* When the up_down_cb() returns false, the user asks us not to retry connecting. */
+ if (!gsupc->up_down_cb(gsupc, false))
+ return;
+ }
+
osmo_timer_schedule(&gsupc->connect_timer,
OSMO_GSUP_CLIENT_RECONNECT_INTERVAL, 0);
}
@@ -258,33 +274,29 @@ static void start_test_procedure(struct osmo_gsup_client *gsupc)
gsup_client_send_ping(gsupc);
}
-/*!
- * Create a gsup client connecting to the specified IP address and TCP port.
- * Use the provided ipaccess unit as the client-side identifier; ipa_dev should
- * be allocated in talloc_ctx talloc_ctx as well.
- * \param[in] talloc_ctx talloc context.
- * \param[in] ipa_dev IP access unit which contains client identification information; must be allocated
- * in talloc_ctx as well to ensure it lives throughout the lifetime of the connection.
- * \param[in] ip_addr GSUP server IP address.
- * \param[in] tcp_port GSUP server TCP port.
- * \param[in] read_cb callback for reading from the GSUP connection.
- * \param[in] oapc_config OPA client configuration.
- * \returns a GSUP client connection or NULL on failure.
- */
-struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
+struct osmo_gsup_client *osmo_gsup_client_create3(void *talloc_ctx,
struct ipaccess_unit *ipa_dev,
const char *ip_addr,
unsigned int tcp_port,
+ struct osmo_oap_client_config *oapc_config,
osmo_gsup_client_read_cb_t read_cb,
- struct osmo_oap_client_config *oapc_config)
+ osmo_gsup_client_up_down_cb_t up_down_cb,
+ void *data)
{
struct osmo_gsup_client *gsupc;
int rc;
+ OSMO_ASSERT(ipa_dev->unit_name);
+
gsupc = talloc_zero(talloc_ctx, struct osmo_gsup_client);
OSMO_ASSERT(gsupc);
- gsupc->unit_name = (const char *)ipa_dev->unit_name; /* API backwards compat */
- gsupc->ipa_dev = ipa_dev;
+ *gsupc = (struct osmo_gsup_client){
+ .unit_name = (const char *)ipa_dev->unit_name, /* API backwards compat */
+ .ipa_dev = ipa_dev,
+ .read_cb = read_cb,
+ .up_down_cb = up_down_cb,
+ .data = data,
+ };
/* a NULL oapc_config will mark oap_state disabled. */
rc = osmo_oap_client_init(oapc_config, &gsupc->oap_state);
@@ -303,14 +315,9 @@ struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
goto failed;
osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc);
-
rc = gsup_client_connect(gsupc);
-
if (rc < 0)
goto failed;
-
- gsupc->read_cb = read_cb;
-
return gsupc;
failed:
@@ -318,6 +325,30 @@ failed:
return NULL;
}
+/*!
+ * Create a gsup client connecting to the specified IP address and TCP port.
+ * Use the provided ipaccess unit as the client-side identifier; ipa_dev should
+ * be allocated in talloc_ctx talloc_ctx as well.
+ * \param[in] talloc_ctx talloc context.
+ * \param[in] ipa_dev IP access unit which contains client identification information; must be allocated
+ * in talloc_ctx as well to ensure it lives throughout the lifetime of the connection.
+ * \param[in] ip_addr GSUP server IP address.
+ * \param[in] tcp_port GSUP server TCP port.
+ * \param[in] read_cb callback for reading from the GSUP connection.
+ * \param[in] oapc_config OPA client configuration.
+ * \returns a GSUP client connection or NULL on failure.
+ */
+struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
+ struct ipaccess_unit *ipa_dev,
+ const char *ip_addr,
+ unsigned int tcp_port,
+ osmo_gsup_client_read_cb_t read_cb,
+ struct osmo_oap_client_config *oapc_config)
+{
+ return osmo_gsup_client_create3(talloc_ctx, ipa_dev, ip_addr, tcp_port, oapc_config,
+ read_cb, NULL, NULL);
+}
+
/**
* Like osmo_gsup_client_create2() except it expects a unit name instead
* of a full-blown ipacess_unit as the client-side identifier.
diff --git a/src/hlr.c b/src/hlr.c
index 7b24017..2ee504c 100644
--- a/src/hlr.c
+++ b/src/hlr.c
@@ -234,15 +234,14 @@ static int rx_send_auth_info(struct osmo_gsup_conn *conn,
const struct osmo_gsup_message *gsup,
struct db_context *dbc)
{
- struct osmo_gsup_message gsup_out;
+ struct osmo_gsup_message gsup_out = {};
struct msgb *msg_out;
int rc;
subscr_create_on_demand(gsup->imsi);
/* initialize return message structure */
- memset(&gsup_out, 0, sizeof(gsup_out));
- memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi));
+ osmo_gsup_set_reply(gsup, &gsup_out);
rc = db_get_auc(dbc, gsup->imsi, conn->auc_3g_ind,
gsup_out.auth_vectors,
@@ -344,7 +343,7 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
const struct osmo_gsup_message *gsup)
{
struct hlr_subscriber *subscr;
- struct lu_operation *luop = lu_op_alloc_conn(conn);
+ struct lu_operation *luop = lu_op_alloc_conn(conn, gsup);
if (!luop) {
LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
return -EINVAL;
@@ -406,9 +405,15 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
#endif
/* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */
- LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s\n",
- subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",
- global_title_name(&luop->peer));
+ if (global_title_cmp(&luop->vlr_number, &luop->peer))
+ LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s, via proxy %s\n",
+ subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",
+ global_title_name(&luop->vlr_number), global_title_name(&luop->peer));
+ else
+ LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s\n",
+ subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",
+ global_title_name(&luop->vlr_number));
+
if (db_subscr_lu(g_hlr->dbc, subscr->id, &luop->peer, &luop->vlr_number, luop->is_ps))
LOGP(DAUC, LOGL_ERROR, "IMSI='%s': Cannot update %s in the database\n",
subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number");
@@ -425,12 +430,12 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
static int rx_purge_ms_req(struct osmo_gsup_conn *conn,
const struct osmo_gsup_message *gsup)
{
- struct osmo_gsup_message gsup_reply = {0};
+ struct osmo_gsup_message gsup_reply = {};
struct msgb *msg_out;
bool is_ps = false;
int rc;
- memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));
+ osmo_gsup_set_reply(gsup, &gsup_reply);
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
is_ps = true;
diff --git a/src/hlr.h b/src/hlr.h
index 52356af..cbce209 100644
--- a/src/hlr.h
+++ b/src/hlr.h
@@ -76,6 +76,7 @@ struct hlr {
} server;
struct {
+ unsigned int result_timeout_milliseconds;
struct osmo_mslookup_client *client;
struct osmo_mslookup_client_method *mdns;
} client;
diff --git a/src/hlr_ussd.c b/src/hlr_ussd.c
index be0fee0..e2dbde1 100644
--- a/src/hlr_ussd.c
+++ b/src/hlr_ussd.c
@@ -262,16 +262,18 @@ static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_m
bool final, struct msgb *ss_msg)
{
- struct osmo_gsup_message resp = {0};
+ struct osmo_gsup_message resp = {
+ .message_type = gsup_msg_type,
+ .cn_domain = OSMO_GSUP_CN_DOMAIN_CS,
+ .session_id = ss->session_id,
+ };
struct msgb *resp_msg;
- resp.message_type = gsup_msg_type;
OSMO_STRLCPY_ARRAY(resp.imsi, ss->imsi);
if (final)
resp.session_state = OSMO_GSUP_SESSION_STATE_END;
else
resp.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
- resp.session_id = ss->session_id;
if (ss_msg) {
resp.ss_info = msgb_data(ss_msg);
resp.ss_info_len = msgb_length(ss_msg);
diff --git a/src/hlr_vty.c b/src/hlr_vty.c
index 0c359f8..314278f 100644
--- a/src/hlr_vty.c
+++ b/src/hlr_vty.c
@@ -147,6 +147,24 @@ DEFUN(cfg_hlr_gsup_bind_ip,
return CMD_SUCCESS;
}
+DEFUN(cfg_hlr_gsup_ipa_name,
+ cfg_hlr_gsup_ipa_name_cmd,
+ "ipa-name NAME",
+ "Set the IPA name of this HLR, for proxying to remote HLRs\n"
+ "A globally unique name for this HLR. For example: PLMN + redundancy server number: HLR-901-70-0. "
+ "This name is used for GSUP routing and must be set if multiple HLRs interconnect (e.g. mslookup "
+ "for Distributed GSM).\n")
+{
+ if (vty->type != VTY_FILE) {
+ vty_out(vty, "gsup/ipa-name: The GSUP IPA name cannot be changed at run-time; "
+ "It can only be set in the configuraton file.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ g_hlr->gsup_proxy.gsup_client_name.serno = talloc_strdup(g_hlr, argv[0]);
+ return CMD_SUCCESS;
+}
+
/***********************************************************************
* USSD Entity
***********************************************************************/
@@ -397,7 +415,6 @@ DEFUN(cfg_no_subscr_create_on_demand, cfg_no_subscr_create_on_demand_cmd,
int hlr_vty_go_parent(struct vty *vty)
{
- dgsm_vty_go_parent_action(vty);
switch (vty->node) {
case GSUP_NODE:
case EUSE_NODE:
@@ -457,6 +474,7 @@ void hlr_vty_init(void)
install_node(&gsup_node, config_write_hlr_gsup);
install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
+ install_element(GSUP_NODE, &cfg_hlr_gsup_ipa_name_cmd);
install_element(HLR_NODE, &cfg_database_cmd);
diff --git a/src/luop.c b/src/luop.c
index b26ed2f..d539c3e 100644
--- a/src/luop.c
+++ b/src/luop.c
@@ -66,6 +66,7 @@ static inline void fill_gsup_msg(struct osmo_gsup_message *out,
osmo_strlcpy(out->imsi, lu->subscr.imsi,
GSM23003_IMSI_MAX_DIGITS + 1);
out->message_type = mt;
+ out->cn_domain = lu->is_ps ? OSMO_GSUP_CN_DOMAIN_PS : OSMO_GSUP_CN_DOMAIN_CS;
}
/* timer call-back in case LU operation doesn't receive an response */
@@ -123,7 +124,7 @@ void lu_op_free(struct lu_operation *luop)
talloc_free(luop);
}
-struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn)
+struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
{
uint8_t *peer_addr;
struct lu_operation *luop = lu_op_alloc(conn->server);
@@ -140,10 +141,14 @@ struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn)
return NULL;
}
+ if (gsup && gsup->source_name_len)
+ global_title_set(&luop->vlr_number, gsup->source_name, gsup->source_name_len);
+ else
+ luop->vlr_number = luop->peer;
+
return luop;
}
-/* FIXME: this doesn't seem to work at all */
struct lu_operation *lu_op_by_imsi(const char *imsi,
const struct llist_head *lst)
{
@@ -255,8 +260,6 @@ void lu_op_tx_del_subscr_data(struct lu_operation *luop)
fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_DELETE_DATA_REQUEST);
- gsup.cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
-
/* Send ISD to new VLR/SGSN */
_luop_tx_gsup(luop, &gsup);
}
diff --git a/src/luop.h b/src/luop.h
index 6ae2407..0848510 100644
--- a/src/luop.h
+++ b/src/luop.h
@@ -67,7 +67,7 @@ struct lu_operation {
struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv);
-struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn);
+struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state);
bool lu_op_fill_subscr(struct lu_operation *luop, struct db_context *dbc,
const char *imsi);
diff --git a/src/mslookup_server.c b/src/mslookup_server.c
index 4255a41..219a20c 100644
--- a/src/mslookup_server.c
+++ b/src/mslookup_server.c
@@ -196,7 +196,7 @@ static bool subscriber_has_done_lu_here_proxy(const struct osmo_mslookup_query *
OSMO_SOCKADDR_STR_FMT "\n",
osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
age, global_title_name(local_msc_name),
- OSMO_SOCKADDR_STR_FMT_ARGS(&subscr->remote_hlr));
+ OSMO_SOCKADDR_STR_FMT_ARGS(&subscr->remote_hlr_addr));
return true;
}
diff --git a/src/mslookup_server_mdns.c b/src/mslookup_server_mdns.c
index c91cd5c..6b705da 100644
--- a/src/mslookup_server_mdns.c
+++ b/src/mslookup_server_mdns.c
@@ -36,7 +36,7 @@ static void osmo_mslookup_server_mdns_tx(struct osmo_mslookup_server_mdns *serve
llist_add_tail(&rec_age->list, &ans.records);
if (osmo_sockaddr_str_is_nonzero(&result->host_v4)) {
- if (osmo_sockaddr_str_to_32(&result->host_v4, &ip_v4)) {
+ if (osmo_sockaddr_str_to_32n(&result->host_v4, &ip_v4)) {
errmsg = "Error encoding IPv4 address";
goto clean_and_exit;
}
diff --git a/src/proxy.c b/src/proxy.c
index a93c004..6724269 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -83,6 +83,19 @@ const struct proxy_subscr *proxy_subscr_get_by_msisdn(struct proxy *proxy, const
return &e->data;
}
+void proxy_subscrs_get_by_remote_hlr(struct proxy *proxy, const struct osmo_sockaddr_str *remote_hlr_addr,
+ bool (*yield)(struct proxy *proxy, const struct proxy_subscr *subscr, void *data),
+ void *data)
+{
+ struct proxy_subscr_listentry *e;
+ llist_for_each_entry(e, &proxy->subscr_list, entry) {
+ if (!osmo_sockaddr_str_ip_cmp(remote_hlr_addr, &e->data.remote_hlr_addr)) {
+ if (!yield(proxy, &e->data, data))
+ return;
+ }
+ }
+}
+
int proxy_subscr_update(struct proxy *proxy, const struct proxy_subscr *proxy_subscr)
{
struct proxy_subscr_listentry *e = _proxy_get_by_imsi(proxy, proxy_subscr->imsi);
diff --git a/src/proxy.h b/src/proxy.h
index 4d332cf..0a99b0d 100644
--- a/src/proxy.h
+++ b/src/proxy.h
@@ -13,6 +13,7 @@ struct proxy {
/* How long to keep proxy entries without a refresh, in seconds. */
uint32_t fresh_time;
+
/* How often to garbage collect the proxy cache, period in seconds.
* To change this and take effect immediately, rather use proxy_set_gc_period(). */
uint32_t gc_period;
@@ -28,14 +29,21 @@ struct proxy_subscr {
struct global_title vlr_via_proxy;
#endif
struct global_title vlr_name;
- struct osmo_sockaddr_str remote_hlr;
+ struct osmo_sockaddr_str remote_hlr_addr;
struct timeval last_lu;
};
struct proxy *proxy_init(void *ctx);
void proxy_del(struct proxy *proxy);
void proxy_set_gc_period(struct proxy *proxy, uint32_t gc_period);
+
+/* The API to access / modify proxy entries keeps the implementation opaque, to make sure that we can easily move proxy
+ * storage to SQLite db. */
const struct proxy_subscr *proxy_subscr_get_by_imsi(struct proxy *proxy, const char *imsi);
const struct proxy_subscr *proxy_subscr_get_by_msisdn(struct proxy *proxy, const char *msisdn);
+void proxy_subscrs_get_by_remote_hlr(struct proxy *proxy, const struct osmo_sockaddr_str *remote_hlr_addr,
+ bool (*yield)(struct proxy *proxy, const struct proxy_subscr *subscr, void *data),
+ void *data);
+const struct proxy_subscr *proxy_subscr_get_by_imsi(struct proxy *proxy, const char *imsi);
int proxy_subscr_update(struct proxy *proxy, const struct proxy_subscr *proxy_subscr);
int proxy_subscr_del(struct proxy *proxy, const char *imsi);
diff --git a/src/remote_hlr.c b/src/remote_hlr.c
index 600b037..37ca913 100644
--- a/src/remote_hlr.c
+++ b/src/remote_hlr.c
@@ -8,6 +8,7 @@
#include "gsup_router.h"
#include "dgsm.h"
#include "remote_hlr.h"
+#include "proxy.h"
static LLIST_HEAD(remote_hlrs);
@@ -49,10 +50,15 @@ void remote_hlr_err_reply(struct osmo_gsup_client *gsupc, const struct osmo_gsup
osmo_quote_str(gsup_orig->imsi, -1));
}
+/* We are receiving back a GSUP message from a remote HLR to go back to a local MSC.
+ * The local MSC shall be indicated by gsup.destination_name. */
static int remote_hlr_rx(struct osmo_gsup_client *gsupc, struct msgb *msg)
{
struct osmo_gsup_message gsup;
- struct osmo_gsup_conn *msc_conn;
+ struct osmo_gsup_conn *vlr_conn;
+ struct proxy *proxy;
+ const struct proxy_subscr *proxy_subscr;
+ struct msgb *gsup_copy;
int rc;
rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
@@ -68,26 +74,66 @@ static int remote_hlr_rx(struct osmo_gsup_client *gsupc, struct msgb *msg)
return -GMM_CAUSE_INV_MAND_INFO;
}
- /* Since this is a proxy link to a remote osmo-msc, we are acting on behalf of a local MSC, and need to know the
- * routing name of that local MSC. We have sent it to the remote HLR as source_name, and we're required to get
- * it back as destination_name. */
- if (!gsup.destination_name || !gsup.destination_name_len) {
- LOG_GSUP_MSG(gsupc, &gsup, LOGL_ERROR, "message lacks Destination Name IE, cannot route to MSC.\n");
+ switch (gsup.cn_domain) {
+ case OSMO_GSUP_CN_DOMAIN_CS:
+ proxy = g_hlr->gsup_proxy.cs;
+ break;
+ case OSMO_GSUP_CN_DOMAIN_PS:
+ proxy = g_hlr->gsup_proxy.ps;
+ break;
+ default:
+ LOG_GSUP_MSG(gsupc, &gsup, LOGL_ERROR, "Unknown cn_domain: %d\n", gsup.cn_domain);
remote_hlr_err_reply(gsupc, &gsup, GMM_CAUSE_INV_MAND_INFO);
return -GMM_CAUSE_INV_MAND_INFO;
}
+ if (!proxy) {
+ LOG_GSUP_MSG(gsupc, &gsup, LOGL_ERROR, "Cannot route, there is no GSUP proxy set up\n");
+ remote_hlr_err_reply(gsupc, &gsup, GMM_CAUSE_NET_FAIL);
+ return -GMM_CAUSE_NET_FAIL;
+ }
+
+ proxy_subscr = proxy_subscr_get_by_imsi(proxy, gsup.imsi);
+ if (!proxy_subscr) {
+ LOG_GSUP_MSG(gsupc, &gsup, LOGL_ERROR, "Cannot route, no GSUP proxy record for this IMSI\n");
+ remote_hlr_err_reply(gsupc, &gsup, GMM_CAUSE_IMSI_UNKNOWN);
+ return -GMM_CAUSE_IMSI_UNKNOWN;
+ }
+
/* Route to MSC that we're proxying for */
- msc_conn = gsup_route_find(g_hlr->gs, gsup.destination_name, gsup.destination_name_len);
- if (!msc_conn) {
- LOG_GSUP_MSG(gsupc, &gsup, LOGL_ERROR, "Destination MSC unreachable: %s\n",
- osmo_quote_str((char*)gsup.destination_name, gsup.destination_name_len));
+ vlr_conn = gsup_route_find_gt(g_hlr->gs, &proxy_subscr->vlr_name);
+ if (!vlr_conn) {
+ LOG_GSUP_MSG(gsupc, &gsup, LOGL_ERROR, "Destination VLR unreachable: %s\n",
+ global_title_name(&proxy_subscr->vlr_name));
remote_hlr_err_reply(gsupc, &gsup, GMM_CAUSE_MSC_TEMP_NOTREACH);
return -GMM_CAUSE_MSC_TEMP_NOTREACH;
}
- /* The outgoing message needs to be a separate msgb, because osmo_gsup_conn_send() takes ownership of it. */
- return osmo_gsup_conn_send(msc_conn, msgb_copy(msg, "GSUP proxy to MSC"));
+ /* The outgoing message needs to be a separate msgb, because osmo_gsup_conn_send() takes ownership of it, an the
+ * gsup_client also does a msgb_free() after dispatching to this callback.
+ * We also need to strip the IPA header and have headroom. Just re-encode. */
+ gsup_copy = osmo_gsup_msgb_alloc("GSUP proxy to VLR");
+ if (osmo_gsup_encode(gsup_copy, &gsup)) {
+ LOG_GSUP_MSG(gsupc, &gsup, LOGL_ERROR, "Failed to re-encode GSUP message, cannot forward\n");
+ remote_hlr_err_reply(gsupc, &gsup, GMM_CAUSE_MSC_TEMP_NOTREACH);
+ return -GMM_CAUSE_MSC_TEMP_NOTREACH;
+ }
+ return osmo_gsup_conn_send(vlr_conn, gsup_copy);
+}
+
+static bool remote_hlr_up_down(struct osmo_gsup_client *gsupc, bool up)
+{
+ struct remote_hlr *remote_hlr = gsupc->data;
+ if (!up) {
+ LOGP(DDGSM, LOGL_ERROR,
+ "link to remote HLR is down, removing GSUP client: " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
+ remote_hlr_destroy(remote_hlr);
+ return false;
+ }
+
+ dgsm_remote_hlr_up(remote_hlr);
+ return true;
}
struct remote_hlr *remote_hlr_get(const struct osmo_sockaddr_str *addr, bool create)
@@ -95,7 +141,7 @@ struct remote_hlr *remote_hlr_get(const struct osmo_sockaddr_str *addr, bool cre
struct remote_hlr *rh;
llist_for_each_entry(rh, &remote_hlrs, entry) {
- if (!osmo_sockaddr_str_cmp(&rh->addr, addr))
+ if (!osmo_sockaddr_str_ip_cmp(&rh->addr, addr))
return rh;
}
@@ -104,20 +150,36 @@ struct remote_hlr *remote_hlr_get(const struct osmo_sockaddr_str *addr, bool cre
/* Doesn't exist yet, create a GSUP client to remote HLR. */
rh = talloc_zero(dgsm_ctx, struct remote_hlr);
+ OSMO_ASSERT(rh);
*rh = (struct remote_hlr){
.addr = *addr,
- .gsupc = osmo_gsup_client_create2(rh, &g_hlr->gsup_proxy.gsup_client_name,
+ .gsupc = osmo_gsup_client_create3(rh, &g_hlr->gsup_proxy.gsup_client_name,
addr->ip, addr->port,
+ NULL,
remote_hlr_rx,
- NULL),
+ remote_hlr_up_down,
+ rh),
};
if (!rh->gsupc) {
+ LOGP(DDGSM, LOGL_ERROR,
+ "Failed to establish connection to remote HLR " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(addr));
talloc_free(rh);
return NULL;
}
+ rh->gsupc->data = rh;
+ llist_add(&rh->entry, &remote_hlrs);
return rh;
}
+void remote_hlr_destroy(struct remote_hlr *remote_hlr)
+{
+ osmo_gsup_client_destroy(remote_hlr->gsupc);
+ remote_hlr->gsupc = NULL;
+ llist_del(&remote_hlr->entry);
+ talloc_free(remote_hlr);
+}
+
/* This function takes ownership of the msg, do not free it after passing to this function. */
int remote_hlr_msgb_send(struct remote_hlr *remote_hlr, struct msgb *msg)
{
@@ -131,8 +193,13 @@ int remote_hlr_msgb_send(struct remote_hlr *remote_hlr, struct msgb *msg)
int remote_hlr_gsup_send(struct remote_hlr *remote_hlr, const struct osmo_gsup_message *gsup)
{
+ int rc;
struct msgb *msg = osmo_gsup_msgb_alloc("GSUP proxy to remote HLR");
- osmo_gsup_encode(msg, gsup);
+ rc = osmo_gsup_encode(msg, gsup);
+ if (rc) {
+ LOG_DGSM(gsup->imsi, LOGL_ERROR, "Failed to encode GSUP message: %s\n",
+ osmo_gsup_message_type_name(gsup->message_type));
+ return rc;
+ }
return remote_hlr_msgb_send(remote_hlr, msg);
}
-
diff --git a/src/remote_hlr.h b/src/remote_hlr.h
index 331ef35..5f2c84d 100644
--- a/src/remote_hlr.h
+++ b/src/remote_hlr.h
@@ -8,6 +8,7 @@ struct osmo_gsup_client;
struct osmo_gsup_message;
struct msgb;
+/* GSUP client link for proxying to a remote HLR. */
struct remote_hlr {
struct llist_head entry;
struct osmo_sockaddr_str addr;
@@ -15,5 +16,6 @@ struct remote_hlr {
};
struct remote_hlr *remote_hlr_get(const struct osmo_sockaddr_str *addr, bool create);
+void remote_hlr_destroy(struct remote_hlr *remote_hlr);
int remote_hlr_msgb_send(struct remote_hlr *remote_hlr, struct msgb *msg);
int remote_hlr_gsup_send(struct remote_hlr *remote_hlr, const struct osmo_gsup_message *gsup);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 357fbac..e580678 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -4,6 +4,7 @@ SUBDIRS = \
db \
gsup \
db_upgrade \
+ mslookup_manual_test \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
diff --git a/tests/mslookup_manual_test/Makefile.am b/tests/mslookup_manual_test/Makefile.am
new file mode 100644
index 0000000..20c3059
--- /dev/null
+++ b/tests/mslookup_manual_test/Makefile.am
@@ -0,0 +1,40 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/src \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ -ggdb3 \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOABIS_CFLAGS) \
+ $(NULL)
+
+AM_LDFLAGS = \
+ -no-install \
+ $(NULL)
+
+EXTRA_DIST = \
+ run.sh \
+ osmo-hlr-1.cfg \
+ osmo-hlr-2.cfg \
+ $(NULL)
+
+noinst_PROGRAMS = \
+ fake_msc \
+ $(NULL)
+
+fake_msc_SOURCES = \
+ fake_msc.c \
+ $(NULL)
+
+fake_msc_LDADD = \
+ $(top_builddir)/src/gsupclient/libosmo-gsup-client.la \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(LIBOSMOABIS_LIBS) \
+ $(NULL)
+
+run:
+ $(srcdir)/run.sh $(srcdir) $(builddir)
diff --git a/tests/mslookup_manual_test/fake_msc.c b/tests/mslookup_manual_test/fake_msc.c
new file mode 100644
index 0000000..36a40b7
--- /dev/null
+++ b/tests/mslookup_manual_test/fake_msc.c
@@ -0,0 +1,80 @@
+#include <osmocom/core/logging.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/application.h>
+#include <osmocom/gsupclient/gsup_client.h>
+
+void *ctx;
+
+int gsup_client_read_cb(struct osmo_gsup_client *gsupc, struct msgb *msg)
+{
+ struct osmo_gsup_message gsup;
+ if (osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup)) {
+ printf("fake_msc: GSUP rx, but failed to decode\n");
+ return 0;
+ }
+ printf("fake_msc: GSUP rx %s %s (destination_name=%s)\n",
+ gsup.imsi, osmo_gsup_message_type_name(gsup.message_type),
+ osmo_quote_str((const char*)gsup.destination_name, gsup.destination_name_len));
+ return 0;
+}
+
+struct osmo_gsup_client *gsupc;
+struct osmo_timer_list do_stuff_timer;
+
+void do_stuff(void *data)
+{
+ static int i = 0;
+ int seq = 0;
+ if (i == seq++) {
+ struct osmo_gsup_message gsup = {
+ .message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST,
+ .imsi = "222222",
+ .cn_domain = OSMO_GSUP_CN_DOMAIN_CS,
+ };
+ osmo_gsup_client_enc_send(gsupc, &gsup);
+ }
+
+ seq += 3;
+ if (i == seq++) {
+ struct osmo_gsup_message gsup = {
+ .message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST,
+ .imsi = "222222",
+ .cn_domain = OSMO_GSUP_CN_DOMAIN_CS,
+ };
+ osmo_gsup_client_enc_send(gsupc, &gsup);
+ }
+
+ seq += 10;
+ if (i == seq++) {
+ exit(0);
+ }
+
+ i++;
+ osmo_timer_schedule(&do_stuff_timer, 1, 0);
+}
+
+int main()
+{
+ ctx = talloc_named_const(NULL, 0, "main");
+ osmo_init_logging2(ctx, NULL);
+
+ log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_level(osmo_stderr_target, 0);
+ log_set_print_category(osmo_stderr_target, 0);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_category_filter(osmo_stderr_target, DLMSLOOKUP, true, LOGL_DEBUG);
+
+ struct ipaccess_unit gsup_client_name = {
+ .unit_name = "fake-msc-1",
+ .serno = "fake-msc-1",
+ };
+ gsupc = osmo_gsup_client_create2(ctx, &gsup_client_name, "127.0.0.1", OSMO_GSUP_PORT, gsup_client_read_cb,
+ NULL);
+
+ osmo_timer_setup(&do_stuff_timer, do_stuff, NULL);
+ osmo_timer_schedule(&do_stuff_timer, 1, 0);
+ for (;;) {
+ osmo_select_main_ctx(0);
+ }
+}
diff --git a/tests/mslookup_manual_test/osmo-hlr-1.cfg b/tests/mslookup_manual_test/osmo-hlr-1.cfg
new file mode 100644
index 0000000..2f33e3e
--- /dev/null
+++ b/tests/mslookup_manual_test/osmo-hlr-1.cfg
@@ -0,0 +1,40 @@
+hlr
+ gsup
+ bind ip 127.0.0.1
+ ipa-name testHLR-1
+ ussd route prefix *0# internal own-msisdn
+ ussd route prefix *1# internal own-imsi
+ ussd route prefix *#100# internal own-msisdn
+ ussd route prefix *#101# internal own-imsi
+ store-imei
+
+line vty
+ bind 127.0.0.1
+ctrl
+ bind 127.0.0.1
+
+mslookup
+ mdns
+
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print level 1
+ logging print category 1
+ logging print category-hex 0
+ logging print file basename last
+ logging print extended-timestamp 1
+ logging level set-all debug
+ logging level linp error
+
+log gsmtap 127.0.0.1
+ logging filter all 1
+ logging color 1
+ logging print level 1
+ logging print category 1
+ logging print category-hex 0
+ logging print file basename last
+ logging print extended-timestamp 1
+ logging level set-all debug
+ logging level linp error
+
diff --git a/tests/mslookup_manual_test/osmo-hlr-2.cfg b/tests/mslookup_manual_test/osmo-hlr-2.cfg
new file mode 100644
index 0000000..06ddd4f
--- /dev/null
+++ b/tests/mslookup_manual_test/osmo-hlr-2.cfg
@@ -0,0 +1,40 @@
+hlr
+ gsup
+ bind ip 127.0.0.2
+ ipa-name testHLR-2
+ ussd route prefix *0# internal own-msisdn
+ ussd route prefix *1# internal own-imsi
+ ussd route prefix *#100# internal own-msisdn
+ ussd route prefix *#101# internal own-imsi
+ store-imei
+
+line vty
+ bind 127.0.0.2
+ctrl
+ bind 127.0.0.2
+
+mslookup
+ mdns
+
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print level 1
+ logging print category 1
+ logging print category-hex 0
+ logging print file basename last
+ logging timestamp 1
+ logging level set-all debug
+ logging level linp error
+
+log gsmtap 127.0.0.1
+ logging filter all 1
+ logging color 1
+ logging print level 1
+ logging print category 1
+ logging print category-hex 0
+ logging print file basename last
+ logging print extended-timestamp 1
+ logging level set-all debug
+ logging level linp error
+
diff --git a/tests/mslookup_manual_test/run.sh b/tests/mslookup_manual_test/run.sh
new file mode 100755
index 0000000..ea4dabf
--- /dev/null
+++ b/tests/mslookup_manual_test/run.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+srcdir="${1:-.}"
+builddir="${2:-.}"
+
+cd "$builddir"
+
+osmo-hlr -c "$srcdir/osmo-hlr-1.cfg" -l hlr1.db &
+sleep 1
+osmo-hlr -c "$srcdir/osmo-hlr-2.cfg" -l hlr2.db &
+
+sleep 1
+osmo_interact_vty.py -H 127.0.0.1 -p 4258 -c 'enable; subscriber imsi 111111 create; subscriber imsi 111111 update msisdn 1'
+osmo_interact_vty.py -H 127.0.0.2 -p 4258 -c 'enable; subscriber imsi 222222 create; subscriber imsi 222222 update msisdn 2'
+sleep 1
+
+./fake_msc &
+
+echo enter to exit
+read enter_to_exit
+kill %1 %2 %3
+killall osmo-hlr
+killall fake_msc