This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
neels gerrit-no-reply at lists.osmocom.orgneels has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-hlr/+/16209 )
Change subject: implement D-GSM in osmo-hlr
......................................................................
implement D-GSM in osmo-hlr
Implement the mslookup server to service remote mslookup requests:
- VTY mslookup/server config with service names,
- the mslookup_mdns_server listening for mslookup requests,
- determine whether a subscriber is on a local MSC.
Use the mslookup client to proxy GSUP to remote HLRs:
- VTY mslookup/client config,
- remote_hlr.c to connect GSUP clients to remote GSUP servers,
- proxy.c as local GSUP proxy state, so far in-memory.
This is kept an opaque API without a mutable struct, so that it can be easily
moved to a persistent database implementation.
dgsm.c orchestrates mslookup server, client, and MUXes GSUP messages to the
right proxy peers.
Change-Id: Ife4a61d71926d08f310a1aeed9d9f1974f64178b
---
M include/osmocom/hlr/Makefile.am
A include/osmocom/hlr/dgsm.h
M include/osmocom/hlr/gsup_server.h
M include/osmocom/hlr/hlr.h
M include/osmocom/hlr/hlr_vty.h
M include/osmocom/hlr/logging.h
A include/osmocom/hlr/mslookup_server.h
A include/osmocom/hlr/mslookup_server_mdns.h
A include/osmocom/hlr/proxy.h
A include/osmocom/hlr/remote_hlr.h
M src/Makefile.am
A src/dgsm.c
A src/dgsm_vty.c
M src/gsup_server.c
M src/hlr.c
M src/hlr_vty.c
M src/hlr_vty_subscr.c
M src/logging.c
A src/mslookup_server.c
A src/mslookup_server_mdns.c
A src/proxy.c
A src/remote_hlr.c
M tests/test_nodes.vty
23 files changed, 2,697 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-hlr refs/changes/09/16209/1
diff --git a/include/osmocom/hlr/Makefile.am b/include/osmocom/hlr/Makefile.am
index 532fa5d..38011f6 100644
--- a/include/osmocom/hlr/Makefile.am
+++ b/include/osmocom/hlr/Makefile.am
@@ -2,6 +2,7 @@
auc.h \
ctrl.h \
db.h \
+ dgsm.h \
gsup_router.h \
gsup_server.h \
hlr.h \
@@ -10,5 +11,9 @@
hlr_vty_subscr.h \
logging.h \
lu_fsm.h \
+ mslookup_server.h \
+ mslookup_server_mdns.h \
+ proxy.h \
rand.h \
+ remote_hlr.h \
$(NULL)
diff --git a/include/osmocom/hlr/dgsm.h b/include/osmocom/hlr/dgsm.h
new file mode 100644
index 0000000..d89ace7
--- /dev/null
+++ b/include/osmocom/hlr/dgsm.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#include <osmocom/mslookup/mslookup.h>
+#include <osmocom/hlr/gsup_server.h>
+#include <osmocom/gsupclient/global_title.h>
+#include <osmocom/gsupclient/gsup_req.h>
+
+#define LOG_DGSM(imsi, level, fmt, args...) \
+ LOGP(DDGSM, level, "(IMSI-%s) " fmt, imsi, ##args)
+
+struct vty;
+struct remote_hlr;
+struct hlr_subscriber;
+
+extern void *dgsm_ctx;
+
+struct dgsm_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;
+};
+
+struct dgsm_msc_config {
+ struct llist_head entry;
+ struct osmo_gt name;
+ struct llist_head service_hosts;
+};
+
+/* "Sketch pad" where the VTY can store config items without yet applying. The changes will be applied by e.g.
+ * dgsm_mdns_server_config_apply() and dgsm_mdns_client_config_apply(). */
+struct dgsm_config {
+ struct {
+ /* Whether to listen for incoming MS Lookup requests */
+ bool enable;
+
+ struct {
+ bool enable;
+ struct osmo_sockaddr_str bind_addr;
+ } mdns;
+
+ struct llist_head msc_configs;
+ } server;
+
+ struct {
+ /* Whether to ask remote HLRs via MS Lookup if an IMSI is not known locally. */
+ bool enable;
+ struct timeval timeout;
+
+ struct {
+ /* Whether to use mDNS for IMSI MS Lookup */
+ bool enable;
+ struct osmo_sockaddr_str query_addr;
+ } mdns;
+
+ struct osmo_sockaddr_str gateway_proxy;
+ } client;
+};
+
+struct dgsm_service_host *dgsm_config_service_get(const struct osmo_gt *msc_name, const char *service);
+
+struct dgsm_service_host *dgsm_config_msc_service_get(struct dgsm_msc_config *msc, const char *service, bool create);
+int dgsm_config_msc_service_set(struct dgsm_msc_config *msc, const char *service, const struct osmo_sockaddr_str *addr);
+int dgsm_config_msc_service_del(struct dgsm_msc_config *msc, const char *service, const struct osmo_sockaddr_str *addr);
+
+extern const struct osmo_gt dgsm_config_msc_wildcard;
+struct dgsm_msc_config *dgsm_config_msc_get(const struct osmo_gt *msc_name, bool create);
+
+void dgsm_init(void *ctx);
+void dgsm_start(void *ctx);
+void dgsm_config_apply(const struct dgsm_config *c);
+void dgsm_stop();
+
+bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req);
+
+void dgsm_vty_init();
+
+bool hlr_subscr_lu_age(const struct hlr_subscriber *subscr, uint32_t *age_p);
diff --git a/include/osmocom/hlr/gsup_server.h b/include/osmocom/hlr/gsup_server.h
index c3efea2..7002da0 100644
--- a/include/osmocom/hlr/gsup_server.h
+++ b/include/osmocom/hlr/gsup_server.h
@@ -27,6 +27,9 @@
struct ipa_server_link *link;
osmo_gsup_read_cb_t read_cb;
struct llist_head routes;
+
+ /* Proxy requests from this server's clients to remote GSUP servers. */
+ struct proxy *proxy;
};
diff --git a/include/osmocom/hlr/hlr.h b/include/osmocom/hlr/hlr.h
index 2214a8b..f9d7450 100644
--- a/include/osmocom/hlr/hlr.h
+++ b/include/osmocom/hlr/hlr.h
@@ -27,6 +27,8 @@
#include <osmocom/gsm/ipa.h>
#include <osmocom/core/tdef.h>
+#include <osmocom/hlr/dgsm.h>
+
#define HLR_DEFAULT_DB_FILE_PATH "hlr.db"
struct hlr_euse;
@@ -68,6 +70,22 @@
/* Bitmask of DB_SUBSCR_FLAG_* */
uint8_t subscr_create_on_demand_flags;
unsigned int subscr_create_on_demand_rand_msisdn_len;
+
+ struct {
+ bool allow_startup;
+ struct dgsm_config vty;
+
+ struct {
+ struct osmo_mslookup_server_mdns *mdns;
+ uint32_t max_age;
+ } server;
+
+ struct {
+ unsigned int result_timeout_milliseconds;
+ struct osmo_mslookup_client *client;
+ struct osmo_mslookup_client_method *mdns;
+ } client;
+ } mslookup;
};
extern struct hlr *g_hlr;
diff --git a/include/osmocom/hlr/hlr_vty.h b/include/osmocom/hlr/hlr_vty.h
index acd6510..1b9b59e 100644
--- a/include/osmocom/hlr/hlr_vty.h
+++ b/include/osmocom/hlr/hlr_vty.h
@@ -31,6 +31,10 @@
HLR_NODE = _LAST_OSMOVTY_NODE + 1,
GSUP_NODE,
EUSE_NODE,
+ MSLOOKUP_NODE,
+ MSLOOKUP_SERVER_NODE,
+ MSLOOKUP_SERVER_MSC_NODE,
+ MSLOOKUP_CLIENT_NODE,
};
int hlr_vty_is_config_node(struct vty *vty, int node);
diff --git a/include/osmocom/hlr/logging.h b/include/osmocom/hlr/logging.h
index 4e0a25c..a8081af 100644
--- a/include/osmocom/hlr/logging.h
+++ b/include/osmocom/hlr/logging.h
@@ -10,6 +10,7 @@
DSS,
DMSLOOKUP,
DLU,
+ DDGSM,
};
extern const struct log_info hlr_log_info;
diff --git a/include/osmocom/hlr/mslookup_server.h b/include/osmocom/hlr/mslookup_server.h
new file mode 100644
index 0000000..68a8695
--- /dev/null
+++ b/include/osmocom/hlr/mslookup_server.h
@@ -0,0 +1,8 @@
+#pragma once
+
+struct osmo_mslookup_query;
+struct osmo_mslookup_result;
+
+struct dgsm_service_host *mslookup_server_get_local_gsup_addr();
+void osmo_mslookup_server_rx(const struct osmo_mslookup_query *query,
+ struct osmo_mslookup_result *result);
diff --git a/include/osmocom/hlr/mslookup_server_mdns.h b/include/osmocom/hlr/mslookup_server_mdns.h
new file mode 100644
index 0000000..8d4d4fc
--- /dev/null
+++ b/include/osmocom/hlr/mslookup_server_mdns.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <stdbool.h>
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/mslookup/mdns_sock.h>
+
+struct osmo_mslookup_server_mdns {
+ struct osmo_mslookup_server *mslookup;
+ struct osmo_sockaddr_str bind_addr;
+ struct osmo_mdns_sock *sock;
+};
+
+struct osmo_mslookup_server_mdns *osmo_mslookup_server_mdns_start(void *ctx, const struct osmo_sockaddr_str *bind_addr);
+void osmo_mslookup_server_mdns_stop(struct osmo_mslookup_server_mdns *server);
diff --git a/include/osmocom/hlr/proxy.h b/include/osmocom/hlr/proxy.h
new file mode 100644
index 0000000..1aaee81
--- /dev/null
+++ b/include/osmocom/hlr/proxy.h
@@ -0,0 +1,86 @@
+#pragma once
+
+#include <time.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/gsupclient/global_title.h>
+
+struct osmo_gsup_req;
+struct remote_hlr;
+
+typedef time_t timestamp_t;
+void timestamp_update(timestamp_t *timestamp);
+bool timestamp_age(const timestamp_t *timestamp, uint32_t *age);
+
+struct proxy_pending_gsup_req {
+ struct llist_head entry;
+ struct osmo_gsup_req *req;
+ timestamp_t received_at;
+};
+
+struct proxy {
+ struct llist_head subscr_list;
+ struct llist_head pending_gsup_reqs;
+
+ /* When messages arrive back from a remote HLR that this is the proxy for, reach the VLR to forward the response
+ * to via this osmo_gsup_server. */
+ struct osmo_gsup_server *gsup_server_to_vlr;
+
+ /* 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;
+
+ struct osmo_timer_list gc_timer;
+};
+
+struct proxy_subscr_domain_state {
+ struct osmo_gt vlr_name;
+ timestamp_t last_lu;
+
+ /* The name from which an Update Location Request was received. Copied to vlr_name as soon as the LU is
+ * completed successfully. */
+ struct osmo_gt vlr_name_preliminary;
+
+ /* Set if this is a middle proxy, i.e. a proxy behind another proxy.
+ * That is mostly to know whether the MS is attached at a local MSC/SGSN or further away.
+ * It could be a boolean, but store the full name for logging. Set only at successful LU acceptance. */
+ struct osmo_gt vlr_via_proxy;
+};
+
+struct proxy_subscr {
+ char imsi[GSM23003_IMSI_MAX_DIGITS+1];
+ char msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
+ struct osmo_sockaddr_str remote_hlr_addr;
+ struct proxy_subscr_domain_state cs, ps;
+};
+
+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);
+
+/* 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);
+
+void proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ struct osmo_gsup_req *req);
+void proxy_subscr_forward_to_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ struct remote_hlr *remote_hlr, struct osmo_gsup_req *req);
+
+int proxy_subscr_forward_to_vlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ const struct osmo_gsup_message *gsup, struct remote_hlr *from_remote_hlr);
+
+void proxy_subscr_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ struct remote_hlr *remote_hlr);
+void proxy_subscr_remote_hlr_up(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ struct remote_hlr *remote_hlr);
diff --git a/include/osmocom/hlr/remote_hlr.h b/include/osmocom/hlr/remote_hlr.h
new file mode 100644
index 0000000..bfa3d95
--- /dev/null
+++ b/include/osmocom/hlr/remote_hlr.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <stdbool.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/sockaddr_str.h>
+
+struct osmo_gsup_client;
+struct osmo_gsup_message;
+struct osmo_gsup_req;
+struct msgb;
+
+#define LOG_REMOTE_HLR(remote_hlr, level, fmt, args...) \
+ LOGP(DDGSM, level, "(Proxy HLR-" OSMO_SOCKADDR_STR_FMT ") " fmt, \
+ OSMO_SOCKADDR_STR_FMT_ARGS((remote_hlr) ? &(remote_hlr)->addr : NULL), ##args)
+
+#define LOG_REMOTE_HLR_MSG(remote_hlr, gsup_msg, level, fmt, args...) \
+ LOG_REMOTE_HLR(remote_hlr, level, "%s: " fmt, osmo_gsup_message_type_name((gsup_msg)->message_type), ##args)
+
+/* GSUP client link for proxying to a remote HLR. */
+struct remote_hlr {
+ struct llist_head entry;
+ struct osmo_sockaddr_str addr;
+ struct osmo_gsup_client *gsupc;
+};
+
+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);
+void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct osmo_gsup_req *req);
diff --git a/src/Makefile.am b/src/Makefile.am
index 3a83616..5113aa4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,6 +9,7 @@
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) \
+ $(LIBOSMOMSLOOKUP_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(SQLITE3_CFLAGS) \
$(NULL)
@@ -52,15 +53,23 @@
hlr_vty_subscr.c \
gsup_send.c \
hlr_ussd.c \
+ proxy.c \
+ dgsm.c \
+ dgsm_vty.c \
+ remote_hlr.c \
+ mslookup_server.c \
+ mslookup_server_mdns.c \
lu_fsm.c \
$(NULL)
osmo_hlr_LDADD = \
$(top_builddir)/src/gsupclient/libosmo-gsup-client.la \
+ $(top_builddir)/src/mslookup/libosmo-mslookup.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOCTRL_LIBS) \
+ $(LIBOSMOMSLOOKUP_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(SQLITE3_LIBS) \
$(NULL)
diff --git a/src/dgsm.c b/src/dgsm.c
new file mode 100644
index 0000000..9856188
--- /dev/null
+++ b/src/dgsm.c
@@ -0,0 +1,402 @@
+#include <errno.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/mslookup/mslookup_client.h>
+#include <osmocom/mslookup/mslookup_client_mdns.h>
+#include <osmocom/gsupclient/gsup_client.h>
+#include <osmocom/gsupclient/global_title.h>
+#include <osmocom/hlr/logging.h>
+#include <osmocom/hlr/hlr.h>
+#include <osmocom/hlr/db.h>
+#include <osmocom/hlr/gsup_router.h>
+#include <osmocom/hlr/gsup_server.h>
+#include <osmocom/hlr/dgsm.h>
+#include <osmocom/hlr/proxy.h>
+#include <osmocom/hlr/remote_hlr.h>
+#include <osmocom/hlr/mslookup_server_mdns.h>
+#include <osmocom/hlr/dgsm.h>
+
+void *dgsm_ctx = NULL;
+
+const struct osmo_gt dgsm_config_msc_wildcard = {};
+
+struct dgsm_msc_config *dgsm_config_msc_get(const struct osmo_gt *msc_name, bool create)
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ struct dgsm_msc_config *msc;
+
+ if (!msc_name)
+ return NULL;
+
+ llist_for_each_entry(msc, &c->server.msc_configs, entry) {
+ if (osmo_gt_cmp(&msc->name, msc_name))
+ continue;
+ return msc;
+ }
+ if (!create)
+ return NULL;
+
+ msc = talloc_zero(dgsm_ctx, struct dgsm_msc_config);
+ OSMO_ASSERT(msc);
+ INIT_LLIST_HEAD(&msc->service_hosts);
+ msc->name = *msc_name;
+ llist_add_tail(&msc->entry, &c->server.msc_configs);
+ return msc;
+}
+
+struct dgsm_service_host *dgsm_config_msc_service_get(struct dgsm_msc_config *msc, const char *service, bool create)
+{
+ struct dgsm_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 dgsm_service_host);
+ OSMO_ASSERT(e);
+ OSMO_STRLCPY_ARRAY(e->service, service);
+ llist_add_tail(&e->entry, &msc->service_hosts);
+ return e;
+}
+
+struct dgsm_service_host *dgsm_config_service_get(const struct osmo_gt *msc_name, const char *service)
+{
+ struct dgsm_msc_config *msc = dgsm_config_msc_get(msc_name, false);
+ if (!msc)
+ return NULL;
+ return dgsm_config_msc_service_get(msc, service, false);
+}
+
+int dgsm_config_msc_service_set(struct dgsm_msc_config *msc, const char *service, const struct osmo_sockaddr_str *addr)
+{
+ struct dgsm_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 = dgsm_config_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 dgsm_config_msc_service_del(struct dgsm_msc_config *msc, const char *service, const struct osmo_sockaddr_str *addr)
+{
+ struct dgsm_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;
+}
+
+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->gs->proxy;
+ const struct proxy_subscr *proxy_subscr;
+ const struct osmo_sockaddr_str *use_addr;
+ 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;
+ }
+
+ 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));
+ proxy_subscr_del(proxy, query->id.imsi);
+ return;
+ }
+
+ if (osmo_sockaddr_str_is_nonzero(&result->host_v4))
+ use_addr = &result->host_v4;
+ else if (osmo_sockaddr_str_is_nonzero(&result->host_v6))
+ use_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));
+ proxy_subscr_del(proxy, query->id.imsi);
+ return;
+ }
+
+ remote_hlr = remote_hlr_get(use_addr, true);
+ if (!remote_hlr) {
+ proxy_subscr_del(proxy, query->id.imsi);
+ 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));
+ return;
+ }
+
+ /* The remote HLR already exists and is connected. Messages for this IMSI were spooled because we did not know
+ * which remote HLR was responsible. Now we know, send this IMSI's messages now. */
+ 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));
+
+ proxy_subscr_remote_hlr_resolved(proxy, proxy_subscr, remote_hlr);
+
+ if (!remote_hlr->gsupc || !remote_hlr->gsupc->is_connected) {
+ LOG_REMOTE_HLR(remote_hlr, LOGL_DEBUG, "Waiting for link-up\n");
+ return;
+ }
+ proxy_subscr_remote_hlr_up(proxy, proxy_subscr, remote_hlr);
+}
+
+/* Return true when the message has been handled by D-GSM. */
+bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req)
+{
+ const struct proxy_subscr *proxy_subscr;
+ struct proxy_subscr proxy_subscr_new;
+ struct proxy *proxy = g_hlr->gs->proxy;
+ struct osmo_mslookup_query query;
+ 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)
+ return false;
+
+ /* Are we already forwarding this IMSI to a remote HLR? */
+ proxy_subscr = proxy_subscr_get_by_imsi(proxy, req->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. */
+
+ /* Add a proxy entry without a remote address to indicate that we are busy querying for a remote HLR. */
+ proxy_subscr_new = (struct proxy_subscr){};
+ OSMO_STRLCPY_ARRAY(proxy_subscr_new.imsi, req->gsup.imsi);
+ proxy_subscr = &proxy_subscr_new;
+ if (proxy_subscr_update(proxy, proxy_subscr)) {
+ LOG_DGSM(req->gsup.imsi, LOGL_ERROR, "Failed to create proxy entry\n");
+ return false;
+ }
+
+ /* Is a fixed gateway proxy configured? */
+ if (osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.vty.client.gateway_proxy)) {
+ struct remote_hlr *gateway_proxy = remote_hlr_get(&g_hlr->mslookup.vty.client.gateway_proxy, true);
+ if (!gateway_proxy) {
+ LOG_DGSM(req->gsup.imsi, LOGL_ERROR,
+ "Failed to set up fixed gateway proxy " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.vty.client.gateway_proxy));
+ return false;
+ }
+
+ proxy_subscr_remote_hlr_resolved(proxy, proxy_subscr, gateway_proxy);
+
+ /* Update info */
+ proxy_subscr = proxy_subscr_get_by_imsi(proxy, req->gsup.imsi);
+ if (!proxy_subscr) {
+ LOG_DGSM(req->gsup.imsi, LOGL_ERROR, "Proxy entry disappeared\n");
+ return false;
+ }
+ goto yes_we_are_proxying;
+ }
+
+ /* Kick off an mslookup for the remote HLR. */
+ if (!g_hlr->mslookup.client.client) {
+ LOG_GSUP_REQ(req, LOGL_DEBUG, "mslookup client not running, cannot query remote home HLR\n");
+ return false;
+ }
+
+ query = (struct osmo_mslookup_query){
+ .id = {
+ .type = OSMO_MSLOOKUP_ID_IMSI,
+ }
+ };
+ OSMO_STRLCPY_ARRAY(query.id.imsi, req->gsup.imsi);
+ OSMO_STRLCPY_ARRAY(query.service, OSMO_MSLOOKUP_SERVICE_HLR_GSUP);
+ handling = (struct osmo_mslookup_query_handling){
+ .min_delay_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(req->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, req->gsup.imsi);
+ return false;
+ }
+
+yes_we_are_proxying:
+ OSMO_ASSERT(proxy_subscr);
+
+ /* 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. */
+ proxy_subscr_forward_to_remote_hlr(proxy, proxy_subscr, req);
+ return true;
+}
+
+void dgsm_init(void *ctx)
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ dgsm_ctx = talloc_named_const(ctx, 0, "dgsm");
+ INIT_LLIST_HEAD(&c->server.msc_configs);
+
+ g_hlr->mslookup.server.max_age = 60 * 60;
+
+ g_hlr->mslookup.client.result_timeout_milliseconds = 2000;
+
+ g_hlr->gsup_unit_name.unit_name = "HLR";
+ g_hlr->gsup_unit_name.serno = "unnamed-HLR";
+ g_hlr->gsup_unit_name.swversion = PACKAGE_NAME "-" PACKAGE_VERSION;
+
+ osmo_sockaddr_str_from_str(&c->server.mdns.bind_addr,
+ OSMO_MSLOOKUP_MDNS_IP4, OSMO_MSLOOKUP_MDNS_PORT);
+ osmo_sockaddr_str_from_str(&c->client.mdns.query_addr,
+ OSMO_MSLOOKUP_MDNS_IP4, OSMO_MSLOOKUP_MDNS_PORT);
+}
+
+void dgsm_start(void *ctx)
+{
+ g_hlr->mslookup.client.client = osmo_mslookup_client_new(dgsm_ctx);
+ OSMO_ASSERT(g_hlr->mslookup.client.client);
+ g_hlr->mslookup.allow_startup = true;
+ dgsm_config_apply(&g_hlr->mslookup.vty);
+}
+
+void dgsm_stop()
+{
+ struct dgsm_config disabled = {};
+ dgsm_config_apply(&disabled);
+}
+
+static void dgsm_mdns_server_config_apply(const struct dgsm_config *c)
+{
+ /* Check whether to start/stop/restart mDNS server */
+ bool should_run;
+ bool should_stop;
+ if (!g_hlr->mslookup.allow_startup)
+ return;
+
+ should_run = c->server.enable && c->server.mdns.enable;
+ should_stop = g_hlr->mslookup.server.mdns
+ && (!should_run
+ || osmo_sockaddr_str_cmp(&c->server.mdns.bind_addr,
+ &g_hlr->mslookup.server.mdns->bind_addr));
+
+ if (should_stop) {
+ osmo_mslookup_server_mdns_stop(g_hlr->mslookup.server.mdns);
+ g_hlr->mslookup.server.mdns = NULL;
+ LOGP(DDGSM, LOGL_NOTICE, "Stopped mslookup mDNS server\n");
+ }
+
+ if (should_run && !g_hlr->mslookup.server.mdns) {
+ g_hlr->mslookup.server.mdns =
+ osmo_mslookup_server_mdns_start(g_hlr, &c->server.mdns.bind_addr);
+ if (!g_hlr->mslookup.server.mdns)
+ LOGP(DDGSM, LOGL_ERROR, "Failed to start mslookup mDNS server on " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.server.mdns->bind_addr));
+ else
+ LOGP(DDGSM, LOGL_NOTICE, "Started mslookup mDNS server, receiving mDNS requests at multicast "
+ OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.server.mdns->bind_addr));
+ }
+}
+
+static void dgsm_mdns_client_config_apply(const struct dgsm_config *c)
+{
+ if (!g_hlr->mslookup.allow_startup)
+ return;
+
+ /* Check whether to start/stop/restart mDNS client */
+ const struct osmo_sockaddr_str *current_bind_addr;
+ current_bind_addr = osmo_mslookup_client_method_mdns_get_bind_addr(g_hlr->mslookup.client.mdns);
+
+ bool should_run = c->client.enable && c->client.mdns.enable;
+ bool should_stop = g_hlr->mslookup.client.mdns &&
+ (!should_run
+ || osmo_sockaddr_str_cmp(&c->client.mdns.query_addr,
+ current_bind_addr));
+
+ if (should_stop) {
+ osmo_mslookup_client_method_del(g_hlr->mslookup.client.client, g_hlr->mslookup.client.mdns);
+ g_hlr->mslookup.client.mdns = NULL;
+ LOGP(DDGSM, LOGL_NOTICE, "Stopped mslookup mDNS client\n");
+ }
+
+ if (should_run && !g_hlr->mslookup.client.mdns) {
+ g_hlr->mslookup.client.mdns =
+ osmo_mslookup_client_add_mdns(g_hlr->mslookup.client.client,
+ c->client.mdns.query_addr.ip,
+ c->client.mdns.query_addr.port,
+ -1);
+ if (!g_hlr->mslookup.client.mdns)
+ LOGP(DDGSM, LOGL_ERROR, "Failed to start mslookup mDNS client with target "
+ OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&c->client.mdns.query_addr));
+ else
+ LOGP(DDGSM, LOGL_NOTICE, "Started mslookup mDNS client, sending mDNS requests to multicast " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&c->client.mdns.query_addr));
+ }
+
+ if (c->client.enable && osmo_sockaddr_str_is_nonzero(&c->client.gateway_proxy))
+ LOGP(DDGSM, LOGL_NOTICE,
+ "mslookup client: all GSUP requests for unknown IMSIs will be forwarded to"
+ " gateway-proxy " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&c->client.gateway_proxy));
+}
+
+void dgsm_config_apply(const struct dgsm_config *c)
+{
+ dgsm_mdns_server_config_apply(c);
+ dgsm_mdns_client_config_apply(c);
+}
diff --git a/src/dgsm_vty.c b/src/dgsm_vty.c
new file mode 100644
index 0000000..83c31a2
--- /dev/null
+++ b/src/dgsm_vty.c
@@ -0,0 +1,515 @@
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/mslookup/mslookup_client_mdns.h>
+#include <osmocom/hlr/hlr_vty.h>
+#include <osmocom/hlr/dgsm.h>
+#include <osmocom/hlr/mslookup_server.h>
+
+struct cmd_node mslookup_node = {
+ MSLOOKUP_NODE,
+ "%s(config-mslookup)# ",
+ 1,
+};
+
+DEFUN(cfg_mslookup,
+ cfg_mslookup_cmd,
+ "mslookup",
+ "Configure Distributed GSM mslookup")
+{
+ vty->node = MSLOOKUP_NODE;
+ return CMD_SUCCESS;
+}
+
+static int mslookup_server_mdns_bind(struct vty *vty, int argc, const char **argv)
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ const char *ip_str = argc > 0? argv[0] : c->server.mdns.bind_addr.ip;
+ const char *port_str = argc > 1? argv[1] : NULL;
+ uint16_t port_nr = port_str ? atoi(port_str) : c->server.mdns.bind_addr.port;
+ struct osmo_sockaddr_str addr;
+ if (osmo_sockaddr_str_from_str(&addr, ip_str, port_nr)
+ || !osmo_sockaddr_str_is_nonzero(&addr)) {
+ vty_out(vty, "%% mslookup server: Invalid mDNS bind address: %s %u%s",
+ ip_str, port_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ c->server.mdns.bind_addr = addr;
+ c->server.mdns.enable = true;
+ c->server.enable = true;
+ dgsm_config_apply(c);
+ return CMD_SUCCESS;
+}
+
+static int mslookup_client_mdns_to(struct vty *vty, int argc, const char **argv)
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ const char *ip_str = argc > 0? argv[0] : c->client.mdns.query_addr.ip;
+ const char *port_str = argc > 1? argv[1] : NULL;
+ uint16_t port_nr = port_str ? atoi(port_str) : c->client.mdns.query_addr.port;
+ struct osmo_sockaddr_str addr;
+ if (osmo_sockaddr_str_from_str(&addr, ip_str, port_nr)
+ || !osmo_sockaddr_str_is_nonzero(&addr)) {
+ vty_out(vty, "%% mslookup client: Invalid mDNS target address: %s %u%s",
+ ip_str, port_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ c->client.mdns.query_addr = addr;
+ c->client.mdns.enable = true;
+ c->client.enable = true;
+ dgsm_config_apply(c);
+ return CMD_SUCCESS;
+}
+
+#define MDNS_IP46_STR "multicast IPv4 address like " OSMO_MSLOOKUP_MDNS_IP4 \
+ " or IPv6 address like " OSMO_MSLOOKUP_MDNS_IP6 "\n"
+#define MDNS_PORT_STR "mDNS UDP Port number\n"
+#define IP46_STR "IPv4 address like 1.2.3.4 or IPv6 address like a:b:c:d::1\n"
+#define PORT_STR "Service-specific port number\n"
+
+DEFUN(cfg_mslookup_mdns,
+ cfg_mslookup_mdns_cmd,
+ "mdns [IP] [<1-65535>]",
+ "Convenience shortcut: enable and configure both server and client for mDNS mslookup\n"
+ MDNS_IP46_STR MDNS_PORT_STR)
+{
+ int rc1 = mslookup_server_mdns_bind(vty, argc, argv);
+ int rc2 = mslookup_client_mdns_to(vty, argc, argv);
+ if (rc1 != CMD_SUCCESS)
+ return rc1;
+ return rc2;
+}
+
+DEFUN(cfg_mslookup_no_mdns,
+ cfg_mslookup_no_mdns_cmd,
+ "no mdns",
+ NO_STR "Disable both server and client for mDNS mslookup\n")
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ c->server.mdns.enable = false;
+ c->client.mdns.enable = false;
+ dgsm_config_apply(c);
+ return CMD_SUCCESS;
+}
+
+struct cmd_node mslookup_server_node = {
+ MSLOOKUP_SERVER_NODE,
+ "%s(config-mslookup-server)# ",
+ 1,
+};
+
+DEFUN(cfg_mslookup_server,
+ cfg_mslookup_server_cmd,
+ "server",
+ "Enable and configure Distributed GSM mslookup server")
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ vty->node = MSLOOKUP_SERVER_NODE;
+ c->server.enable = true;
+ dgsm_config_apply(c);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mslookup_no_server,
+ cfg_mslookup_no_server_cmd,
+ "no server",
+ NO_STR "Disable Distributed GSM mslookup server")
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ c->server.enable = false;
+ dgsm_config_apply(c);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mslookup_server_mdns_bind,
+ cfg_mslookup_server_mdns_bind_cmd,
+ "mdns [IP] [<1-65535>]",
+ "Configure where the mDNS server listens for mslookup requests\n"
+ MDNS_IP46_STR MDNS_PORT_STR)
+{
+ return mslookup_server_mdns_bind(vty, argc, argv);
+}
+
+DEFUN(cfg_mslookup_server_no_mdns,
+ cfg_mslookup_server_no_mdns_cmd,
+ "no mdns",
+ NO_STR "Disable server for mDNS mslookup (do not answer remote requests)\n")
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ c->server.mdns.enable = false;
+ dgsm_config_apply(c);
+ return CMD_SUCCESS;
+}
+
+struct cmd_node mslookup_server_msc_node = {
+ MSLOOKUP_SERVER_MSC_NODE,
+ "%s(config-mslookup-server-msc)# ",
+ 1,
+};
+
+DEFUN(cfg_mslookup_server_msc,
+ cfg_mslookup_server_msc_cmd,
+ "msc .UNIT_NAME",
+ "Configure services for individual local MSCs\n"
+ "IPA Unit Name of the local MSC to configure\n")
+{
+ struct osmo_gt msc_name;
+ struct dgsm_msc_config *msc;
+ osmo_gt_set_str(&msc_name, argv_concat(argv, argc, 0));
+
+ msc = dgsm_config_msc_get(&msc_name, true);
+ if (!msc) {
+ vty_out(vty, "%% Error creating MSC %s%s", osmo_gt_name(&msc_name), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ vty->node = MSLOOKUP_SERVER_MSC_NODE;
+ vty->index = msc;
+ return CMD_SUCCESS;
+}
+
+#define SERVICE_NAME_STR \
+ "mslookup service name, e.g. " OSMO_MSLOOKUP_SERVICE_SIP " or " OSMO_MSLOOKUP_SERVICE_SMPP "\n"
+
+static struct dgsm_msc_config *msc_from_node(struct vty *vty)
+{
+ switch (vty->node) {
+ case MSLOOKUP_SERVER_NODE:
+ /* On the mslookup.server node, set services on the wildcard msc, without a particular name. */
+ return dgsm_config_msc_get(&dgsm_config_msc_wildcard, true);
+ case MSLOOKUP_SERVER_MSC_NODE:
+ return vty->index;
+ default:
+ return NULL;
+ }
+}
+
+DEFUN(cfg_mslookup_server_msc_service,
+ cfg_mslookup_server_msc_service_cmd,
+ "service NAME at IP <1-65535>",
+ "Configure addresses of local services, as sent in replies to remote mslookup requests.\n"
+ SERVICE_NAME_STR "at\n" IP46_STR PORT_STR)
+{
+ /* If this command is run on the 'server' node, it produces an empty unit name and serves as wildcard for all
+ * MSCs. If on a 'server' / 'msc' node, set services only for that MSC Unit Name. */
+ struct dgsm_msc_config *msc = msc_from_node(vty);
+ const char *service = argv[0];
+ const char *ip_str = argv[1];
+ const char *port_str = argv[2];
+ struct osmo_sockaddr_str addr;
+
+ if (!msc) {
+ vty_out(vty, "%% Error: no MSC object on this node%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (osmo_sockaddr_str_from_str(&addr, ip_str, atoi(port_str))
+ || !osmo_sockaddr_str_is_nonzero(&addr)) {
+ vty_out(vty, "%% mslookup server: Invalid address for service %s: %s %s%s",
+ service, ip_str, port_str, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (dgsm_config_msc_service_set(msc, service, &addr)) {
+ vty_out(vty, "%% mslookup server: Error setting service %s to %s %s%s",
+ service, ip_str, port_str, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+#define NO_SERVICE_AND_NAME_STR NO_STR "Remove one or more service address entries\n" SERVICE_NAME_STR
+
+DEFUN(cfg_mslookup_server_msc_no_service,
+ cfg_mslookup_server_msc_no_service_cmd,
+ "no service NAME",
+ NO_SERVICE_AND_NAME_STR)
+{
+ /* If this command is run on the 'server' node, it produces an empty unit name and serves as wildcard for all
+ * MSCs. If on a 'server' / 'msc' node, set services only for that MSC Unit Name. */
+ struct dgsm_msc_config *msc = msc_from_node(vty);
+ const char *service = argv[0];
+
+ if (!msc) {
+ vty_out(vty, "%% Error: no MSC object on this node%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (dgsm_config_msc_service_del(msc, service, NULL) < 1) {
+ vty_out(vty, "%% mslookup server: cannot remove service '%s'%s",
+ service, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mslookup_server_msc_no_service_addr,
+ cfg_mslookup_server_msc_no_service_addr_cmd,
+ "no service NAME at IP <1-65535>",
+ NO_SERVICE_AND_NAME_STR "at\n" IP46_STR PORT_STR)
+{
+ /* If this command is run on the 'server' node, it produces an empty unit name and serves as wildcard for all
+ * MSCs. If on a 'server' / 'msc' node, set services only for that MSC Unit Name. */
+ struct dgsm_msc_config *msc = msc_from_node(vty);
+ const char *service = argv[0];
+ const char *ip_str = argv[1];
+ const char *port_str = argv[2];
+ struct osmo_sockaddr_str addr;
+
+ if (!msc) {
+ vty_out(vty, "%% Error: no MSC object on this node%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (osmo_sockaddr_str_from_str(&addr, ip_str, atoi(port_str))
+ || !osmo_sockaddr_str_is_nonzero(&addr)) {
+ vty_out(vty, "%% mslookup server: Invalid address for 'no service' %s: %s %s%s",
+ service, ip_str, port_str, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (dgsm_config_msc_service_del(msc, service, &addr) < 1) {
+ vty_out(vty, "%% mslookup server: cannot remove service '%s' to %s %s%s",
+ service, ip_str, port_str, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+struct cmd_node mslookup_client_node = {
+ MSLOOKUP_CLIENT_NODE,
+ "%s(config-mslookup-client)# ",
+ 1,
+};
+
+DEFUN(cfg_mslookup_client,
+ cfg_mslookup_client_cmd,
+ "client",
+ "Enable and configure Distributed GSM mslookup client")
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ vty->node = MSLOOKUP_CLIENT_NODE;
+ c->client.enable = true;
+ dgsm_config_apply(c);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mslookup_no_client,
+ cfg_mslookup_no_client_cmd,
+ "no client",
+ NO_STR "Disable Distributed GSM mslookup client")
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ c->client.enable = false;
+ dgsm_config_apply(c);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mslookup_client_timeout,
+ cfg_mslookup_client_timeout_cmd,
+ "timeout <1-100000>",
+ "How long should the mslookup client wait for remote responses before evaluating received results\n"
+ "timeout in milliseconds\n")
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ uint32_t val = atol(argv[0]);
+ c->client.timeout.tv_sec = val / 1000;
+ c->client.timeout.tv_usec = (val % 1000) * 1000;
+ return CMD_SUCCESS;
+}
+
+#define EXIT_HINT() \
+ if (vty->type != VTY_FILE) \
+ vty_out(vty, "%% 'exit' this node to apply changes%s", VTY_NEWLINE)
+
+
+DEFUN(cfg_mslookup_client_mdns,
+ cfg_mslookup_client_mdns_cmd,
+ "mdns [IP] [<1-65535>]",
+ "Enable mDNS client, and configure multicast address to send mDNS mslookup requests to\n"
+ MDNS_IP46_STR MDNS_PORT_STR)
+{
+ return mslookup_client_mdns_to(vty, argc, argv);
+}
+
+DEFUN(cfg_mslookup_client_no_mdns,
+ cfg_mslookup_client_no_mdns_cmd,
+ "no mdns",
+ NO_STR "Disable mDNS client, do not query remote services by mDNS\n")
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ c->client.mdns.enable = false;
+ dgsm_config_apply(c);
+ return CMD_SUCCESS;
+}
+
+void config_write_msc_services(struct vty *vty, const char *indent, struct dgsm_msc_config *msc)
+{
+ struct dgsm_service_host *e;
+
+ llist_for_each_entry(e, &msc->service_hosts, entry) {
+ if (osmo_sockaddr_str_is_nonzero(&e->host_v4))
+ vty_out(vty, "%sservice %s at %s %u%s", indent, e->service, e->host_v4.ip, e->host_v4.port,
+ VTY_NEWLINE);
+ if (osmo_sockaddr_str_is_nonzero(&e->host_v6))
+ vty_out(vty, "%sservice %s at %s %u%s", indent, e->service, e->host_v6.ip, e->host_v6.port,
+ VTY_NEWLINE);
+ }
+}
+
+int config_write_mslookup(struct vty *vty)
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+
+ if (!c->server.enable && !c->client.enable
+ && llist_empty(&c->server.msc_configs))
+ return CMD_SUCCESS;
+
+ vty_out(vty, "mslookup%s", VTY_NEWLINE);
+
+ if (c->server.enable || !llist_empty(&c->server.msc_configs)) {
+ struct dgsm_msc_config *msc;
+
+ vty_out(vty, " server%s", VTY_NEWLINE);
+
+ if (c->server.mdns.enable
+ && osmo_sockaddr_str_is_nonzero(&c->server.mdns.bind_addr))
+ vty_out(vty, " mdns bind %s %u%s",
+ c->server.mdns.bind_addr.ip,
+ c->server.mdns.bind_addr.port,
+ VTY_NEWLINE);
+
+ msc = dgsm_config_msc_get(&dgsm_config_msc_wildcard, false);
+ if (msc)
+ config_write_msc_services(vty, " ", msc);
+
+ llist_for_each_entry(msc, &c->server.msc_configs, entry) {
+ if (!osmo_gt_cmp(&dgsm_config_msc_wildcard, &msc->name))
+ continue;
+ vty_out(vty, " msc %s%s", osmo_gt_name(&msc->name), VTY_NEWLINE);
+ config_write_msc_services(vty, " ", msc);
+ }
+
+ /* If the server is disabled, still output the above to not lose the service config. */
+ if (!c->server.enable)
+ vty_out(vty, " no server%s", VTY_NEWLINE);
+ }
+
+ if (c->client.enable) {
+ vty_out(vty, " client%s", VTY_NEWLINE);
+
+ if (osmo_sockaddr_str_is_nonzero(&c->client.gateway_proxy))
+ vty_out(vty, " gateway-proxy %s %u%s",
+ c->client.gateway_proxy.ip,
+ c->client.gateway_proxy.port,
+ VTY_NEWLINE);
+
+ if (c->client.mdns.enable
+ && osmo_sockaddr_str_is_nonzero(&c->client.mdns.query_addr))
+ vty_out(vty, " mdns to %s %u%s",
+ c->client.mdns.query_addr.ip,
+ c->client.mdns.query_addr.port,
+ VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mslookup_client_gateway_proxy,
+ cfg_mslookup_client_gateway_proxy_cmd,
+ "gateway-proxy IP [<1-65535>]",
+ "Configure a fixed IP address to send all GSUP requests for unknown IMSIs to, without invoking a lookup for IMSI\n"
+ "IP address of the remote HLR\n" "GSUP port number (omit for default " OSMO_STRINGIFY_VAL(OSMO_GSUP_PORT) ")\n")
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ const char *ip_str = argv[0];
+ const char *port_str = argc > 1 ? argv[1] : NULL;
+ struct osmo_sockaddr_str addr;
+
+ if (osmo_sockaddr_str_from_str(&addr, ip_str, port_str ? atoi(port_str) : OSMO_GSUP_PORT)
+ || !osmo_sockaddr_str_is_nonzero(&addr)) {
+ vty_out(vty, "%% mslookup client: Invalid address for gateway-proxy: %s %s%s",
+ ip_str, port_str ? : "", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ c->client.gateway_proxy = addr;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mslookup_client_no_gateway_proxy,
+ cfg_mslookup_client_no_gateway_proxy_cmd,
+ "no gateway-proxy",
+ NO_STR "Disable gateway proxy for GSUP with unknown IMSIs\n")
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ c->client.gateway_proxy = (struct osmo_sockaddr_str){};
+ return CMD_SUCCESS;
+}
+
+DEFUN(do_mslookup_show_services,
+ do_mslookup_show_services_cmd,
+ "show mslookup services",
+ SHOW_STR "Distributed GSM / mslookup related information\n"
+ "List configured service addresses as sent to remote mslookup requests\n")
+{
+ struct dgsm_config *c = &g_hlr->mslookup.vty;
+ struct dgsm_msc_config *msc;
+ struct dgsm_service_host *local_hlr = mslookup_server_get_local_gsup_addr();
+
+ vty_out(vty, "Local GSUP HLR address returned in mslookup responses for local IMSIs:");
+ if (osmo_sockaddr_str_is_nonzero(&local_hlr->host_v4))
+ vty_out(vty, " " OSMO_SOCKADDR_STR_FMT,
+ OSMO_SOCKADDR_STR_FMT_ARGS(&local_hlr->host_v4));
+ if (osmo_sockaddr_str_is_nonzero(&local_hlr->host_v6))
+ vty_out(vty, " " OSMO_SOCKADDR_STR_FMT,
+ OSMO_SOCKADDR_STR_FMT_ARGS(&local_hlr->host_v6));
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ msc = dgsm_config_msc_get(&dgsm_config_msc_wildcard, false);
+ if (msc)
+ config_write_msc_services(vty, "", msc);
+
+ llist_for_each_entry(msc, &c->server.msc_configs, entry) {
+ if (!osmo_gt_cmp(&dgsm_config_msc_wildcard, &msc->name))
+ continue;
+ vty_out(vty, "msc %s%s", osmo_gt_name(&msc->name), VTY_NEWLINE);
+ config_write_msc_services(vty, " ", msc);
+ }
+ return CMD_SUCCESS;
+}
+
+void dgsm_vty_init()
+{
+ install_element(CONFIG_NODE, &cfg_mslookup_cmd);
+
+ install_node(&mslookup_node, config_write_mslookup);
+ install_element(MSLOOKUP_NODE, &cfg_mslookup_mdns_cmd);
+ install_element(MSLOOKUP_NODE, &cfg_mslookup_no_mdns_cmd);
+ install_element(MSLOOKUP_NODE, &cfg_mslookup_server_cmd);
+ install_element(MSLOOKUP_NODE, &cfg_mslookup_no_server_cmd);
+
+ install_node(&mslookup_server_node, NULL);
+ install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_mdns_bind_cmd);
+ install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_no_mdns_cmd);
+ install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_service_cmd);
+ install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_no_service_cmd);
+ install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_no_service_addr_cmd);
+ install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_msc_cmd);
+
+ install_node(&mslookup_server_msc_node, NULL);
+ install_element(MSLOOKUP_SERVER_MSC_NODE, &cfg_mslookup_server_msc_service_cmd);
+ install_element(MSLOOKUP_SERVER_MSC_NODE, &cfg_mslookup_server_msc_no_service_cmd);
+ install_element(MSLOOKUP_SERVER_MSC_NODE, &cfg_mslookup_server_msc_no_service_addr_cmd);
+
+ 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_timeout_cmd);
+ install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_mdns_cmd);
+ install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_mdns_cmd);
+ install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_gateway_proxy_cmd);
+ install_element(MSLOOKUP_CLIENT_NODE, &cfg_mslookup_client_no_gateway_proxy_cmd);
+
+ install_element_ve(&do_mslookup_show_services_cmd);
+}
diff --git a/src/gsup_server.c b/src/gsup_server.c
index 83329a0..151a882 100644
--- a/src/gsup_server.c
+++ b/src/gsup_server.c
@@ -28,8 +28,10 @@
#include <osmocom/gsm/apn.h>
#include <osmocom/gsm/gsm23003.h>
+#include <osmocom/hlr/hlr.h>
#include <osmocom/hlr/gsup_server.h>
#include <osmocom/hlr/gsup_router.h>
+#include <osmocom/hlr/proxy.h>
#define LOG_GSUP_CONN(conn, level, fmt, args...) \
LOGP(DLGSUP, level, "GSUP peer %s: " fmt, \
diff --git a/src/hlr.c b/src/hlr.c
index d1647a0..536a240 100644
--- a/src/hlr.c
+++ b/src/hlr.c
@@ -36,6 +36,7 @@
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/gsm23003.h>
+#include <osmocom/mslookup/mslookup_client.h>
#include <osmocom/gsupclient/global_title.h>
#include <osmocom/hlr/db.h>
@@ -47,6 +48,8 @@
#include <osmocom/hlr/rand.h>
#include <osmocom/hlr/hlr_vty.h>
#include <osmocom/hlr/hlr_ussd.h>
+#include <osmocom/hlr/dgsm.h>
+#include <osmocom/hlr/proxy.h>
#include <osmocom/hlr/lu_fsm.h>
struct hlr *g_hlr;
@@ -489,6 +492,15 @@
}
}
+ /* Distributed GSM: check whether to proxy for / lookup a remote HLR.
+ * It would require less database hits to do this only if a local-only operation fails with "unknown IMSI", but
+ * it becomes semantically easier if we do this once-off ahead of time. */
+ if (osmo_mslookup_client_active(g_hlr->mslookup.client.client)
+ || osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.vty.client.gateway_proxy)) {
+ if (dgsm_check_forward_gsup_msg(req))
+ return 0;
+ }
+
switch (req->gsup.message_type) {
/* requests sent to us */
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
@@ -692,6 +704,9 @@
exit(1);
}
+ /* Set up llists and objects, startup is happening from VTY commands. */
+ dgsm_init(hlr_ctx);
+
osmo_stats_init(hlr_ctx);
vty_init(&vty_info);
ctrl_vty_init(hlr_ctx);
@@ -746,10 +761,13 @@
LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
exit(1);
}
+ proxy_init(g_hlr->gs);
g_hlr->ctrl_bind_addr = ctrl_vty_get_bind_addr();
g_hlr->ctrl = hlr_controlif_setup(g_hlr);
+ dgsm_start(hlr_ctx);
+
osmo_init_ignore_signals();
signal(SIGINT, &signal_hdlr);
signal(SIGTERM, &signal_hdlr);
@@ -766,6 +784,7 @@
while (!quit)
osmo_select_main_ctx(0);
+ dgsm_stop();
osmo_gsup_server_destroy(g_hlr->gs);
db_close(g_hlr->dbc);
diff --git a/src/hlr_vty.c b/src/hlr_vty.c
index 6701cd9..e20d2bb 100644
--- a/src/hlr_vty.c
+++ b/src/hlr_vty.c
@@ -39,6 +39,7 @@
#include <osmocom/hlr/hlr_vty_subscr.h>
#include <osmocom/hlr/hlr_ussd.h>
#include <osmocom/hlr/gsup_server.h>
+#include <osmocom/hlr/dgsm.h>
struct cmd_node hlr_node = {
HLR_NODE,
@@ -146,6 +147,24 @@
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_unit_name.serno = talloc_strdup(g_hlr, argv[0]);
+ return CMD_SUCCESS;
+}
+
/***********************************************************************
* USSD Entity
***********************************************************************/
@@ -403,6 +422,17 @@
vty->index = NULL;
vty->index_sub = NULL;
break;
+ case MSLOOKUP_CLIENT_NODE:
+ case MSLOOKUP_SERVER_NODE:
+ vty->node = CONFIG_NODE;
+ vty->index = NULL;
+ vty->index_sub = NULL;
+ break;
+ case MSLOOKUP_SERVER_MSC_NODE:
+ vty->node = CONFIG_NODE;
+ vty->index = NULL;
+ vty->index_sub = NULL;
+ break;
default:
case HLR_NODE:
vty->node = CONFIG_NODE;
@@ -444,6 +474,7 @@
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);
@@ -462,4 +493,5 @@
install_element(HLR_NODE, &cfg_no_subscr_create_on_demand_cmd);
hlr_vty_subscriber_init();
+ dgsm_vty_init();
}
diff --git a/src/hlr_vty_subscr.c b/src/hlr_vty_subscr.c
index adbfcab..b8eba4d 100644
--- a/src/hlr_vty_subscr.c
+++ b/src/hlr_vty_subscr.c
@@ -30,6 +30,7 @@
#include <osmocom/hlr/hlr.h>
#include <osmocom/hlr/db.h>
+#include <osmocom/hlr/proxy.h>
struct vty;
diff --git a/src/logging.c b/src/logging.c
index d123fcd..9d3cc62 100644
--- a/src/logging.c
+++ b/src/logging.c
@@ -31,6 +31,12 @@
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
+ [DDGSM] = {
+ .name = "DDGSM",
+ .description = "Distributed GSM: MS lookup and proxy",
+ .color = "\033[1;35m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
};
diff --git a/src/mslookup_server.c b/src/mslookup_server.c
new file mode 100644
index 0000000..af25e06
--- /dev/null
+++ b/src/mslookup_server.c
@@ -0,0 +1,302 @@
+#include <string.h>
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/mslookup/mslookup.h>
+#include <osmocom/hlr/logging.h>
+#include <osmocom/hlr/hlr.h>
+#include <osmocom/hlr/db.h>
+#include <osmocom/hlr/dgsm.h>
+#include <osmocom/hlr/mslookup_server.h>
+#include <osmocom/hlr/proxy.h>
+
+static const struct osmo_mslookup_result not_found = {
+ .rc = OSMO_MSLOOKUP_RC_NOT_FOUND,
+ };
+
+static void set_result(struct osmo_mslookup_result *result,
+ const struct dgsm_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;
+}
+
+struct dgsm_service_host *mslookup_server_get_local_gsup_addr()
+{
+ static struct dgsm_service_host gsup_bind = {};
+ struct dgsm_service_host *host;
+
+ /* Find a HLR/GSUP service set for the server (no VLR unit name) */
+ host = dgsm_config_service_get(&dgsm_config_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;
+}
+
+/* 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)
+{
+ struct dgsm_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(DDGSM, LOGL_ERROR, "Unknown mslookup ID type: %d\n", query->id.type);
+ *result = not_found;
+ return;
+ }
+
+ if (rc) {
+ LOGP(DDGSM, LOGL_DEBUG, "%s: does not exist in local HLR\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
+ *result = not_found;
+ return;
+ }
+
+ LOGP(DDGSM, 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(DDGSM, 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_gt *local_msc_name)
+{
+ struct hlr_subscriber subscr;
+ int rc;
+ uint32_t age;
+
+ 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(DDGSM, LOGL_ERROR, "Unknown mslookup ID type: %d\n", query->id.type);
+ return false;
+ }
+
+ if (rc) {
+ LOGP(DDGSM, 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(DDGSM, LOGL_DEBUG, "%s: not attached (vlr_number unset)\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
+ }
+
+ 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(DDGSM, 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_gt_name(&subscr.vlr_via_proxy));
+ return false;
+ }
+
+ if (!timestamp_age(&subscr.last_lu_seen, &age)) {
+ LOG_DGSM(subscr.imsi, LOGL_ERROR, "Invalid last_lu_seen timestamp for subscriber\n");
+ return false;
+ }
+ if (age > g_hlr->mslookup.server.max_age) {
+ LOGP(DDGSM, 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.max_age);
+ return false;
+ }
+
+ *lu_age = age;
+ osmo_gt_set_str(local_msc_name, subscr.vlr_number);
+ LOGP(DDGSM, LOGL_DEBUG, "%s: attached %u seconds ago at local VLR %s\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
+ age, osmo_gt_name(local_msc_name));
+
+ return true;
+}
+
+
+/* Determine whether the subscriber with the given ID has routed a Location Updating via this HLR as first hop. Return
+ * true if it is attached at a local VLR, and we are serving as proxy for a remote home HLR.
+ */
+static bool subscriber_has_done_lu_here_proxy(const struct osmo_mslookup_query *query,
+ uint32_t *lu_age,
+ struct osmo_gt *local_msc_name)
+{
+ const struct proxy_subscr *proxy_subscr;
+ uint32_t age;
+
+ /* See the local HLR record. If the subscriber is "at home" in this HLR and is also currently located here, we
+ * will find a valid location updating and no vlr_via_proxy entry. */
+ switch (query->id.type) {
+ case OSMO_MSLOOKUP_ID_IMSI:
+ proxy_subscr = proxy_subscr_get_by_imsi(g_hlr->gs->proxy, query->id.imsi);
+ break;
+ case OSMO_MSLOOKUP_ID_MSISDN:
+ proxy_subscr = proxy_subscr_get_by_msisdn(g_hlr->gs->proxy, query->id.msisdn);
+ break;
+ default:
+ LOGP(DDGSM, LOGL_ERROR, "%s: unknown ID type\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
+ return false;
+ }
+
+ if (!proxy_subscr) {
+ LOGP(DDGSM, LOGL_DEBUG, "%s: does not exist in GSUP proxy\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, NULL));
+ return false;
+ }
+
+ /* We only need to care about CS LU, since only CS services need D-GSM routing. */
+ if (!timestamp_age(&proxy_subscr->cs.last_lu, &age)
+ || age > g_hlr->mslookup.server.max_age) {
+ LOGP(DDGSM, LOGL_ERROR,
+ "%s: last attach was at local VLR (proxying for remote HLR), but too long ago: %us > %us\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
+ age, g_hlr->mslookup.server.max_age);
+ return false;
+ }
+
+ if (proxy_subscr->cs.vlr_via_proxy.len) {
+ LOGP(DDGSM, 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),
+ osmo_gt_name(&proxy_subscr->cs.vlr_name),
+ osmo_gt_name(&proxy_subscr->cs.vlr_via_proxy));
+ return false;
+ }
+
+ *lu_age = age;
+ *local_msc_name = proxy_subscr->cs.vlr_name;
+ LOGP(DDGSM, LOGL_DEBUG, "%s: attached %u seconds ago at local VLR %s; proxying for remote HLR "
+ OSMO_SOCKADDR_STR_FMT "\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
+ age, osmo_gt_name(local_msc_name),
+ OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr->remote_hlr_addr));
+
+ return true;
+}
+
+static bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query,
+ uint32_t *lu_age_p,
+ struct osmo_gt *local_msc_name)
+{
+ bool attached_here;
+ uint32_t lu_age = 0;
+ struct osmo_gt msc_name = {};
+ bool attached_here_proxy;
+ uint32_t proxy_lu_age = 0;
+ struct osmo_gt proxy_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);
+ attached_here_proxy = subscriber_has_done_lu_here_proxy(query, &proxy_lu_age, &proxy_msc_name);
+
+ /* If proxy has a younger lu, replace. */
+ if (attached_here_proxy && (!attached_here || (proxy_lu_age < lu_age))) {
+ attached_here = true;
+ lu_age = proxy_lu_age;
+ msc_name = proxy_msc_name;
+ }
+
+ if (attached_here && !msc_name.len) {
+ LOGP(DDGSM, 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(DDGSM, LOGL_INFO, "%s: attached here, at VLR %s\n",
+ osmo_mslookup_result_name_c(OTC_SELECT, query, NULL),
+ osmo_gt_name(&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 osmo_mslookup_server_rx(const struct osmo_mslookup_query *query,
+ struct osmo_mslookup_result *result)
+{
+ const struct dgsm_service_host *service_host;
+ uint32_t age;
+ struct osmo_gt 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 = dgsm_config_service_get(&msc_name, query->service);
+
+ if (!service_host) {
+ /* Find such service set globally (no VLR unit name) */
+ service_host = dgsm_config_service_get(&dgsm_config_msc_wildcard, query->service);
+ }
+
+ if (!service_host) {
+ LOGP(DDGSM, 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);
+}
diff --git a/src/mslookup_server_mdns.c b/src/mslookup_server_mdns.c
new file mode 100644
index 0000000..b758cb5
--- /dev/null
+++ b/src/mslookup_server_mdns.c
@@ -0,0 +1,100 @@
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <osmocom/mslookup/mslookup.h>
+#include <osmocom/mslookup/mdns.h>
+#include <osmocom/hlr/logging.h>
+#include <osmocom/hlr/mslookup_server.h>
+#include <osmocom/hlr/mslookup_server_mdns.h>
+
+static void osmo_mslookup_server_mdns_tx(struct osmo_mslookup_server_mdns *server,
+ uint16_t packet_id,
+ const struct osmo_mslookup_query *query,
+ const struct osmo_mslookup_result *result)
+{
+ struct msgb *msg;
+ const char *errmsg = NULL;
+ void *ctx = talloc_named_const(server, 0, __func__);
+
+ msg = osmo_mdns_result_encode(ctx, packet_id, query, result);
+ if (!msg)
+ errmsg = "Error encoding mDNS answer packet";
+ else if (osmo_mdns_sock_send(server->sock, msg))
+ errmsg = "Error sending mDNS answer";
+ if (errmsg)
+ LOGP(DDGSM, LOGL_ERROR, "%s: mDNS: %s\n", osmo_mslookup_result_name_c(ctx, query, result), errmsg);
+ talloc_free(ctx);
+}
+
+static void osmo_mslookup_server_mdns_handle_request(uint16_t packet_id,
+ struct osmo_mslookup_server_mdns *server,
+ const struct osmo_mslookup_query *query)
+{
+ struct osmo_mslookup_result result;
+
+ osmo_mslookup_server_rx(query, &result);
+ /* Error logging already happens in osmo_mslookup_server_rx() */
+ if (result.rc != OSMO_MSLOOKUP_RC_RESULT)
+ return;
+
+ osmo_mslookup_server_mdns_tx(server, packet_id, query, &result);
+}
+
+static int osmo_mslookup_server_mdns_rx(struct osmo_fd *osmo_fd, unsigned int what)
+{
+ struct osmo_mslookup_server_mdns *server = osmo_fd->data;
+ struct osmo_mslookup_query *query;
+ uint16_t packet_id;
+ int n;
+ uint8_t buffer[1024];
+ void *ctx;
+
+ /* Parse the message and print it */
+ n = read(osmo_fd->fd, buffer, sizeof(buffer));
+ if (n < 0)
+ return n;
+
+ ctx = talloc_named_const(server, 0, __func__);
+ query = osmo_mdns_query_decode(ctx, buffer, n, &packet_id);
+ if (!query) {
+ talloc_free(ctx);
+ return -1;
+ }
+
+ osmo_mslookup_id_name_buf((char *)buffer, sizeof(buffer), &query->id);
+ LOGP(DDGSM, LOGL_DEBUG, "mDNS rx request: %s.%s\n", query->service, buffer);
+ osmo_mslookup_server_mdns_handle_request(packet_id, server, query);
+ talloc_free(ctx);
+ return n;
+}
+
+struct osmo_mslookup_server_mdns *osmo_mslookup_server_mdns_start(void *ctx, const struct osmo_sockaddr_str *bind_addr)
+{
+ struct osmo_mslookup_server_mdns *server = talloc_zero(ctx, struct osmo_mslookup_server_mdns);
+ OSMO_ASSERT(server);
+ *server = (struct osmo_mslookup_server_mdns){
+ .bind_addr = *bind_addr,
+ };
+
+ server->sock = osmo_mdns_sock_init(server,
+ bind_addr->ip, bind_addr->port,
+ osmo_mslookup_server_mdns_rx,
+ server, 0);
+ if (!server->sock) {
+ LOGP(DDGSM, LOGL_ERROR,
+ "mslookup mDNS server: error initializing multicast bind on " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(bind_addr));
+ talloc_free(server);
+ return NULL;
+ }
+
+ return server;
+}
+
+void osmo_mslookup_server_mdns_stop(struct osmo_mslookup_server_mdns *server)
+{
+ if (!server)
+ return;
+ osmo_mdns_sock_cleanup(server->sock);
+ talloc_free(server);
+}
diff --git a/src/proxy.c b/src/proxy.c
new file mode 100644
index 0000000..7ccedbb
--- /dev/null
+++ b/src/proxy.c
@@ -0,0 +1,557 @@
+
+#include <sys/time.h>
+#include <string.h>
+#include <talloc.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <osmocom/core/timer.h>
+#include <osmocom/gsm/gsup.h>
+#include <osmocom/gsm/gsm23003.h>
+#include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsupclient/gsup_client.h>
+#include <osmocom/gsupclient/gsup_req.h>
+
+#include <osmocom/hlr/logging.h>
+#include <osmocom/hlr/proxy.h>
+#include <osmocom/hlr/remote_hlr.h>
+#include <osmocom/hlr/gsup_server.h>
+#include <osmocom/hlr/gsup_router.h>
+
+#define LOG_PROXY_SUBSCR(proxy_subscr, level, fmt, args...) \
+ LOGP(DDGSM, level, "(Proxy IMSI-%s MSISDN-%s HLR-" OSMO_SOCKADDR_STR_FMT ") " fmt, \
+ ((proxy_subscr) && *(proxy_subscr)->imsi)? (proxy_subscr)->imsi : "?", \
+ ((proxy_subscr) && *(proxy_subscr)->msisdn)? (proxy_subscr)->msisdn : "?", \
+ OSMO_SOCKADDR_STR_FMT_ARGS((proxy_subscr)? &(proxy_subscr)->remote_hlr_addr : NULL), \
+ ##args)
+
+#define LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup_msg, level, fmt, args...) \
+ LOG_PROXY_SUBSCR(proxy_subscr, level, "%s: " fmt, \
+ (gsup_msg) ? osmo_gsup_message_type_name((gsup_msg)->message_type) : "NULL", \
+ ##args)
+
+/* Why have a separate struct to add an llist_head entry?
+ * This is to keep the option open to store the proxy data in the database instead, without any visible effect outside
+ * of proxy.c. */
+struct proxy_subscr_listentry {
+ struct llist_head entry;
+ timestamp_t last_update;
+ struct proxy_subscr data;
+};
+
+/* 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;
+}
+
+/* Defer a GSUP message until we know a remote HLR to proxy to.
+ * Where to send this GSUP message is indicated by its IMSI: as soon as an MS lookup has yielded the IMSI's home HLR,
+ * that's where the message should go. */
+static void proxy_defer_gsup_req(struct proxy *proxy, struct osmo_gsup_req *req)
+{
+ struct proxy_pending_gsup_req *m;
+
+ m = talloc_zero(proxy, struct proxy_pending_gsup_req);
+ OSMO_ASSERT(m);
+ m->req = req;
+ timestamp_update(&m->received_at);
+ llist_add_tail(&m->entry, &proxy->pending_gsup_reqs);
+}
+
+/* Unable to resolve remote HLR for this IMSI, Answer with error back to the sender. */
+static void proxy_defer_gsup_message_err(struct proxy *proxy, struct proxy_pending_gsup_req *m)
+{
+ osmo_gsup_req_respond_err(m->req, GMM_CAUSE_IMSI_UNKNOWN, "could not reach home HLR");
+ m->req = NULL;
+}
+
+/* Forward spooled message for this IMSI to remote HLR. */
+static void proxy_defer_gsup_message_send(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ struct proxy_pending_gsup_req *m, struct remote_hlr *remote_hlr)
+{
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, &m->req->gsup, LOGL_INFO, "Forwarding deferred message\n");
+ proxy_subscr_forward_to_remote_hlr_resolved(proxy, proxy_subscr, remote_hlr, m->req);
+ m->req = NULL;
+}
+
+/* Result of looking for remote HLR. If it failed, pass remote_hlr as NULL. On failure, the proxy_subscr and the
+ * remote_hlr may be passed NULL. The IMSI then reflects who the error was for. */
+static void proxy_defer_gsup_message_pop(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ const char *imsi, struct remote_hlr *remote_hlr)
+{
+ struct proxy_pending_gsup_req *m, *n;
+ if (!imsi && proxy_subscr)
+ imsi = proxy_subscr->imsi;
+ OSMO_ASSERT(imsi);
+
+ if (!remote_hlr)
+ LOGP(DDGSM, LOGL_ERROR, "IMSI-%s: No remote HLR found, dropping spooled GSUP messages\n", imsi);
+
+ llist_for_each_entry_safe(m, n, &proxy->pending_gsup_reqs, entry) {
+ if (strcmp(m->req->gsup.imsi, imsi))
+ continue;
+
+ if (!remote_hlr)
+ proxy_defer_gsup_message_err(proxy, m);
+ else
+ proxy_defer_gsup_message_send(proxy, proxy_subscr, m, remote_hlr);
+
+ llist_del(&m->entry);
+ talloc_free(m);
+ }
+}
+
+
+static bool proxy_subscr_matches_imsi(const struct proxy_subscr *proxy_subscr, const char *imsi)
+{
+ if (!proxy_subscr || !imsi)
+ return false;
+ return strcmp(proxy_subscr->imsi, imsi) == 0;
+}
+
+static bool proxy_subscr_matches_msisdn(const struct proxy_subscr *proxy_subscr, const char *msisdn)
+{
+ if (!proxy_subscr || !msisdn)
+ return false;
+ return strcmp(proxy_subscr->msisdn, msisdn) == 0;
+}
+
+static struct proxy_subscr_listentry *_proxy_get_by_imsi(struct proxy *proxy, const char *imsi)
+{
+ struct proxy_subscr_listentry *e;
+ if (!proxy)
+ return NULL;
+ llist_for_each_entry(e, &proxy->subscr_list, entry) {
+ if (proxy_subscr_matches_imsi(&e->data, imsi))
+ return e;
+ }
+ return NULL;
+}
+
+static struct proxy_subscr_listentry *_proxy_get_by_msisdn(struct proxy *proxy, const char *msisdn)
+{
+ struct proxy_subscr_listentry *e;
+ if (!proxy)
+ return NULL;
+ llist_for_each_entry(e, &proxy->subscr_list, entry) {
+ if (proxy_subscr_matches_msisdn(&e->data, msisdn))
+ return e;
+ }
+ return NULL;
+}
+
+const struct proxy_subscr *proxy_subscr_get_by_imsi(struct proxy *proxy, const char *imsi)
+{
+ struct proxy_subscr_listentry *e = _proxy_get_by_imsi(proxy, imsi);
+ if (!e)
+ return NULL;
+ return &e->data;
+}
+
+const struct proxy_subscr *proxy_subscr_get_by_msisdn(struct proxy *proxy, const char *msisdn)
+{
+ struct proxy_subscr_listentry *e = _proxy_get_by_msisdn(proxy, msisdn);
+ if (!e)
+ return NULL;
+ 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;
+ if (!proxy)
+ return;
+ llist_for_each_entry(e, &proxy->subscr_list, entry) {
+ if (!osmo_sockaddr_str_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);
+ if (!e) {
+ /* Does not exist yet */
+ e = talloc_zero(proxy, struct proxy_subscr_listentry);
+ llist_add(&e->entry, &proxy->subscr_list);
+ }
+ e->data = *proxy_subscr;
+ timestamp_update(&e->last_update);
+ return 0;
+}
+
+int _proxy_subscr_del(struct proxy_subscr_listentry *e)
+{
+ llist_del(&e->entry);
+ return 0;
+}
+
+int proxy_subscr_del(struct proxy *proxy, const char *imsi)
+{
+ struct proxy_subscr_listentry *e;
+ proxy_defer_gsup_message_pop(proxy, NULL, imsi, NULL);
+ e = _proxy_get_by_imsi(proxy, imsi);
+ if (!e)
+ return -ENOENT;
+ return _proxy_subscr_del(e);
+}
+
+/* Discard stale proxy entries. */
+static void proxy_cleanup(void *proxy_v)
+{
+ struct proxy *proxy = proxy_v;
+ struct proxy_subscr_listentry *e, *n;
+ uint32_t age;
+ llist_for_each_entry_safe(e, n, &proxy->subscr_list, entry) {
+ if (!timestamp_age(&e->last_update, &age))
+ LOGP(DDGSM, LOGL_ERROR, "Invalid timestamp, deleting proxy entry\n");
+ else if (age <= proxy->fresh_time)
+ continue;
+ LOG_PROXY_SUBSCR(&e->data, LOGL_INFO, "proxy entry timed out, deleting\n");
+ _proxy_subscr_del(e);
+ }
+ if (proxy->gc_period)
+ osmo_timer_schedule(&proxy->gc_timer, proxy->gc_period, 0);
+ else
+ LOGP(DDGSM, LOGL_NOTICE, "Proxy cleanup is switched off (gc_period == 0)\n");
+}
+
+void proxy_set_gc_period(struct proxy *proxy, uint32_t gc_period)
+{
+ proxy->gc_period = gc_period;
+ proxy_cleanup(proxy);
+}
+
+void proxy_init(struct osmo_gsup_server *gsup_server_to_vlr)
+{
+ OSMO_ASSERT(!gsup_server_to_vlr->proxy);
+ struct proxy *proxy = talloc_zero(gsup_server_to_vlr, struct proxy);
+ *proxy = (struct proxy){
+ .gsup_server_to_vlr = gsup_server_to_vlr,
+ .fresh_time = 60*60,
+ .gc_period = 60,
+ };
+ INIT_LLIST_HEAD(&proxy->subscr_list);
+ INIT_LLIST_HEAD(&proxy->pending_gsup_reqs);
+
+ osmo_timer_setup(&proxy->gc_timer, proxy_cleanup, proxy);
+ /* Invoke to trigger the first timer schedule */
+ proxy_set_gc_period(proxy, proxy->gc_period);
+ gsup_server_to_vlr->proxy = proxy;
+}
+
+void proxy_del(struct proxy *proxy)
+{
+ osmo_timer_del(&proxy->gc_timer);
+ talloc_free(proxy);
+}
+
+void proxy_subscr_remote_hlr_up(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ struct remote_hlr *remote_hlr)
+{
+ proxy_defer_gsup_message_pop(proxy, proxy_subscr, proxy_subscr->imsi, remote_hlr);
+}
+
+void proxy_subscr_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ struct remote_hlr *remote_hlr)
+{
+ struct proxy_subscr proxy_subscr_new;
+
+ if (osmo_sockaddr_str_is_nonzero(&proxy_subscr->remote_hlr_addr)) {
+ if (!osmo_sockaddr_str_cmp(&remote_hlr->addr, &proxy_subscr->remote_hlr_addr)) {
+ /* Already have this remote address */
+ return;
+ } else {
+ LOG_PROXY_SUBSCR(proxy_subscr, LOGL_NOTICE,
+ "Remote HLR address changes to " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
+ }
+ }
+
+ /* Store the address. Make a copy to modify. */
+ proxy_subscr_new = *proxy_subscr;
+ proxy_subscr = &proxy_subscr_new;
+ proxy_subscr_new.remote_hlr_addr = remote_hlr->addr;
+
+ if (proxy_subscr_update(proxy, proxy_subscr)) {
+ LOG_PROXY_SUBSCR(proxy_subscr, LOGL_ERROR, "Failed to store proxy entry for remote HLR\n");
+ /* If no remote HLR is known for the IMSI, the proxy entry is pointless. */
+ proxy_subscr_del(proxy, proxy_subscr->imsi);
+ return;
+ }
+ LOG_PROXY_SUBSCR(proxy_subscr, LOGL_DEBUG, "Remote HLR resolved, stored address\n");
+}
+
+/* All GSUP messages sent to the remote HLR pass through this function, to modify the subscriber state or disallow
+ * sending the message. Return 0 to allow sending the message. */
+static int proxy_acknowledge_gsup_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ const struct osmo_gsup_req *req)
+{
+ struct proxy_subscr proxy_subscr_new = *proxy_subscr;
+ bool ps;
+ bool cs;
+ int rc;
+
+ switch (req->gsup.message_type) {
+
+ case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
+ /* Store the CS and PS VLR name in vlr_name_preliminary to later update the right {cs,ps} LU timestamp
+ * when receiving an OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT. Store in vlr_name_preliminary so that in
+ * case the LU fails, we keep the vlr_name intact. */
+ switch (req->gsup.cn_domain) {
+ case OSMO_GSUP_CN_DOMAIN_CS:
+ proxy_subscr_new.cs.vlr_name_preliminary = req->source_name;
+ break;
+ case OSMO_GSUP_CN_DOMAIN_PS:
+ proxy_subscr_new.ps.vlr_name_preliminary = req->source_name;
+ break;
+ default:
+ break;
+ }
+
+ ps = cs = false;
+ if (osmo_gt_cmp(&proxy_subscr_new.cs.vlr_name_preliminary, &proxy_subscr->cs.vlr_name_preliminary))
+ cs = true;
+ if (osmo_gt_cmp(&proxy_subscr_new.ps.vlr_name_preliminary, &proxy_subscr->ps.vlr_name_preliminary))
+ ps = true;
+
+ if (!(cs || ps)) {
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_DEBUG, "VLR names remain unchanged\n");
+ break;
+ }
+
+ rc = proxy_subscr_update(proxy, &proxy_subscr_new);
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, rc ? LOGL_ERROR : LOGL_INFO,
+ "%s: preliminary VLR name for%s%s to %s\n",
+ rc ? "failed to update" : "updated",
+ cs ? " CS" : "", ps ? " PS" : "",
+ osmo_gt_name(&req->source_name));
+ break;
+ /* TODO: delete proxy entry in case of a Purge Request? */
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* All GSUP messages received from the remote HLR to be sent to a local MSC pass through this function, to modify the
+ * subscriber state or disallow sending the message. Return 0 to allow sending the message.
+ * The local MSC shall be indicated by gsup.destination_name. */
+static int proxy_acknowledge_gsup_from_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ const struct osmo_gsup_message *gsup,
+ struct remote_hlr *from_remote_hlr,
+ const struct osmo_gt *destination,
+ const struct osmo_gt *via_peer)
+{
+ struct proxy_subscr proxy_subscr_new = *proxy_subscr;
+ bool ps;
+ bool cs;
+ bool vlr_name_changed_cs;
+ bool vlr_name_changed_ps;
+ int rc;
+ struct osmo_gt via_proxy = {};
+ if (osmo_gt_cmp(via_peer, destination))
+ via_proxy = *via_peer;
+
+ switch (gsup->message_type) {
+ case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST:
+ /* Remember the MSISDN of the subscriber. This does not need to be a preliminary record, because when
+ * the HLR tells us about subscriber data, it is definitive info and there is no ambiguity (like there
+ * would be with failed LU attempts from various sources). */
+ if (!gsup->msisdn_enc_len)
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_DEBUG, "No MSISDN in this Insert Data Request\n");
+ else if (gsm48_decode_bcd_number2(proxy_subscr_new.msisdn, sizeof(proxy_subscr_new.msisdn),
+ gsup->msisdn_enc, gsup->msisdn_enc_len, 0))
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, "Failed to decode MSISDN\n");
+ else if (!osmo_msisdn_str_valid(proxy_subscr_new.msisdn))
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, "invalid MSISDN: %s\n",
+ osmo_quote_str_c(OTC_SELECT, proxy_subscr_new.msisdn, -1));
+ else if (!strcmp(proxy_subscr->msisdn, proxy_subscr_new.msisdn))
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_DEBUG, "already have MSISDN = %s\n",
+ proxy_subscr_new.msisdn);
+ else if (proxy_subscr_update(proxy, &proxy_subscr_new))
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, "failed to update MSISDN to %s\n",
+ proxy_subscr_new.msisdn);
+ else
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_INFO, "stored MSISDN=%s\n",
+ proxy_subscr_new.msisdn);
+ break;
+
+ case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT:
+ /* Update the Location Updating timestamp */
+ cs = ps = false;
+ if (!osmo_gt_cmp(destination, &proxy_subscr->cs.vlr_name_preliminary)) {
+ timestamp_update(&proxy_subscr_new.cs.last_lu);
+ proxy_subscr_new.cs.vlr_name_preliminary = (struct osmo_gt){};
+ vlr_name_changed_cs =
+ osmo_gt_cmp(&proxy_subscr->cs.vlr_name, destination)
+ || osmo_gt_cmp(&proxy_subscr->cs.vlr_via_proxy, &via_proxy);
+ proxy_subscr_new.cs.vlr_name = *destination;
+ proxy_subscr_new.cs.vlr_via_proxy = via_proxy;
+ cs = true;
+ }
+ if (!osmo_gt_cmp(destination, &proxy_subscr->ps.vlr_name_preliminary)) {
+ timestamp_update(&proxy_subscr_new.ps.last_lu);
+ proxy_subscr_new.ps.vlr_name_preliminary = (struct osmo_gt){};
+ proxy_subscr_new.ps.vlr_name = *destination;
+ vlr_name_changed_ps =
+ osmo_gt_cmp(&proxy_subscr->ps.vlr_name, destination)
+ || osmo_gt_cmp(&proxy_subscr->ps.vlr_via_proxy, &via_proxy);
+ proxy_subscr_new.ps.vlr_via_proxy = via_proxy;
+ ps = true;
+ }
+ if (!(cs || ps)) {
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR,
+ "destination is neither CS nor PS VLR: %s\n",
+ osmo_gt_name(destination));
+ return GMM_CAUSE_PROTO_ERR_UNSPEC;
+ }
+ rc = proxy_subscr_update(proxy, &proxy_subscr_new);
+
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, rc ? LOGL_ERROR : LOGL_INFO,
+ "%s LU: timestamp for%s%s%s%s%s%s%s%s%s%s\n",
+ rc ? "failed to update" : "updated",
+ cs ? " CS" : "", ps ? " PS" : "",
+ vlr_name_changed_cs? ", CS VLR=" : "",
+ vlr_name_changed_cs? osmo_gt_name(&proxy_subscr_new.cs.vlr_name) : "",
+ proxy_subscr_new.cs.vlr_via_proxy.len ? " via proxy " : "",
+ proxy_subscr_new.cs.vlr_via_proxy.len ?
+ osmo_gt_name(&proxy_subscr_new.cs.vlr_via_proxy) : "",
+ vlr_name_changed_ps? ", PS VLR=" : "",
+ vlr_name_changed_ps? osmo_gt_name(&proxy_subscr_new.ps.vlr_name) : "",
+ proxy_subscr_new.ps.vlr_via_proxy.len ? " via proxy " : "",
+ proxy_subscr_new.ps.vlr_via_proxy.len ?
+ osmo_gt_name(&proxy_subscr_new.ps.vlr_via_proxy) : ""
+ );
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+void proxy_subscr_forward_to_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ struct remote_hlr *remote_hlr, struct osmo_gsup_req *req)
+{
+ if (proxy_acknowledge_gsup_to_remote_hlr(proxy, proxy_subscr, req)) {
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_PROTO_ERR_UNSPEC, "Proxy does not allow this message");
+ return;
+ }
+
+ remote_hlr_gsup_forward_to_remote_hlr(remote_hlr, req);
+}
+
+void proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, struct osmo_gsup_req *req)
+{
+ struct remote_hlr *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_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_DEBUG, "deferring until remote HLR is known\n");
+ proxy_defer_gsup_req(proxy, req);
+ return;
+ }
+
+ if (req->via_proxy.len) {
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_INFO, "VLR->HLR: forwarding from %s via proxy %s\n",
+ osmo_gt_name(&req->source_name),
+ osmo_gt_name(&req->via_proxy));
+ } else {
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_INFO, "VLR->HLR: forwarding from %s\n",
+ osmo_gt_name(&req->source_name));
+ }
+
+ remote_hlr = remote_hlr_get(&proxy_subscr->remote_hlr_addr, true);
+ if (!remote_hlr) {
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL,
+ "Proxy: Failed to establish connection to remote HLR " OSMO_SOCKADDR_STR_FMT,
+ OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr->remote_hlr_addr));
+ return;
+ }
+
+ if (!remote_hlr->gsupc || !remote_hlr->gsupc->is_connected) {
+ /* GSUP link is still busy establishing... */
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_DEBUG,
+ "deferring until link to remote HLR is up\n");
+ proxy_defer_gsup_req(proxy, req);
+ return;
+ }
+
+ proxy_subscr_forward_to_remote_hlr_resolved(proxy, proxy_subscr, remote_hlr, req);
+}
+
+int proxy_subscr_forward_to_vlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr,
+ const struct osmo_gsup_message *gsup, struct remote_hlr *from_remote_hlr)
+{
+ struct osmo_gt destination;
+ struct osmo_gsup_conn *vlr_conn;
+ struct msgb *msg;
+
+ if (osmo_gt_set(&destination, gsup->destination_name, gsup->destination_name_len)) {
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR,
+ "no valid Destination Name IE, cannot route to VLR.\n");
+ return GMM_CAUSE_INV_MAND_INFO;
+ }
+
+ /* Route to MSC/SGSN that we're proxying for */
+ vlr_conn = gsup_route_find_gt(proxy->gsup_server_to_vlr, &destination);
+ if (!vlr_conn) {
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR,
+ "Destination VLR unreachable: %s\n", osmo_gt_name(&destination));
+ return GMM_CAUSE_MSC_TEMP_NOTREACH;
+ }
+
+ if (proxy_acknowledge_gsup_from_remote_hlr(proxy, proxy_subscr, gsup, from_remote_hlr, &destination,
+ &vlr_conn->peer_name)) {
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR,
+ "Proxy does not allow forwarding this message\n");
+ return GMM_CAUSE_PROTO_ERR_UNSPEC;
+ }
+
+ msg = osmo_gsup_msgb_alloc("GSUP proxy to VLR");
+ if (osmo_gsup_encode(msg, gsup)) {
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR,
+ "Failed to re-encode GSUP message, cannot forward\n");
+ return GMM_CAUSE_INV_MAND_INFO;
+ }
+
+ LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_INFO, "VLR<-HLR: forwarding to %s%s%s\n",
+ osmo_gt_name(&destination),
+ osmo_gt_cmp(&destination, &vlr_conn->peer_name) ? " via " : "",
+ osmo_gt_cmp(&destination, &vlr_conn->peer_name) ?
+ osmo_gt_name(&vlr_conn->peer_name) : "");
+ return osmo_gsup_conn_send(vlr_conn, msg);
+}
diff --git a/src/remote_hlr.c b/src/remote_hlr.c
new file mode 100644
index 0000000..78339d4
--- /dev/null
+++ b/src/remote_hlr.c
@@ -0,0 +1,181 @@
+#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/gsupclient/gsup_client.h>
+#include <osmocom/hlr/logging.h>
+#include <osmocom/hlr/hlr.h>
+#include <osmocom/hlr/gsup_server.h>
+#include <osmocom/hlr/gsup_router.h>
+#include <osmocom/hlr/dgsm.h>
+#include <osmocom/hlr/remote_hlr.h>
+#include <osmocom/hlr/proxy.h>
+
+static LLIST_HEAD(remote_hlrs);
+
+static void remote_hlr_err_reply(struct remote_hlr *rh, 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,
+
+ /* 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);
+
+ /* 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(rh->gsupc, &gsup_reply))
+ LOGP(DLGSUP, LOGL_ERROR, "Failed to send Error reply (imsi=%s)\n",
+ osmo_quote_str(gsup_orig->imsi, -1));
+}
+
+/* We are receiving 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 remote_hlr *rh = gsupc->data;
+ const struct proxy_subscr *proxy_subscr;
+ struct osmo_gsup_message gsup;
+ int rc;
+
+ rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
+ if (rc < 0) {
+ LOG_REMOTE_HLR(rh, 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));
+ return rc;
+ }
+
+ if (!osmo_imsi_str_valid(gsup.imsi)) {
+ LOG_REMOTE_HLR_MSG(rh, &gsup, LOGL_ERROR, "Invalid IMSI\n");
+ remote_hlr_err_reply(rh, &gsup, GMM_CAUSE_INV_MAND_INFO);
+ return -GMM_CAUSE_INV_MAND_INFO;
+ }
+
+ proxy_subscr = proxy_subscr_get_by_imsi(g_hlr->gs->proxy, gsup.imsi);
+ if (!proxy_subscr) {
+ LOG_REMOTE_HLR_MSG(rh, &gsup, LOGL_ERROR, "No proxy entry for this IMSI\n");
+ remote_hlr_err_reply(rh, &gsup, GMM_CAUSE_NET_FAIL);
+ return -GMM_CAUSE_NET_FAIL;
+ }
+
+ rc = proxy_subscr_forward_to_vlr(g_hlr->gs->proxy, proxy_subscr, &gsup, rh);
+ if (rc) {
+ LOG_REMOTE_HLR_MSG(rh, &gsup, LOGL_ERROR, "Failed to forward GSUP message towards VLR\n");
+ remote_hlr_err_reply(rh, &gsup, GMM_CAUSE_NET_FAIL);
+ return -GMM_CAUSE_NET_FAIL;
+ }
+ return 0;
+}
+
+static bool remote_hlr_up_yield(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, void *data)
+{
+ struct remote_hlr *remote_hlr = data;
+ proxy_subscr_remote_hlr_up(proxy, proxy_subscr, remote_hlr);
+ return true;
+}
+
+static bool remote_hlr_up_down(struct osmo_gsup_client *gsupc, bool up)
+{
+ struct remote_hlr *remote_hlr = gsupc->data;
+ if (!up) {
+ LOG_REMOTE_HLR(remote_hlr, LOGL_NOTICE, "link to remote HLR is down, removing GSUP client\n");
+ remote_hlr_destroy(remote_hlr);
+ return false;
+ }
+
+ LOG_REMOTE_HLR(remote_hlr, LOGL_NOTICE, "link up\n");
+ proxy_subscrs_get_by_remote_hlr(g_hlr->gs->proxy, &remote_hlr->addr, remote_hlr_up_yield, remote_hlr);
+ return true;
+}
+
+struct remote_hlr *remote_hlr_get(const struct osmo_sockaddr_str *addr, bool create)
+{
+ struct remote_hlr *rh;
+
+ llist_for_each_entry(rh, &remote_hlrs, entry) {
+ if (!osmo_sockaddr_str_cmp(&rh->addr, addr))
+ return rh;
+ }
+
+ if (!create)
+ return NULL;
+
+ /* 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_create3(rh, &g_hlr->gsup_unit_name,
+ addr->ip, addr->port,
+ NULL,
+ remote_hlr_rx,
+ 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)
+{
+ int rc = osmo_gsup_client_send(remote_hlr->gsupc, msg);
+ if (rc) {
+ LOGP(DDGSM, LOGL_ERROR, "Failed to send GSUP message to " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
+ }
+ return rc;
+}
+
+/* A GSUP message was received from the MS/MSC side, forward it to the remote HLR. */
+void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct osmo_gsup_req *req)
+{
+ int rc;
+ struct msgb *msg = osmo_gsup_msgb_alloc("GSUP proxy to remote HLR");
+ /* To forward to a remote HLR, we need to indicate the source MSC's name in the Source Name IE 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. */
+ struct osmo_gsup_message forward = req->gsup;
+ forward.source_name = req->source_name.val;
+ forward.source_name_len = req->source_name.len;
+
+ rc = osmo_gsup_encode(msg, &forward);
+ if (rc) {
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Failed to encode GSUP message for forwarding\n");
+ return;
+ }
+ remote_hlr_msgb_send(remote_hlr, msg);
+ osmo_gsup_req_free(req);
+}
diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty
index dd8dbcf..ac9ff31 100644
--- a/tests/test_nodes.vty
+++ b/tests/test_nodes.vty
@@ -35,6 +35,7 @@
show gsup-connections
subscriber (imsi|msisdn|id|imei) IDENT show
show subscriber (imsi|msisdn|id|imei) IDENT
+ show mslookup services
OsmoHLR> enable
OsmoHLR# ?
@@ -89,6 +90,7 @@
end
...
hlr
+ mslookup
OsmoHLR(config)# hlr
OsmoHLR(config-hlr)# ?
@@ -127,6 +129,7 @@
OsmoHLR(config-hlr-gsup)# list
...
bind ip A.B.C.D
+ ipa-name NAME
OsmoHLR(config-hlr-gsup)# exit
OsmoHLR(config-hlr)# exit
@@ -151,6 +154,7 @@
logging level auc notice
logging level ss info
logging level lu notice
+ logging level dgsm notice
...
hlr
store-imei
@@ -160,3 +164,324 @@
ussd route prefix *#100# internal own-msisdn
ussd route prefix *#101# internal own-imsi
end
+
+OsmoHLR# configure terminal
+
+OsmoHLR(config)# mslookup
+OsmoHLR(config-mslookup)# ?
+...
+ mdns Convenience shortcut: enable and configure both server and client for mDNS mslookup
+ no Negate a command or set its defaults
+ server Enable and configure Distributed GSM mslookup server
+ client Enable and configure Distributed GSM mslookup client
+OsmoHLR(config-mslookup)# list
+...
+ mdns [IP] [<1-65535>]
+ no mdns
+ server
+ no server
+ client
+ no client
+
+OsmoHLR(config-mslookup)# ?
+...
+ mdns Convenience shortcut: enable and configure both server and client for mDNS mslookup
+ no Negate a command or set its defaults
+ server Enable and configure Distributed GSM mslookup server
+ client Enable and configure Distributed GSM mslookup client
+OsmoHLR(config-mslookup)# no?
+ no Negate a command or set its defaults
+OsmoHLR(config-mslookup)# no ?
+ mdns Disable both server and client for mDNS mslookup
+ server Disable Distributed GSM mslookup server
+ client Disable Distributed GSM mslookup client
+OsmoHLR(config-mslookup)# mdns ?
+ [IP] multicast IPv4 address like 239.192.23.42 or IPv6 address like ff08::23:42
+OsmoHLR(config-mslookup)# mdns 1.2.3.4 ?
+ [<1-65535>] mDNS UDP Port number
+
+OsmoHLR(config-mslookup)# server
+OsmoHLR(config-mslookup-server)# ?
+...
+ mdns Configure where the mDNS server listens for mslookup requests
+ no Negate a command or set its defaults
+ service Configure addresses of local services, as sent in replies to remote mslookup requests.
+ msc Configure services for individual local MSCs
+OsmoHLR(config-mslookup-server)# list
+...
+ mdns [IP] [<1-65535>]
+ no mdns
+ service NAME at IP <1-65535>
+ no service NAME
+ no service NAME at IP <1-65535>
+ msc .UNIT_NAME
+
+OsmoHLR(config-mslookup-server)# mdns?
+ mdns Configure where the mDNS server listens for mslookup requests
+OsmoHLR(config-mslookup-server)# mdns ?
+ [IP] multicast IPv4 address like 239.192.23.42 or IPv6 address like ff08::23:42
+OsmoHLR(config-mslookup-server)# mdns bind ?
+ [<1-65535>] mDNS UDP Port number
+OsmoHLR(config-mslookup-server)# mdns bind 1.2.3.4 ?
+% There is no matched command.
+OsmoHLR(config-mslookup-server)# mdns bind 1.2.3.4 ?
+% There is no matched command.
+
+OsmoHLR(config-mslookup-server)# service?
+ service Configure addresses of local services, as sent in replies to remote mslookup requests.
+OsmoHLR(config-mslookup-server)# service ?
+ NAME mslookup service name, e.g. sip.voice or smpp.sms
+OsmoHLR(config-mslookup-server)# service foo ?
+ at at
+OsmoHLR(config-mslookup-server)# service foo at ?
+ IP IPv4 address like 1.2.3.4 or IPv6 address like a:b:c:d::1
+OsmoHLR(config-mslookup-server)# service foo at 1.2.3.4 ?
+ <1-65535> Service-specific port number
+
+OsmoHLR(config-mslookup-server)# no ?
+ mdns Disable server for mDNS mslookup (do not answer remote requests)
+ service Remove one or more service address entries
+OsmoHLR(config-mslookup-server)# no service ?
+ NAME mslookup service name, e.g. sip.voice or smpp.sms
+OsmoHLR(config-mslookup-server)# no service foo ?
+ at at
+ <cr>
+OsmoHLR(config-mslookup-server)# no service foo at ?
+ IP IPv4 address like 1.2.3.4 or IPv6 address like a:b:c:d::1
+OsmoHLR(config-mslookup-server)# no service foo at 1.2.3.4 ?
+ <1-65535> Service-specific port number
+
+OsmoHLR(config-mslookup-server)# msc?
+ msc Configure services for individual local MSCs
+OsmoHLR(config-mslookup-server)# msc ?
+ UNIT_NAME IPA Unit Name of the local MSC to configure
+
+OsmoHLR(config-mslookup-server)# msc MSC-1
+OsmoHLR(config-mslookup-server-msc)# ?
+...
+ service Configure addresses of local services, as sent in replies to remote mslookup requests.
+ no Negate a command or set its defaults
+OsmoHLR(config-mslookup-server-msc)# list
+...
+ service NAME at IP <1-65535>
+ no service NAME
+ no service NAME at IP <1-65535>
+
+OsmoHLR(config-mslookup-server-msc)# service?
+ service Configure addresses of local services, as sent in replies to remote mslookup requests.
+OsmoHLR(config-mslookup-server-msc)# service ?
+ NAME mslookup service name, e.g. sip.voice or smpp.sms
+OsmoHLR(config-mslookup-server-msc)# service foo ?
+ at at
+OsmoHLR(config-mslookup-server-msc)# service foo at ?
+ IP IPv4 address like 1.2.3.4 or IPv6 address like a:b:c:d::1
+OsmoHLR(config-mslookup-server-msc)# service foo at 1.2.3.4 ?
+ <1-65535> Service-specific port number
+
+OsmoHLR(config-mslookup-server-msc)# no ?
+ service Remove one or more service address entries
+OsmoHLR(config-mslookup-server-msc)# no service ?
+ NAME mslookup service name, e.g. sip.voice or smpp.sms
+OsmoHLR(config-mslookup-server-msc)# no service foo ?
+ at at
+ <cr>
+OsmoHLR(config-mslookup-server-msc)# no service foo at ?
+ IP IPv4 address like 1.2.3.4 or IPv6 address like a:b:c:d::1
+OsmoHLR(config-mslookup-server-msc)# no service foo at 1.2.3.4 ?
+ <1-65535> Service-specific port number
+
+OsmoHLR(config-mslookup-server-msc)# exit
+OsmoHLR(config-mslookup-server)# exit
+OsmoHLR(config-mslookup)# client
+OsmoHLR(config-mslookup-client)# ?
+...
+ timeout How long should the mslookup client wait for remote responses before evaluating received results
+ mdns Enable mDNS client, and configure multicast address to send mDNS mslookup requests to
+ no Negate a command or set its defaults
+ gateway-proxy Configure a fixed IP address to send all GSUP requests for unknown IMSIs to, without invoking a lookup for IMSI
+OsmoHLR(config-mslookup-client)# list
+...
+ timeout <1-100000>
+ mdns [IP] [<1-65535>]
+ no mdns
+ gateway-proxy IP [<1-65535>]
+ no gateway-proxy
+
+OsmoHLR(config-mslookup-client)# timeout?
+ timeout How long should the mslookup client wait for remote responses before evaluating received results
+OsmoHLR(config-mslookup-client)# timeout ?
+ <1-100000> timeout in milliseconds
+
+OsmoHLR(config-mslookup-client)# mdns?
+ mdns Enable mDNS client, and configure multicast address to send mDNS mslookup requests to
+OsmoHLR(config-mslookup-client)# mdns to ?
+ [<1-65535>] mDNS UDP Port number
+OsmoHLR(config-mslookup-client)# mdns to 1.2.3.4 ?
+% There is no matched command.
+
+OsmoHLR(config-mslookup-client)# gateway-proxy?
+ gateway-proxy Configure a fixed IP address to send all GSUP requests for unknown IMSIs to, without invoking a lookup for IMSI
+OsmoHLR(config-mslookup-client)# gateway-proxy ?
+ IP IP address of the remote HLR
+OsmoHLR(config-mslookup-client)# gateway-proxy 1.2.3.4 ?
+ [<1-65535>] GSUP port number (omit for default 4222)
+
+OsmoHLR(config-mslookup-client)# no?
+ no Negate a command or set its defaults
+OsmoHLR(config-mslookup-client)# no ?
+ mdns Disable mDNS client, do not query remote services by mDNS
+ gateway-proxy Disable gateway proxy for GSUP with unknown IMSIs
+
+OsmoHLR(config-mslookup-client)# gateway-proxy ?
+ IP IP address of the remote HLR
+OsmoHLR(config-mslookup-client)# gateway-proxy 1.2.3.4 ?
+ [<1-65535>] GSUP port number (omit for default 4222)
+
+OsmoHLR(config-mslookup-client)# do show mslookup?
+ mslookup Distributed GSM / mslookup related information
+OsmoHLR(config-mslookup-client)# do show mslookup ?
+ services List configured service addresses as sent to remote mslookup requests
+
+OsmoHLR(config-mslookup-client)# gateway-proxy 1.2.3.4
+
+OsmoHLR(config-mslookup-client)# exit
+
+OsmoHLR(config-mslookup)# mdns
+OsmoHLR(config-mslookup)# server
+OsmoHLR(config-mslookup-server)# service qwert at 123.45.67.89 qwert
+% Unknown command.
+OsmoHLR(config-mslookup-server)# service qwert at qwert 1234
+% mslookup server: Invalid address for service qwert: qwert 1234
+OsmoHLR(config-mslookup-server)# service foo.bar at 123.45.67.89 1011
+OsmoHLR(config-mslookup-server)# service baz.bar at 121.31.41.5 1617
+OsmoHLR(config-mslookup-server)# service baz.bar at a:b:c::d 1819
+OsmoHLR(config-mslookup-server)# msc msc-901-70-23
+OsmoHLR(config-mslookup-server-msc)# service foo.bar at 76.54.32.10 1234
+OsmoHLR(config-mslookup-server-msc)# service baz.bar at 12.11.10.98 7654
+OsmoHLR(config-mslookup-server-msc)# service baz.bar at 999:999:999::999 9999
+OsmoHLR(config-mslookup-server-msc)# service baz.bar at dd:cc:bb::a 3210
+OsmoHLR(config-mslookup-server-msc)# exit
+OsmoHLR(config-mslookup-server)# msc msc-901-70-42
+OsmoHLR(config-mslookup-server-msc)# service foo.bar at 1.1.1.1 1111
+OsmoHLR(config-mslookup-server-msc)# service baz.bar at 2.2.2.2 2222
+OsmoHLR(config-mslookup-server-msc)# service baz.bar at 2222:2222:2222::2 2222
+OsmoHLR(config-mslookup-server-msc)# do show mslookup services
+Local GSUP HLR address returned in mslookup responses for local IMSIs: 127.0.0.1:4222
+service foo.bar at 123.45.67.89 1011
+service baz.bar at 121.31.41.5 1617
+service baz.bar at a:b:c::d 1819
+msc MSC-1
+msc msc-901-70-23
+ service foo.bar at 76.54.32.10 1234
+ service baz.bar at 12.11.10.98 7654
+ service baz.bar at dd:cc:bb::a 3210
+msc msc-901-70-42
+ service foo.bar at 1.1.1.1 1111
+ service baz.bar at 2.2.2.2 2222
+ service baz.bar at 2222:2222:2222::2 2222
+
+OsmoHLR(config-mslookup-server-msc)# show running-config
+...
+mslookup
+ server
+ mdns bind 239.192.23.42 4266
+ service foo.bar at 123.45.67.89 1011
+ service baz.bar at 121.31.41.5 1617
+ service baz.bar at a:b:c::d 1819
+ msc MSC-1
+ msc msc-901-70-23
+ service foo.bar at 76.54.32.10 1234
+ service baz.bar at 12.11.10.98 7654
+ service baz.bar at dd:cc:bb::a 3210
+ msc msc-901-70-42
+ service foo.bar at 1.1.1.1 1111
+ service baz.bar at 2.2.2.2 2222
+ service baz.bar at 2222:2222:2222::2 2222
+ client
+ gateway-proxy 1.2.3.4 4222
+ mdns to 239.192.23.42 4266
+...
+
+OsmoHLR(config-mslookup-server-msc)# no service baz.bar
+OsmoHLR(config-mslookup-server-msc)# no service asdf
+% mslookup server: cannot remove service 'asdf'
+OsmoHLR(config-mslookup-server-msc)# exit
+OsmoHLR(config-mslookup-server)# msc msc-901-70-23
+OsmoHLR(config-mslookup-server-msc)# no service baz.bar at dd:cc:bb::a 3210
+% mslookup server: cannot remove service 'baz.bar' to dd:cc:bb::a 3210
+OsmoHLR(config-mslookup-server-msc)# no service asdf at asdf asdf
+% Unknown command.
+OsmoHLR(config-mslookup-server-msc)# no service asdf at asdf 3210
+% mslookup server: Invalid address for 'no service' asdf: asdf 3210
+OsmoHLR(config-mslookup-server-msc)# no service asdf at dd:cc:bb::a 3210
+% mslookup server: cannot remove service 'asdf' to dd:cc:bb::a 3210
+OsmoHLR(config-mslookup-server-msc)# exit
+OsmoHLR(config-mslookup-server)# no service baz.bar at 2.2.2.2 2222
+% mslookup server: cannot remove service 'baz.bar' to 2.2.2.2 2222
+OsmoHLR(config-mslookup-server)# no service baz.bar at a:b:c::d 1819
+% mslookup server: cannot remove service 'baz.bar' to a:b:c::d 1819
+
+OsmoHLR(config-mslookup-server)# exit
+OsmoHLR(config-mslookup)# client
+OsmoHLR(config-mslookup-client)# no gateway-proxy
+
+OsmoHLR(config-mslookup-client)# do show mslookup services
+Local GSUP HLR address returned in mslookup responses for local IMSIs: 127.0.0.1:4222
+service foo.bar at 123.45.67.89 1011
+service baz.bar at 121.31.41.5 1617
+msc MSC-1
+msc msc-901-70-23
+ service foo.bar at 76.54.32.10 1234
+ service baz.bar at 12.11.10.98 7654
+msc msc-901-70-42
+ service foo.bar at 1.1.1.1 1111
+
+OsmoHLR(config-mslookup-client)# show running-config
+...
+mslookup
+ server
+ mdns bind 239.192.23.42 4266
+ service foo.bar at 123.45.67.89 1011
+ service baz.bar at 121.31.41.5 1617
+ msc MSC-1
+ msc msc-901-70-23
+ service foo.bar at 76.54.32.10 1234
+ service baz.bar at 12.11.10.98 7654
+ msc msc-901-70-42
+ service foo.bar at 1.1.1.1 1111
+ client
+ mdns to 239.192.23.42 4266
+...
+
+OsmoHLR(config-mslookup-client)# exit
+OsmoHLR(config-mslookup)# server
+OsmoHLR(config-mslookup-server)# service gsup.hlr at 23.42.17.11 4223
+OsmoHLR(config-mslookup-server)# do show mslookup services
+Local GSUP HLR address returned in mslookup responses for local IMSIs: 23.42.17.11:4223
+service foo.bar at 123.45.67.89 1011
+service baz.bar at 121.31.41.5 1617
+service gsup.hlr at 23.42.17.11 4223
+msc MSC-1
+msc msc-901-70-23
+ service foo.bar at 76.54.32.10 1234
+ service baz.bar at 12.11.10.98 7654
+msc msc-901-70-42
+ service foo.bar at 1.1.1.1 1111
+
+OsmoHLR(config-mslookup-server)# show running-config
+...
+mslookup
+ server
+ mdns bind 239.192.23.42 4266
+ service foo.bar at 123.45.67.89 1011
+ service baz.bar at 121.31.41.5 1617
+ service gsup.hlr at 23.42.17.11 4223
+ msc MSC-1
+ msc msc-901-70-23
+ service foo.bar at 76.54.32.10 1234
+ service baz.bar at 12.11.10.98 7654
+ msc msc-901-70-42
+ service foo.bar at 1.1.1.1 1111
+ client
+ mdns to 239.192.23.42 4266
+...
--
To view, visit https://gerrit.osmocom.org/c/osmo-hlr/+/16209
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-hlr
Gerrit-Branch: master
Gerrit-Change-Id: Ife4a61d71926d08f310a1aeed9d9f1974f64178b
Gerrit-Change-Number: 16209
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr at sysmocom.de>
Gerrit-CC: Jenkins Builder
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20191125/b8effe1c/attachment.htm>