Neels Hofmeyr | 32cc07a | 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 <string.h> |
| 21 | #include <errno.h> |
| 22 | #include <osmocom/core/sockaddr_str.h> |
| 23 | #include <osmocom/gsm/gsup.h> |
| 24 | #include <osmocom/mslookup/mslookup.h> |
| 25 | #include <osmocom/hlr/logging.h> |
| 26 | #include <osmocom/hlr/hlr.h> |
| 27 | #include <osmocom/hlr/db.h> |
| 28 | #include <osmocom/hlr/timestamp.h> |
| 29 | #include <osmocom/hlr/mslookup_server.h> |
Neels Hofmeyr | 647c106 | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 30 | #include <osmocom/hlr/proxy.h> |
Neels Hofmeyr | 32cc07a | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 31 | |
| 32 | static const struct osmo_mslookup_result not_found = { |
| 33 | .rc = OSMO_MSLOOKUP_RC_NOT_FOUND, |
| 34 | }; |
| 35 | const struct osmo_ipa_name mslookup_server_msc_wildcard = {}; |
| 36 | |
| 37 | static void set_result(struct osmo_mslookup_result *result, |
| 38 | const struct mslookup_service_host *service_host, |
| 39 | uint32_t age) |
| 40 | { |
| 41 | if (!osmo_sockaddr_str_is_nonzero(&service_host->host_v4) |
| 42 | && !osmo_sockaddr_str_is_nonzero(&service_host->host_v6)) { |
| 43 | *result = not_found; |
| 44 | return; |
| 45 | } |
| 46 | result->rc = OSMO_MSLOOKUP_RC_RESULT; |
| 47 | result->host_v4 = service_host->host_v4; |
| 48 | result->host_v6 = service_host->host_v6; |
| 49 | result->age = age; |
| 50 | } |
| 51 | |
| 52 | const struct mslookup_service_host *mslookup_server_get_local_gsup_addr() |
| 53 | { |
| 54 | static struct mslookup_service_host gsup_bind = {}; |
| 55 | struct mslookup_service_host *host; |
| 56 | |
| 57 | /* Find a HLR/GSUP service set for the server (no VLR unit name) */ |
| 58 | host = mslookup_server_service_get(&mslookup_server_msc_wildcard, OSMO_MSLOOKUP_SERVICE_HLR_GSUP); |
| 59 | if (host) |
| 60 | return host; |
| 61 | |
| 62 | /* Try to use the locally configured GSUP bind address */ |
| 63 | osmo_sockaddr_str_from_str(&gsup_bind.host_v4, g_hlr->gsup_bind_addr, OSMO_GSUP_PORT); |
| 64 | if (gsup_bind.host_v4.af == AF_INET6) { |
| 65 | gsup_bind.host_v6 = gsup_bind.host_v4; |
| 66 | gsup_bind.host_v4 = (struct osmo_sockaddr_str){}; |
| 67 | } |
| 68 | return &gsup_bind; |
| 69 | } |
| 70 | |
| 71 | struct mslookup_server_msc_cfg *mslookup_server_msc_get(const struct osmo_ipa_name *msc_name, bool create) |
| 72 | { |
| 73 | struct llist_head *c = &g_hlr->mslookup.server.local_site_services; |
| 74 | struct mslookup_server_msc_cfg *msc; |
| 75 | |
| 76 | if (!msc_name) |
| 77 | return NULL; |
| 78 | |
| 79 | llist_for_each_entry(msc, c, entry) { |
| 80 | if (osmo_ipa_name_cmp(&msc->name, msc_name)) |
| 81 | continue; |
| 82 | return msc; |
| 83 | } |
| 84 | if (!create) |
| 85 | return NULL; |
| 86 | |
| 87 | msc = talloc_zero(g_hlr, struct mslookup_server_msc_cfg); |
| 88 | OSMO_ASSERT(msc); |
| 89 | INIT_LLIST_HEAD(&msc->service_hosts); |
| 90 | msc->name = *msc_name; |
| 91 | llist_add_tail(&msc->entry, c); |
| 92 | return msc; |
| 93 | } |
| 94 | |
| 95 | struct mslookup_service_host *mslookup_server_msc_service_get(struct mslookup_server_msc_cfg *msc, const char *service, |
| 96 | bool create) |
| 97 | { |
| 98 | struct mslookup_service_host *e; |
| 99 | if (!msc) |
| 100 | return NULL; |
| 101 | |
| 102 | llist_for_each_entry(e, &msc->service_hosts, entry) { |
| 103 | if (!strcmp(e->service, service)) |
| 104 | return e; |
| 105 | } |
| 106 | |
| 107 | if (!create) |
| 108 | return NULL; |
| 109 | |
| 110 | e = talloc_zero(msc, struct mslookup_service_host); |
| 111 | OSMO_ASSERT(e); |
| 112 | OSMO_STRLCPY_ARRAY(e->service, service); |
| 113 | llist_add_tail(&e->entry, &msc->service_hosts); |
| 114 | return e; |
| 115 | } |
| 116 | |
| 117 | struct mslookup_service_host *mslookup_server_service_get(const struct osmo_ipa_name *msc_name, const char *service) |
| 118 | { |
| 119 | struct mslookup_server_msc_cfg *msc = mslookup_server_msc_get(msc_name, false); |
| 120 | if (!msc) |
| 121 | return NULL; |
| 122 | return mslookup_server_msc_service_get(msc, service, false); |
| 123 | } |
| 124 | |
| 125 | int mslookup_server_msc_service_set(struct mslookup_server_msc_cfg *msc, const char *service, |
| 126 | const struct osmo_sockaddr_str *addr) |
| 127 | { |
| 128 | struct mslookup_service_host *e; |
| 129 | |
| 130 | if (!service || !service[0] |
| 131 | || strlen(service) > OSMO_MSLOOKUP_SERVICE_MAXLEN) |
| 132 | return -EINVAL; |
| 133 | if (!addr || !osmo_sockaddr_str_is_nonzero(addr)) |
| 134 | return -EINVAL; |
| 135 | |
| 136 | e = mslookup_server_msc_service_get(msc, service, true); |
| 137 | if (!e) |
| 138 | return -EINVAL; |
| 139 | |
| 140 | switch (addr->af) { |
| 141 | case AF_INET: |
| 142 | e->host_v4 = *addr; |
| 143 | break; |
| 144 | case AF_INET6: |
| 145 | e->host_v6 = *addr; |
| 146 | break; |
| 147 | default: |
| 148 | return -EINVAL; |
| 149 | } |
| 150 | return 0; |
| 151 | } |
| 152 | |
| 153 | int mslookup_server_msc_service_del(struct mslookup_server_msc_cfg *msc, const char *service, |
| 154 | const struct osmo_sockaddr_str *addr) |
| 155 | { |
| 156 | struct mslookup_service_host *e, *n; |
| 157 | int deleted = 0; |
| 158 | |
| 159 | if (!msc) |
| 160 | return -ENOENT; |
| 161 | |
| 162 | llist_for_each_entry_safe(e, n, &msc->service_hosts, entry) { |
| 163 | if (service && strcmp(service, e->service)) |
| 164 | continue; |
| 165 | |
| 166 | if (addr) { |
| 167 | if (!osmo_sockaddr_str_cmp(addr, &e->host_v4)) { |
| 168 | e->host_v4 = (struct osmo_sockaddr_str){}; |
| 169 | /* Removed one addr. If the other is still there, keep the entry. */ |
| 170 | if (osmo_sockaddr_str_is_nonzero(&e->host_v6)) |
| 171 | continue; |
| 172 | } else if (!osmo_sockaddr_str_cmp(addr, &e->host_v6)) { |
| 173 | e->host_v6 = (struct osmo_sockaddr_str){}; |
| 174 | /* Removed one addr. If the other is still there, keep the entry. */ |
| 175 | if (osmo_sockaddr_str_is_nonzero(&e->host_v4)) |
| 176 | continue; |
| 177 | } else |
| 178 | /* No addr match, keep the entry. */ |
| 179 | continue; |
| 180 | /* Addr matched and none is left. Delete. */ |
| 181 | } |
| 182 | llist_del(&e->entry); |
| 183 | talloc_free(e); |
| 184 | deleted++; |
| 185 | } |
| 186 | return deleted; |
| 187 | } |
| 188 | |
| 189 | /* A remote entity is asking us whether we are the home HLR of the given subscriber. */ |
| 190 | static void mslookup_server_rx_hlr_gsup(const struct osmo_mslookup_query *query, |
| 191 | struct osmo_mslookup_result *result) |
| 192 | { |
| 193 | const struct mslookup_service_host *host; |
| 194 | int rc; |
| 195 | switch (query->id.type) { |
| 196 | case OSMO_MSLOOKUP_ID_IMSI: |
| 197 | rc = db_subscr_exists_by_imsi(g_hlr->dbc, query->id.imsi); |
| 198 | break; |
| 199 | case OSMO_MSLOOKUP_ID_MSISDN: |
| 200 | rc = db_subscr_exists_by_msisdn(g_hlr->dbc, query->id.msisdn); |
| 201 | break; |
| 202 | default: |
| 203 | LOGP(DMSLOOKUP, LOGL_ERROR, "Unknown mslookup ID type: %d\n", query->id.type); |
| 204 | *result = not_found; |
| 205 | return; |
| 206 | } |
| 207 | |
| 208 | if (rc) { |
| 209 | LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: does not exist in local HLR\n", |
| 210 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); |
| 211 | *result = not_found; |
| 212 | return; |
| 213 | } |
| 214 | |
| 215 | LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: found in local HLR\n", |
| 216 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); |
| 217 | |
| 218 | host = mslookup_server_get_local_gsup_addr(); |
| 219 | |
| 220 | set_result(result, host, 0); |
| 221 | if (result->rc != OSMO_MSLOOKUP_RC_RESULT) { |
| 222 | LOGP(DMSLOOKUP, LOGL_ERROR, |
| 223 | "Subscriber found, but error in service '" OSMO_MSLOOKUP_SERVICE_HLR_GSUP "' config:" |
| 224 | " v4: " OSMO_SOCKADDR_STR_FMT " v6: " OSMO_SOCKADDR_STR_FMT "\n", |
| 225 | OSMO_SOCKADDR_STR_FMT_ARGS(&host->host_v4), |
| 226 | OSMO_SOCKADDR_STR_FMT_ARGS(&host->host_v6)); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | /* Look in the local HLR record: If the subscriber is "at home" in this HLR and is also currently located at a local |
| 231 | * VLR, we will find a valid location updating with vlr_number, and no vlr_via_proxy entry. */ |
| 232 | static bool subscriber_has_done_lu_here_hlr(const struct osmo_mslookup_query *query, |
| 233 | uint32_t *lu_age, |
| 234 | struct osmo_ipa_name *local_msc_name, |
| 235 | struct hlr_subscriber *ret_subscr) |
| 236 | { |
| 237 | struct hlr_subscriber _subscr; |
| 238 | int rc; |
| 239 | uint32_t age; |
| 240 | |
| 241 | struct hlr_subscriber *subscr = ret_subscr ? : &_subscr; |
| 242 | |
| 243 | switch (query->id.type) { |
| 244 | case OSMO_MSLOOKUP_ID_IMSI: |
| 245 | rc = db_subscr_get_by_imsi(g_hlr->dbc, query->id.imsi, subscr); |
| 246 | break; |
| 247 | case OSMO_MSLOOKUP_ID_MSISDN: |
| 248 | rc = db_subscr_get_by_msisdn(g_hlr->dbc, query->id.msisdn, subscr); |
| 249 | break; |
| 250 | default: |
| 251 | LOGP(DMSLOOKUP, LOGL_ERROR, "Unknown mslookup ID type: %d\n", query->id.type); |
| 252 | return false; |
| 253 | } |
| 254 | |
| 255 | if (rc) { |
| 256 | LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: does not exist in local HLR\n", |
| 257 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); |
| 258 | return false; |
| 259 | } |
| 260 | |
| 261 | if (!subscr->vlr_number[0]) { |
| 262 | LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: not attached (vlr_number unset)\n", |
| 263 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); |
| 264 | return false; |
| 265 | } |
| 266 | |
| 267 | if (subscr->vlr_via_proxy.len) { |
| 268 | /* The VLR is behind a proxy, the subscriber is not attached to a local VLR but a remote one. That |
| 269 | * remote proxy should instead respond to the service lookup request. */ |
| 270 | LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: last attach is not at local VLR, but at VLR '%s' via proxy %s\n", |
| 271 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), |
| 272 | subscr->vlr_number, |
| 273 | osmo_ipa_name_to_str(&subscr->vlr_via_proxy)); |
| 274 | return false; |
| 275 | } |
| 276 | |
| 277 | if (!timestamp_age(&subscr->last_lu_seen, &age)) { |
| 278 | LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: Invalid last_lu_seen timestamp for subscriber\n", |
| 279 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); |
| 280 | return false; |
| 281 | } |
| 282 | if (age > g_hlr->mslookup.server.local_attach_max_age) { |
| 283 | LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: last attach was here, but too long ago: %us > %us\n", |
| 284 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), |
| 285 | age, g_hlr->mslookup.server.local_attach_max_age); |
| 286 | return false; |
| 287 | } |
| 288 | |
| 289 | *lu_age = age; |
| 290 | osmo_ipa_name_set_str(local_msc_name, subscr->vlr_number); |
| 291 | LOGP(DMSLOOKUP, LOGL_DEBUG, "%s: attached %u seconds ago at local VLR %s\n", |
| 292 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), |
| 293 | age, osmo_ipa_name_to_str(local_msc_name)); |
| 294 | |
| 295 | return true; |
| 296 | } |
| 297 | |
Neels Hofmeyr | 647c106 | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 298 | |
| 299 | /* Determine whether the subscriber with the given ID has routed a Location Updating via this HLR as first hop. Return |
| 300 | * true if it is attached at a local VLR, and we are serving as proxy for a remote home HLR. |
| 301 | */ |
| 302 | static bool subscriber_has_done_lu_here_proxy(const struct osmo_mslookup_query *query, |
| 303 | uint32_t *lu_age, |
| 304 | struct osmo_ipa_name *local_msc_name, |
| 305 | struct proxy_subscr *ret_proxy_subscr) |
| 306 | { |
| 307 | struct proxy_subscr proxy_subscr; |
| 308 | uint32_t age; |
| 309 | int rc; |
| 310 | |
| 311 | /* See the local HLR record. If the subscriber is "at home" in this HLR and is also currently located here, we |
| 312 | * will find a valid location updating and no vlr_via_proxy entry. */ |
| 313 | switch (query->id.type) { |
| 314 | case OSMO_MSLOOKUP_ID_IMSI: |
| 315 | rc = proxy_subscr_get_by_imsi(&proxy_subscr, g_hlr->gs->proxy, query->id.imsi); |
| 316 | break; |
| 317 | case OSMO_MSLOOKUP_ID_MSISDN: |
| 318 | rc = proxy_subscr_get_by_msisdn(&proxy_subscr, g_hlr->gs->proxy, query->id.msisdn); |
| 319 | break; |
| 320 | default: |
| 321 | LOGP(DDGSM, LOGL_ERROR, "%s: unknown ID type\n", |
| 322 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); |
| 323 | return false; |
| 324 | } |
| 325 | |
| 326 | if (rc) { |
| 327 | LOGP(DDGSM, LOGL_DEBUG, "%s: does not exist in GSUP proxy\n", |
| 328 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); |
| 329 | return false; |
| 330 | } |
| 331 | |
| 332 | /* We only need to care about CS LU, since only CS services need D-GSM routing. */ |
| 333 | if (!timestamp_age(&proxy_subscr.cs.last_lu, &age) |
| 334 | || age > g_hlr->mslookup.server.local_attach_max_age) { |
| 335 | LOGP(DDGSM, LOGL_ERROR, |
| 336 | "%s: last attach was at local VLR (proxying for remote HLR), but too long ago: %us > %us\n", |
| 337 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), |
| 338 | age, g_hlr->mslookup.server.local_attach_max_age); |
| 339 | return false; |
| 340 | } |
| 341 | |
| 342 | if (proxy_subscr.cs.vlr_via_proxy.len) { |
| 343 | LOGP(DDGSM, LOGL_DEBUG, "%s: last attach is not at local VLR, but at VLR '%s' via proxy '%s'\n", |
| 344 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), |
| 345 | osmo_ipa_name_to_str(&proxy_subscr.cs.vlr_name), |
| 346 | osmo_ipa_name_to_str(&proxy_subscr.cs.vlr_via_proxy)); |
| 347 | return false; |
| 348 | } |
| 349 | |
| 350 | *lu_age = age; |
| 351 | *local_msc_name = proxy_subscr.cs.vlr_name; |
| 352 | LOGP(DDGSM, LOGL_DEBUG, "%s: attached %u seconds ago at local VLR %s; proxying for remote HLR " |
| 353 | OSMO_SOCKADDR_STR_FMT "\n", |
| 354 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), |
| 355 | age, osmo_ipa_name_to_str(local_msc_name), |
| 356 | OSMO_SOCKADDR_STR_FMT_ARGS(&proxy_subscr.remote_hlr_addr)); |
| 357 | |
| 358 | if (ret_proxy_subscr) |
| 359 | *ret_proxy_subscr = proxy_subscr; |
| 360 | return true; |
| 361 | } |
| 362 | |
| 363 | bool subscriber_has_done_lu_here(const struct osmo_mslookup_query *query, |
| 364 | uint32_t *lu_age_p, struct osmo_ipa_name *local_msc_name, |
| 365 | char *ret_imsi, size_t ret_imsi_len) |
Neels Hofmeyr | 32cc07a | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 366 | { |
| 367 | bool attached_here; |
| 368 | uint32_t lu_age = 0; |
| 369 | struct osmo_ipa_name msc_name = {}; |
Neels Hofmeyr | 647c106 | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 370 | bool attached_here_proxy; |
| 371 | uint32_t proxy_lu_age = 0; |
| 372 | struct osmo_ipa_name proxy_msc_name = {}; |
| 373 | struct proxy_subscr proxy_subscr; |
| 374 | struct hlr_subscriber db_subscr; |
| 375 | |
Neels Hofmeyr | 32cc07a | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 376 | |
| 377 | /* First ask the local HLR db, but if the local proxy record indicates a more recent LU, use that instead. |
| 378 | * For all usual cases, only one of these will reflect a LU, even if a subscriber had more than one home HLR: |
| 379 | * - if the subscriber is known here, we will never proxy. |
| 380 | * - if the subscriber is not known here, this local HLR db will never record a LU. |
| 381 | * However, if a subscriber was being proxied to a remote home HLR, and if then the subscriber was also added to |
| 382 | * the local HLR database, there might occur a situation where both reflect a LU. So, to be safe against all |
| 383 | * situations, compare the two entries. |
| 384 | */ |
Neels Hofmeyr | 647c106 | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 385 | attached_here = subscriber_has_done_lu_here_hlr(query, &lu_age, &msc_name, &db_subscr); |
| 386 | attached_here_proxy = subscriber_has_done_lu_here_proxy(query, &proxy_lu_age, &proxy_msc_name, &proxy_subscr); |
Neels Hofmeyr | 32cc07a | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 387 | |
Neels Hofmeyr | 647c106 | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 388 | /* If proxy has a younger lu, replace. */ |
| 389 | if (attached_here_proxy && (!attached_here || (proxy_lu_age < lu_age))) { |
| 390 | attached_here = true; |
| 391 | lu_age = proxy_lu_age; |
| 392 | msc_name = proxy_msc_name; |
| 393 | if (ret_imsi) |
| 394 | osmo_strlcpy(ret_imsi, proxy_subscr.imsi, ret_imsi_len); |
| 395 | } else if (attached_here) { |
| 396 | if (ret_imsi) |
| 397 | osmo_strlcpy(ret_imsi, db_subscr.imsi, ret_imsi_len); |
| 398 | } |
Neels Hofmeyr | 32cc07a | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 399 | |
| 400 | if (attached_here && !msc_name.len) { |
| 401 | LOGP(DMSLOOKUP, LOGL_ERROR, "%s: attached here, but no VLR name known\n", |
| 402 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL)); |
| 403 | return false; |
| 404 | } |
| 405 | |
| 406 | if (!attached_here) { |
| 407 | /* Already logged "not attached" for both local-db and proxy attach */ |
| 408 | return false; |
| 409 | } |
| 410 | |
| 411 | LOGP(DMSLOOKUP, LOGL_INFO, "%s: attached here, at VLR %s\n", |
| 412 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), |
| 413 | osmo_ipa_name_to_str(&msc_name)); |
| 414 | *lu_age_p = lu_age; |
| 415 | *local_msc_name = msc_name; |
| 416 | return true; |
| 417 | } |
| 418 | |
| 419 | /* A remote entity is asking us whether we are providing the given service for the given subscriber. */ |
| 420 | void mslookup_server_rx(const struct osmo_mslookup_query *query, |
| 421 | struct osmo_mslookup_result *result) |
| 422 | { |
| 423 | const struct mslookup_service_host *service_host; |
| 424 | uint32_t age; |
| 425 | struct osmo_ipa_name msc_name; |
| 426 | |
| 427 | /* A request for a home HLR: answer exactly if this is the subscriber's home HLR, i.e. the IMSI is listed in the |
| 428 | * HLR database. */ |
| 429 | if (strcmp(query->service, OSMO_MSLOOKUP_SERVICE_HLR_GSUP) == 0) |
| 430 | return mslookup_server_rx_hlr_gsup(query, result); |
| 431 | |
| 432 | /* All other service types: answer when the subscriber has done a LU that is either listed in the local HLR or |
| 433 | * in the GSUP proxy database: i.e. if the subscriber has done a Location Updating at an VLR belonging to this |
| 434 | * HLR. Respond with whichever services are configured in the osmo-hlr.cfg. */ |
Neels Hofmeyr | 647c106 | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 435 | if (!subscriber_has_done_lu_here(query, &age, &msc_name, NULL, 0)) { |
Neels Hofmeyr | 32cc07a | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 436 | *result = not_found; |
| 437 | return; |
| 438 | } |
| 439 | |
| 440 | /* We've detected a LU here. The VLR where the LU happened is stored in msc_unit_name, and the LU age is stored |
| 441 | * in 'age'. Figure out the address configured for that VLR and service name. */ |
| 442 | service_host = mslookup_server_service_get(&msc_name, query->service); |
| 443 | |
| 444 | if (!service_host) { |
| 445 | /* Find such service set globally (no VLR unit name) */ |
| 446 | service_host = mslookup_server_service_get(&mslookup_server_msc_wildcard, query->service); |
| 447 | } |
| 448 | |
| 449 | if (!service_host) { |
| 450 | LOGP(DMSLOOKUP, LOGL_ERROR, |
| 451 | "%s: subscriber found, but no service %s configured, cannot service lookup request\n", |
| 452 | osmo_mslookup_result_name_c(OTC_SELECT, query, NULL), |
| 453 | osmo_quote_str_c(OTC_SELECT, query->service, -1)); |
| 454 | *result = not_found; |
| 455 | return; |
| 456 | } |
| 457 | |
| 458 | set_result(result, service_host, age); |
| 459 | } |