diff options
Diffstat (limited to 'src/libmsc/ran_peer.c')
-rw-r--r-- | src/libmsc/ran_peer.c | 659 |
1 files changed, 659 insertions, 0 deletions
diff --git a/src/libmsc/ran_peer.c b/src/libmsc/ran_peer.c new file mode 100644 index 000000000..ac2bb4f96 --- /dev/null +++ b/src/libmsc/ran_peer.c @@ -0,0 +1,659 @@ +/* + * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Neels Hofmeyr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/fsm.h> +#include <osmocom/sigtran/sccp_helpers.h> + +#include <osmocom/msc/ran_peer.h> +#include <osmocom/msc/sccp_ran.h> +#include <osmocom/msc/msub.h> +#include <osmocom/msc/msc_i.h> +#include <osmocom/msc/msc_a.h> +#include <osmocom/msc/vlr.h> +#include <osmocom/msc/ran_conn.h> +#include <osmocom/msc/cell_id_list.h> + +static struct osmo_fsm ran_peer_fsm; + +static __attribute__((constructor)) void ran_peer_init() +{ + OSMO_ASSERT( osmo_fsm_register(&ran_peer_fsm) == 0); +} + +/* Allocate a RAN peer with FSM instance. To deallocate, call osmo_fsm_inst_term(ran_peer->fi). */ +static struct ran_peer *ran_peer_alloc(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *peer_addr) +{ + struct ran_peer *rp; + struct osmo_fsm_inst *fi; + char *sccp_addr; + char *pos; + + fi = osmo_fsm_inst_alloc(&ran_peer_fsm, sri, NULL, LOGL_DEBUG, NULL); + OSMO_ASSERT(fi); + + /* Unfortunately, osmo_sccp_inst_addr_name() returns "RI=SSN_PC,PC=0.24.1,SSN=BSSAP" but neither commas nor + * full-stops are allowed as FSM inst id. Make it "RI=SSN_PC:PC-0-24-1:SSN-BSSAP". */ + sccp_addr = osmo_sccp_inst_addr_name(sri->sccp, peer_addr); + for (pos = sccp_addr; *pos; pos++) { + if (*pos == ',') + *pos = ':'; + else if (*pos == '.' || *pos == '=') + *pos = '-'; + } + osmo_fsm_inst_update_id_f(fi, "%s:%s", osmo_rat_type_name(sri->ran->type), sccp_addr); + + rp = talloc_zero(fi, struct ran_peer); + OSMO_ASSERT(rp); + *rp = (struct ran_peer){ + .sri = sri, + .peer_addr = *peer_addr, + .fi = fi, + }; + INIT_LLIST_HEAD(&rp->cells_seen); + fi->priv = rp; + + llist_add(&rp->entry, &sri->ran_peers); + + return rp; +} + +struct ran_peer *ran_peer_find_or_create(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *peer_addr) +{ + struct ran_peer *rp = ran_peer_find(sri, peer_addr); + if (rp) + return rp; + return ran_peer_alloc(sri, peer_addr); +} + +struct ran_peer *ran_peer_find(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *peer_addr) +{ + struct ran_peer *rp; + llist_for_each_entry(rp, &sri->ran_peers, entry) { + if (osmo_sccp_addr_ri_cmp(peer_addr, &rp->peer_addr)) + continue; + return rp; + } + return NULL; +} + +void ran_peer_cells_seen_add(struct ran_peer *ran_peer, const struct gsm0808_cell_id *cid) +{ + if (!cell_id_list_add_cell(ran_peer, &ran_peer->cells_seen, cid)) + return; + LOG_RAN_PEER_CAT(ran_peer, DPAG, LOGL_NOTICE, "Added seen cell to this RAN peer: %s\n", + gsm0808_cell_id_name(cid)); +} + +static const struct osmo_tdef_state_timeout ran_peer_fsm_timeouts[32] = { + [RAN_PEER_ST_WAIT_RX_RESET_ACK] = { .T = -1 }, + [RAN_PEER_ST_DISCARDING] = { .T = -2 }, +}; + +#define ran_peer_state_chg(RAN_PEER, NEXT_STATE) \ + osmo_tdef_fsm_inst_state_chg((RAN_PEER)->fi, NEXT_STATE, ran_peer_fsm_timeouts, g_sccp_tdefs, 5) + +void ran_peer_discard_all_conns(struct ran_peer *rp) +{ + struct ran_conn *conn, *next; + + ran_peer_for_each_ran_conn_safe(conn, next, rp) { + ran_conn_discard(conn); + } +} + +/* Drop all SCCP connections for this ran_peer, respond with RESET ACKNOWLEDGE and move to READY state. */ +static void ran_peer_rx_reset(struct ran_peer *rp) +{ + struct msgb *reset_ack; + + ran_peer_discard_all_conns(rp); + + reset_ack = rp->sri->ran->sccp_ran_ops.make_reset_msg(rp->sri, SCCP_RAN_MSG_RESET_ACK); + + if (!reset_ack) { + LOG_RAN_PEER(rp, LOGL_ERROR, "Failed to compose RESET ACKNOWLEDGE message\n"); + ran_peer_state_chg(rp, RAN_PEER_ST_WAIT_RX_RESET); + return; + } + + if (sccp_ran_down_l2_cl(rp->sri, &rp->peer_addr, reset_ack)) { + LOG_RAN_PEER(rp, LOGL_ERROR, "Failed to send RESET ACKNOWLEDGE message\n"); + ran_peer_state_chg(rp, RAN_PEER_ST_WAIT_RX_RESET); + return; + } + + LOG_RAN_PEER(rp, LOGL_INFO, "Sent RESET ACKNOWLEDGE\n"); + + ran_peer_state_chg(rp, RAN_PEER_ST_READY); +} + +void ran_peer_reset(struct ran_peer *rp) +{ + struct msgb *reset; + + ran_peer_state_chg(rp, RAN_PEER_ST_WAIT_RX_RESET_ACK); + ran_peer_discard_all_conns(rp); + + reset = rp->sri->ran->sccp_ran_ops.make_reset_msg(rp->sri, SCCP_RAN_MSG_RESET); + + if (!reset) { + LOG_RAN_PEER(rp, LOGL_ERROR, "Failed to compose RESET message\n"); + ran_peer_state_chg(rp, RAN_PEER_ST_WAIT_RX_RESET); + return; + } + + if (sccp_ran_down_l2_cl(rp->sri, &rp->peer_addr, reset)) { + LOG_RAN_PEER(rp, LOGL_ERROR, "Failed to send RESET message\n"); + ran_peer_state_chg(rp, RAN_PEER_ST_WAIT_RX_RESET); + return; + } +} + +void ran_peer_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct ran_peer *rp = fi->priv; + struct ran_peer_ev_ctx *ctx = data; + struct msgb *msg = ctx->msg; + + switch (event) { + case RAN_PEER_EV_MSG_UP_CL: + switch (rp->sri->ran->sccp_ran_ops.is_reset_msg(rp->sri, msg)) { + case 1: + osmo_fsm_inst_dispatch(fi, RAN_PEER_EV_RX_RESET, msg); + return; + case 2: + osmo_fsm_inst_dispatch(fi, RAN_PEER_EV_RX_RESET_ACK, msg); + return; + default: + LOG_RAN_PEER(rp, LOGL_ERROR, "Unhandled ConnectionLess message received: %s\n", + rp->sri->ran->sccp_ran_ops.msg_name(rp->sri, msg)); + return; + } + + default: + LOG_RAN_PEER(rp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&ran_peer_fsm, event)); + return; + } +} + +void clear_and_disconnect(struct ran_peer *rp, uint32_t conn_id) +{ + struct msgb *clear; + struct ran_msg ran_enc_msg = { + .msg_type = RAN_MSG_CLEAR_COMMAND, + .clear_command = { + .gsm0808_cause = GSM0808_CAUSE_EQUIPMENT_FAILURE, + }, + }; + + clear = rp->sri->ran->ran_encode(rp->fi, &ran_enc_msg); + if (!clear + || sccp_ran_down_l2_co(rp->sri, conn_id, clear)) + LOG_RAN_PEER(rp, LOGL_ERROR, "Cannot sent Clear command\n"); + + sccp_ran_disconnect(rp->sri, conn_id, 0); +} + +void ran_peer_st_wait_rx_reset(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct ran_peer *rp = fi->priv; + struct ran_peer_ev_ctx *ctx; + + switch (event) { + + case RAN_PEER_EV_MSG_UP_CO: + case RAN_PEER_EV_MSG_UP_CO_INITIAL: + ctx = data; + OSMO_ASSERT(ctx); + + if (rp->sri->ignore_missing_reset) { + LOG_RAN_PEER(rp, LOGL_ERROR, "Receiving CO message on RAN peer that has not done a proper RESET yet." + " Accepting RAN peer implicitly (legacy compat)\n"); + ran_peer_state_chg(rp, RAN_PEER_ST_READY); + osmo_fsm_inst_dispatch(rp->fi, event, data); + return; + } + + LOG_RAN_PEER(rp, LOGL_ERROR, "Receiving CO message on RAN peer that has not done a proper RESET yet." + " Disconnecting on incoming message, sending RESET to RAN peer.\n"); + /* No valid RESET procedure has happened here yet. Usually, we're expecting the RAN peer (BSC, + * RNC) to first send a RESET message before sending Connection Oriented messages. So if we're + * getting a CO message, likely we've just restarted or something. Send a RESET to the peer. */ + + /* Make sure the MS / UE properly disconnects. */ + clear_and_disconnect(rp, ctx->conn_id); + + ran_peer_reset(rp); + return; + + case RAN_PEER_EV_RX_RESET: + ran_peer_rx_reset(rp); + return; + + default: + LOG_RAN_PEER(rp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&ran_peer_fsm, event)); + return; + } +} + +void ran_peer_st_wait_rx_reset_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct ran_peer *rp = fi->priv; + struct ran_peer_ev_ctx *ctx; + + switch (event) { + + case RAN_PEER_EV_RX_RESET_ACK: + ran_peer_state_chg(rp, RAN_PEER_ST_READY); + return; + + case RAN_PEER_EV_MSG_UP_CO: + case RAN_PEER_EV_MSG_UP_CO_INITIAL: + ctx = data; + OSMO_ASSERT(ctx); + LOG_RAN_PEER(rp, LOGL_ERROR, "Receiving CO message on RAN peer that has not done a proper RESET yet." + " Disconnecting on incoming message, sending RESET to RAN peer.\n"); + sccp_ran_disconnect(rp->sri, ctx->conn_id, 0); + /* No valid RESET procedure has happened here yet. */ + ran_peer_reset(rp); + return; + + case RAN_PEER_EV_RX_RESET: + ran_peer_rx_reset(rp); + return; + + default: + LOG_RAN_PEER(rp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&ran_peer_fsm, event)); + return; + } +} + +static struct ran_conn *new_incoming_conn(struct ran_peer *rp, uint32_t conn_id) +{ + struct gsm_network *net = rp->sri->user_data; + struct msub *msub; + struct msc_i *msc_i; + struct msc_a *msc_a; + struct ran_conn *ran_conn; + + msub = msub_alloc(net); + OSMO_ASSERT(msub); + msc_i = msc_i_alloc(msub, rp->sri->ran); + OSMO_ASSERT(msc_i); + + ran_conn = ran_conn_create_incoming(rp, conn_id); + if (!ran_conn) { + LOG_RAN_PEER(rp, LOGL_ERROR, "Cannot allocate ran_conn\n"); + return NULL; + } + msc_i_set_ran_conn(msc_i, ran_conn); + + msc_a = msc_a_alloc(msub, rp->sri->ran); + OSMO_ASSERT(msc_a); + + return msc_i->ran_conn; +} + +void ran_peer_st_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct ran_peer *rp = fi->priv; + struct ran_peer_ev_ctx *ctx; + struct ran_conn *conn; + struct an_apdu an_apdu; + + switch (event) { + + case RAN_PEER_EV_MSG_UP_CO_INITIAL: + ctx = data; + OSMO_ASSERT(ctx) + OSMO_ASSERT(!ctx->conn); + OSMO_ASSERT(ctx->msg); + + conn = new_incoming_conn(rp, ctx->conn_id); + if (!conn) + return; + if (!conn->msc_role) { + LOG_RAN_PEER(rp, LOGL_ERROR, + "Rx CO Initial message on conn that is not associated with any MSC role\n"); + return; + } + + + an_apdu = (struct an_apdu){ + .an_proto = rp->sri->ran->an_proto, + .msg = ctx->msg, + }; + + osmo_fsm_inst_dispatch(conn->msc_role, MSC_EV_FROM_RAN_COMPLETE_LAYER_3, &an_apdu); + return; + + case RAN_PEER_EV_MSG_UP_CO: + ctx = data; + OSMO_ASSERT(ctx); + OSMO_ASSERT(ctx->conn); + OSMO_ASSERT(ctx->msg); + + if (!ctx->conn->msc_role) { + LOG_RAN_PEER(rp, LOGL_ERROR, + "Rx CO message on conn that is not associated with any MSC role\n"); + return; + } + + an_apdu = (struct an_apdu){ + .an_proto = rp->sri->ran->an_proto, + .msg = ctx->msg, + }; + + osmo_fsm_inst_dispatch(ctx->conn->msc_role, MSC_EV_FROM_RAN_UP_L2, &an_apdu); + return; + + case RAN_PEER_EV_MSG_DOWN_CO_INITIAL: + ctx = data; + OSMO_ASSERT(ctx); + OSMO_ASSERT(ctx->msg); + sccp_ran_down_l2_co_initial(rp->sri, &rp->peer_addr, ctx->conn_id, ctx->msg); + return; + + case RAN_PEER_EV_MSG_DOWN_CO: + ctx = data; + OSMO_ASSERT(ctx); + OSMO_ASSERT(ctx->msg); + sccp_ran_down_l2_co(rp->sri, ctx->conn_id, ctx->msg); + return; + + case RAN_PEER_EV_MSG_DOWN_CL: + OSMO_ASSERT(data); + sccp_ran_down_l2_cl(rp->sri, &rp->peer_addr, (struct msgb*)data); + return; + + case RAN_PEER_EV_RX_RESET: + ran_peer_rx_reset(rp); + return; + + default: + LOG_RAN_PEER(rp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&ran_peer_fsm, event)); + return; + } +} + +static int ran_peer_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + struct ran_peer *rp = fi->priv; + ran_peer_state_chg(rp, RAN_PEER_ST_WAIT_RX_RESET); + return 0; +} + +void ran_peer_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) +{ + struct ran_peer *rp = fi->priv; + ran_peer_discard_all_conns(rp); + llist_del(&rp->entry); +} + +static const struct value_string ran_peer_fsm_event_names[] = { + OSMO_VALUE_STRING(RAN_PEER_EV_MSG_UP_CL), + OSMO_VALUE_STRING(RAN_PEER_EV_MSG_UP_CO_INITIAL), + OSMO_VALUE_STRING(RAN_PEER_EV_MSG_UP_CO), + OSMO_VALUE_STRING(RAN_PEER_EV_MSG_DOWN_CL), + OSMO_VALUE_STRING(RAN_PEER_EV_MSG_DOWN_CO_INITIAL), + OSMO_VALUE_STRING(RAN_PEER_EV_MSG_DOWN_CO), + OSMO_VALUE_STRING(RAN_PEER_EV_RX_RESET), + OSMO_VALUE_STRING(RAN_PEER_EV_RX_RESET_ACK), + OSMO_VALUE_STRING(RAN_PEER_EV_CONNECTION_SUCCESS), + OSMO_VALUE_STRING(RAN_PEER_EV_CONNECTION_TIMEOUT), + {} +}; + +#define S(x) (1 << (x)) + +static const struct osmo_fsm_state ran_peer_fsm_states[] = { + [RAN_PEER_ST_WAIT_RX_RESET] = { + .name = "WAIT_RX_RESET", + .action = ran_peer_st_wait_rx_reset, + .in_event_mask = 0 + | S(RAN_PEER_EV_RX_RESET) + | S(RAN_PEER_EV_MSG_UP_CO_INITIAL) + | S(RAN_PEER_EV_MSG_UP_CO) + | S(RAN_PEER_EV_CONNECTION_TIMEOUT) + , + .out_state_mask = 0 + | S(RAN_PEER_ST_WAIT_RX_RESET) + | S(RAN_PEER_ST_WAIT_RX_RESET_ACK) + | S(RAN_PEER_ST_READY) + | S(RAN_PEER_ST_DISCARDING) + , + }, + [RAN_PEER_ST_WAIT_RX_RESET_ACK] = { + .name = "WAIT_RX_RESET_ACK", + .action = ran_peer_st_wait_rx_reset_ack, + .in_event_mask = 0 + | S(RAN_PEER_EV_RX_RESET) + | S(RAN_PEER_EV_RX_RESET_ACK) + | S(RAN_PEER_EV_MSG_UP_CO_INITIAL) + | S(RAN_PEER_EV_MSG_UP_CO) + | S(RAN_PEER_EV_CONNECTION_TIMEOUT) + , + .out_state_mask = 0 + | S(RAN_PEER_ST_WAIT_RX_RESET) + | S(RAN_PEER_ST_WAIT_RX_RESET_ACK) + | S(RAN_PEER_ST_READY) + | S(RAN_PEER_ST_DISCARDING) + , + }, + [RAN_PEER_ST_READY] = { + .name = "READY", + .action = ran_peer_st_ready, + .in_event_mask = 0 + | S(RAN_PEER_EV_RX_RESET) + | S(RAN_PEER_EV_MSG_UP_CO_INITIAL) + | S(RAN_PEER_EV_MSG_UP_CO) + | S(RAN_PEER_EV_MSG_DOWN_CO_INITIAL) + | S(RAN_PEER_EV_MSG_DOWN_CO) + | S(RAN_PEER_EV_MSG_DOWN_CL) + , + .out_state_mask = 0 + | S(RAN_PEER_ST_WAIT_RX_RESET) + | S(RAN_PEER_ST_WAIT_RX_RESET_ACK) + | S(RAN_PEER_ST_READY) + | S(RAN_PEER_ST_DISCARDING) + , + }, + [RAN_PEER_ST_DISCARDING] = { + .name = "DISCARDING", + }, +}; + +static struct osmo_fsm ran_peer_fsm = { + .name = "ran_peer", + .states = ran_peer_fsm_states, + .num_states = ARRAY_SIZE(ran_peer_fsm_states), + .log_subsys = DRR, + .event_names = ran_peer_fsm_event_names, + .timer_cb = ran_peer_fsm_timer_cb, + .cleanup = ran_peer_fsm_cleanup, + .allstate_action = ran_peer_allstate_action, + .allstate_event_mask = 0 + | S(RAN_PEER_EV_MSG_UP_CL) + , +}; + +int ran_peer_up_l2(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *calling_addr, bool co, uint32_t conn_id, + struct msgb *l2) +{ + struct ran_peer *ran_peer = NULL; + uint32_t event; + struct ran_peer_ev_ctx ctx = { + .conn_id = conn_id, + .msg = l2, + }; + + if (co) { + struct ran_conn *conn; + llist_for_each_entry(conn, &sri->ran_conns, entry) { + if (conn->sccp_conn_id == conn_id) { + ran_peer = conn->ran_peer; + ctx.conn = conn; + break; + } + } + + if (ran_peer && calling_addr) { + LOG_SCCP_RAN_CO(sri, calling_addr, conn_id, LOGL_ERROR, + "Connection-Oriented Initial message for already existing conn_id." + " Dropping message.\n"); + return -EINVAL; + } + + if (!ran_peer && !calling_addr) { + LOG_SCCP_RAN_CO(sri, calling_addr, conn_id, LOGL_ERROR, + "Connection-Oriented non-Initial message for unknown conn_id %u." + " Dropping message.\n", conn_id); + return -EINVAL; + } + } + + if (calling_addr) { + ran_peer = ran_peer_find_or_create(sri, calling_addr); + if (!ran_peer) { + LOG_SCCP_RAN_CL(sri, calling_addr, LOGL_ERROR, "Cannot register RAN peer\n"); + return -EIO; + } + } + + OSMO_ASSERT(ran_peer && ran_peer->fi); + + if (co) + event = calling_addr ? RAN_PEER_EV_MSG_UP_CO_INITIAL : RAN_PEER_EV_MSG_UP_CO; + else + event = RAN_PEER_EV_MSG_UP_CL; + + return osmo_fsm_inst_dispatch(ran_peer->fi, event, &ctx); +} + +void ran_peer_disconnect(struct sccp_ran_inst *sri, uint32_t conn_id) +{ + struct ran_conn *conn; + llist_for_each_entry(conn, &sri->ran_conns, entry) { + if (conn->sccp_conn_id == conn_id) { + ran_conn_discard(conn); + return; + } + } +} + +struct ran_peer *ran_peer_find_by_cell_id(struct sccp_ran_inst *sri, const struct gsm0808_cell_id *cid, + bool expecting_single_match) +{ + struct ran_peer *rp; + struct ran_peer *found = NULL; + + llist_for_each_entry(rp, &sri->ran_peers, entry) { + if (cell_id_list_find(&rp->cells_seen, cid, 0, false)) { + if (!expecting_single_match) + return rp; + /* Otherwise continue iterating and log errors for multiple matches... */ + if (found) { + LOG_RAN_PEER(found, LOGL_ERROR, "Cell appears in more than one RAN peer:" + " %s also appears in %s\n", + gsm0808_cell_id_name(cid), rp->fi->id); + } else + found = rp; + } + } + return found; +} + +struct ran_peer *ran_peer_find_by_addr(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *addr) +{ + struct ran_peer *rp; + + llist_for_each_entry(rp, &sri->ran_peers, entry) { + if (!osmo_sccp_addr_ri_cmp(addr, &rp->peer_addr)) + return rp; + } + return NULL; +} + +int ran_peers_down_paging(struct sccp_ran_inst *sri, enum CELL_IDENT page_where, struct vlr_subscr *vsub, + enum paging_cause cause) +{ + struct ran_peer *rp; + int ret = 0; + struct gsm0808_cell_id page_id; + gsm0808_cell_id_from_cgi(&page_id, page_where, &vsub->cgi); + + switch (page_where) { + case CELL_IDENT_NO_CELL: + LOG_SCCP_RAN_CAT(sri, DPAG, LOGL_ERROR, "Asked to page on NO_CELL, wich doesn't make sense.\n"); + return 0; + + case CELL_IDENT_UTRAN_PLMN_LAC_RNC: + case CELL_IDENT_UTRAN_RNC: + case CELL_IDENT_UTRAN_LAC_RNC: + LOG_SCCP_RAN_CAT(sri, DPAG, LOGL_ERROR, "Don't know how to page on %s\n", + gsm0808_cell_id_name(&page_id)); + return 0; + + default: + break; + }; + + llist_for_each_entry(rp, &sri->ran_peers, entry) { + ret += ran_peer_down_paging(rp, &page_id, vsub, cause); + } + + if (!ret) + LOG_SCCP_RAN_CAT(sri, DPAG, LOGL_ERROR, "Paging failed, no RAN peers found for %s\n", + gsm0808_cell_id_name(&page_id)); + return ret; +} + +/* If the given vsub->cgi matches this ran_peer with respect to page_where, page and return 1. + * Otherwise return 0. (Return value: number of pagings sent) */ +int ran_peer_down_paging(struct ran_peer *rp, const struct gsm0808_cell_id *page_id, struct vlr_subscr *vsub, + enum paging_cause cause) +{ + struct msgb *l2; + + if (cell_id_list_find(&rp->cells_seen, page_id, 0, false)) + goto page_it; + + /* There are also the RAN peers that are configured in the neighbor ident for Handover, but if those aren't + * connected, then we can't Page there. */ + + return 0; + +page_it: + LOG_RAN_PEER_CAT(rp, DPAG, LOGL_DEBUG, "Paging for %s on %s\n", vlr_subscr_name(vsub), + gsm0808_cell_id_name(page_id)); + l2 = rp->sri->ran->sccp_ran_ops.make_paging_msg(rp->sri, page_id, vsub->imsi, vsub->tmsi, cause); + if (osmo_fsm_inst_dispatch(rp->fi, RAN_PEER_EV_MSG_DOWN_CL, l2)) { + /* Not allowed to send messages, the peer is not properly connected yet/anymore */ + LOG_RAN_PEER_CAT(rp, DPAG, LOGL_ERROR, + "Paging for %s matched this RAN peer, but emitting a Paging failed\n", + gsm0808_cell_id_name(page_id)); + return 0; + } + return 1; +} |