Neels Hofmeyr | 647c106 | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 1 | /* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> |
| 2 | * |
| 3 | * All Rights Reserved |
| 4 | * |
| 5 | * This program is free software; you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU Affero General Public License as published by |
| 7 | * the Free Software Foundation; either version 3 of the License, or |
| 8 | * (at your option) any later version. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU Affero General Public License for more details. |
| 14 | * |
| 15 | * You should have received a copy of the GNU Affero General Public License |
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 17 | * |
| 18 | */ |
| 19 | |
| 20 | #include <errno.h> |
| 21 | #include <osmocom/core/logging.h> |
| 22 | #include <osmocom/mslookup/mslookup_client.h> |
| 23 | #include <osmocom/mslookup/mslookup_client_mdns.h> |
| 24 | #include <osmocom/gsupclient/gsup_client.h> |
| 25 | #include <osmocom/gsupclient/gsup_peer_id.h> |
| 26 | #include <osmocom/hlr/logging.h> |
| 27 | #include <osmocom/hlr/hlr.h> |
| 28 | #include <osmocom/hlr/db.h> |
| 29 | #include <osmocom/hlr/gsup_router.h> |
| 30 | #include <osmocom/hlr/gsup_server.h> |
| 31 | #include <osmocom/hlr/dgsm.h> |
| 32 | #include <osmocom/hlr/proxy.h> |
| 33 | #include <osmocom/hlr/remote_hlr.h> |
| 34 | #include <osmocom/hlr/mslookup_server.h> |
| 35 | #include <osmocom/hlr/mslookup_server_mdns.h> |
| 36 | #include <osmocom/hlr/dgsm.h> |
| 37 | |
| 38 | void *dgsm_ctx = NULL; |
| 39 | |
| 40 | static void resolve_hlr_result_cb(struct osmo_mslookup_client *client, |
| 41 | uint32_t request_handle, |
| 42 | const struct osmo_mslookup_query *query, |
| 43 | const struct osmo_mslookup_result *result) |
| 44 | { |
| 45 | struct proxy *proxy = g_hlr->gs->proxy; |
| 46 | struct proxy_subscr proxy_subscr; |
| 47 | const struct osmo_sockaddr_str *remote_hlr_addr; |
| 48 | |
| 49 | /* A remote HLR is answering back, indicating that it is the home HLR for a given IMSI. |
| 50 | * There should be a mostly empty proxy entry for that IMSI. |
| 51 | * Add the remote address data in the proxy. */ |
| 52 | if (query->id.type != OSMO_MSLOOKUP_ID_IMSI) { |
| 53 | LOGP(DDGSM, LOGL_ERROR, "Expected IMSI ID type in mslookup query+result: %s\n", |
| 54 | osmo_mslookup_result_name_c(OTC_SELECT, query, result)); |
| 55 | return; |
| 56 | } |
| 57 | |
| 58 | if (result->rc != OSMO_MSLOOKUP_RC_RESULT) { |
| 59 | LOG_DGSM(query->id.imsi, LOGL_ERROR, "Failed to resolve remote HLR: %s\n", |
| 60 | osmo_mslookup_result_name_c(OTC_SELECT, query, result)); |
| 61 | proxy_subscr_del(proxy, query->id.imsi); |
| 62 | return; |
| 63 | } |
| 64 | |
| 65 | if (osmo_sockaddr_str_is_nonzero(&result->host_v4)) |
| 66 | remote_hlr_addr = &result->host_v4; |
| 67 | else if (osmo_sockaddr_str_is_nonzero(&result->host_v6)) |
| 68 | remote_hlr_addr = &result->host_v6; |
| 69 | else { |
| 70 | LOG_DGSM(query->id.imsi, LOGL_ERROR, "Invalid address for remote HLR: %s\n", |
| 71 | osmo_mslookup_result_name_c(OTC_SELECT, query, result)); |
| 72 | proxy_subscr_del(proxy, query->id.imsi); |
| 73 | return; |
| 74 | } |
| 75 | |
| 76 | if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, query->id.imsi)) { |
| 77 | LOG_DGSM(query->id.imsi, LOGL_ERROR, "No proxy entry for mslookup result: %s\n", |
| 78 | osmo_mslookup_result_name_c(OTC_SELECT, query, result)); |
| 79 | return; |
| 80 | } |
| 81 | |
| 82 | proxy_subscr_remote_hlr_resolved(proxy, &proxy_subscr, remote_hlr_addr); |
| 83 | } |
| 84 | |
| 85 | /* Return true when the message has been handled by D-GSM. */ |
| 86 | bool dgsm_check_forward_gsup_msg(struct osmo_gsup_req *req) |
| 87 | { |
| 88 | struct proxy_subscr proxy_subscr; |
| 89 | struct proxy *proxy = g_hlr->gs->proxy; |
| 90 | struct osmo_mslookup_query query; |
| 91 | struct osmo_mslookup_query_handling handling; |
| 92 | uint32_t request_handle; |
| 93 | |
| 94 | /* If the IMSI is known in the local HLR, then we won't proxy. */ |
| 95 | if (db_subscr_exists_by_imsi(g_hlr->dbc, req->gsup.imsi) == 0) |
| 96 | return false; |
| 97 | |
| 98 | /* Are we already forwarding this IMSI to a remote HLR? */ |
| 99 | if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, req->gsup.imsi) == 0) { |
| 100 | proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req); |
| 101 | return true; |
| 102 | } |
| 103 | |
| 104 | /* The IMSI is not known locally, so we want to proxy to a remote HLR, but no proxy entry exists yet. We need to |
| 105 | * look up the subscriber in remote HLRs via D-GSM mslookup, forward GSUP and reply once a result is back from |
| 106 | * there. Defer message and kick off MS lookup. */ |
| 107 | |
| 108 | /* Add a proxy entry without a remote address to indicate that we are busy querying for a remote HLR. */ |
| 109 | proxy_subscr = (struct proxy_subscr){}; |
| 110 | OSMO_STRLCPY_ARRAY(proxy_subscr.imsi, req->gsup.imsi); |
| 111 | if (proxy_subscr_create_or_update(proxy, &proxy_subscr)) { |
| 112 | osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Failed to create proxy entry\n"); |
| 113 | return true; |
| 114 | } |
| 115 | |
| 116 | /* Is a fixed gateway proxy configured? */ |
| 117 | if (osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_gateway_proxy)) { |
| 118 | proxy_subscr_remote_hlr_resolved(proxy, &proxy_subscr, &g_hlr->mslookup.client.gsup_gateway_proxy); |
| 119 | |
| 120 | /* Proxy database modified, update info */ |
| 121 | if (proxy_subscr_get_by_imsi(&proxy_subscr, proxy, req->gsup.imsi)) { |
| 122 | osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Internal proxy error\n"); |
| 123 | return true; |
| 124 | } |
| 125 | |
| 126 | proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req); |
| 127 | return true; |
| 128 | } |
| 129 | |
| 130 | /* Kick off an mslookup for the remote HLR? This check could be up first on the top, but do it only now so that |
| 131 | * if the mslookup client disconnected, we still continue to service open proxy entries. */ |
| 132 | if (!osmo_mslookup_client_active(g_hlr->mslookup.client.client)) { |
| 133 | LOG_GSUP_REQ(req, LOGL_DEBUG, "mslookup client not running, cannot query remote home HLR\n"); |
| 134 | return false; |
| 135 | } |
| 136 | |
| 137 | /* First spool message, then kick off mslookup. If the proxy denies this message type, then don't do anything. */ |
| 138 | if (proxy_subscr_forward_to_remote_hlr(proxy, &proxy_subscr, req)) { |
| 139 | /* If the proxy denied forwarding, an error response was already generated. */ |
| 140 | return true; |
| 141 | } |
| 142 | |
| 143 | query = (struct osmo_mslookup_query){ |
| 144 | .id = { |
| 145 | .type = OSMO_MSLOOKUP_ID_IMSI, |
| 146 | }, |
| 147 | }; |
| 148 | OSMO_STRLCPY_ARRAY(query.id.imsi, req->gsup.imsi); |
| 149 | OSMO_STRLCPY_ARRAY(query.service, OSMO_MSLOOKUP_SERVICE_HLR_GSUP); |
| 150 | handling = (struct osmo_mslookup_query_handling){ |
| 151 | .min_wait_milliseconds = g_hlr->mslookup.client.result_timeout_milliseconds, |
| 152 | .result_cb = resolve_hlr_result_cb, |
| 153 | }; |
| 154 | request_handle = osmo_mslookup_client_request(g_hlr->mslookup.client.client, &query, &handling); |
| 155 | if (!request_handle) { |
| 156 | LOG_DGSM(req->gsup.imsi, LOGL_ERROR, "Error dispatching mslookup query for home HLR: %s\n", |
| 157 | osmo_mslookup_result_name_c(OTC_SELECT, &query, NULL)); |
| 158 | proxy_subscr_del(proxy, req->gsup.imsi); |
| 159 | /* mslookup seems to not be working. Try handling it locally. */ |
| 160 | return false; |
| 161 | } |
| 162 | |
| 163 | return true; |
| 164 | } |
| 165 | |
| 166 | void dgsm_init(void *ctx) |
| 167 | { |
| 168 | dgsm_ctx = talloc_named_const(ctx, 0, "dgsm"); |
| 169 | INIT_LLIST_HEAD(&g_hlr->mslookup.server.local_site_services); |
| 170 | |
| 171 | g_hlr->mslookup.server.local_attach_max_age = 60 * 60; |
| 172 | |
| 173 | g_hlr->mslookup.client.result_timeout_milliseconds = 2000; |
| 174 | |
| 175 | g_hlr->gsup_unit_name.unit_name = "HLR"; |
| 176 | g_hlr->gsup_unit_name.serno = "unnamed-HLR"; |
| 177 | g_hlr->gsup_unit_name.swversion = PACKAGE_NAME "-" PACKAGE_VERSION; |
| 178 | |
| 179 | osmo_sockaddr_str_from_str(&g_hlr->mslookup.server.mdns.bind_addr, |
| 180 | OSMO_MSLOOKUP_MDNS_IP4, OSMO_MSLOOKUP_MDNS_PORT); |
| 181 | osmo_sockaddr_str_from_str(&g_hlr->mslookup.client.mdns.query_addr, |
| 182 | OSMO_MSLOOKUP_MDNS_IP4, OSMO_MSLOOKUP_MDNS_PORT); |
| 183 | } |
| 184 | |
| 185 | void dgsm_start(void *ctx) |
| 186 | { |
| 187 | g_hlr->mslookup.client.client = osmo_mslookup_client_new(dgsm_ctx); |
| 188 | OSMO_ASSERT(g_hlr->mslookup.client.client); |
| 189 | g_hlr->mslookup.allow_startup = true; |
| 190 | mslookup_server_mdns_config_apply(); |
| 191 | dgsm_mdns_client_config_apply(); |
| 192 | } |
| 193 | |
| 194 | void dgsm_stop() |
| 195 | { |
| 196 | g_hlr->mslookup.allow_startup = false; |
| 197 | mslookup_server_mdns_config_apply(); |
| 198 | dgsm_mdns_client_config_apply(); |
| 199 | } |
| 200 | |
| 201 | void dgsm_mdns_client_config_apply(void) |
| 202 | { |
| 203 | /* Check whether to start/stop/restart mDNS client */ |
| 204 | const struct osmo_sockaddr_str *current_bind_addr; |
| 205 | const char *current_domain_suffix; |
| 206 | current_bind_addr = osmo_mslookup_client_method_mdns_get_bind_addr(g_hlr->mslookup.client.mdns.running); |
| 207 | current_domain_suffix = osmo_mslookup_client_method_mdns_get_domain_suffix(g_hlr->mslookup.client.mdns.running); |
| 208 | |
| 209 | bool should_run = g_hlr->mslookup.allow_startup |
| 210 | && g_hlr->mslookup.client.enable && g_hlr->mslookup.client.mdns.enable; |
| 211 | |
| 212 | bool should_stop = g_hlr->mslookup.client.mdns.running && |
| 213 | (!should_run |
| 214 | || osmo_sockaddr_str_cmp(&g_hlr->mslookup.client.mdns.query_addr, |
| 215 | current_bind_addr) |
| 216 | || strcmp(g_hlr->mslookup.client.mdns.domain_suffix, |
| 217 | current_domain_suffix)); |
| 218 | |
| 219 | if (should_stop) { |
| 220 | osmo_mslookup_client_method_del(g_hlr->mslookup.client.client, g_hlr->mslookup.client.mdns.running); |
| 221 | g_hlr->mslookup.client.mdns.running = NULL; |
| 222 | LOGP(DDGSM, LOGL_NOTICE, "Stopped mslookup mDNS client\n"); |
| 223 | } |
| 224 | |
| 225 | if (should_run && !g_hlr->mslookup.client.mdns.running) { |
| 226 | g_hlr->mslookup.client.mdns.running = |
| 227 | osmo_mslookup_client_add_mdns(g_hlr->mslookup.client.client, |
| 228 | g_hlr->mslookup.client.mdns.query_addr.ip, |
| 229 | g_hlr->mslookup.client.mdns.query_addr.port, |
| 230 | -1, |
| 231 | g_hlr->mslookup.client.mdns.domain_suffix); |
| 232 | if (!g_hlr->mslookup.client.mdns.running) |
| 233 | LOGP(DDGSM, LOGL_ERROR, "Failed to start mslookup mDNS client with target " |
| 234 | OSMO_SOCKADDR_STR_FMT "\n", |
| 235 | OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.client.mdns.query_addr)); |
| 236 | else |
| 237 | LOGP(DDGSM, LOGL_NOTICE, "Started mslookup mDNS client, sending mDNS requests to multicast " |
| 238 | OSMO_SOCKADDR_STR_FMT "\n", |
| 239 | OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.client.mdns.query_addr)); |
| 240 | } |
| 241 | |
| 242 | if (g_hlr->mslookup.client.enable && osmo_sockaddr_str_is_nonzero(&g_hlr->mslookup.client.gsup_gateway_proxy)) |
| 243 | LOGP(DDGSM, LOGL_NOTICE, |
| 244 | "mslookup client: all GSUP requests for unknown IMSIs will be forwarded to" |
| 245 | " gateway-proxy " OSMO_SOCKADDR_STR_FMT "\n", |
| 246 | OSMO_SOCKADDR_STR_FMT_ARGS(&g_hlr->mslookup.client.gsup_gateway_proxy)); |
| 247 | } |