Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 1 | /* Manage all MSC roles of a connected subscriber (MSC-A, MSC-I, MSC-T) */ |
| 2 | /* |
Vadim Yanitskiy | 999a593 | 2023-05-18 17:22:26 +0700 | [diff] [blame] | 3 | * (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 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/gsm/gsm48.h> |
| 25 | |
| 26 | #include <osmocom/msc/msub.h> |
| 27 | #include <osmocom/msc/msc_roles.h> |
| 28 | #include <osmocom/msc/msc_a.h> |
| 29 | #include <osmocom/msc/msc_i.h> |
| 30 | #include <osmocom/msc/msc_t.h> |
Alexander Couzens | 7900a05 | 2024-08-27 16:17:39 +0200 | [diff] [blame] | 31 | #include <osmocom/msc/ran_conn.h> |
Alexander Couzens | eff28ab | 2024-09-12 00:55:25 +0200 | [diff] [blame^] | 32 | #include <osmocom/vlr/vlr.h> |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 33 | #include <osmocom/msc/e_link.h> |
| 34 | |
| 35 | const struct value_string msc_role_names[] = { |
| 36 | { MSC_ROLE_A, "MSC-A" }, |
| 37 | { MSC_ROLE_I, "MSC-I" }, |
| 38 | { MSC_ROLE_T, "MSC-T" }, |
| 39 | {} |
| 40 | }; |
| 41 | |
| 42 | LLIST_HEAD(msub_list); |
| 43 | |
| 44 | #define for_each_msub_role(msub, role_idx) \ |
| 45 | for ((role_idx) = 0; (role_idx) < ARRAY_SIZE((msub)->role); (role_idx)++) \ |
| 46 | if ((msub)->role[role_idx]) |
| 47 | |
| 48 | enum msub_fsm_state { |
| 49 | MSUB_ST_ACTIVE, |
| 50 | MSUB_ST_TERMINATING, |
| 51 | }; |
| 52 | |
| 53 | enum msub_fsm_event { |
| 54 | MSUB_EV_ROLE_TERMINATED, |
| 55 | }; |
| 56 | |
| 57 | static void msub_check_for_release(struct osmo_fsm_inst *fi) |
| 58 | { |
| 59 | struct msub *msub = fi->priv; |
Harald Welte | db6855c | 2019-05-09 10:28:43 +0200 | [diff] [blame] | 60 | struct msc_role_common *msc_role_a_c = NULL; |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 61 | enum msc_role role_idx; |
| 62 | int role_present[MSC_ROLES_COUNT] = {}; |
| 63 | struct osmo_fsm_inst *child; |
| 64 | |
| 65 | /* See what child FSMs are still present. A caller might exchange roles by first allocating a new one as child |
| 66 | * of this FSM, and then exchanging the msub->role[] pointer. Even though the currently active role is removing |
| 67 | * itself from msub, we can still see whether another one is pending as a child of this msub. */ |
| 68 | llist_for_each_entry(child, &fi->proc.children, proc.child) { |
| 69 | struct msc_role_common *c = child->priv; |
| 70 | role_present[c->role]++; |
| 71 | if (c->role == MSC_ROLE_A) |
| 72 | msc_role_a_c = c; |
| 73 | } |
| 74 | |
| 75 | /* Log. */ |
| 76 | for (role_idx = 0; role_idx < ARRAY_SIZE(role_present); role_idx++) { |
| 77 | if (!role_present[role_idx]) |
| 78 | continue; |
| 79 | LOG_MSUB(msub, LOGL_DEBUG, "%d %s still active\n", role_present[role_idx], msc_role_name(role_idx)); |
| 80 | } |
| 81 | |
| 82 | /* To remain valid, there must be both an MSC-A role and one of MSC-I or MSC-T; |
| 83 | * except, SGs connections need no MSC-I or MSC-T. */ |
| 84 | if (role_present[MSC_ROLE_A] |
| 85 | && (role_present[MSC_ROLE_I] || role_present[MSC_ROLE_T] |
| 86 | || (msc_role_a_c && msc_role_a_c->ran->type == OSMO_RAT_EUTRAN_SGS))) |
| 87 | return; |
| 88 | |
| 89 | /* The subscriber has become invalid. Go to terminating state to clearly signal that this msub is definitely |
| 90 | * going now. */ |
| 91 | osmo_fsm_inst_state_chg(fi, MSUB_ST_TERMINATING, 0, 0); |
| 92 | } |
| 93 | |
| 94 | void msub_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| 95 | { |
| 96 | struct msub *msub = fi->priv; |
| 97 | struct osmo_fsm_inst *role_fi; |
| 98 | |
| 99 | switch (event) { |
| 100 | case MSUB_EV_ROLE_TERMINATED: |
| 101 | role_fi = data; |
| 102 | /* Role implementations are required to pass their own osmo_fsm_inst pointer to osmo_fsm_inst_term(). */ |
| 103 | msub_remove_role(msub, role_fi); |
| 104 | msub_check_for_release(fi); |
| 105 | return; |
| 106 | default: |
| 107 | return; |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | void msub_fsm_terminating_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) |
| 112 | { |
| 113 | osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); |
| 114 | } |
| 115 | |
| 116 | void msub_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) |
| 117 | { |
| 118 | struct msub *msub = fi->priv; |
| 119 | LOG_MSUB(msub, LOGL_DEBUG, "Free\n"); |
| 120 | msub_set_vsub(msub, NULL); |
| 121 | llist_del(&msub->entry); |
| 122 | } |
| 123 | |
| 124 | #define S(x) (1 << (x)) |
| 125 | |
| 126 | static const struct osmo_fsm_state msub_fsm_states[] = { |
| 127 | [MSUB_ST_ACTIVE] = { |
| 128 | .name = "active", |
| 129 | .in_event_mask = S(MSUB_EV_ROLE_TERMINATED), |
| 130 | .out_state_mask = S(MSUB_ST_TERMINATING), |
| 131 | .action = msub_fsm_active, |
| 132 | }, |
| 133 | [MSUB_ST_TERMINATING] = { |
| 134 | .name = "terminating", |
| 135 | .onenter = msub_fsm_terminating_onenter, |
| 136 | }, |
| 137 | }; |
| 138 | |
| 139 | static const struct value_string msub_fsm_event_names[] = { |
| 140 | OSMO_VALUE_STRING(MSUB_EV_ROLE_TERMINATED), |
| 141 | {} |
| 142 | }; |
| 143 | |
| 144 | struct osmo_fsm msub_fsm = { |
| 145 | .name = "msub_fsm", |
| 146 | .states = msub_fsm_states, |
| 147 | .num_states = ARRAY_SIZE(msub_fsm_states), |
| 148 | .log_subsys = DMSC, |
| 149 | .event_names = msub_fsm_event_names, |
| 150 | .cleanup = msub_fsm_cleanup, |
| 151 | }; |
| 152 | |
| 153 | static __attribute__((constructor)) void msub_fsm_init() |
| 154 | { |
| 155 | OSMO_ASSERT(osmo_fsm_register(&msub_fsm) == 0); |
| 156 | } |
| 157 | |
| 158 | struct msc_role_common *_msub_role_alloc(struct msub *msub, enum msc_role role, struct osmo_fsm *role_fsm, |
| 159 | size_t struct_size, const char *struct_name, struct ran_infra *ran) |
| 160 | { |
| 161 | struct osmo_fsm_inst *fi; |
| 162 | struct msc_role_common *c; |
| 163 | |
| 164 | fi = osmo_fsm_inst_alloc_child(role_fsm, msub->fi, MSUB_EV_ROLE_TERMINATED); |
| 165 | OSMO_ASSERT(fi); |
| 166 | |
| 167 | c = (struct msc_role_common*)talloc_named_const(fi, struct_size, struct_name); |
| 168 | OSMO_ASSERT(c); |
| 169 | memset(c, 0, struct_size); |
| 170 | fi->priv = c; |
| 171 | |
| 172 | *c = (struct msc_role_common){ |
| 173 | .role = role, |
| 174 | .fi = fi, |
| 175 | .ran = ran, |
| 176 | }; |
| 177 | |
| 178 | msub_set_role(msub, fi); |
| 179 | return c; |
| 180 | } |
| 181 | |
| 182 | struct msub *msub_alloc(struct gsm_network *net) |
| 183 | { |
| 184 | struct msub *msub; |
| 185 | struct osmo_fsm_inst *msub_fi = osmo_fsm_inst_alloc(&msub_fsm, net, NULL, LOGL_DEBUG, NULL); |
| 186 | OSMO_ASSERT(msub_fi); |
| 187 | |
| 188 | msub = talloc(msub_fi, struct msub); |
| 189 | OSMO_ASSERT(msub); |
| 190 | msub_fi->priv = msub; |
| 191 | *msub = (struct msub){ |
| 192 | .net = net, |
| 193 | .fi = msub_fi, |
| 194 | }; |
| 195 | |
| 196 | llist_add_tail(&msub->entry, &msub_list); |
| 197 | return msub; |
| 198 | } |
| 199 | |
| 200 | /* Careful: the subscriber may not yet be authenticated, or may already be in release. Better use |
| 201 | * msc_a_for_vsub(for_vsub, true) to make sure you don't use an invalid conn. */ |
| 202 | struct msub *msub_for_vsub(const struct vlr_subscr *for_vsub) |
| 203 | { |
| 204 | struct msub *msub; |
| 205 | if (!for_vsub) |
| 206 | return NULL; |
| 207 | |
| 208 | llist_for_each_entry(msub, &msub_list, entry) { |
| 209 | if (msub->vsub == for_vsub) |
| 210 | return msub; |
| 211 | } |
| 212 | |
| 213 | return NULL; |
| 214 | } |
| 215 | |
| 216 | const char *msub_name(const struct msub *msub) |
| 217 | { |
| 218 | return vlr_subscr_name(msub? msub->vsub : NULL); |
| 219 | } |
| 220 | |
| 221 | void msub_set_role(struct msub *msub, struct osmo_fsm_inst *msc_role) |
| 222 | { |
| 223 | struct osmo_fsm_inst *prev_role; |
| 224 | struct msc_role_common *c; |
| 225 | |
| 226 | OSMO_ASSERT(msc_role); |
| 227 | c = msc_role->priv; |
| 228 | |
| 229 | prev_role = msub->role[c->role]; |
| 230 | if (prev_role) |
| 231 | LOGPFSML(prev_role, LOGL_DEBUG, "Replaced by another %s\n", msc_role_name(c->role)); |
| 232 | |
| 233 | c->msub = msub; |
| 234 | msub->role[c->role] = msc_role; |
| 235 | msub_update_id(msub); |
| 236 | |
| 237 | if (prev_role) { |
| 238 | struct msc_role_common *prev_c = prev_role->priv; |
| 239 | switch (prev_c->role) { |
| 240 | case MSC_ROLE_I: |
| 241 | msc_i_clear(prev_role->priv); |
| 242 | break; |
| 243 | case MSC_ROLE_T: |
| 244 | msc_t_clear(prev_role->priv); |
| 245 | break; |
| 246 | default: |
| 247 | osmo_fsm_inst_term(prev_role, OSMO_FSM_TERM_REQUEST, prev_role); |
| 248 | break; |
| 249 | } |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | void msub_remove_role(struct msub *msub, struct osmo_fsm_inst *fi) |
| 254 | { |
| 255 | enum msc_role idx; |
| 256 | struct msc_role_common *c; |
| 257 | if (!msub || !fi) |
| 258 | return; |
| 259 | |
| 260 | c = fi->priv; |
| 261 | LOG_MSUB(msub, LOGL_DEBUG, "%s terminated\n", msc_role_name(c->role)); |
| 262 | |
| 263 | for_each_msub_role(msub, idx) { |
| 264 | if (msub->role[idx] == fi) |
| 265 | msub->role[idx] = NULL; |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | struct msc_a *msub_msc_a(const struct msub *msub) |
| 270 | { |
| 271 | struct osmo_fsm_inst *fi; |
| 272 | if (!msub) |
| 273 | return NULL; |
| 274 | fi = msub->role[MSC_ROLE_A]; |
| 275 | if (!fi) |
| 276 | return NULL; |
| 277 | return (struct msc_a*)fi->priv; |
| 278 | } |
| 279 | |
| 280 | struct msc_i *msub_msc_i(const struct msub *msub) |
| 281 | { |
| 282 | struct osmo_fsm_inst *fi; |
| 283 | if (!msub) |
| 284 | return NULL; |
| 285 | fi = msub->role[MSC_ROLE_I]; |
| 286 | if (!fi) |
| 287 | return NULL; |
| 288 | return (struct msc_i*)fi->priv; |
| 289 | } |
| 290 | |
| 291 | struct msc_t *msub_msc_t(const struct msub *msub) |
| 292 | { |
| 293 | struct osmo_fsm_inst *fi; |
| 294 | if (!msub) |
| 295 | return NULL; |
| 296 | fi = msub->role[MSC_ROLE_T]; |
| 297 | if (!fi) |
| 298 | return NULL; |
| 299 | return (struct msc_t*)fi->priv; |
| 300 | } |
| 301 | |
| 302 | /* Return the ran_conn of the MSC-I role, if available. If the MSC-I role is handled by a remote MSC, return NULL. */ |
| 303 | struct ran_conn *msub_ran_conn(const struct msub *msub) |
| 304 | { |
| 305 | struct msc_i *msc_i = msub_msc_i(msub); |
| 306 | if (!msc_i) |
| 307 | return NULL; |
| 308 | return msc_i->ran_conn; |
| 309 | } |
| 310 | |
| 311 | static struct ran_infra *msub_ran(const struct msub *msub) |
| 312 | { |
| 313 | int i; |
| 314 | struct msc_role_common *c; |
| 315 | |
| 316 | for (i = 0; i < MSC_ROLES_COUNT; i++) { |
| 317 | if (!msub->role[i]) |
| 318 | continue; |
| 319 | c = msub->role[i]->priv; |
| 320 | if (!c->ran) |
| 321 | continue; |
| 322 | return c->ran; |
| 323 | } |
| 324 | |
| 325 | return &msc_ran_infra[OSMO_RAT_UNKNOWN]; |
| 326 | } |
| 327 | |
| 328 | const char *msub_ran_conn_name(const struct msub *msub) |
| 329 | { |
| 330 | struct msc_i *msc_i = msub_msc_i(msub); |
| 331 | struct msc_t *msc_t = msub_msc_t(msub); |
| 332 | if (msc_i && msc_i->c.remote_to) |
| 333 | return e_link_name(msc_i->c.remote_to); |
| 334 | if (msc_i && msc_i->ran_conn) |
| 335 | return ran_conn_name(msc_i->ran_conn); |
| 336 | if (msc_t && msc_t->c.remote_to) |
| 337 | return e_link_name(msc_t->c.remote_to); |
| 338 | if (msc_t && msc_t->ran_conn) |
| 339 | return ran_conn_name(msc_t->ran_conn); |
| 340 | return osmo_rat_type_name(msub_ran(msub)->type); |
| 341 | } |
| 342 | |
| 343 | int msub_set_vsub(struct msub *msub, struct vlr_subscr *vsub) |
| 344 | { |
| 345 | OSMO_ASSERT(msub); |
| 346 | if (msub->vsub == vsub) |
| 347 | return 0; |
| 348 | if (msub->vsub && vsub) { |
| 349 | LOG_MSUB(msub, LOGL_ERROR, |
| 350 | "Changing a connection's VLR Subscriber is not allowed: not changing to %s\n", |
| 351 | vlr_subscr_name(vsub)); |
| 352 | return -ENOTSUP; |
| 353 | } |
| 354 | if (vsub) { |
| 355 | struct msub *other_msub = msub_for_vsub(vsub); |
| 356 | if (other_msub) { |
| 357 | struct msc_a *msc_a = msub_msc_a(msub); |
| 358 | struct msc_a *other_msc_a = msub_msc_a(other_msub); |
| 359 | LOG_MSC_A(msc_a, LOGL_ERROR, |
| 360 | "Cannot associate with VLR subscr, another connection is already active%s%s\n", |
| 361 | other_msc_a ? " at " : "", other_msc_a ? other_msc_a->c.fi->id : ""); |
| 362 | LOG_MSC_A(other_msc_a, LOGL_ERROR, "Attempt to associate a second subscriber connection%s%s\n", |
| 363 | msc_a ? " at " : "", msc_a ? msc_a->c.fi->id : ""); |
| 364 | if (other_msc_a && msc_a_in_release(other_msc_a)) { |
| 365 | LOG_MSC_A(other_msc_a, LOGL_ERROR, |
| 366 | "Another connection for this subscriber is coming up, since this" |
| 367 | " is already in release, forcefully discarding it\n"); |
| 368 | osmo_fsm_inst_term(other_msc_a->c.fi, OSMO_FSM_TERM_ERROR, other_msc_a->c.fi); |
| 369 | /* Count this as "recovered from duplicate connection" error and do associate. */ |
| 370 | } else |
| 371 | return -EINVAL; |
| 372 | } |
| 373 | } |
| 374 | if (msub->vsub) { |
| 375 | vlr_subscr_put(msub->vsub, VSUB_USE_MSUB); |
| 376 | msub->vsub = NULL; |
| 377 | } |
| 378 | if (vsub) { |
| 379 | vlr_subscr_get(vsub, VSUB_USE_MSUB); |
| 380 | msub->vsub = vsub; |
| 381 | vsub->cs.attached_via_ran = msub_ran(msub)->type; |
| 382 | msub_update_id(msub); |
| 383 | } |
| 384 | return 0; |
| 385 | } |
| 386 | |
| 387 | struct vlr_subscr *msub_vsub(const struct msub *msub) |
| 388 | { |
| 389 | return msub ? msub->vsub : NULL; |
| 390 | } |
| 391 | |
| 392 | struct gsm_network *msub_net(const struct msub *msub) |
| 393 | { |
| 394 | OSMO_ASSERT(msub->net); |
| 395 | return msub->net; |
| 396 | } |
| 397 | |
| 398 | int msub_role_to_role_event(struct msub *msub, enum msc_role from_role, enum msc_role to_role) |
| 399 | { |
| 400 | switch (from_role) { |
| 401 | case MSC_ROLE_A: |
| 402 | switch (to_role) { |
| 403 | case MSC_ROLE_I: |
| 404 | return MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST; |
| 405 | case MSC_ROLE_T: |
| 406 | return MSC_T_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST; |
| 407 | default: |
| 408 | break; |
| 409 | } |
| 410 | break; |
| 411 | |
| 412 | case MSC_ROLE_I: |
| 413 | switch (to_role) { |
| 414 | case MSC_ROLE_A: |
| 415 | return MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST; |
| 416 | default: |
| 417 | break; |
| 418 | } |
| 419 | break; |
| 420 | |
| 421 | case MSC_ROLE_T: |
| 422 | switch (to_role) { |
| 423 | case MSC_ROLE_A: |
| 424 | return MSC_A_EV_FROM_T_PROCESS_ACCESS_SIGNALLING_REQUEST; |
| 425 | default: |
| 426 | break; |
| 427 | } |
| 428 | break; |
| 429 | |
| 430 | default: |
| 431 | break; |
| 432 | } |
| 433 | |
| 434 | LOG_MSUB(msub, LOGL_ERROR, "Cannot tx DTAP from %s to %s\n", msc_role_name(from_role), msc_role_name(to_role)); |
| 435 | return -1; |
| 436 | } |
| 437 | |
| 438 | /* The caller retains ownership of the an_apdu_msg -- don't forget to msgb_free() it. */ |
| 439 | int _msub_role_dispatch(struct msub *msub, enum msc_role to_role, uint32_t to_role_event, const struct an_apdu *an_apdu, |
| 440 | const char *file, int line) |
| 441 | { |
| 442 | struct osmo_fsm_inst *to_fi = msub->role[to_role]; |
| 443 | |
| 444 | if (!to_fi) { |
| 445 | LOG_MSUB_CAT_SRC(msub, DMSC, LOGL_ERROR, file, line, |
| 446 | "Cannot tx event to %s, no such role defined\n", msc_role_name(to_role)); |
| 447 | return -EINVAL; |
| 448 | } |
| 449 | |
| 450 | return _osmo_fsm_inst_dispatch(to_fi, to_role_event, (void*)an_apdu, file, line); |
| 451 | } |
| 452 | |
| 453 | /* The caller retains ownership of the an_apdu_msg -- don't forget to msgb_free() it. */ |
| 454 | int msub_tx_an_apdu(struct msub *msub, enum msc_role from_role, enum msc_role to_role, struct an_apdu *an_apdu) |
| 455 | { |
| 456 | int event = msub_role_to_role_event(msub, from_role, to_role); |
| 457 | if (event < 0) |
| 458 | return event; |
| 459 | return msub_role_dispatch(msub, to_role, event, an_apdu); |
| 460 | } |
| 461 | |
| 462 | static void _msub_update_id(struct msub *msub, const char *subscr_name) |
| 463 | { |
| 464 | enum msc_role idx; |
| 465 | struct msc_a *msc_a = msub_msc_a(msub); |
| 466 | struct vlr_subscr *vsub = msub_vsub(msub); |
| 467 | const char *compl_l3_name = NULL; |
| 468 | char id[128]; |
| 469 | |
| 470 | if (msc_a) |
| 471 | compl_l3_name = get_value_string_or_null(complete_layer3_type_names, msc_a->complete_layer3_type); |
| 472 | if (!compl_l3_name) |
| 473 | compl_l3_name = "no-compl-l3"; |
| 474 | |
| 475 | snprintf(id, sizeof(id), "%s:%s:%s", subscr_name, msub_ran_conn_name(msub), compl_l3_name); |
| 476 | osmo_identifier_sanitize_buf(id, NULL, '-'); |
| 477 | |
| 478 | for_each_msub_role(msub, idx) { |
| 479 | osmo_fsm_inst_update_id(msub->role[idx], id); |
| 480 | } |
| 481 | if (vsub) { |
| 482 | if (vsub->lu_fsm) |
| 483 | osmo_fsm_inst_update_id(vsub->lu_fsm, id); |
| 484 | if (vsub->auth_fsm) |
| 485 | osmo_fsm_inst_update_id(vsub->auth_fsm, id); |
| 486 | if (vsub->proc_arq_fsm) |
| 487 | osmo_fsm_inst_update_id(vsub->proc_arq_fsm, id); |
| 488 | } |
| 489 | } |
| 490 | |
| 491 | /* Compose an ID almost like gsm48_mi_to_string(), but print the MI type along, and print a TMSI as hex. */ |
Neels Hofmeyr | 46d526a | 2020-05-29 03:27:50 +0200 | [diff] [blame] | 492 | void msub_update_id_from_mi(struct msub *msub, const struct osmo_mobile_identity *mi) |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 493 | { |
Neels Hofmeyr | 46d526a | 2020-05-29 03:27:50 +0200 | [diff] [blame] | 494 | _msub_update_id(msub, osmo_mobile_identity_to_str_c(OTC_SELECT, mi)); |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 495 | } |
| 496 | |
| 497 | /* Update msub->fi id string from current msub->vsub and msub->complete_layer3_type. */ |
| 498 | void msub_update_id(struct msub *msub) |
| 499 | { |
| 500 | if (!msub) |
| 501 | return; |
| 502 | _msub_update_id(msub, vlr_subscr_name(msub->vsub)); |
| 503 | } |
| 504 | |
| 505 | /* Iterate all msub instances that are relevant for this subscriber, and update FSM ID strings for all of the FSM |
| 506 | * instances. */ |
| 507 | void msub_update_id_for_vsub(struct vlr_subscr *for_vsub) |
| 508 | { |
| 509 | struct msub *msub; |
| 510 | if (!for_vsub) |
| 511 | return; |
| 512 | |
| 513 | llist_for_each_entry(msub, &msub_list, entry) { |
| 514 | if (msub->vsub == for_vsub) |
| 515 | msub_update_id(msub); |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | void msc_role_forget_conn(struct osmo_fsm_inst *role, struct ran_conn *conn) |
| 520 | { |
| 521 | struct msc_i *old_i = role->priv; |
| 522 | struct msc_t *old_t = role->priv; |
| 523 | struct msc_role_common *c = role->priv; |
| 524 | struct ran_conn **conn_p = NULL; |
| 525 | |
| 526 | switch (c->role) { |
| 527 | case MSC_ROLE_I: |
| 528 | conn_p = &old_i->ran_conn; |
| 529 | break; |
| 530 | |
| 531 | case MSC_ROLE_T: |
| 532 | conn_p = &old_t->ran_conn; |
| 533 | break; |
| 534 | default: |
| 535 | break; |
| 536 | } |
| 537 | |
| 538 | if (!conn_p) |
| 539 | return; |
| 540 | |
| 541 | if (*conn_p != conn) |
| 542 | return; |
| 543 | |
| 544 | (*conn_p)->msc_role = NULL; |
| 545 | *conn_p = NULL; |
| 546 | } |
| 547 | |
Vadim Yanitskiy | c44342b | 2021-12-07 18:32:35 +0300 | [diff] [blame] | 548 | /* NOTE: the resulting message buffer will be attached to OTC_SELECT, so its lifetime |
| 549 | * is limited by the current select() loop iteration. Use talloc_steal() to avoid this. */ |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 550 | struct msgb *msc_role_ran_encode(struct osmo_fsm_inst *fi, const struct ran_msg *ran_msg) |
| 551 | { |
| 552 | struct msc_role_common *c = fi->priv; |
| 553 | struct msgb *msg; |
| 554 | if (!c->ran->ran_encode) { |
| 555 | LOGPFSML(fi, LOGL_ERROR, "Cannot encode %s: no NAS encoding function defined for RAN type %s\n", |
| 556 | ran_msg_type_name(ran_msg->msg_type), osmo_rat_type_name(c->ran->type)); |
| 557 | return NULL; |
| 558 | } |
| 559 | msg = c->ran->ran_encode(fi, ran_msg); |
| 560 | if (!msg) |
| 561 | LOGPFSML(fi, LOGL_ERROR, "Failed to encode %s\n", ran_msg_type_name(ran_msg->msg_type)); |
Vadim Yanitskiy | c44342b | 2021-12-07 18:32:35 +0300 | [diff] [blame] | 562 | else |
| 563 | talloc_steal(OTC_SELECT, msg); |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 564 | return msg; |
| 565 | } |
| 566 | |
| 567 | int msc_role_ran_decode(struct osmo_fsm_inst *fi, const struct an_apdu *an_apdu, |
| 568 | ran_decode_cb_t decode_cb, void *decode_cb_data) |
| 569 | { |
| 570 | struct ran_dec ran_dec; |
| 571 | struct msc_role_common *c = fi->priv; |
| 572 | if (!an_apdu) { |
| 573 | LOGPFSML(fi, LOGL_ERROR, "NULL AN-APDU\n"); |
| 574 | return -EINVAL; |
| 575 | } |
| 576 | if (an_apdu->an_proto != c->ran->an_proto) { |
| 577 | LOGPFSML(fi, LOGL_ERROR, "Unexpected AN-APDU protocol: %s\n", an_proto_name(an_apdu->an_proto)); |
| 578 | return -EINVAL; |
| 579 | } |
| 580 | if (!an_apdu->msg) { |
| 581 | LOGPFSML(fi, LOGL_DEBUG, "No PDU in this AN-APDU\n"); |
| 582 | return 0; |
| 583 | } |
| 584 | ran_dec = (struct ran_dec) { |
| 585 | .caller_fi = fi, |
| 586 | .caller_data = decode_cb_data, |
| 587 | .decode_cb = decode_cb, |
| 588 | }; |
| 589 | if (!c->ran->ran_dec_l2) { |
| 590 | LOGPFSML(fi, LOGL_ERROR, "No ran_dec_l2() defined for RAN type %s\n", |
| 591 | osmo_rat_type_name(c->ran->type)); |
| 592 | return -ENOTSUP; |
| 593 | } |
| 594 | return c->ran->ran_dec_l2(&ran_dec, an_apdu->msg); |
| 595 | } |