| /* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> |
| * |
| * All Rights Reserved |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU Affero General Public License as published by |
| * the Free Software Foundation; either version 3 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU Affero General Public License for more details. |
| * |
| * You should have received a copy of the GNU Affero General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| * |
| */ |
| |
| #include <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/cni_peer_id.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.h> |
| #include <osmocom/hlr/mslookup_server_mdns.h> |
| #include <osmocom/hlr/dgsm.h> |
| |
| void *dgsm_ctx = NULL; |
| |
| 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; |
| struct proxy_subscr proxy_subscr; |
| const struct osmo_sockaddr_str *remote_hlr_addr; |
| |
| /* 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)) |
| remote_hlr_addr = &result->host_v4; |
| else if (osmo_sockaddr_str_is_nonzero(&result->host_v6)) |
| remote_hlr_addr = &result->host_v6; |
| else { |
| LOG_DGSM(query->id.imsi, LOGL_ERROR, "Invalid address for remote HLR: %s\n", |
| osmo_mslookup_result_name_c(OTC_SELECT, query, result)); |
| proxy_subscr_del(proxy, query->id.imsi); |
| return; |
| } |
| |
| if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, query->id.imsi)) { |
| 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; |
| } |
| |
| proxy_subscr_remote_hlr_resolved(proxy, &proxy_subscr, remote_hlr_addr); |
| } |
| |
| /* Return true when the message has been handled by D-GSM. */ |
| bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req) |
| { |
| struct proxy_subscr proxy_subscr; |
| 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? */ |
| if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, req->gsup.imsi) == 0) { |
| proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req); |
| return true; |
| } |
| |
| /* 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 = (struct proxy_subscr){}; |
| OSMO_STRLCPY_ARRAY(proxy_subscr.imsi, req->gsup.imsi); |
| if (proxy_subscr_create_or_update(proxy, &proxy_subscr)) { |
| osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Failed to create proxy entry\n"); |
| return true; |
| } |
| |
| /* Is a fixed gateway proxy configured? */ |
| if (osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_gateway_proxy)) { |
| proxy_subscr_remote_hlr_resolved(proxy, &proxy_subscr, &g_hlr->mslookup.client.gsup_gateway_proxy); |
| |
| /* Proxy database modified, update info */ |
| if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, req->gsup.imsi)) { |
| osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Internal proxy error\n"); |
| return true; |
| } |
| |
| proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req); |
| return true; |
| } |
| |
| /* Kick off an mslookup for the remote HLR? This check could be up first on the top, but do it only now so that |
| * if the mslookup client disconnected, we still continue to service open proxy entries. */ |
| if (!osmo_mslookup_client_active(g_hlr->mslookup.client.client)) { |
| LOG_GSUP_REQ(req, LOGL_DEBUG, "mslookup client not running, cannot query remote home HLR\n"); |
| return false; |
| } |
| |
| /* First spool message, then kick off mslookup. If the proxy denies this message type, then don't do anything. */ |
| if (proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req)) { |
| /* If the proxy denied forwarding, an error response was already generated. */ |
| return true; |
| } |
| |
| 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_wait_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); |
| /* mslookup seems to not be working. Try handling it locally. */ |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void dgsm_init(void *ctx) |
| { |
| dgsm_ctx = talloc_named_const(ctx, 0, "dgsm"); |
| INIT_LLIST_HEAD(&g_hlr->mslookup.server.local_site_services); |
| |
| g_hlr->mslookup.server.local_attach_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(&g_hlr->mslookup.server.mdns.bind_addr, |
| OSMO_MSLOOKUP_MDNS_IP4, OSMO_MSLOOKUP_MDNS_PORT); |
| osmo_sockaddr_str_from_str(&g_hlr->mslookup.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; |
| mslookup_server_mdns_config_apply(); |
| dgsm_mdns_client_config_apply(); |
| } |
| |
| void dgsm_stop() |
| { |
| g_hlr->mslookup.allow_startup = false; |
| mslookup_server_mdns_config_apply(); |
| dgsm_mdns_client_config_apply(); |
| } |
| |
| void dgsm_mdns_client_config_apply(void) |
| { |
| /* Check whether to start/stop/restart mDNS client */ |
| const struct osmo_sockaddr_str *current_bind_addr; |
| const char *current_domain_suffix; |
| current_bind_addr = osmo_mslookup_client_method_mdns_get_bind_addr(g_hlr->mslookup.client.mdns.running); |
| current_domain_suffix = osmo_mslookup_client_method_mdns_get_domain_suffix(g_hlr->mslookup.client.mdns.running); |
| |
| bool should_run = g_hlr->mslookup.allow_startup |
| && g_hlr->mslookup.client.enable && g_hlr->mslookup.client.mdns.enable; |
| |
| bool should_stop = g_hlr->mslookup.client.mdns.running && |
| (!should_run |
| || osmo_sockaddr_str_cmp(&g_hlr->mslookup.client.mdns.query_addr, |
| current_bind_addr) |
| || strcmp(g_hlr->mslookup.client.mdns.domain_suffix, |
| current_domain_suffix)); |
| |
| if (should_stop) { |
| osmo_mslookup_client_method_del(g_hlr->mslookup.client.client, g_hlr->mslookup.client.mdns.running); |
| g_hlr->mslookup.client.mdns.running = NULL; |
| LOGP(DDGSM, LOGL_NOTICE, "Stopped mslookup mDNS client\n"); |
| } |
| |
| if (should_run && !g_hlr->mslookup.client.mdns.running) { |
| g_hlr->mslookup.client.mdns.running = |
| osmo_mslookup_client_add_mdns(g_hlr->mslookup.client.client, |
| g_hlr->mslookup.client.mdns.query_addr.ip, |
| g_hlr->mslookup.client.mdns.query_addr.port, |
| -1, |
| g_hlr->mslookup.client.mdns.domain_suffix); |
| if (!g_hlr->mslookup.client.mdns.running) |
| 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.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(&g_hlr->mslookup.client.mdns.query_addr)); |
| } |
| |
| if (g_hlr->mslookup.client.enable && osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_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(&g_hlr->mslookup.client.gsup_gateway_proxy)); |
| } |