diff options
author | Neels Hofmeyr <neels@hofmeyr.de> | 2019-11-05 21:24:45 +0100 |
---|---|---|
committer | Neels Hofmeyr <neels@hofmeyr.de> | 2019-11-11 05:31:59 +0100 |
commit | 0a5eac957a3997c1048396af0e69fcf914a17c9e (patch) | |
tree | bac05f196fe44ef09d5e6a23f03d945b32367629 | |
parent | 82f4521319f37868a26b07ee89ea739e611cac98 (diff) |
dgsm: osmo-hlr is opening mDNS server and client sockets
Change-Id: I377b3bab7334c3212e40c4cf19aa223ac1be1644
-rw-r--r-- | sql/hlr.sql | 3 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/db.c | 6 | ||||
-rw-r--r-- | src/db.h | 2 | ||||
-rw-r--r-- | src/db_hlr.c | 17 | ||||
-rw-r--r-- | src/dgsm.c | 225 | ||||
-rw-r--r-- | src/dgsm.h | 51 | ||||
-rw-r--r-- | src/dgsm_vty.c | 182 | ||||
-rw-r--r-- | src/global_title.c | 5 | ||||
-rw-r--r-- | src/global_title.h | 1 | ||||
-rw-r--r-- | src/hlr.c | 12 | ||||
-rw-r--r-- | src/hlr.h | 12 | ||||
-rw-r--r-- | src/mslookup_server.c | 269 | ||||
-rw-r--r-- | src/mslookup_server.h | 10 | ||||
-rw-r--r-- | src/mslookup_server_mdns.c | 163 | ||||
-rw-r--r-- | src/mslookup_server_mdns.h | 14 | ||||
-rw-r--r-- | src/proxy.c | 92 | ||||
-rw-r--r-- | src/proxy.h | 37 | ||||
-rw-r--r-- | tests/db/db_test.c | 2 | ||||
-rw-r--r-- | tests/db_upgrade/db_upgrade_test.ok | 15 | ||||
-rw-r--r-- | tests/test_nodes.vty | 14 |
21 files changed, 824 insertions, 311 deletions
diff --git a/sql/hlr.sql b/sql/hlr.sql index b951632..1bfd86f 100644 --- a/sql/hlr.sql +++ b/sql/hlr.sql @@ -45,7 +45,8 @@ CREATE TABLE subscriber ( last_lu_seen TIMESTAMP default NULL, -- When a LU was received via a proxy, that proxy's hlr_number is stored here, -- while vlr_number reflects the MSC on the far side of that proxy. - vlr_via_proxy VARCHAR + vlr_via_proxy VARCHAR, + sgsn_via_proxy VARCHAR ); CREATE TABLE subscriber_apn ( diff --git a/src/Makefile.am b/src/Makefile.am index 1b8e85c..3485ce6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -42,6 +42,8 @@ noinst_HEADERS = \ dgsm.h \ remote_hlr.h \ global_title.h \ + mslookup_server.h \ + mslookup_server_mdns.h \ $(NULL) bin_PROGRAMS = \ @@ -71,6 +73,7 @@ osmo_hlr_SOURCES = \ dgsm_vty.c \ remote_hlr.c \ mslookup_server.c \ + mslookup_server_mdns.c \ global_title.c \ $(NULL) @@ -47,7 +47,8 @@ "ms_purged_cs," \ "ms_purged_ps," \ "last_lu_seen," \ - "vlr_via_proxy" + "vlr_via_proxy," \ + "sgsn_via_proxy" static const char *stmt_sql[] = { [DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?", @@ -55,7 +56,7 @@ static const char *stmt_sql[] = { [DB_STMT_SEL_BY_ID] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE id = ?", [DB_STMT_SEL_BY_IMEI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imei = ?", [DB_STMT_UPD_VLR_BY_ID] = "UPDATE subscriber SET vlr_number = $number, vlr_via_proxy = $proxy WHERE id = $subscriber_id", - [DB_STMT_UPD_SGSN_BY_ID] = "UPDATE subscriber SET sgsn_number = $number WHERE id = $subscriber_id", + [DB_STMT_UPD_SGSN_BY_ID] = "UPDATE subscriber SET sgsn_number = $number, sgsn_via_proxy = $proxy WHERE id = $subscriber_id", [DB_STMT_UPD_IMEI_BY_IMSI] = "UPDATE subscriber SET imei = $imei WHERE imsi = $imsi", [DB_STMT_AUC_BY_IMSI] = "SELECT id, algo_id_2g, ki, algo_id_3g, k, op, opc, sqn, ind_bitlen" @@ -456,6 +457,7 @@ static int db_upgrade_v4(struct db_context *dbc) int rc; const char *statements[] = { "ALTER TABLE subscriber ADD COLUMN vlr_via_proxy VARCHAR", + "ALTER TABLE subscriber ADD COLUMN sgsn_via_proxy VARCHAR", "PRAGMA user_version = 4", }; @@ -185,5 +185,5 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, #define copy_sqlite3_text_to_gt(gt, stmt, idx) \ do { \ const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \ - global_title_set(gt, (uint8_t*)_txt, _txt ? strlen(_txt)+1 : 0); \ + global_title_set_str(gt, _txt); \ } while (0) diff --git a/src/db_hlr.c b/src/db_hlr.c index af1e1b2..8d606f8 100644 --- a/src/db_hlr.c +++ b/src/db_hlr.c @@ -739,15 +739,14 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id, if (!db_bind_text(stmt, "$number", (char*)vlr_or_sgsn_number->val)) return -EIO; - if (!is_ps) { - if (gsup_peer && gsup_peer->len - && global_title_cmp(gsup_peer, vlr_or_sgsn_number)) { - if (!db_bind_text(stmt, "$proxy", (char*)gsup_peer->val)) - return -EIO; - } else { - if (!db_bind_null(stmt, "$proxy")) - return -EIO; - } + /* If the VLR/SGSN is not the direct GSUP peer, the gsup_peer is a proxy towards the actual VLR/SGSN. + * -> store the gsup_peer as proxy only when it differs from the vlr_or_sgsn_number. */ + if (global_title_cmp(gsup_peer, vlr_or_sgsn_number)) { + if (!db_bind_text(stmt, "$proxy", (char*)gsup_peer->val)) + return -EIO; + } else { + if (!db_bind_null(stmt, "$proxy")) + return -EIO; } /* execute the statement */ @@ -1,7 +1,7 @@ #include <errno.h> #include <osmocom/core/logging.h> #include <osmocom/mslookup/mslookup_client.h> -#include <osmocom/mslookup/mslookup_client_dns.h> +#include <osmocom/mslookup/mslookup_client_mdns.h> #include <osmocom/gsupclient/gsup_client.h> #include "logging.h" #include "hlr.h" @@ -10,36 +10,25 @@ #include "dgsm.h" #include "proxy.h" #include "remote_hlr.h" -#include "mslookup_server.h" +#include "mslookup_server_mdns.h" +#include "global_title.h" #define LOG_DGSM(imsi, level, fmt, args...) \ LOGP(DDGSM, level, "(IMSI-%s) " fmt, imsi, ##args) void *dgsm_ctx = NULL; -struct dgsm_config dgsm_config = { - .server = { - .dns = { - .multicast_bind_addr = { - .ip = OSMO_MSLOOKUP_MDNS_IP4, - .port = OSMO_MSLOOKUP_MDNS_PORT, - }, - }, - }, -}; +const struct global_title dgsm_config_msc_wildcard = {}; -struct dgsm_msc_config *dgsm_config_msc_get(const uint8_t *ipa_unit_name, size_t ipa_unit_name_len, - bool create) +struct dgsm_msc_config *dgsm_config_msc_get(const struct global_title *msc_name, bool create) { struct dgsm_msc_config *msc; - if (!ipa_unit_name) + if (!msc_name) return NULL; - llist_for_each_entry(msc, &dgsm_config.server.msc_configs, entry) { - if (ipa_unit_name_len != msc->unit_name_len) - continue; - if (memcmp(ipa_unit_name, msc->unit_name, ipa_unit_name_len)) + llist_for_each_entry(msc, &g_hlr->mslookup.vty.server.msc_configs, entry) { + if (global_title_cmp(&msc->name, msc_name)) continue; return msc; } @@ -48,18 +37,18 @@ struct dgsm_msc_config *dgsm_config_msc_get(const uint8_t *ipa_unit_name, size_t msc = talloc_zero(dgsm_ctx, struct dgsm_msc_config); OSMO_ASSERT(msc); - INIT_LLIST_HEAD(&msc->service_addrs); - msc->unit_name = talloc_memdup(msc, ipa_unit_name, ipa_unit_name_len); - OSMO_ASSERT(msc->unit_name); - msc->unit_name_len = ipa_unit_name_len; + INIT_LLIST_HEAD(&msc->service_hosts); + msc->name = *msc_name; return msc; } -static struct dgsm_service_addr *dgsm_config_msc_service_get(struct dgsm_msc_config *msc, const char *service, - bool create) +struct dgsm_service_host *dgsm_config_msc_service_get(struct dgsm_msc_config *msc, const char *service, bool create) { - struct dgsm_service_addr *e; - llist_for_each_entry(e, &msc->service_addrs, entry) { + 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; } @@ -67,27 +56,24 @@ static struct dgsm_service_addr *dgsm_config_msc_service_get(struct dgsm_msc_con if (!create) return NULL; - e = talloc_zero(msc, struct dgsm_service_addr); + e = talloc_zero(msc, struct dgsm_service_host); OSMO_ASSERT(e); OSMO_STRLCPY_ARRAY(e->service, service); - llist_add_tail(&e->entry, &msc->service_addrs); + llist_add_tail(&e->entry, &msc->service_hosts); return e; } -struct dgsm_service_addr *dgsm_config_service_get(const uint8_t *ipa_unit_name, size_t ipa_unit_name_len, - const char *service) +struct dgsm_service_host *dgsm_config_service_get(const struct global_title *msc_name, const char *service) { - struct dgsm_msc_config *msc = dgsm_config_msc_get(ipa_unit_name, ipa_unit_name_len, false); + 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_service_set(const uint8_t *ipa_unit_name, size_t ipa_unit_name_len, - const char *service, const struct osmo_sockaddr_str *addr) +int dgsm_config_msc_service_set(struct dgsm_msc_config *msc, const char *service, const struct osmo_sockaddr_str *addr) { - struct dgsm_msc_config *msc; - struct dgsm_service_addr *e; + struct dgsm_service_host *e; if (!service || !service[0] || strlen(service) > OSMO_MSLOOKUP_SERVICE_MAXLEN) @@ -95,20 +81,16 @@ int dgsm_config_service_set(const uint8_t *ipa_unit_name, size_t ipa_unit_name_l if (!addr || !osmo_sockaddr_str_is_nonzero(addr)) return -EINVAL; - msc = dgsm_config_msc_get(ipa_unit_name, ipa_unit_name_len, true); - if (!msc) - return -EINVAL; - e = dgsm_config_msc_service_get(msc, service, true); if (!e) return -EINVAL; switch (addr->af) { case AF_INET: - e->addr_v4 = *addr; + e->host_v4 = *addr; break; case AF_INET6: - e->addr_v6 = *addr; + e->host_v6 = *addr; break; default: return -EINVAL; @@ -116,30 +98,38 @@ int dgsm_config_service_set(const uint8_t *ipa_unit_name, size_t ipa_unit_name_l return 0; } -int dgsm_config_service_del(const uint8_t *ipa_unit_name, size_t ipa_unit_name_len, - const char *service, const struct osmo_sockaddr_str *addr) +int dgsm_config_service_set(const struct global_title *msc_name, const char *service, const struct osmo_sockaddr_str *addr) { struct dgsm_msc_config *msc; - struct dgsm_service_addr *e, *n; - msc = dgsm_config_msc_get(ipa_unit_name, ipa_unit_name_len, false); + msc = dgsm_config_msc_get(msc_name, true); + if (!msc) + return -EINVAL; + + return dgsm_config_msc_service_set(msc, service, addr); +} + +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; + if (!msc) return -ENOENT; - llist_for_each_entry_safe(e, n, &msc->service_addrs, entry) { + 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->addr_v4)) { - e->addr_v4 = (struct osmo_sockaddr_str){}; + 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->addr_v6)) + if (osmo_sockaddr_str_is_nonzero(&e->host_v6)) continue; - } else if (!osmo_sockaddr_str_cmp(addr, &e->addr_v6)) { - e->addr_v6 = (struct osmo_sockaddr_str){}; + } 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->addr_v4)) + if (osmo_sockaddr_str_is_nonzero(&e->host_v4)) continue; } else /* No addr match, keep the entry. */ @@ -152,8 +142,14 @@ int dgsm_config_service_del(const uint8_t *ipa_unit_name, size_t ipa_unit_name_l return 0; } +int dgsm_config_service_del(const struct global_title *msc_name, + const char *service, const struct osmo_sockaddr_str *addr) +{ + return dgsm_config_msc_service_del(dgsm_config_msc_get(msc_name, false), + service, addr); +} + static void *dgsm_pending_messages_ctx = NULL; -static struct osmo_mslookup_client *mslookup_client = NULL; struct pending_gsup_message { struct llist_head entry; @@ -212,13 +208,16 @@ void dgsm_send_to_remote_hlr(const struct proxy_subscr *ps, const struct osmo_gs /* Return true when the message has been handled by D-GSM. */ bool dgsm_check_forward_gsup_msg(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup) { - const struct proxy_subscr *ps; + const struct proxy_subscr *proxy_subscr; struct proxy_subscr ps_new; struct gsup_route *r; struct osmo_gsup_message gsup_copy; + struct proxy *proxy = g_hlr->gsup_proxy.cs; + if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) + proxy = g_hlr->gsup_proxy.ps; - ps = proxy_subscr_get(gsup->imsi); - if (ps) + proxy_subscr = proxy_subscr_get_by_imsi(proxy, gsup->imsi); + if (proxy_subscr) goto yes_we_are_proxying; /* No proxy entry exists. If the IMSI is known in the local HLR, then we won't proxy. */ @@ -232,11 +231,11 @@ bool dgsm_check_forward_gsup_msg(struct osmo_gsup_conn *conn, const struct osmo_ /* Add a proxy entry without a remote address to indicate that we are busy querying for a remote HLR. */ ps_new = (struct proxy_subscr){}; OSMO_STRLCPY_ARRAY(ps_new.imsi, gsup->imsi); - proxy_subscr_update(&ps_new); - ps = &ps_new; + proxy_subscr_update(proxy, &ps_new); + proxy_subscr = &ps_new; yes_we_are_proxying: - OSMO_ASSERT(ps); + OSMO_ASSERT(proxy_subscr); /* To forward to a remote HLR, we need to indicate the source MSC's name to make sure the reply can be routed * back. Store the sender MSC in gsup->source_name -- the remote HLR is required to return this as @@ -254,7 +253,7 @@ yes_we_are_proxying: gsup_copy.source_name = r->addr; gsup_copy.source_name_len = talloc_total_size(r->addr); - dgsm_send_to_remote_hlr(ps, &gsup_copy); + dgsm_send_to_remote_hlr(proxy_subscr, &gsup_copy); return true; } @@ -263,62 +262,96 @@ void dgsm_init(void *ctx) { dgsm_ctx = talloc_named_const(ctx, 0, "dgsm"); dgsm_pending_messages_ctx = talloc_named_const(dgsm_ctx, 0, "dgsm_pending_messages"); - INIT_LLIST_HEAD(&dgsm_config.server.msc_configs); + INIT_LLIST_HEAD(&g_hlr->mslookup.vty.server.msc_configs); + osmo_sockaddr_str_from_str(&g_hlr->mslookup.vty.server.mdns.bind_addr, + OSMO_MSLOOKUP_MDNS_IP4, OSMO_MSLOOKUP_MDNS_PORT); + osmo_sockaddr_str_from_str(&g_hlr->mslookup.vty.client.mdns.query_addr, + OSMO_MSLOOKUP_MDNS_IP4, OSMO_MSLOOKUP_MDNS_PORT); } void dgsm_start(void *ctx) { - mslookup_client = osmo_mslookup_client_new(dgsm_ctx); - OSMO_ASSERT(mslookup_client); + 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(); } -void dgsm_dns_server_config_apply() +static void dgsm_mdns_server_config_apply() { - /* Check whether to start/stop/restart DNS server */ - bool should_run = dgsm_config.server.enable && dgsm_config.server.dns.enable; - bool should_stop = g_hlr->mslookup.server.dns + /* Check whether to start/stop/restart mDNS server */ + bool should_run; + bool should_stop; + if (!g_hlr->mslookup.allow_startup) + return; + + g_hlr->mslookup.server.max_age = g_hlr->mslookup.vty.server.max_age; + + should_run = g_hlr->mslookup.vty.server.enable && g_hlr->mslookup.vty.server.mdns.enable; + should_stop = g_hlr->mslookup.server.mdns && (!should_run - || osmo_sockaddr_str_cmp(&dgsm_config.server.dns.multicast_bind_addr, - &g_hlr->mslookup.server.dns->multicast_bind_addr)); + || osmo_sockaddr_str_cmp(&g_hlr->mslookup.vty.server.mdns.bind_addr, + &g_hlr->mslookup.server.mdns->bind_addr)); if (should_stop) { - osmo_mslookup_server_dns_stop(g_hlr->mslookup.server.dns); - LOGP(DDGSM, LOGL_ERROR, "Stopped MS Lookup DNS server\n"); + 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.dns = - osmo_mslookup_server_dns_start(&dgsm_config.server.dns.multicast_bind_addr); - if (!g_hlr->mslookup.server.dns) - LOGP(DDGSM, LOGL_ERROR, "Failed to start MS Lookup DNS server\n"); + if (should_run && !g_hlr->mslookup.server.mdns) { + g_hlr->mslookup.server.mdns = + osmo_mslookup_server_mdns_start(g_hlr, &g_hlr->mslookup.vty.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_ERROR, "Started MS Lookup DNS server on " OSMO_SOCKADDR_STR_FMT "\n", - OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.server.dns->multicast_bind_addr)); + 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)); } } -void dgsm_dns_client_config_apply() +static void dgsm_mdns_client_config_apply() { - /* Check whether to start/stop/restart DNS client */ - struct osmo_mslookup_client_method *dns_method = g_hlr->mslookup.client.dns; - const struct osmo_sockaddr_str *current_bind_addr = osmo_mslookup_client_method_dns_get_bind_addr(dns_method); + if (!g_hlr->mslookup.allow_startup) + return; - bool should_run = dgsm_config.client.enable && dgsm_config.client.dns.enable; - bool should_stop = dns_method && + /* 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 = g_hlr->mslookup.vty.client.enable && g_hlr->mslookup.vty.client.mdns.enable; + bool should_stop = g_hlr->mslookup.client.mdns && (!should_run - || osmo_sockaddr_str_cmp(&dgsm_config.client.dns.multicast_query_addr, + || osmo_sockaddr_str_cmp(&g_hlr->mslookup.vty.client.mdns.query_addr, current_bind_addr)); - if (should_stop) - osmo_mslookup_client_method_del(mslookup_client, dns_method); - if (should_run) { - if (osmo_mslookup_client_add_dns(mslookup_client, - dgsm_config.client.dns.multicast_query_addr.ip, - dgsm_config.client.dns.multicast_query_addr.port, - true)) - LOGP(DDGSM, LOGL_ERROR, "Failed to start MS Lookup DNS client\n"); + 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, + g_hlr->mslookup.vty.client.mdns.query_addr.ip, + g_hlr->mslookup.vty.client.mdns.query_addr.port, + true); + 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(&g_hlr->mslookup.vty.client.mdns.query_addr)); else - LOGP(DDGSM, LOGL_ERROR, "Started MS Lookup DNS client with " OSMO_SOCKADDR_STR_FMT "\n", - OSMO_SOCKADDR_STR_FMT_ARGS(&dgsm_config.client.dns.multicast_query_addr)); + LOGP(DDGSM, LOGL_NOTICE, "Started mslookup mDNS client, sending mDNS requests to multicast " OSMO_SOCKADDR_STR_FMT "\n", + OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.vty.client.mdns.query_addr)); } } + +void dgsm_config_apply() +{ + dgsm_mdns_server_config_apply(); + dgsm_mdns_client_config_apply(); +} + @@ -2,32 +2,40 @@ #include <osmocom/mslookup/mslookup.h> #include "gsup_server.h" +#include "global_title.h" + +struct vty; extern void *dgsm_ctx; -struct dgsm_service_addr { +struct dgsm_service_host { struct llist_head entry; char service[OSMO_MSLOOKUP_SERVICE_MAXLEN+1]; - struct osmo_sockaddr_str addr_v4; - struct osmo_sockaddr_str addr_v6; + struct osmo_sockaddr_str host_v4; + struct osmo_sockaddr_str host_v6; }; struct dgsm_msc_config { struct llist_head entry; - uint8_t *unit_name; - size_t unit_name_len; - struct llist_head service_addrs; + struct global_title 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; + /* If we have a local record of a LU, but it is older than max_age (in seconds), + * do not send it as mslookup result. */ + uint32_t max_age; + struct { bool enable; - struct osmo_sockaddr_str multicast_bind_addr; - } dns; + struct osmo_sockaddr_str bind_addr; + } mdns; struct llist_head msc_configs; } server; @@ -35,30 +43,31 @@ struct dgsm_config { 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 multicast_query_addr; - } dns; + struct osmo_sockaddr_str query_addr; + } mdns; } client; }; -extern struct dgsm_config dgsm_config; -void dgsm_dns_server_config_apply(); -void dgsm_dns_client_config_apply(); +void dgsm_config_apply(); + +struct dgsm_service_host *dgsm_config_service_get(const struct global_title *msc_name, const char *service); +int dgsm_config_service_set(const struct global_title *msc_name, const char *service, const struct osmo_sockaddr_str *addr); +int dgsm_config_service_del(const struct global_title *msc_name, const char *service, const struct osmo_sockaddr_str *addr); -struct dgsm_service_addr *dgsm_config_service_get(const uint8_t *ipa_unit_name, size_t ipa_unit_name_len, - const char *service); -int dgsm_config_service_set(const uint8_t *ipa_unit_name, size_t ipa_unit_name_len, - const char *service, const struct osmo_sockaddr_str *addr); -int dgsm_config_service_del(const uint8_t *ipa_unit_name, size_t ipa_unit_name_len, - const char *service, const struct osmo_sockaddr_str *addr); +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); -struct dgsm_msc_config *dgsm_config_msc_get(const uint8_t *ipa_unit_name, size_t ipa_unit_name_len, - bool create); +extern const struct global_title dgsm_config_msc_wildcard; +struct dgsm_msc_config *dgsm_config_msc_get(const struct global_title *msc_name, bool create); void dgsm_init(void *ctx); +void dgsm_start(void *ctx); bool dgsm_check_forward_gsup_msg(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup); void dgsm_vty_init(); diff --git a/src/dgsm_vty.c b/src/dgsm_vty.c index 4567be9..c3da81e 100644 --- a/src/dgsm_vty.c +++ b/src/dgsm_vty.c @@ -3,8 +3,6 @@ #include "hlr_vty.h" #include "dgsm.h" -static struct dgsm_config dgsm_config_vty = {}; - struct cmd_node mslookup_node = { MSLOOKUP_NODE, "%s(config-mslookup)# ", @@ -17,29 +15,30 @@ DEFUN(cfg_mslookup, "Configure Distributed GSM / multicast MS Lookup") { vty->node = MSLOOKUP_NODE; - printf("%s vty->node = %d\n", __func__, vty->node); return CMD_SUCCESS; } -DEFUN(cfg_mslookup_dns, - cfg_mslookup_dns_cmd, - "dns", +DEFUN(cfg_mslookup_mdns, + cfg_mslookup_mdns_cmd, + "mdns", "Convenience shortcut: enable both server and client for DNS/mDNS MS Lookup with default config\n") { - dgsm_config_vty.server.enable = true; - dgsm_config_vty.server.dns.enable = true; - dgsm_config_vty.client.enable = true; - dgsm_config_vty.client.dns.enable = true; + g_hlr->mslookup.vty.server.enable = true; + g_hlr->mslookup.vty.server.mdns.enable = true; + g_hlr->mslookup.vty.client.enable = true; + g_hlr->mslookup.vty.client.mdns.enable = true; + dgsm_config_apply(); return CMD_SUCCESS; } -DEFUN(cfg_mslookup_no_dns, - cfg_mslookup_no_dns_cmd, - "no dns", +DEFUN(cfg_mslookup_no_mdns, + cfg_mslookup_no_mdns_cmd, + "no mdns", NO_STR "Disable both server and client for DNS/mDNS MS Lookup\n") { - dgsm_config_vty.server.dns.enable = false; - dgsm_config_vty.client.dns.enable = false; + g_hlr->mslookup.vty.server.mdns.enable = false; + g_hlr->mslookup.vty.client.mdns.enable = false; + dgsm_config_apply(); return CMD_SUCCESS; } @@ -55,8 +54,8 @@ DEFUN(cfg_mslookup_server, "Enable and configure Distributed GSM / multicast MS Lookup server") { vty->node = MSLOOKUP_SERVER_NODE; - dgsm_config_vty.server.enable = true; - printf("%s vty->node = %d\n", __func__, vty->node); + g_hlr->mslookup.vty.server.enable = true; + dgsm_config_apply(); return CMD_SUCCESS; } @@ -65,41 +64,45 @@ DEFUN(cfg_mslookup_no_server, "no server", NO_STR "Disable Distributed GSM / multicast MS Lookup server") { - dgsm_config_vty.server.enable = false; + g_hlr->mslookup.vty.server.enable = false; + dgsm_config_apply(); return CMD_SUCCESS; } -#define DNS_STR "Configure DNS/mDNS MS Lookup\n" -#define DNS_BIND_STR DNS_STR "Configure where the DNS/mDNS server listens for MS Lookup requests\n" +#define MDNS_STR "Configure mslookup by multicast DNS\n" +#define MDNS_BIND_STR MDNS_STR "Configure where the mDNS server listens for MS Lookup requests\n" #define IP46_STR "IPv4 address like 1.2.3.4 or IPv6 address like a:b:c:d::1\n" #define PORT_STR "Port number\n" -DEFUN(cfg_mslookup_server_dns_bind_multicast, - cfg_mslookup_server_dns_bind_multicast_cmd, - "dns bind multicast IP <1-65535>", - DNS_BIND_STR "Configure mDNS multicast listen address\n" IP46_STR PORT_STR) +DEFUN(cfg_mslookup_server_mdns_bind, + cfg_mslookup_server_mdns_bind_cmd, + "mdns [bind] [IP] [<1-65535>]", + MDNS_BIND_STR IP46_STR PORT_STR) { - const char *ip_str = argv[1]; - const char *port_str = argv[2]; + const char *ip_str = argc > 1? argv[1] : g_hlr->mslookup.vty.server.mdns.bind_addr.ip; + const char *port_str = argc > 2? argv[2] : NULL; + uint16_t port_nr = port_str ? atoi(port_str) : g_hlr->mslookup.vty.server.mdns.bind_addr.port; struct osmo_sockaddr_str addr; - if (osmo_sockaddr_str_from_str(&addr, ip_str, atoi(port_str)) + if (osmo_sockaddr_str_from_str(&addr, ip_str, port_nr) || !osmo_sockaddr_str_is_nonzero(&addr)) { - vty_out(vty, "%% MS Lookup server: Invalid mDNS bind address: %s %s%s", - ip_str, port_str, VTY_NEWLINE); + vty_out(vty, "%% MS Lookup server: Invalid mDNS bind address: %s %u%s", + ip_str, port_nr, VTY_NEWLINE); return CMD_WARNING; } - dgsm_config_vty.server.dns.multicast_bind_addr = addr; - dgsm_config_vty.server.dns.enable = true; + g_hlr->mslookup.vty.server.mdns.bind_addr = addr; + g_hlr->mslookup.vty.server.mdns.enable = true; + dgsm_config_apply(); return CMD_SUCCESS; } -DEFUN(cfg_mslookup_server_no_dns, - cfg_mslookup_server_no_dns_cmd, - "no dns", +DEFUN(cfg_mslookup_server_no_mdns, + cfg_mslookup_server_no_mdns_cmd, + "no mdns", NO_STR "Disable server for DNS/mDNS MS Lookup (do not answer remote requests)\n") { - dgsm_config_vty.server.dns.enable = false; + g_hlr->mslookup.vty.server.mdns.enable = false; + dgsm_config_apply(); return CMD_SUCCESS; } @@ -115,17 +118,17 @@ DEFUN(cfg_mslookup_server_msc, "Configure services for individual local MSCs\n" "IPA Unit Name of the local MSC to configure\n") { - const char *unit_name = argv_concat(argv, argc, 0); - struct dgsm_msc_config *msc = dgsm_config_msc_get((uint8_t*)unit_name, strlen(unit_name), - true); + struct global_title msc_name; + struct dgsm_msc_config *msc; + global_title_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_quote_str(unit_name, -1), VTY_NEWLINE); + vty_out(vty, "%% Error creating MSC %s%s", global_title_name(&msc_name), VTY_NEWLINE); return CMD_WARNING; } vty->node = MSLOOKUP_SERVER_MSC_NODE; vty->index = msc; - printf("%s vty->node = %d\n", __func__, vty->node); return CMD_SUCCESS; } @@ -145,13 +148,20 @@ DEFUN(cfg_mslookup_server_msc_service, /* 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 = (vty->node == MSLOOKUP_SERVER_MSC_NODE) ? vty->index : NULL; - uint8_t *unit_name = msc ? msc->unit_name : NULL; - size_t unit_name_len = msc ? msc->unit_name_len : 0; const char *service = argv[0]; const char *ip_str = argv[1]; const char *port_str = argv[2]; struct osmo_sockaddr_str addr; + /* On the mslookup.server node, set services on the wildcard msc, without a particular name. */ + if (vty->node == MSLOOKUP_SERVER_NODE) + msc = dgsm_config_msc_get(&dgsm_config_msc_wildcard, true); + + 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, "%% MS Lookup server: Invalid address for service %s: %s %s%s", @@ -159,7 +169,7 @@ DEFUN(cfg_mslookup_server_msc_service, return CMD_WARNING; } - if (dgsm_config_service_set(unit_name, unit_name_len, service, &addr)) { + if (dgsm_config_msc_service_set(msc, service, &addr)) { vty_out(vty, "%% MS Lookup server: Error setting service %s to %s %s%s", service, ip_str, port_str, VTY_NEWLINE); return CMD_WARNING; @@ -177,11 +187,9 @@ DEFUN(cfg_mslookup_server_msc_no_service, /* 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 = (vty->node == MSLOOKUP_SERVER_MSC_NODE) ? vty->index : NULL; - uint8_t *unit_name = msc ? msc->unit_name : NULL; - size_t unit_name_len = msc ? msc->unit_name_len : 0; const char *service = argv[0]; - if (dgsm_config_service_del(unit_name, unit_name_len, service, NULL)) { + if (dgsm_config_msc_service_del(msc, service, NULL)) { vty_out(vty, "%% MS Lookup server: Error removing service %s%s", service, VTY_NEWLINE); return CMD_WARNING; @@ -197,8 +205,6 @@ DEFUN(cfg_mslookup_server_msc_no_service_addr, /* 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 = (vty->node == MSLOOKUP_SERVER_MSC_NODE) ? vty->index : NULL; - uint8_t *unit_name = msc ? msc->unit_name : NULL; - size_t unit_name_len = msc ? msc->unit_name_len : 0; const char *service = argv[0]; const char *ip_str = argv[1]; const char *port_str = argv[2]; @@ -211,7 +217,7 @@ DEFUN(cfg_mslookup_server_msc_no_service_addr, return CMD_WARNING; } - if (dgsm_config_service_del(unit_name, unit_name_len, service, &addr)) { + if (dgsm_config_service_del(&msc->name, service, &addr)) { vty_out(vty, "%% MS Lookup server: Error removing service %s to %s %s%s", service, ip_str, port_str, VTY_NEWLINE); return CMD_WARNING; @@ -231,7 +237,8 @@ DEFUN(cfg_mslookup_client, "Enable and configure Distributed GSM / multicast MS Lookup client") { vty->node = MSLOOKUP_CLIENT_NODE; - dgsm_config.client.enable = true; + g_hlr->mslookup.vty.client.enable = true; + dgsm_config_apply(); return CMD_SUCCESS; } @@ -240,7 +247,58 @@ DEFUN(cfg_mslookup_no_client, "no client", NO_STR "Disable Distributed GSM / multicast MS Lookup client") { - dgsm_config.client.enable = false; + g_hlr->mslookup.vty.client.enable = false; + dgsm_config_apply(); + return CMD_SUCCESS; +} + +#define MDNS_TO_STR MDNS_STR "Configure to which multicast address mDNS MS Lookup requests are sent\n" + +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") +{ + uint32_t val = atol(argv[0]); + g_hlr->mslookup.vty.client.timeout.tv_sec = val / 1000; + g_hlr->mslookup.vty.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 [to] [IP] [<1-65535>]", + MDNS_STR "Configure multicast address to send mDNS mslookup requests to\n" IP46_STR PORT_STR) +{ + const char *ip_str = argc > 1? argv[1] : g_hlr->mslookup.vty.client.mdns.query_addr.ip; + const char *port_str = argc > 2? argv[2] : NULL; + uint16_t port_nr = port_str ? atoi(port_str) : g_hlr->mslookup.vty.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, "%% MS Lookup client: Invalid mDNS target address: %s %u%s", + ip_str, port_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + g_hlr->mslookup.vty.client.mdns.query_addr = addr; + g_hlr->mslookup.vty.client.mdns.enable = true; + dgsm_config_apply(); + return CMD_SUCCESS; +} + +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") +{ + g_hlr->mslookup.vty.client.mdns.enable = false; + dgsm_config_apply(); return CMD_SUCCESS; } @@ -269,14 +327,14 @@ void dgsm_vty_init() install_element(CONFIG_NODE, &cfg_mslookup_cmd); install_node(&mslookup_node, config_write_mslookup); - install_element(MSLOOKUP_NODE, &cfg_mslookup_dns_cmd); - install_element(MSLOOKUP_NODE, &cfg_mslookup_no_dns_cmd); + 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, config_write_mslookup_server); - install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_dns_bind_multicast_cmd); - install_element(MSLOOKUP_SERVER_NODE, &cfg_mslookup_server_no_dns_cmd); + 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); @@ -290,18 +348,12 @@ void dgsm_vty_init() install_element(MSLOOKUP_NODE, &cfg_mslookup_client_cmd); install_element(MSLOOKUP_NODE, &cfg_mslookup_no_client_cmd); install_node(&mslookup_client_node, config_write_mslookup_client); + 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); } void dgsm_vty_go_parent_action(struct vty *vty) { - /* Exiting 'mslookup' VTY node, apply new config. */ - switch (vty->node) { - case MSLOOKUP_SERVER_NODE: - dgsm_dns_server_config_apply(); - break; - case MSLOOKUP_CLIENT_NODE: - dgsm_dns_client_config_apply(); - break; - } } diff --git a/src/global_title.c b/src/global_title.c index 9df45d6..10fbb11 100644 --- a/src/global_title.c +++ b/src/global_title.c @@ -16,6 +16,11 @@ int global_title_set(struct global_title *gt, const uint8_t *val, size_t len) return 0; } +int global_title_set_str(struct global_title *gt, const char *str) +{ + return global_title_set(gt, (const uint8_t*)str, str ? strlen(str)+1 : 0); +} + int global_title_cmp(const struct global_title *a, const struct global_title *b) { int cmp; diff --git a/src/global_title.h b/src/global_title.h index 017c7fe..54e23a3 100644 --- a/src/global_title.h +++ b/src/global_title.h @@ -12,5 +12,6 @@ struct global_title { }; int global_title_set(struct global_title *gt, const uint8_t *val, size_t len); +int global_title_set_str(struct global_title *gt, const char *str); int global_title_cmp(const struct global_title *a, const struct global_title *b); const char *global_title_name(const struct global_title *gt); @@ -49,6 +49,7 @@ #include "hlr_vty.h" #include "hlr_ussd.h" #include "dgsm.h" +#include "proxy.h" struct hlr *g_hlr; static void *hlr_ctx = NULL; @@ -429,14 +430,14 @@ static int rx_purge_ms_req(struct osmo_gsup_conn *conn, bool is_ps = false; int rc; - LOGP(DAUC, LOGL_INFO, "%s: Purge MS (%s)\n", gsup->imsi, - is_ps ? "PS" : "CS"); - memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi)); if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) is_ps = true; + LOGP(DAUC, LOGL_INFO, "%s: Purge MS (%s)\n", gsup->imsi, + is_ps ? "PS" : "CS"); + /* FIXME: check if the VLR that sends the purge is the same that * we have on record. Only update if yes */ @@ -836,6 +837,9 @@ int main(int argc, char **argv) /* Init default (call independent) SS session guard timeout value */ g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT; + g_hlr->gsup_proxy.cs = proxy_init(g_hlr); + g_hlr->gsup_proxy.ps = proxy_init(g_hlr); + rc = osmo_init_logging2(hlr_ctx, &hlr_log_info); if (rc < 0) { fprintf(stderr, "Error initializing logging\n"); @@ -903,6 +907,8 @@ int main(int argc, char **argv) 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); @@ -25,6 +25,7 @@ #include <stdbool.h> #include <osmocom/core/linuxlist.h> #include <osmocom/gsm/ipa.h> +#include "dgsm.h" #define HLR_DEFAULT_DB_FILE_PATH "hlr.db" @@ -66,19 +67,24 @@ struct hlr { unsigned int subscr_create_on_demand_rand_msisdn_len; struct { + bool allow_startup; + struct dgsm_config vty; + struct { - struct osmo_mslookup_server_dns *dns; + struct osmo_mslookup_server_mdns *mdns; + uint32_t max_age; } server; struct { struct osmo_mslookup_client *client; - - struct osmo_mslookup_client_method *dns; + struct osmo_mslookup_client_method *mdns; } client; } mslookup; struct { struct ipaccess_unit gsup_client_name; + struct proxy *cs; + struct proxy *ps; } gsup_proxy; }; diff --git a/src/mslookup_server.c b/src/mslookup_server.c index e2f3b75..b426417 100644 --- a/src/mslookup_server.c +++ b/src/mslookup_server.c @@ -6,37 +6,32 @@ #include "db.h" #include "dgsm.h" #include "mslookup_server.h" +#include "proxy.h" static const struct osmo_mslookup_result not_found = { - .ip_v4.rc = OSMO_MSLOOKUP_RC_NOT_FOUND, - .ip_v6.rc = OSMO_MSLOOKUP_RC_NOT_FOUND, + .rc = OSMO_MSLOOKUP_RC_NOT_FOUND, }; -static void set_result(struct osmo_mslookup_result_part *result, - const struct osmo_sockaddr_str *addr, +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(addr)) { - result->rc = OSMO_MSLOOKUP_RC_NOT_FOUND; + 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_OK; - result->host = *addr; + result->host_v4 = service_host->host_v4; + result->host_v6 = service_host->host_v6; result->age = age; } -static void set_results(struct osmo_mslookup_result *result, - const struct dgsm_service_addr *service_addr, - uint32_t age) +/* 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) { - set_result(&result->ip_v4, &service_addr->addr_v4, age); - set_result(&result->ip_v6, &service_addr->addr_v6, age); -} - -void mslookup_server_rx_hlr_gsup(const struct osmo_mslookup_query *query, - struct osmo_mslookup_result *result) -{ - struct dgsm_service_addr *addr; + struct dgsm_service_host *host; int rc; switch (query->id.type) { case OSMO_MSLOOKUP_ID_IMSI: @@ -62,41 +57,203 @@ void mslookup_server_rx_hlr_gsup(const struct osmo_mslookup_query *query, osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); /* Find a HLR/GSUP service set for the server (no MSC unit name) */ - addr = dgsm_config_service_get(NULL, 0, OSMO_MSLOOKUP_SERVICE_HLR_GSUP); - if (!addr) { - LOGP(DDGSM, LOGL_ERROR, - "Subscriber found, but no service '" OSMO_MSLOOKUP_SERVICE_HLR_GSUP "' configured," - " cannot service HLR lookup request\n"); - *result = not_found; + host = dgsm_config_service_get(&dgsm_config_msc_wildcard, OSMO_MSLOOKUP_SERVICE_HLR_GSUP); + if (!host) { + struct dgsm_service_host gsup_bind = {}; + /* 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){}; + } + set_result(result, &gsup_bind, 0); + if (result->rc != OSMO_MSLOOKUP_RC_OK) { + LOGP(DDGSM, LOGL_ERROR, + "Subscriber found, but no service '" OSMO_MSLOOKUP_SERVICE_HLR_GSUP "' configured," + " and cannot use configured GSUP bind address %s in mslookup response." + " Cannot service HLR lookup request\n", + osmo_quote_str(g_hlr->gsup_bind_addr, -1)); + } return; } - set_results(result, addr, 0); + set_result(result, host, 0); + if (result->rc != OSMO_MSLOOKUP_RC_OK) { + 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 + * MSC, 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 global_title *local_msc_name) +{ + struct hlr_subscriber subscr; + struct timeval age_tv; + 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\n", + osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); + } + + if (subscr.vlr_via_proxy.len) { + /* The MSC is behind a proxy, the subscriber is not attached to a local MSC 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 MSC, but via proxy %s\n", + osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), + global_title_name(&subscr.vlr_via_proxy)); + return false; + } + + age_tv = (struct timeval){ .tv_sec = subscr.last_lu_seen }; + age = timestamp_age(&age_tv); + + if (age > g_hlr->mslookup.server.max_age) { + LOGP(DDGSM, LOGL_ERROR, "%s: last attach was here, but too long ago: %us\n", + osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), + age); + return false; + } + + *lu_age = age; + global_title_set_str(local_msc_name, subscr.vlr_number); + LOGP(DDGSM, LOGL_DEBUG, "%s: attached %u seconds ago at local MSC %s\n", + osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), + age, global_title_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 -- either - * entirely here, or here first but proxying to a remote HLR. Do not return a match if the LU was registered here - * (because this is the home HLR) but the LU was routed via a closer HLR first. - * A proxy adds source_name IEs to forwarded GSUP requests that indicates the MSC where the subscriber is attached. - * So a) if the LU that was received at the home HLR contained a source_name, we know that the LU happened at a remote - * MSC. b) The source_name is stored as the vlr_number; hence if that vlr_number is not known locally, we know the LU - * happened at a remote MSC. (at the time of writing it is not yet clear whether we'll use a or b). + +/* 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 MSC, and we are serving as proxy for a remote home HLR. */ -bool subscriber_has_done_location_updating_here(const struct osmo_mslookup_id *id, - uint32_t *lu_age, - const uint8_t **lu_msc_unit_name, - size_t *lu_msc_unit_name_len) +static bool subscriber_has_done_lu_here_proxy(const struct osmo_mslookup_query *query, + uint32_t *lu_age, + struct global_title *local_msc_name) +{ + const struct proxy_subscr *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: + subscr = proxy_subscr_get_by_imsi(g_hlr->gsup_proxy.cs, query->id.imsi); + break; + case OSMO_MSLOOKUP_ID_MSISDN: + subscr = proxy_subscr_get_by_msisdn(g_hlr->gsup_proxy.cs, query->id.msisdn); + break; + default: + LOGP(DDGSM, LOGL_ERROR, "Unknown mslookup ID type: %d\n", query->id.type); + return false; + } + + if (!subscr) { + LOGP(DDGSM, LOGL_DEBUG, "%s: does not exist in GSUP proxy\n", + osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); + return false; + } + + age = timestamp_age(&subscr->last_lu); + + if (age > g_hlr->mslookup.server.max_age) { + LOGP(DDGSM, LOGL_ERROR, "%s: last attach was here (proxy), but too long ago: %us\n", + osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), + age); + return false; + } + + *lu_age = age; + *local_msc_name = subscr->vlr_name; + LOGP(DDGSM, LOGL_DEBUG, "%s: attached %u seconds ago at local MSC %s; proxying for remote HLR " + OSMO_SOCKADDR_STR_FMT "\n", + osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), + age, global_title_name(local_msc_name), + OSMO_SOCKADDR_STR_FMT_ARGS(&subscr->remote_hlr)); + + return true; +} + +static bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query, + uint32_t *lu_age_p, + struct global_title *local_msc_name) { - return false; + uint32_t lu_age = 0; + struct global_title msc_name = {}; + uint32_t proxy_lu_age = 0; + struct global_title 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. + */ + if (!subscriber_has_done_lu_here_hlr(query, &lu_age, &msc_name)) + lu_age = 0; + if (!subscriber_has_done_lu_here_proxy(query, &proxy_lu_age, &proxy_msc_name)) + proxy_lu_age = 0; + if (lu_age && proxy_lu_age) { + LOGP(DDGSM, LOGL_DEBUG, + "%s: a LU is on record both in the local HLR (age %us) and the GSUP proxy (age %us)\n", + osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), + lu_age, proxy_lu_age); + } + /* If proxy has a younger lu, replace. */ + if (proxy_lu_age && (!lu_age || (proxy_lu_age < lu_age))) { + lu_age = proxy_lu_age; + msc_name = proxy_msc_name; + } + + if (!lu_age || !msc_name.len) { + LOGP(DDGSM, LOGL_DEBUG, "%s: not attached here\n", + osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); + return false; + } + + LOGP(DDGSM, LOGL_DEBUG, "%s: attached here, at MSC %s\n", + osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), + global_title_name(&msc_name)); + *lu_age_p = lu_age; + *local_msc_name = msc_name; + return true; } -void mslookup_server_rx(const struct osmo_mslookup_query *query, - struct osmo_mslookup_result *result) +/* 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 uint8_t *msc_unit_name; - size_t msc_unit_name_len; - const struct dgsm_service_addr *service_addr; + const struct dgsm_service_host *service_host; uint32_t age; + struct global_title 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. */ @@ -106,28 +263,28 @@ void mslookup_server_rx(const struct osmo_mslookup_query *query, /* 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 MSC belonging to this * HLR. Respond with whichever services are configured in the osmo-hlr.cfg. */ - if (!subscriber_has_done_location_updating_here(&query->id, &age, &msc_unit_name, &msc_unit_name_len)) { + if (!subscriber_has_done_lu_here(query, &age, &msc_name)) { *result = not_found; return; } /* We've detected a LU here. The MSC 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 MSC and service name. */ - service_addr = dgsm_config_service_get(msc_unit_name, msc_unit_name_len, query->service); - if (!service_addr) { + service_host = dgsm_config_service_get(&msc_name, query->service); + + if (!service_host) { + /* Find such service set globally (no MSC 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_results(result, service_addr, age); -} - -struct osmo_mslookup_server_dns *osmo_mslookup_server_dns_start(const struct osmo_sockaddr_str *multicast_bind_addr) -{ - return NULL; + set_result(result, service_host, age); } - -void osmo_mslookup_server_dns_stop(struct osmo_mslookup_server_dns *server) -{ -} - diff --git a/src/mslookup_server.h b/src/mslookup_server.h index 2b8d494..c61792a 100644 --- a/src/mslookup_server.h +++ b/src/mslookup_server.h @@ -1,9 +1,7 @@ #pragma once -struct osmo_mslookup_server_dns { - bool running; - struct osmo_sockaddr_str multicast_bind_addr; -}; +struct osmo_mslookup_query; +struct osmo_mslookup_result; -struct osmo_mslookup_server_dns *osmo_mslookup_server_dns_start(const struct osmo_sockaddr_str *multicast_bind_addr); -void osmo_mslookup_server_dns_stop(struct osmo_mslookup_server_dns *server); +void osmo_mslookup_server_rx(const struct osmo_mslookup_query *query, + struct osmo_mslookup_result *result); diff --git a/src/mslookup_server_mdns.c b/src/mslookup_server_mdns.c new file mode 100644 index 0000000..982ba15 --- /dev/null +++ b/src/mslookup_server_mdns.c @@ -0,0 +1,163 @@ +#include <stdlib.h> +#include <unistd.h> + +#include <osmocom/mslookup/mslookup.h> +#include <osmocom/mslookup/mdns.h> +#include "logging.h" +#include "mslookup_server.h" +#include "mslookup_server_mdns.h" + +static void osmo_mslookup_server_mdns_tx(struct osmo_mslookup_server_mdns *server, + const struct osmo_mdns_request *req, + const struct osmo_mslookup_query *query, + const struct osmo_mslookup_result *result) +{ + const char *errmsg = NULL; + struct msgb *msg; + struct osmo_mdns_answer ans; + struct osmo_mdns_record *rec_age; + struct osmo_mdns_record rec_ip_v4 = {}; + struct osmo_mdns_record *rec_ip_v4_port; + struct osmo_mdns_record rec_ip_v6 = {}; + struct osmo_mdns_record *rec_ip_v6_port; + uint32_t ip_v4; + struct in6_addr ip_v6; + + void *ctx = talloc_named_const(server, 0, __func__); + + osmo_mdns_answer_init(&ans); + ans.id = req->id; + ans.domain = req->domain; + + rec_age = dns_encode_txt_record(ctx, "age", "%u", result->age); + llist_add(&rec_age->list, &ans.records); + + if (osmo_sockaddr_str_is_nonzero(&result->host_v4)) { + if (osmo_sockaddr_str_to_32n(&result->host_v4, &ip_v4)) { + errmsg = "Error encoding IPv4 address"; + goto clean_and_exit; + } + rec_ip_v4.type = OSMO_MSLOOKUP_MDNS_RECORD_TYPE_A; + rec_ip_v4.data = (void*)&ip_v4; + rec_ip_v4.length = sizeof(ip_v4); + llist_add(&rec_ip_v4.list, &ans.records); + + rec_ip_v4_port = dns_encode_txt_record(ctx, "port", "%u", result->host_v4.port); + if (!rec_ip_v4_port) { + errmsg = "Error encoding IPv4 port"; + goto clean_and_exit; + } + llist_add(&rec_ip_v4_port->list, &ans.records); + } + + if (osmo_sockaddr_str_is_nonzero(&result->host_v6)) { + if (osmo_sockaddr_str_to_in6_addr(&result->host_v6, &ip_v6)) { + errmsg = "Error encoding IPv6 address"; + goto clean_and_exit; + } + + rec_ip_v6.type = OSMO_MSLOOKUP_MDNS_RECORD_TYPE_AAAA; + rec_ip_v6.data = (void*)&ip_v6; + rec_ip_v6.length = sizeof(ip_v6); + llist_add(&rec_ip_v6.list, &ans.records); + + rec_ip_v6_port = dns_encode_txt_record(ctx, "port", "%u", result->host_v6.port); + if (!rec_ip_v6_port) { + errmsg = "Error encoding IPv6 port"; + goto clean_and_exit; + } + llist_add(&rec_ip_v6_port->list, &ans.records); + } + + msg = msgb_alloc(1024, __func__); + if (dns_encode_answer(ctx, msg, &ans)) { + errmsg = "Error encoding DNS answer packet"; + goto clean_and_exit; + } + + if (osmo_mdns_sock_send(server->sock, msg)) + errmsg = "Error sending DNS answer"; + +clean_and_exit: + if (errmsg) + LOGP(DDGSM, LOGL_ERROR, "%s: DNS: %s\n", osmo_mslookup_result_name_c(ctx, query, result), errmsg); + talloc_free(ctx); +} + +static void osmo_mslookup_server_mdns_handle_request(struct osmo_mslookup_server_mdns *server, + const struct osmo_mdns_request *req) +{ + struct osmo_mslookup_query query; + struct osmo_mslookup_result result; + + if (osmo_mslookup_query_from_domain_str(&query, req->domain)) { + LOGP(DDGSM, LOGL_ERROR, "mDNS mslookup server: unable to parse request domain string: %s\n", + osmo_quote_str_c(OTC_SELECT, req->domain, -1)); + return; + } + + osmo_mslookup_server_rx(&query, &result); + /* Error logging already happens in osmo_mslookup_server_rx() */ + if (result.rc != OSMO_MSLOOKUP_RC_OK) + return; + + osmo_mslookup_server_mdns_tx(server, req, &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_mdns_request *req; + 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__); + req = dns_decode_request(ctx, buffer, n); + if (!req) { + LOGP(DDGSM, LOGL_DEBUG, "mDNS rx: ignoring: not a request\n"); + talloc_free(ctx); + return -1; + } + + LOGP(DDGSM, LOGL_DEBUG, "mDNS rx request: %s\n", osmo_quote_str_c(OTC_SELECT, req->domain, -1)); + osmo_mslookup_server_mdns_handle_request(server, req); + 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, true, + 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/mslookup_server_mdns.h b/src/mslookup_server_mdns.h new file mode 100644 index 0000000..8d4d4fc --- /dev/null +++ b/src/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/src/proxy.c b/src/proxy.c index 7bcd080..a93c004 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -6,6 +6,7 @@ #include <osmocom/core/timer.h> +#include "logging.h" #include "proxy.h" /* Why have a separate struct to add an llist_head entry? @@ -17,14 +18,6 @@ struct proxy_subscr_listentry { struct proxy_subscr data; }; -static LLIST_HEAD(proxy_subscr_list); -static void *proxy_ctx = NULL; - -/* How long to keep proxy entries without a refresh, in seconds. */ -static time_t proxy_fresh_time = 60 * 60; -static time_t proxy_fresh_check_period = 60; -static struct osmo_timer_list proxy_cleanup_timer; - /* Central implementation to set a timestamp to the current time, in case we want to modify this in the future. */ void timestamp_update(struct timeval *tv) { @@ -47,31 +40,56 @@ static bool proxy_subscr_matches_imsi(const struct proxy_subscr *proxy_subscr, c return strcmp(proxy_subscr->imsi, imsi) == 0; } -static struct proxy_subscr_listentry *_proxy_get(const char *imsi) +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; - llist_for_each_entry(e, &proxy_subscr_list, entry) { + llist_for_each_entry(e, &proxy->subscr_list, entry) { if (proxy_subscr_matches_imsi(&e->data, imsi)) return e; } return NULL; } -const struct proxy_subscr *proxy_subscr_get(const char *imsi) +static struct proxy_subscr_listentry *_proxy_get_by_msisdn(struct proxy *proxy, const char *msisdn) +{ + struct proxy_subscr_listentry *e; + 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(imsi); + struct proxy_subscr_listentry *e = _proxy_get_by_msisdn(proxy, msisdn); if (!e) return NULL; return &e->data; } -int proxy_subscr_update(const struct proxy_subscr *proxy_subscr) +int proxy_subscr_update(struct proxy *proxy, const struct proxy_subscr *proxy_subscr) { - struct proxy_subscr_listentry *e = _proxy_get(proxy_subscr->imsi); + struct proxy_subscr_listentry *e = _proxy_get_by_imsi(proxy, proxy_subscr->imsi); if (!e) { /* Does not exist yet */ - e = talloc_zero(proxy_ctx, struct proxy_subscr_listentry); - llist_add(&e->entry, &proxy_subscr_list); + e = talloc_zero(proxy, struct proxy_subscr_listentry); + llist_add(&e->entry, &proxy->subscr_list); } e->data = *proxy_subscr; timestamp_update(&e->last_update); @@ -84,31 +102,53 @@ int _proxy_subscr_del(struct proxy_subscr_listentry *e) return 0; } -int proxy_subscr_del(const char *imsi) +int proxy_subscr_del(struct proxy *proxy, const char *imsi) { - struct proxy_subscr_listentry *e = _proxy_get(imsi); + struct proxy_subscr_listentry *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 *ignore) +static void proxy_cleanup(void *proxy_v) { + struct proxy *proxy = proxy_v; struct proxy_subscr_listentry *e, *n; - llist_for_each_entry_safe(e, n, &proxy_subscr_list, entry) { - if (timestamp_age(&e->last_update) <= proxy_fresh_time) + llist_for_each_entry_safe(e, n, &proxy->subscr_list, entry) { + if (timestamp_age(&e->last_update) <= proxy->fresh_time) continue; _proxy_subscr_del(e); } - osmo_timer_schedule(&proxy_cleanup_timer, proxy_fresh_check_period, 0); + 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(void *ctx) +struct proxy *proxy_init(void *ctx) { - proxy_ctx = ctx; - osmo_timer_setup(&proxy_cleanup_timer, &proxy_cleanup, NULL); + struct proxy *proxy = talloc_zero(ctx, struct proxy); + *proxy = (struct proxy){ + .fresh_time = 60*60, + .gc_period = 60, + }; + INIT_LLIST_HEAD(&proxy->subscr_list); + + osmo_timer_setup(&proxy->gc_timer, proxy_cleanup, proxy); /* Invoke to trigger the first timer schedule */ - proxy_cleanup(NULL); + proxy_set_gc_period(proxy, proxy->gc_period); + return proxy; } +void proxy_del(struct proxy *proxy) +{ + osmo_timer_del(&proxy->gc_timer); + talloc_free(proxy); +} diff --git a/src/proxy.h b/src/proxy.h index 91d433d..4d332cf 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -3,16 +3,39 @@ #include <time.h> #include <osmocom/gsm/protocol/gsm_23_003.h> #include <osmocom/core/sockaddr_str.h> +#include "global_title.h" + +void timestamp_update(struct timeval *timestamp); +time_t timestamp_age(const struct timeval *timestamp); + +struct proxy { + struct llist_head subscr_list; + + /* 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 { char imsi[GSM23003_IMSI_MAX_DIGITS+1]; + char msisdn[GSM23003_MSISDN_MAX_DIGITS+1]; +#if 0 + /* Set if this is a middle proxy, i.e. a proxy behind another proxy. */ + struct global_title vlr_via_proxy; +#endif + struct global_title vlr_name; struct osmo_sockaddr_str remote_hlr; + struct timeval last_lu; }; -void proxy_init(void *ctx); -const struct proxy_subscr *proxy_subscr_get(const char *imsi); -int proxy_subscr_update(const struct proxy_subscr *proxy_subscr); -int proxy_subscr_del(const char *imsi); - -void timestamp_update(struct timeval *timestamp); -time_t timestamp_age(const struct timeval *timestamp); +struct proxy *proxy_init(void *ctx); +void proxy_del(struct proxy *proxy); +void proxy_set_gc_period(struct proxy *proxy, uint32_t gc_period); +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); +int proxy_subscr_update(struct proxy *proxy, const struct proxy_subscr *proxy_subscr); +int proxy_subscr_del(struct proxy *proxy, const char *imsi); diff --git a/tests/db/db_test.c b/tests/db/db_test.c index 8902998..94295e1 100644 --- a/tests/db/db_test.c +++ b/tests/db/db_test.c @@ -241,7 +241,7 @@ static int db_subscr_lu_str(struct db_context *dbc, int64_t subscr_id, const char *vlr_or_sgsn_number, bool is_ps) { struct global_title vlr_nr; - global_title_set(&vlr_nr, (uint8_t*)vlr_or_sgsn_number, vlr_or_sgsn_number ? strlen(vlr_or_sgsn_number)+1 : 0); + global_title_set_str(&vlr_nr, vlr_or_sgsn_number); return db_subscr_lu(dbc, subscr_id, &vlr_nr, &vlr_nr, is_ps); } diff --git a/tests/db_upgrade/db_upgrade_test.ok b/tests/db_upgrade/db_upgrade_test.ok index 52b6735..f467f06 100644 --- a/tests/db_upgrade/db_upgrade_test.ok +++ b/tests/db_upgrade/db_upgrade_test.ok @@ -136,18 +136,19 @@ periodic_lu_tmr|INTEGER|0||0 periodic_rau_tau_tmr|INTEGER|0||0 sgsn_address|VARCHAR|0||0 sgsn_number|VARCHAR(15)|0||0 +sgsn_via_proxy|VARCHAR|0||0 smsc_number|VARCHAR(15)|0||0 vlr_number|VARCHAR(15)|0||0 vlr_via_proxy|VARCHAR|0||0 Table subscriber contents: -ggsn_number|gmlc_number|id|imei|imeisv|imsi|last_lu_seen|lmsi|ms_purged_cs|ms_purged_ps|msc_number|msisdn|nam_cs|nam_ps|periodic_lu_tmr|periodic_rau_tau_tmr|sgsn_address|sgsn_number|smsc_number|vlr_number|vlr_via_proxy -||1|||123456789012345|||0|0||098765432109876|1|1||||||MSC-1| -||2|||111111111|||1|0|||1|1||||||| -||3|||222222222|||0|1||22222|1|1||||||| -||4|||333333|||0|0||3|0|1||||||| -||5|||444444444444444|||0|0||4444|1|0||||||| -||6|||5555555|||0|0||55555555555555|0|0||||||| +ggsn_number|gmlc_number|id|imei|imeisv|imsi|last_lu_seen|lmsi|ms_purged_cs|ms_purged_ps|msc_number|msisdn|nam_cs|nam_ps|periodic_lu_tmr|periodic_rau_tau_tmr|sgsn_address|sgsn_number|sgsn_via_proxy|smsc_number|vlr_number|vlr_via_proxy +||1|||123456789012345|||0|0||098765432109876|1|1|||||||MSC-1| +||2|||111111111|||1|0|||1|1|||||||| +||3|||222222222|||0|1||22222|1|1|||||||| +||4|||333333|||0|0||3|0|1|||||||| +||5|||444444444444444|||0|0||4444|1|0|||||||| +||6|||5555555|||0|0||55555555555555|0|0|||||||| Table: subscriber_apn name|type|notnull|dflt_value|pk diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty index 844b1fd..e36d6ad 100644 --- a/tests/test_nodes.vty +++ b/tests/test_nodes.vty @@ -119,8 +119,8 @@ OsmoHLR# configure terminal OsmoHLR(config)# mslookup OsmoHLR(config-mslookup)# list ... - dns - no dns + mdns + no mdns server no server client @@ -129,8 +129,8 @@ OsmoHLR(config-mslookup)# list OsmoHLR(config-mslookup)# server OsmoHLR(config-mslookup-server)# list ... - dns bind multicast IP <1-65535> - no dns + mdns bind IP <1-65535> + no mdns service NAME at IP <1-65535> no service NAME no service NAME at IP <1-65535> @@ -148,7 +148,7 @@ OsmoHLR(config-mslookup-server)# exit OsmoHLR(config-mslookup)# client OsmoHLR(config-mslookup-client)# list ... - timeout <1-255> - dns to multicast IP <1-65535> - no dns + timeout <1-100000> + mdns to IP <1-65535> + no mdns |