blob: e909d5acb4c7e5582db1d781598a172e112c9f1a [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
Alexander Couzens268a33e2020-01-12 00:48:07 +010032#include <osmocom/hlr/hlr.h>
Neels Hofmeyr76328bd2019-11-20 03:35:37 +010033#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. */
55struct proxy_subscr_listentry {
56 struct llist_head entry;
57 timestamp_t last_update;
58 struct proxy_subscr data;
59};
60
61struct 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. */
70static 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
81static 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 Couzens268a33e2020-01-12 00:48:07 +010084 /* 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 Hofmeyr76328bd2019-11-20 03:35:37 +010097 return;
98 }
99
100 remote_hlr_gsup_forward_to_remote_hlr(remote_hlr, req, NULL);
101}
102
103static 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. */
118static 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
134static 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
141static 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
148static 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
160static 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
172int 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
181int 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
190int 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
203int _proxy_subscr_del(struct proxy_subscr_listentry *e)
204{
205 llist_del(&e->entry);
Neels Hofmeyr8804a232023-01-15 22:45:20 +0100206 talloc_free(e);
Neels Hofmeyr76328bd2019-11-20 03:35:37 +0100207 return 0;
208}
209
210int 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. */
221static 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
240void proxy_set_gc_period(struct proxy *proxy, uint32_t gc_period)
241{
242 proxy->gc_period = gc_period;
243 proxy_cleanup(proxy);
244}
245
246void 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
264void 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. */
272static 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. */
332static 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
427static 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 */
445void 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
481int 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
527int 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}