blob: 685ca7cfe385cc267f7fd0c9fbd478c219f5afd7 [file] [log] [blame]
Neels Hofmeyrc4628a32018-12-07 14:47:34 +01001/* E-interface messaging over a GSUP connection */
2/*
3 * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
4 * All Rights Reserved
5 *
6 * SPDX-License-Identifier: AGPL-3.0+
7 *
8 * Author: Neels Hofmeyr
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
19 *
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include <osmocom/core/fsm.h>
25#include <osmocom/gsupclient/gsup_client.h>
26
27#include <osmocom/msc/gsm_data.h>
28#include <osmocom/msc/gsup_client_mux.h>
29#include <osmocom/msc/e_link.h>
30#include <osmocom/msc/msub.h>
31#include <osmocom/msc/msc_roles.h>
32#include <osmocom/msc/vlr.h>
33#include <osmocom/msc/ran_infra.h>
34#include <osmocom/msc/msc_a.h>
35#include <osmocom/msc/msc_a_remote.h>
36#include <osmocom/msc/msc_i.h>
37#include <osmocom/msc/msc_i_remote.h>
38#include <osmocom/msc/msc_t.h>
39#include <osmocom/msc/msc_t_remote.h>
40
41#define LOG_E_LINK(e_link, level, fmt, args...) \
42 LOGPFSML(e_link->msc_role, level, fmt, ##args)
43
44#define LOG_E_LINK_CAT(e_link, ss, level, fmt, args...) \
45 LOGPFSMSL(e_link->msc_role, ss, level, fmt, ##args)
46
47void e_link_assign(struct e_link *e, struct osmo_fsm_inst *msc_role)
48{
49 struct msc_role_common *c;
50 if (e->msc_role) {
51 c = e->msc_role->priv;
52 if (c->remote_to == e) {
53 c->remote_to = NULL;
54 msub_update_id(c->msub);
55 }
56 }
57
58 c = msc_role->priv;
59 e->msc_role = msc_role;
60 c->remote_to = e;
61
62 msub_update_id(c->msub);
63 LOG_E_LINK(e, LOGL_DEBUG, "Assigned E-link to %s\n", e_link_name(e));
64}
65
66struct e_link *e_link_alloc(struct gsup_client_mux *gcm, struct osmo_fsm_inst *msc_role,
67 const uint8_t *remote_name, size_t remote_name_len)
68{
69 struct e_link *e;
70 struct msc_role_common *c = msc_role->priv;
71
72 /* use msub as talloc parent, so we can move an e_link from msc_t to msc_i when it is established. */
73 e = talloc_zero(c->msub, struct e_link);
74 if (!e)
75 return NULL;
76
77 e->gcm = gcm;
78
79 /* Expecting all code paths to print the remote name according to remote_name_len. To be paranoid, place a nul
80 * character after the end. */
81 e->remote_name = talloc_size(e, remote_name_len + 1);
82 OSMO_ASSERT(e->remote_name);
83 memcpy(e->remote_name, remote_name, remote_name_len);
84 e->remote_name[remote_name_len] = '\0';
85 e->remote_name_len = remote_name_len;
86
87 e_link_assign(e, msc_role);
88 return e;
89}
90
91void e_link_free(struct e_link *e)
92{
93 if (!e)
94 return;
95 if (e->msc_role) {
96 struct msc_role_common *c = e->msc_role->priv;
97 if (c->remote_to == e)
98 c->remote_to = NULL;
99 }
100 talloc_free(e);
101}
102
103/* Set up IMSI, source and destination names in given gsup_msg struct. */
104int e_prep_gsup_msg(struct e_link *e, struct osmo_gsup_message *gsup_msg)
105{
106 struct msc_role_common *c;
107 struct vlr_subscr *vsub;
108 const char *local_msc_name = NULL;
109
110 if (e->gcm && e->gcm->gsup_client && e->gcm->gsup_client->ipa_dev) {
111 local_msc_name = e->gcm->gsup_client->ipa_dev->serno;
112 if (!local_msc_name)
113 local_msc_name = e->gcm->gsup_client->ipa_dev->unit_name;
114 }
115
116 if (!local_msc_name) {
117 LOG_E_LINK(e, LOGL_ERROR, "Cannot prep E-interface GSUP message: no local MSC name defined\n");
118 return -ENODEV;
119 }
120
121 c = e->msc_role->priv;
122 vsub = c->msub->vsub;
123 *gsup_msg = (struct osmo_gsup_message){
124 .message_class = OSMO_GSUP_MESSAGE_CLASS_INTER_MSC,
125 .source_name = (const uint8_t*)local_msc_name,
126 .source_name_len = strlen(local_msc_name),
127 .destination_name = (const uint8_t*)e->remote_name,
128 .destination_name_len = e->remote_name_len,
129 };
130
131 if (vsub)
132 OSMO_STRLCPY_ARRAY(gsup_msg->imsi, vsub->imsi);
133 return 0;
134}
135
136int e_tx(struct e_link *e, const struct osmo_gsup_message *gsup_msg)
137{
138 LOG_E_LINK_CAT(e, DLGSUP, LOGL_DEBUG, "Tx GSUP %s to %s\n",
139 osmo_gsup_message_type_name(gsup_msg->message_type),
140 e_link_name(e));
141 return gsup_client_mux_tx(e->gcm, gsup_msg);
142}
143
144const char *e_link_name(struct e_link *e)
145{
146 return osmo_escape_str((const char*)e->remote_name, e->remote_name_len);
147}
148
149static struct msub *msc_new_msc_t_for_handover_request(struct gsm_network *net,
150 const struct osmo_gsup_message *gsup_msg)
151{
152 struct ran_infra *ran;
153 struct msub *msub;
154 struct msc_a *msc_a;
155 struct vlr_subscr *vsub;
156
157 switch (gsup_msg->an_apdu.access_network_proto) {
158 case OSMO_GSUP_ACCESS_NETWORK_PROTOCOL_TS3G_48006:
159 ran = &msc_ran_infra[OSMO_RAT_GERAN_A];
160 break;
161 case OSMO_GSUP_ACCESS_NETWORK_PROTOCOL_TS3G_25413:
162 ran = &msc_ran_infra[OSMO_RAT_UTRAN_IU];
163 break;
164 default:
165 ran = NULL;
166 break;
167 }
168
169 if (!ran || !ran->ran_dec_l2) {
170 LOGP(DLGSUP, LOGL_ERROR, "Cannot handle AN-proto %s\n",
171 an_proto_name(gsup_msg->an_apdu.access_network_proto));
172 return NULL;
173 }
174
175 msub = msub_alloc(net);
176
177 /* To properly compose GSUP messages going back to the remote peer, make sure the incoming IMSI is set in a
178 * vlr_subscr associated with the msub. */
179 vsub = vlr_subscr_find_or_create_by_imsi(net->vlr, gsup_msg->imsi, __func__, NULL);
180 msub_set_vsub(msub, vsub);
181 vlr_subscr_put(vsub, __func__);
182
183 LOG_MSUB_CAT(msub, DLGSUP, LOGL_DEBUG, "New subscriber for incoming inter-MSC Handover Request\n");
184
185 msc_a = msc_a_remote_alloc(msub, ran, gsup_msg->source_name, gsup_msg->source_name_len);
186 if (!msc_a) {
187 osmo_fsm_inst_term(msub->fi, OSMO_FSM_TERM_REQUEST, NULL);
188 return NULL;
189 }
190
191 LOG_MSC_A_REMOTE_CAT(msc_a, DLGSUP, LOGL_DEBUG, "New subscriber for incoming inter-MSC Handover Request\n");
192 return msub;
193}
194
195static bool name_matches(const uint8_t *name, size_t len, const uint8_t *match_name, size_t match_len)
196{
197 if (!match_name)
198 return !name || !len;
199 if (len != match_len)
200 return false;
201 return memcmp(name, match_name, len) == 0;
202}
203
204static bool e_link_matches_gsup_msg_source_name(const struct e_link *e, const struct osmo_gsup_message *gsup_msg)
205{
206 return name_matches(gsup_msg->source_name, gsup_msg->source_name_len, e->remote_name, e->remote_name_len);
207}
208
209static int msc_a_i_t_gsup_rx(struct gsup_client_mux *gcm, void *data, const struct osmo_gsup_message *gsup_msg)
210{
211 struct gsm_network *net = data;
212 struct vlr_instance *vlr = net->vlr;
213 struct vlr_subscr *vsub;
214 struct msub *msub;
215 struct osmo_fsm_inst *msc_role = NULL;
216 struct e_link *e;
217 struct msc_role_common *c;
218 int i;
219
220 OSMO_ASSERT(net);
221
222 vsub = vlr_subscr_find_by_imsi(vlr, gsup_msg->imsi, __func__);
223 if (vsub)
224 LOGP(DLGSUP, LOGL_DEBUG, "Found VLR entry for IMSI %s\n", gsup_msg->imsi);
225
226 msub = msub_for_vsub(vsub);
227 if (msub)
228 LOG_MSUB_CAT(msub, DLGSUP, LOGL_DEBUG, "Found already attached subscriber for IMSI %s\n",
229 gsup_msg->imsi);
230
231 if (vsub) {
232 vlr_subscr_put(vsub, __func__);
233 vsub = NULL;
234 }
235
236 /* Only for an incoming Handover Request: create a new remote-MSC-A as proxy for the MSC-A that is sending the
237 * Handover Request */
238 if (!msub && gsup_msg->message_type == OSMO_GSUP_MSGT_E_PREPARE_HANDOVER_REQUEST) {
239 msub = msc_new_msc_t_for_handover_request(net, gsup_msg);
240 }
241
242 if (!msub) {
243 LOGP(DLGSUP, LOGL_ERROR, "%s: Cannot find subscriber for IMSI %s\n",
244 __func__, osmo_quote_str(gsup_msg->imsi, -1));
245 return -EINVAL;
246 }
247
248 LOG_MSUB_CAT(msub, DLGSUP, LOGL_DEBUG, "Rx GSUP %s\n", osmo_gsup_message_type_name(gsup_msg->message_type));
249
250 e = NULL;
251 for (i = 0; i < ARRAY_SIZE(msub->role); i++) {
252 msc_role = msub->role[i];
253 if (!msc_role) {
254 LOG_MSUB_CAT(msub, DLGSUP, LOGL_DEBUG, "No %s\n", msc_role_name(i));
255 continue;
256 }
257 c = msc_role->priv;
258 if (!c->remote_to) {
259 LOG_MSUB_CAT(msub, DLGSUP, LOGL_DEBUG, "%s has no remote\n", msc_role_name(i));
260 continue;
261 }
262 if (!e_link_matches_gsup_msg_source_name(c->remote_to, gsup_msg)) {
263 LOG_MSUB_CAT(msub, DLGSUP, LOGL_DEBUG, "%s has remote to mismatching %s\n", msc_role_name(i),
264 c->remote_to->remote_name);
265 continue;
266 }
267 /* Found a match. */
268 e = c->remote_to;
269 break;
270 }
271
272 if (!e) {
273 LOG_MSUB_CAT(msub, DLGSUP, LOGL_ERROR,
274 "There is no E link that matches: Rx GSUP %s from %s\n",
275 osmo_gsup_message_type_name(gsup_msg->message_type),
276 osmo_quote_str((const char*)gsup_msg->source_name, gsup_msg->source_name_len));
277 return -EINVAL;
278 }
279
280 LOG_MSUB_CAT(msub, DLGSUP, LOGL_DEBUG,
281 "Rx GSUP %s from %s %s\n",
282 osmo_gsup_message_type_name(gsup_msg->message_type),
283 msc_role_name(c->role),
284 e_link_name(e));
285
286 return osmo_fsm_inst_dispatch(msc_role, MSC_REMOTE_EV_RX_GSUP, (void*)gsup_msg);
287}
288
289void msc_a_i_t_gsup_init(struct gsm_network *net)
290{
291 OSMO_ASSERT(net->gcm);
292 OSMO_ASSERT(net->vlr);
293
294 net->gcm->rx_cb[OSMO_GSUP_MESSAGE_CLASS_INTER_MSC] = (struct gsup_client_mux_rx_cb){
295 .func = msc_a_i_t_gsup_rx,
296 .data = net,
297 };
298}
299
300int gsup_msg_assign_an_apdu(struct osmo_gsup_message *gsup_msg, struct an_apdu *an_apdu)
301{
302 if (!an_apdu) {
303 LOGP(DLGSUP, LOGL_ERROR, "Cannot assign NULL AN-APDU\n");
304 return -EINVAL;
305 }
306
307 gsup_msg->an_apdu = (struct osmo_gsup_an_apdu){
308 .access_network_proto = an_apdu->an_proto,
309 };
310
311 if (an_apdu->msg) {
312 gsup_msg->an_apdu.data = msgb_l2(an_apdu->msg);
313 gsup_msg->an_apdu.data_len = msgb_l2len(an_apdu->msg);
314 if (!gsup_msg->an_apdu.data || !gsup_msg->an_apdu.data_len) {
315 LOGP(DLGSUP, LOGL_ERROR, "Cannot assign AN-APDU without msg->l2 to GSUP message: %s\n",
316 msgb_hexdump(an_apdu->msg));
317 return -EINVAL;
318 }
319 }
320
321 /* We are composing a struct osmo_gsup_msg from the osmo-msc internal struct an_apdu. The an_apdu may contain
322 * additional info in form of a partly filled an_apdu->e_info. Make sure that data ends up in the resulting full
323 * osmo_gsup_message. */
324 if (an_apdu->e_info) {
325 const struct osmo_gsup_message *s = an_apdu->e_info;
326
327 gsup_msg->msisdn_enc = s->msisdn_enc;
328 gsup_msg->msisdn_enc_len = s->msisdn_enc_len;
329
330 if (s->cause_rr_set) {
331 gsup_msg->cause_rr = s->cause_rr;
332 gsup_msg->cause_rr_set = true;
333 }
334 if (s->cause_bssap_set) {
335 gsup_msg->cause_bssap = s->cause_bssap;
336 gsup_msg->cause_bssap_set = true;
337 }
338 if (s->cause_sm)
339 gsup_msg->cause_sm = s->cause_sm;
340 }
341 return 0;
342}
343
344/* Allocate a new msgb to contain the gsup_msg->an_apdu's data as l2h.
345 * The msgb will have sufficient headroom to be passed down a RAN peer's SCCP user SAP. */
346struct msgb *gsup_msg_to_msgb(const struct osmo_gsup_message *gsup_msg)
347{
348 struct msgb *pdu;
349 const uint8_t *pdu_data = gsup_msg->an_apdu.data;
350 uint8_t pdu_len = gsup_msg->an_apdu.data_len;
351
352 if (!pdu_data || !pdu_len)
353 return NULL;
354
355 /* Strictly speaking this is not limited to BSSMAP, but why not just use those sizes. */
356 pdu = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "AN-APDU from gsup_msg");
357
358 pdu->l2h = msgb_put(pdu, pdu_len);
359 memcpy(pdu->l2h, pdu_data, pdu_len);
360 return pdu;
361}
362
363/* Compose a struct an_apdu from the data found in gsup_msg. gsup_msg_to_msgb() is used to wrap the data in a static
364 * msgb, so the returned an_apdu->msg must be freed if not NULL. */
365void gsup_msg_to_an_apdu(struct an_apdu *an_apdu, const struct osmo_gsup_message *gsup_msg)
366{
367 *an_apdu = (struct an_apdu){
368 .an_proto = gsup_msg->an_apdu.access_network_proto,
369 .msg = gsup_msg_to_msgb(gsup_msg),
370 .e_info = gsup_msg,
371 };
372}