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