Neels Hofmeyr | 76328bd | 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 <talloc.h> |
| 22 | #include <errno.h> |
| 23 | #include <inttypes.h> |
| 24 | |
| 25 | #include <osmocom/core/timer.h> |
| 26 | #include <osmocom/gsm/gsup.h> |
| 27 | #include <osmocom/gsm/gsm23003.h> |
| 28 | #include <osmocom/gsm/gsm48_ie.h> |
| 29 | #include <osmocom/gsupclient/gsup_client.h> |
| 30 | #include <osmocom/gsupclient/gsup_req.h> |
| 31 | |
Alexander Couzens | 268a33e | 2020-01-12 00:48:07 +0100 | [diff] [blame] | 32 | #include <osmocom/hlr/hlr.h> |
Neels Hofmeyr | 76328bd | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 33 | #include <osmocom/hlr/logging.h> |
| 34 | #include <osmocom/hlr/proxy.h> |
| 35 | #include <osmocom/hlr/remote_hlr.h> |
| 36 | #include <osmocom/hlr/gsup_server.h> |
| 37 | #include <osmocom/hlr/gsup_router.h> |
| 38 | |
| 39 | #define LOG_PROXY_SUBSCR(proxy_subscr, level, fmt, args...) \ |
| 40 | LOGP(DDGSM, level, "(Proxy IMSI-%s MSISDN-%s HLR-" OSMO_SOCKADDR_STR_FMT ") " fmt, \ |
| 41 | ((proxy_subscr) && *(proxy_subscr)->imsi)? (proxy_subscr)->imsi : "?", \ |
| 42 | ((proxy_subscr) && *(proxy_subscr)->msisdn)? (proxy_subscr)->msisdn : "?", \ |
| 43 | OSMO_SOCKADDR_STR_FMT_ARGS((proxy_subscr)? &(proxy_subscr)->remote_hlr_addr : NULL), \ |
| 44 | ##args) |
| 45 | |
| 46 | #define LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup_msg, level, fmt, args...) \ |
| 47 | LOG_PROXY_SUBSCR(proxy_subscr, level, "%s: " fmt, \ |
| 48 | (gsup_msg) ? osmo_gsup_message_type_name((gsup_msg)->message_type) : "NULL", \ |
| 49 | ##args) |
| 50 | |
| 51 | /* The proxy subscriber database. |
| 52 | * Why have a separate struct to add an llist_head entry? |
| 53 | * This is to keep the option open to store the proxy data in the database instead, without any visible effect outside |
| 54 | * of proxy.c. */ |
| 55 | struct proxy_subscr_listentry { |
| 56 | struct llist_head entry; |
| 57 | timestamp_t last_update; |
| 58 | struct proxy_subscr data; |
| 59 | }; |
| 60 | |
| 61 | struct proxy_pending_gsup_req { |
| 62 | struct llist_head entry; |
| 63 | struct osmo_gsup_req *req; |
| 64 | timestamp_t received_at; |
| 65 | }; |
| 66 | |
| 67 | /* Defer a GSUP message until we know a remote HLR to proxy to. |
| 68 | * Where to send this GSUP message is indicated by its IMSI: as soon as an MS lookup has yielded the IMSI's home HLR, |
| 69 | * that's where the message should go. */ |
| 70 | static void proxy_deferred_gsup_req_add(struct proxy *proxy, struct osmo_gsup_req *req) |
| 71 | { |
| 72 | struct proxy_pending_gsup_req *m; |
| 73 | |
| 74 | m = talloc_zero(proxy, struct proxy_pending_gsup_req); |
| 75 | OSMO_ASSERT(m); |
| 76 | m->req = req; |
| 77 | timestamp_update(&m->received_at); |
| 78 | llist_add_tail(&m->entry, &proxy->pending_gsup_reqs); |
| 79 | } |
| 80 | |
| 81 | static void proxy_pending_req_remote_hlr_connect_result(struct osmo_gsup_req *req, struct remote_hlr *remote_hlr) |
| 82 | { |
| 83 | if (!remote_hlr || !remote_hlr_is_up(remote_hlr)) { |
Alexander Couzens | 268a33e | 2020-01-12 00:48:07 +0100 | [diff] [blame] | 84 | /* Do not respond with an error to a CHECK_IMEI_REQUEST as osmo-msc will send a LU Reject Cause #6 |
| 85 | * Just respond ACK and deal with the IMSI check that comes next. */ |
| 86 | if (req->gsup.message_type == OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST) { |
| 87 | /* Accept all IMEIs */ |
| 88 | struct osmo_gsup_message gsup_reply = (struct osmo_gsup_message){ |
| 89 | .message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT, |
| 90 | .imei_result = OSMO_GSUP_IMEI_RESULT_ACK, |
| 91 | }; |
| 92 | osmo_gsup_req_respond(req, &gsup_reply, false, true); |
| 93 | return; |
| 94 | } |
| 95 | osmo_gsup_req_respond_err(req, g_hlr->no_proxy_reject_cause, |
| 96 | "Proxy: Failed to connect to home HLR"); |
Neels Hofmeyr | 76328bd | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 97 | return; |
| 98 | } |
| 99 | |
| 100 | remote_hlr_gsup_forward_to_remote_hlr(remote_hlr, req, NULL); |
| 101 | } |
| 102 | |
| 103 | static bool proxy_deferred_gsup_req_waiting(struct proxy *proxy, const char *imsi) |
| 104 | { |
| 105 | struct proxy_pending_gsup_req *p; |
| 106 | OSMO_ASSERT(imsi); |
| 107 | |
| 108 | llist_for_each_entry(p, &proxy->pending_gsup_reqs, entry) { |
| 109 | if (strcmp(p->req->gsup.imsi, imsi)) |
| 110 | continue; |
| 111 | return true; |
| 112 | } |
| 113 | return false; |
| 114 | } |
| 115 | |
| 116 | /* Result of looking for remote HLR. If it failed, pass remote_hlr as NULL. On failure, the remote_hlr may be passed |
| 117 | * NULL. */ |
| 118 | static void proxy_deferred_gsup_req_pop(struct proxy *proxy, const char *imsi, struct remote_hlr *remote_hlr) |
| 119 | { |
| 120 | struct proxy_pending_gsup_req *p, *n; |
| 121 | OSMO_ASSERT(imsi); |
| 122 | |
| 123 | llist_for_each_entry_safe(p, n, &proxy->pending_gsup_reqs, entry) { |
| 124 | if (strcmp(p->req->gsup.imsi, imsi)) |
| 125 | continue; |
| 126 | |
| 127 | proxy_pending_req_remote_hlr_connect_result(p->req, remote_hlr); |
| 128 | p->req = NULL; |
| 129 | llist_del(&p->entry); |
| 130 | talloc_free(p); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | static bool proxy_subscr_matches_imsi(const struct proxy_subscr *proxy_subscr, const char *imsi) |
| 135 | { |
| 136 | if (!proxy_subscr || !imsi) |
| 137 | return false; |
| 138 | return strcmp(proxy_subscr->imsi, imsi) == 0; |
| 139 | } |
| 140 | |
| 141 | static bool proxy_subscr_matches_msisdn(const struct proxy_subscr *proxy_subscr, const char *msisdn) |
| 142 | { |
| 143 | if (!proxy_subscr || !msisdn) |
| 144 | return false; |
| 145 | return strcmp(proxy_subscr->msisdn, msisdn) == 0; |
| 146 | } |
| 147 | |
| 148 | static struct proxy_subscr_listentry *_proxy_get_by_imsi(struct proxy *proxy, const char *imsi) |
| 149 | { |
| 150 | struct proxy_subscr_listentry *e; |
| 151 | if (!proxy) |
| 152 | return NULL; |
| 153 | llist_for_each_entry(e, &proxy->subscr_list, entry) { |
| 154 | if (proxy_subscr_matches_imsi(&e->data, imsi)) |
| 155 | return e; |
| 156 | } |
| 157 | return NULL; |
| 158 | } |
| 159 | |
| 160 | static struct proxy_subscr_listentry *_proxy_get_by_msisdn(struct proxy *proxy, const char *msisdn) |
| 161 | { |
| 162 | struct proxy_subscr_listentry *e; |
| 163 | if (!proxy) |
| 164 | return NULL; |
| 165 | llist_for_each_entry(e, &proxy->subscr_list, entry) { |
| 166 | if (proxy_subscr_matches_msisdn(&e->data, msisdn)) |
| 167 | return e; |
| 168 | } |
| 169 | return NULL; |
| 170 | } |
| 171 | |
| 172 | int proxy_subscr_get_by_imsi(struct proxy_subscr *dst, struct proxy *proxy, const char *imsi) |
| 173 | { |
| 174 | struct proxy_subscr_listentry *e = _proxy_get_by_imsi(proxy, imsi); |
| 175 | if (!e) |
| 176 | return -ENOENT; |
| 177 | *dst = e->data; |
| 178 | return 0; |
| 179 | } |
| 180 | |
| 181 | int proxy_subscr_get_by_msisdn(struct proxy_subscr *dst, struct proxy *proxy, const char *msisdn) |
| 182 | { |
| 183 | struct proxy_subscr_listentry *e = _proxy_get_by_msisdn(proxy, msisdn); |
| 184 | if (!e) |
| 185 | return -ENOENT; |
| 186 | *dst = e->data; |
| 187 | return 0; |
| 188 | } |
| 189 | |
| 190 | int proxy_subscr_create_or_update(struct proxy *proxy, const struct proxy_subscr *proxy_subscr) |
| 191 | { |
| 192 | struct proxy_subscr_listentry *e = _proxy_get_by_imsi(proxy, proxy_subscr->imsi); |
| 193 | if (!e) { |
| 194 | /* Does not exist yet */ |
| 195 | e = talloc_zero(proxy, struct proxy_subscr_listentry); |
| 196 | llist_add(&e->entry, &proxy->subscr_list); |
| 197 | } |
| 198 | e->data = *proxy_subscr; |
| 199 | timestamp_update(&e->last_update); |
| 200 | return 0; |
| 201 | } |
| 202 | |
| 203 | int _proxy_subscr_del(struct proxy_subscr_listentry *e) |
| 204 | { |
| 205 | llist_del(&e->entry); |
Neels Hofmeyr | 8804a23 | 2023-01-15 22:45:20 +0100 | [diff] [blame] | 206 | talloc_free(e); |
Neels Hofmeyr | 76328bd | 2019-11-20 03:35:37 +0100 | [diff] [blame] | 207 | return 0; |
| 208 | } |
| 209 | |
| 210 | int proxy_subscr_del(struct proxy *proxy, const char *imsi) |
| 211 | { |
| 212 | struct proxy_subscr_listentry *e; |
| 213 | proxy_deferred_gsup_req_pop(proxy, imsi, NULL); |
| 214 | e = _proxy_get_by_imsi(proxy, imsi); |
| 215 | if (!e) |
| 216 | return -ENOENT; |
| 217 | return _proxy_subscr_del(e); |
| 218 | } |
| 219 | |
| 220 | /* Discard stale proxy entries. */ |
| 221 | static void proxy_cleanup(void *proxy_v) |
| 222 | { |
| 223 | struct proxy *proxy = proxy_v; |
| 224 | struct proxy_subscr_listentry *e, *n; |
| 225 | uint32_t age; |
| 226 | llist_for_each_entry_safe(e, n, &proxy->subscr_list, entry) { |
| 227 | if (!timestamp_age(&e->last_update, &age)) |
| 228 | LOGP(DDGSM, LOGL_ERROR, "Invalid timestamp, deleting proxy entry\n"); |
| 229 | else if (age <= proxy->fresh_time) |
| 230 | continue; |
| 231 | LOG_PROXY_SUBSCR(&e->data, LOGL_INFO, "proxy entry timed out, deleting\n"); |
| 232 | _proxy_subscr_del(e); |
| 233 | } |
| 234 | if (proxy->gc_period) |
| 235 | osmo_timer_schedule(&proxy->gc_timer, proxy->gc_period, 0); |
| 236 | else |
| 237 | LOGP(DDGSM, LOGL_NOTICE, "Proxy cleanup is switched off (gc_period == 0)\n"); |
| 238 | } |
| 239 | |
| 240 | void proxy_set_gc_period(struct proxy *proxy, uint32_t gc_period) |
| 241 | { |
| 242 | proxy->gc_period = gc_period; |
| 243 | proxy_cleanup(proxy); |
| 244 | } |
| 245 | |
| 246 | void proxy_init(struct osmo_gsup_server *gsup_server_to_vlr) |
| 247 | { |
| 248 | OSMO_ASSERT(!gsup_server_to_vlr->proxy); |
| 249 | struct proxy *proxy = talloc_zero(gsup_server_to_vlr, struct proxy); |
| 250 | *proxy = (struct proxy){ |
| 251 | .gsup_server_to_vlr = gsup_server_to_vlr, |
| 252 | .fresh_time = 60*60, |
| 253 | .gc_period = 60, |
| 254 | }; |
| 255 | INIT_LLIST_HEAD(&proxy->subscr_list); |
| 256 | INIT_LLIST_HEAD(&proxy->pending_gsup_reqs); |
| 257 | |
| 258 | osmo_timer_setup(&proxy->gc_timer, proxy_cleanup, proxy); |
| 259 | /* Invoke to trigger the first timer schedule */ |
| 260 | proxy_set_gc_period(proxy, proxy->gc_period); |
| 261 | gsup_server_to_vlr->proxy = proxy; |
| 262 | } |
| 263 | |
| 264 | void proxy_del(struct proxy *proxy) |
| 265 | { |
| 266 | osmo_timer_del(&proxy->gc_timer); |
| 267 | talloc_free(proxy); |
| 268 | } |
| 269 | |
| 270 | /* All GSUP messages sent to the remote HLR pass through this function, to modify the subscriber state or disallow |
| 271 | * sending the message. Return 0 to allow sending the message. */ |
| 272 | static int proxy_acknowledge_gsup_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, |
| 273 | const struct osmo_gsup_req *req) |
| 274 | { |
| 275 | struct proxy_subscr proxy_subscr_new = *proxy_subscr; |
| 276 | bool ps; |
| 277 | bool cs; |
| 278 | int rc; |
| 279 | |
| 280 | if (req->source_name.type != OSMO_CNI_PEER_ID_IPA_NAME) { |
| 281 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_ERROR, |
| 282 | "Unsupported GSUP peer id type: %s\n", |
| 283 | osmo_cni_peer_id_type_name(req->source_name.type)); |
| 284 | return -ENOTSUP; |
| 285 | } |
| 286 | |
| 287 | switch (req->gsup.message_type) { |
| 288 | |
| 289 | case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST: |
| 290 | /* Store the CS and PS VLR name in vlr_name_preliminary to later update the right {cs,ps} LU timestamp |
| 291 | * when receiving an OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT. Store in vlr_name_preliminary so that in |
| 292 | * case the LU fails, we keep the vlr_name intact. */ |
| 293 | switch (req->gsup.cn_domain) { |
| 294 | case OSMO_GSUP_CN_DOMAIN_CS: |
| 295 | proxy_subscr_new.cs.vlr_name_preliminary = req->source_name.ipa_name; |
| 296 | break; |
| 297 | case OSMO_GSUP_CN_DOMAIN_PS: |
| 298 | proxy_subscr_new.ps.vlr_name_preliminary = req->source_name.ipa_name; |
| 299 | break; |
| 300 | default: |
| 301 | break; |
| 302 | } |
| 303 | |
| 304 | ps = cs = false; |
| 305 | if (osmo_ipa_name_cmp(&proxy_subscr_new.cs.vlr_name_preliminary, &proxy_subscr->cs.vlr_name_preliminary)) |
| 306 | cs = true; |
| 307 | if (osmo_ipa_name_cmp(&proxy_subscr_new.ps.vlr_name_preliminary, &proxy_subscr->ps.vlr_name_preliminary)) |
| 308 | ps = true; |
| 309 | |
| 310 | if (!(cs || ps)) { |
| 311 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_DEBUG, "VLR names remain unchanged\n"); |
| 312 | break; |
| 313 | } |
| 314 | |
| 315 | rc = proxy_subscr_create_or_update(proxy, &proxy_subscr_new); |
| 316 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, rc ? LOGL_ERROR : LOGL_INFO, |
| 317 | "%s: preliminary VLR name for%s%s to %s\n", |
| 318 | rc ? "failed to update" : "updated", |
| 319 | cs ? " CS" : "", ps ? " PS" : "", |
| 320 | osmo_cni_peer_id_to_str(&req->source_name)); |
| 321 | break; |
| 322 | /* TODO: delete proxy entry in case of a Purge Request? */ |
| 323 | default: |
| 324 | break; |
| 325 | } |
| 326 | return 0; |
| 327 | } |
| 328 | |
| 329 | /* All GSUP messages received from the remote HLR to be sent to a local MSC pass through this function, to modify the |
| 330 | * subscriber state or disallow sending the message. Return 0 to allow sending the message. |
| 331 | * The local MSC shall be indicated by gsup.destination_name. */ |
| 332 | static int proxy_acknowledge_gsup_from_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, |
| 333 | const struct osmo_gsup_message *gsup, |
| 334 | struct remote_hlr *from_remote_hlr, |
| 335 | const struct osmo_ipa_name *destination, |
| 336 | const struct osmo_ipa_name *via_peer) |
| 337 | { |
| 338 | struct proxy_subscr proxy_subscr_new = *proxy_subscr; |
| 339 | bool ps; |
| 340 | bool cs; |
| 341 | bool vlr_name_changed_cs = false; |
| 342 | bool vlr_name_changed_ps = false; |
| 343 | int rc; |
| 344 | struct osmo_ipa_name via_proxy = {}; |
| 345 | if (osmo_ipa_name_cmp(via_peer, destination)) |
| 346 | via_proxy = *via_peer; |
| 347 | |
| 348 | switch (gsup->message_type) { |
| 349 | case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST: |
| 350 | /* Remember the MSISDN of the subscriber. This does not need to be a preliminary record, because when |
| 351 | * the HLR tells us about subscriber data, it is definitive info and there is no ambiguity (like there |
| 352 | * would be with failed LU attempts from various sources). */ |
| 353 | if (!gsup->msisdn_enc_len) |
| 354 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_DEBUG, "No MSISDN in this Insert Data Request\n"); |
| 355 | else if (gsm48_decode_bcd_number2(proxy_subscr_new.msisdn, sizeof(proxy_subscr_new.msisdn), |
| 356 | gsup->msisdn_enc, gsup->msisdn_enc_len, 0)) |
| 357 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, "Failed to decode MSISDN\n"); |
| 358 | else if (!osmo_msisdn_str_valid(proxy_subscr_new.msisdn)) |
| 359 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, "invalid MSISDN: %s\n", |
| 360 | osmo_quote_str_c(OTC_SELECT, proxy_subscr_new.msisdn, -1)); |
| 361 | else if (!strcmp(proxy_subscr->msisdn, proxy_subscr_new.msisdn)) |
| 362 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_DEBUG, "already have MSISDN = %s\n", |
| 363 | proxy_subscr_new.msisdn); |
| 364 | else if (proxy_subscr_create_or_update(proxy, &proxy_subscr_new)) |
| 365 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, "failed to update MSISDN to %s\n", |
| 366 | proxy_subscr_new.msisdn); |
| 367 | else |
| 368 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_INFO, "stored MSISDN=%s\n", |
| 369 | proxy_subscr_new.msisdn); |
| 370 | break; |
| 371 | |
| 372 | case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: |
| 373 | /* Update the Location Updating timestamp */ |
| 374 | cs = ps = false; |
| 375 | if (!osmo_ipa_name_cmp(destination, &proxy_subscr->cs.vlr_name_preliminary)) { |
| 376 | timestamp_update(&proxy_subscr_new.cs.last_lu); |
| 377 | proxy_subscr_new.cs.vlr_name_preliminary = (struct osmo_ipa_name){}; |
| 378 | vlr_name_changed_cs = |
| 379 | osmo_ipa_name_cmp(&proxy_subscr->cs.vlr_name, destination) |
| 380 | || osmo_ipa_name_cmp(&proxy_subscr->cs.vlr_via_proxy, &via_proxy); |
| 381 | proxy_subscr_new.cs.vlr_name = *destination; |
| 382 | proxy_subscr_new.cs.vlr_via_proxy = via_proxy; |
| 383 | cs = true; |
| 384 | } |
| 385 | if (!osmo_ipa_name_cmp(destination, &proxy_subscr->ps.vlr_name_preliminary)) { |
| 386 | timestamp_update(&proxy_subscr_new.ps.last_lu); |
| 387 | proxy_subscr_new.ps.vlr_name_preliminary = (struct osmo_ipa_name){}; |
| 388 | proxy_subscr_new.ps.vlr_name = *destination; |
| 389 | vlr_name_changed_ps = |
| 390 | osmo_ipa_name_cmp(&proxy_subscr->ps.vlr_name, destination) |
| 391 | || osmo_ipa_name_cmp(&proxy_subscr->ps.vlr_via_proxy, &via_proxy); |
| 392 | proxy_subscr_new.ps.vlr_via_proxy = via_proxy; |
| 393 | ps = true; |
| 394 | } |
| 395 | if (!(cs || ps)) { |
| 396 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, |
| 397 | "destination is neither CS nor PS VLR: %s\n", |
| 398 | osmo_ipa_name_to_str(destination)); |
| 399 | return GMM_CAUSE_PROTO_ERR_UNSPEC; |
| 400 | } |
| 401 | rc = proxy_subscr_create_or_update(proxy, &proxy_subscr_new); |
| 402 | |
| 403 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, rc ? LOGL_ERROR : LOGL_INFO, |
| 404 | "%s LU: timestamp for%s%s%s%s%s%s%s%s%s%s\n", |
| 405 | rc ? "failed to update" : "updated", |
| 406 | cs ? " CS" : "", ps ? " PS" : "", |
| 407 | vlr_name_changed_cs? ", CS VLR=" : "", |
| 408 | vlr_name_changed_cs? osmo_ipa_name_to_str(&proxy_subscr_new.cs.vlr_name) : "", |
| 409 | proxy_subscr_new.cs.vlr_via_proxy.len ? " via proxy " : "", |
| 410 | proxy_subscr_new.cs.vlr_via_proxy.len ? |
| 411 | osmo_ipa_name_to_str(&proxy_subscr_new.cs.vlr_via_proxy) : "", |
| 412 | vlr_name_changed_ps? ", PS VLR=" : "", |
| 413 | vlr_name_changed_ps? osmo_ipa_name_to_str(&proxy_subscr_new.ps.vlr_name) : "", |
| 414 | proxy_subscr_new.ps.vlr_via_proxy.len ? " via proxy " : "", |
| 415 | proxy_subscr_new.ps.vlr_via_proxy.len ? |
| 416 | osmo_ipa_name_to_str(&proxy_subscr_new.ps.vlr_via_proxy) : "" |
| 417 | ); |
| 418 | break; |
| 419 | |
| 420 | default: |
| 421 | break; |
| 422 | } |
| 423 | |
| 424 | return 0; |
| 425 | } |
| 426 | |
| 427 | static void proxy_remote_hlr_connect_result_cb(const struct osmo_sockaddr_str *addr, struct remote_hlr *remote_hlr, |
| 428 | void *data) |
| 429 | { |
| 430 | struct proxy *proxy = data; |
| 431 | struct proxy_subscr_listentry *e; |
| 432 | if (!proxy) |
| 433 | return; |
| 434 | llist_for_each_entry(e, &proxy->subscr_list, entry) { |
| 435 | if (!osmo_sockaddr_str_cmp(addr, &e->data.remote_hlr_addr)) { |
| 436 | proxy_deferred_gsup_req_pop(proxy, e->data.imsi, remote_hlr); |
| 437 | } |
| 438 | } |
| 439 | } |
| 440 | |
| 441 | /* Store the remote HLR's GSUP address for this proxy subscriber. |
| 442 | * This can be set before the remote_hlr is connected, or after. |
| 443 | * And, this can be set before the gsup_req has been queued for this HLR, or after. |
| 444 | */ |
| 445 | void proxy_subscr_remote_hlr_resolved(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, |
| 446 | const struct osmo_sockaddr_str *remote_hlr_addr) |
| 447 | { |
| 448 | struct proxy_subscr proxy_subscr_new; |
| 449 | |
| 450 | if (osmo_sockaddr_str_is_nonzero(&proxy_subscr->remote_hlr_addr)) { |
| 451 | if (!osmo_sockaddr_str_cmp(remote_hlr_addr, &proxy_subscr->remote_hlr_addr)) { |
| 452 | /* Already have this remote address */ |
| 453 | return; |
| 454 | } else { |
| 455 | LOG_PROXY_SUBSCR(proxy_subscr, LOGL_NOTICE, |
| 456 | "Remote HLR address changes to " OSMO_SOCKADDR_STR_FMT "\n", |
| 457 | OSMO_SOCKADDR_STR_FMT_ARGS(remote_hlr_addr)); |
| 458 | } |
| 459 | } |
| 460 | |
| 461 | /* Store the address. Make a copy to modify. */ |
| 462 | proxy_subscr_new = *proxy_subscr; |
| 463 | proxy_subscr = &proxy_subscr_new; |
| 464 | proxy_subscr_new.remote_hlr_addr = *remote_hlr_addr; |
| 465 | |
| 466 | if (proxy_subscr_create_or_update(proxy, proxy_subscr)) { |
| 467 | LOG_PROXY_SUBSCR(proxy_subscr, LOGL_ERROR, "Failed to store proxy entry for remote HLR\n"); |
| 468 | /* If no remote HLR is known for the IMSI, the proxy entry is pointless. */ |
| 469 | proxy_subscr_del(proxy, proxy_subscr->imsi); |
| 470 | return; |
| 471 | } |
| 472 | LOG_PROXY_SUBSCR(proxy_subscr, LOGL_DEBUG, "Remote HLR resolved, stored address\n"); |
| 473 | |
| 474 | /* If any messages for this HLR are already spooled, connect now. Otherwise wait for |
| 475 | * proxy_subscr_forward_to_remote_hlr() to connect then. */ |
| 476 | if (proxy_deferred_gsup_req_waiting(proxy, proxy_subscr->imsi)) |
| 477 | remote_hlr_get_or_connect(&proxy_subscr->remote_hlr_addr, true, |
| 478 | proxy_remote_hlr_connect_result_cb, proxy); |
| 479 | } |
| 480 | |
| 481 | int proxy_subscr_forward_to_remote_hlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, struct osmo_gsup_req *req) |
| 482 | { |
| 483 | struct remote_hlr *remote_hlr; |
| 484 | int rc; |
| 485 | |
| 486 | rc = proxy_acknowledge_gsup_to_remote_hlr(proxy, proxy_subscr, req); |
| 487 | if (rc) { |
| 488 | osmo_gsup_req_respond_err(req, GMM_CAUSE_PROTO_ERR_UNSPEC, "Proxy does not allow this message"); |
| 489 | return rc; |
| 490 | } |
| 491 | |
| 492 | if (!osmo_sockaddr_str_is_nonzero(&proxy_subscr->remote_hlr_addr)) { |
| 493 | /* We don't know the remote target yet. Still waiting for an MS lookup response, which will end up |
| 494 | * calling proxy_subscr_remote_hlr_resolved(). See dgsm.c. */ |
| 495 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_DEBUG, "deferring until remote HLR is known\n"); |
| 496 | proxy_deferred_gsup_req_add(proxy, req); |
| 497 | return 0; |
| 498 | } |
| 499 | |
| 500 | if (!osmo_cni_peer_id_is_empty(&req->via_proxy)) { |
| 501 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_INFO, "VLR->HLR: forwarding from %s via proxy %s\n", |
| 502 | osmo_cni_peer_id_to_str(&req->source_name), |
| 503 | osmo_cni_peer_id_to_str(&req->via_proxy)); |
| 504 | } else { |
| 505 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, &req->gsup, LOGL_INFO, "VLR->HLR: forwarding from %s\n", |
| 506 | osmo_cni_peer_id_to_str(&req->source_name)); |
| 507 | } |
| 508 | |
| 509 | /* We could always store in the defer queue and empty the queue if the connection is already up. |
| 510 | * Slight optimisation: if the remote_hlr is already up and running, skip the defer queue. |
| 511 | * First ask for an existing remote_hlr. */ |
| 512 | remote_hlr = remote_hlr_get_or_connect(&proxy_subscr->remote_hlr_addr, false, NULL, NULL); |
| 513 | if (remote_hlr && remote_hlr_is_up(remote_hlr)) { |
| 514 | proxy_pending_req_remote_hlr_connect_result(req, remote_hlr); |
| 515 | return 0; |
| 516 | } |
| 517 | |
| 518 | /* Not existing or not up. Defer req and ask to be notified when it is up. |
| 519 | * If the remote_hlr exists but is not connected yet, there should actually already be a pending |
| 520 | * proxy_remote_hlr_connect_result_cb queued, but it doesn't hurt to do that more often. */ |
| 521 | proxy_deferred_gsup_req_add(proxy, req); |
| 522 | remote_hlr_get_or_connect(&proxy_subscr->remote_hlr_addr, true, |
| 523 | proxy_remote_hlr_connect_result_cb, proxy); |
| 524 | return 0; |
| 525 | } |
| 526 | |
| 527 | int proxy_subscr_forward_to_vlr(struct proxy *proxy, const struct proxy_subscr *proxy_subscr, |
| 528 | const struct osmo_gsup_message *gsup, struct remote_hlr *from_remote_hlr) |
| 529 | { |
| 530 | struct osmo_ipa_name destination; |
| 531 | struct osmo_gsup_conn *vlr_conn; |
| 532 | struct msgb *msg; |
| 533 | |
| 534 | if (osmo_ipa_name_set(&destination, gsup->destination_name, gsup->destination_name_len)) { |
| 535 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, |
| 536 | "no valid Destination Name IE, cannot route to VLR.\n"); |
| 537 | return GMM_CAUSE_INV_MAND_INFO; |
| 538 | } |
| 539 | |
| 540 | /* Route to MSC/SGSN that we're proxying for */ |
| 541 | vlr_conn = gsup_route_find_by_ipa_name(proxy->gsup_server_to_vlr, &destination); |
| 542 | if (!vlr_conn) { |
| 543 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, |
| 544 | "Destination VLR unreachable: %s\n", osmo_ipa_name_to_str(&destination)); |
| 545 | return GMM_CAUSE_MSC_TEMP_NOTREACH; |
| 546 | } |
| 547 | |
| 548 | if (proxy_acknowledge_gsup_from_remote_hlr(proxy, proxy_subscr, gsup, from_remote_hlr, &destination, |
| 549 | &vlr_conn->peer_name)) { |
| 550 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, |
| 551 | "Proxy does not allow forwarding this message\n"); |
| 552 | return GMM_CAUSE_PROTO_ERR_UNSPEC; |
| 553 | } |
| 554 | |
| 555 | msg = osmo_gsup_msgb_alloc("GSUP proxy to VLR"); |
| 556 | if (osmo_gsup_encode(msg, gsup)) { |
| 557 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_ERROR, |
| 558 | "Failed to re-encode GSUP message, cannot forward\n"); |
| 559 | return GMM_CAUSE_INV_MAND_INFO; |
| 560 | } |
| 561 | |
| 562 | LOG_PROXY_SUBSCR_MSG(proxy_subscr, gsup, LOGL_INFO, "VLR<-HLR: forwarding to %s%s%s\n", |
| 563 | osmo_ipa_name_to_str(&destination), |
| 564 | osmo_ipa_name_cmp(&destination, &vlr_conn->peer_name) ? " via " : "", |
| 565 | osmo_ipa_name_cmp(&destination, &vlr_conn->peer_name) ? |
| 566 | osmo_ipa_name_to_str(&vlr_conn->peer_name) : ""); |
| 567 | return osmo_gsup_conn_send(vlr_conn, msg); |
| 568 | } |