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>