blob: e2e7d473755d25e38681a157554e37e1395d00d8 [file] [log] [blame]
Neels Hofmeyr62d916f2019-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 <osmocom/gsm/protocol/gsm_04_08_gprs.h>
21#include <osmocom/gsm/gsup.h>
22#include <osmocom/gsm/gsm23003.h>
23#include <osmocom/abis/ipa.h>
24#include <osmocom/gsupclient/gsup_client.h>
25#include <osmocom/hlr/logging.h>
26#include <osmocom/hlr/hlr.h>
27#include <osmocom/hlr/gsup_server.h>
28#include <osmocom/hlr/gsup_router.h>
29#include <osmocom/hlr/remote_hlr.h>
30#include <osmocom/hlr/proxy.h>
31
32static LLIST_HEAD(remote_hlrs);
33
34static void remote_hlr_err_reply(struct remote_hlr *rh, const struct osmo_gsup_message *gsup_orig,
35 enum gsm48_gmm_cause cause)
36{
37 struct osmo_gsup_message gsup_reply;
38
39 /* No need to answer if we couldn't parse an ERROR message type, only REQUESTs need an error reply. */
40 if (!OSMO_GSUP_IS_MSGT_REQUEST(gsup_orig->message_type))
41 return;
42
43 gsup_reply = (struct osmo_gsup_message){
44 .cause = cause,
45 .message_type = OSMO_GSUP_TO_MSGT_ERROR(gsup_orig->message_type),
46 .message_class = gsup_orig->message_class,
47
48 /* RP-Message-Reference is mandatory for SM Service */
49 .sm_rp_mr = gsup_orig->sm_rp_mr,
50 };
51
52 OSMO_STRLCPY_ARRAY(gsup_reply.imsi, gsup_orig->imsi);
53
54 /* For SS/USSD, it's important to keep both session state and ID IEs */
55 if (gsup_orig->session_state != OSMO_GSUP_SESSION_STATE_NONE) {
56 gsup_reply.session_state = OSMO_GSUP_SESSION_STATE_END;
57 gsup_reply.session_id = gsup_orig->session_id;
58 }
59
60 if (osmo_gsup_client_enc_send(rh->gsupc, &gsup_reply))
61 LOGP(DLGSUP, LOGL_ERROR, "Failed to send Error reply (imsi=%s)\n",
62 osmo_quote_str(gsup_orig->imsi, -1));
63}
64
65/* We are receiving a GSUP message from a remote HLR to go back to a local MSC.
66 * The local MSC shall be indicated by gsup.destination_name. */
67static int remote_hlr_rx(struct osmo_gsup_client *gsupc, struct msgb *msg)
68{
69 struct remote_hlr *rh = gsupc->data;
70 struct proxy_subscr proxy_subscr;
71 struct osmo_gsup_message gsup;
72 int rc;
73
74 rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
75 if (rc < 0) {
76 LOG_REMOTE_HLR(rh, LOGL_ERROR, "Failed to decode GSUP message: '%s' (%d) [ %s]\n",
77 get_value_string(gsm48_gmm_cause_names, -rc),
78 -rc, osmo_hexdump(msg->data, msg->len));
79 return rc;
80 }
81
82 if (!osmo_imsi_str_valid(gsup.imsi)) {
83 LOG_REMOTE_HLR_MSG(rh, &gsup, LOGL_ERROR, "Invalid IMSI\n");
84 remote_hlr_err_reply(rh, &gsup, GMM_CAUSE_INV_MAND_INFO);
85 return -GMM_CAUSE_INV_MAND_INFO;
86 }
87
88 if (proxy_subscr_get_by_imsi(&proxy_subscr, g_hlr->gs->proxy, gsup.imsi)) {
89 LOG_REMOTE_HLR_MSG(rh, &gsup, LOGL_ERROR, "No proxy entry for this IMSI\n");
90 remote_hlr_err_reply(rh, &gsup, GMM_CAUSE_NET_FAIL);
91 return -GMM_CAUSE_NET_FAIL;
92 }
93
94 rc = proxy_subscr_forward_to_vlr(g_hlr->gs->proxy, &proxy_subscr, &gsup, rh);
95 if (rc) {
96 LOG_REMOTE_HLR_MSG(rh, &gsup, LOGL_ERROR, "Failed to forward GSUP message towards VLR\n");
97 remote_hlr_err_reply(rh, &gsup, GMM_CAUSE_NET_FAIL);
98 return -GMM_CAUSE_NET_FAIL;
99 }
100 return 0;
101}
102
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100103struct remote_hlr_pending_up {
104 struct llist_head entry;
105 remote_hlr_connect_result_cb_t connect_result_cb;
106 void *data;
107};
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100108
109static bool remote_hlr_up_down(struct osmo_gsup_client *gsupc, bool up)
110{
111 struct remote_hlr *remote_hlr = gsupc->data;
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100112 struct remote_hlr_pending_up *p, *n;
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100113 if (!up) {
114 LOG_REMOTE_HLR(remote_hlr, LOGL_NOTICE, "link to remote HLR is down, removing GSUP client\n");
115 remote_hlr_destroy(remote_hlr);
116 return false;
117 }
118
119 LOG_REMOTE_HLR(remote_hlr, LOGL_NOTICE, "link up\n");
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100120 llist_for_each_entry_safe(p, n, &remote_hlr->pending_up_callbacks, entry) {
121 if (p->connect_result_cb)
122 p->connect_result_cb(&remote_hlr->addr, remote_hlr, p->data);
123 llist_del(&p->entry);
124 }
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100125 return true;
126}
127
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100128bool remote_hlr_is_up(struct remote_hlr *remote_hlr)
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100129{
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100130 return remote_hlr && remote_hlr->gsupc && remote_hlr->gsupc->is_connected;
131}
132
133struct remote_hlr *remote_hlr_get_or_connect(const struct osmo_sockaddr_str *addr, bool connect,
134 remote_hlr_connect_result_cb_t connect_result_cb, void *data)
135{
136 struct remote_hlr *rh = NULL;
137 struct remote_hlr *rh_i;
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100138 struct osmo_gsup_client_config cfg;
139
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100140 llist_for_each_entry(rh_i, &remote_hlrs, entry) {
141 if (!osmo_sockaddr_str_cmp(&rh_i->addr, addr)) {
142 rh = rh_i;
143 break;
144 }
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100145 }
146
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100147 if (rh)
148 goto add_result_cb;
149
150 if (!connect) {
151 if (connect_result_cb)
152 connect_result_cb(addr, NULL, data);
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100153 return NULL;
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100154 }
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100155
156 /* Doesn't exist yet, create a GSUP client to remote HLR. */
157 cfg = (struct osmo_gsup_client_config){
158 .ipa_dev = &g_hlr->gsup_unit_name,
159 .ip_addr = addr->ip,
160 .tcp_port = addr->port,
161 .oapc_config = NULL,
162 .read_cb = remote_hlr_rx,
163 .up_down_cb = remote_hlr_up_down,
164 .data = rh,
165 };
166 rh = talloc_zero(dgsm_ctx, struct remote_hlr);
167 OSMO_ASSERT(rh);
168 *rh = (struct remote_hlr){
169 .addr = *addr,
170 .gsupc = osmo_gsup_client_create3(rh, &cfg),
171 };
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100172 INIT_LLIST_HEAD(&rh->pending_up_callbacks);
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100173 if (!rh->gsupc) {
174 LOGP(DDGSM, LOGL_ERROR,
175 "Failed to establish connection to remote HLR " OSMO_SOCKADDR_STR_FMT "\n",
176 OSMO_SOCKADDR_STR_FMT_ARGS(addr));
177 talloc_free(rh);
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100178 if (connect_result_cb)
179 connect_result_cb(addr, NULL, data);
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100180 return NULL;
181 }
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100182
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100183 rh->gsupc->data = rh;
184 llist_add(&rh->entry, &remote_hlrs);
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100185
186add_result_cb:
187 if (connect_result_cb) {
188 if (remote_hlr_is_up(rh)) {
189 connect_result_cb(addr, rh, data);
190 } else {
191 struct remote_hlr_pending_up *p;
192 p = talloc_zero(rh, struct remote_hlr_pending_up);
193 OSMO_ASSERT(p);
194 p->connect_result_cb = connect_result_cb;
195 p->data = data;
196 llist_add_tail(&p->entry, &rh->pending_up_callbacks);
197 }
198 }
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100199 return rh;
200}
201
202void remote_hlr_destroy(struct remote_hlr *remote_hlr)
203{
204 osmo_gsup_client_destroy(remote_hlr->gsupc);
205 remote_hlr->gsupc = NULL;
206 llist_del(&remote_hlr->entry);
207 talloc_free(remote_hlr);
208}
209
210/* This function takes ownership of the msg, do not free it after passing to this function. */
211int remote_hlr_msgb_send(struct remote_hlr *remote_hlr, struct msgb *msg)
212{
213 int rc = osmo_gsup_client_send(remote_hlr->gsupc, msg);
214 if (rc) {
215 LOGP(DDGSM, LOGL_ERROR, "Failed to send GSUP message to " OSMO_SOCKADDR_STR_FMT "\n",
216 OSMO_SOCKADDR_STR_FMT_ARGS(&remote_hlr->addr));
217 }
218 return rc;
219}
220
221/* A GSUP message was received from the MS/MSC side, forward it to the remote HLR. */
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100222void remote_hlr_gsup_forward_to_remote_hlr(struct remote_hlr *remote_hlr, struct osmo_gsup_req *req,
223 struct osmo_gsup_message *modified_gsup)
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100224{
225 int rc;
226 struct msgb *msg;
227 /* To forward to a remote HLR, we need to indicate the source MSC's name in the Source Name IE to make sure the
228 * reply can be routed back. Store the sender MSC in gsup->source_name -- the remote HLR is required to return
229 * this as gsup->destination_name so that the reply gets routed to the original MSC. */
Neels Hofmeyre78241a2019-12-06 17:09:56 +0100230 struct osmo_gsup_message forward;
231 if (modified_gsup)
232 forward = *modified_gsup;
233 else
234 forward = req->gsup;
Neels Hofmeyr62d916f2019-11-20 03:35:37 +0100235
236 if (req->source_name.type != OSMO_GSUP_PEER_ID_IPA_NAME) {
237 osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Unsupported GSUP peer id type: %s",
238 osmo_gsup_peer_id_type_name(req->source_name.type));
239 return;
240 }
241 forward.source_name = req->source_name.ipa_name.val;
242 forward.source_name_len = req->source_name.ipa_name.len;
243
244 msg = osmo_gsup_msgb_alloc("GSUP proxy to remote HLR");
245 rc = osmo_gsup_encode(msg, &forward);
246 if (rc) {
247 osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "Failed to encode GSUP message for forwarding");
248 return;
249 }
250 remote_hlr_msgb_send(remote_hlr, msg);
251 osmo_gsup_req_free(req);
252}