aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2018-12-07 14:47:34 +0100
committerNeels Hofmeyr <neels@hofmeyr.de>2019-05-06 23:45:28 +0200
commit62cd38da6928199ec87cf098c169268681e0e2d7 (patch)
tree31effc8d495f68964d1151f17a83f949d48cc238 /include
parent5b1e0309b513ebe05a4b5b6dfa0c8ad620d34a99 (diff)
large refactoring: support inter-BSC and inter-MSC Handover
3GPP TS 49.008 '4.3 Roles of MSC-A, MSC-I and MSC-T' defines distinct roles: - MSC-A is responsible for managing subscribers, - MSC-I is the gateway to the RAN. - MSC-T is a second transitory gateway to another RAN during Handover. After inter-MSC Handover, the MSC-I is handled by a remote MSC instance, while the original MSC-A retains the responsibility of subscriber management. MSC-T exists in this patch but is not yet used, since Handover is only prepared for, not yet implemented. Facilitate Inter-MSC and inter-BSC Handover by the same internal split of MSC roles. Compared to inter-MSC Handover, mere inter-BSC has the obvious simplifications: - all of MSC-A, MSC-I and MSC-T roles will be served by the same osmo-msc instance, - messages between MSC-A and MSC-{I,T} don't need to be routed via E-interface (GSUP), - no call routing between MSC-A and -I via MNCC necessary. This is the largest code bomb I have submitted, ever. Out of principle, I apologize to everyone trying to read this as a whole. Unfortunately, I see no sense in trying to split this patch into smaller bits. It would be a huge amount of work to introduce these changes in separate chunks, especially if each should in turn be useful and pass all test suites. So, unfortunately, we are stuck with this code bomb. The following are some details and rationale for this rather huge refactoring: * separate MSC subscriber management from ran_conn struct ran_conn is reduced from the pivotal subscriber management entity it has been so far to a mere storage for an SCCP connection ID and an MSC subscriber reference. The new pivotal subscriber management entity is struct msc_a -- struct msub lists the msc_a, msc_i, msc_t roles, the vast majority of code paths however use msc_a, since MSC-A is where all the interesting stuff happens. Before handover, msc_i is an FSM implementation that encodes to the local ran_conn. After inter-MSC Handover, msc_i is a compatible but different FSM implementation that instead forwards via/from GSUP. Same goes for the msc_a struct: if osmo-msc is the MSC-I "RAN proxy" for a remote MSC-A role, the msc_a->fi is an FSM implementation that merely forwards via/from GSUP. * New SCCP implementation for RAN access To be able to forward BSSAP and RANAP messages via the GSUP interface, the individual message layers need to be cleanly separated. The IuCS implementation used until now (iu_client from libosmo-ranap) did not provide this level of separation, and needed a complete rewrite. It was trivial to implement this in such a way that both BSSAP and RANAP can be handled by the same SCCP code, hence the new SCCP-RAN layer also replaces BSSAP handling. sccp_ran.h: struct sccp_ran_inst provides an abstract handler for incoming RAN connections. A set of callback functions provides implementation specific details. * RAN Abstraction (BSSAP vs. RANAP) The common SCCP implementation did set the theme for the remaining refactoring: make all other MSC code paths entirely RAN-implementation-agnostic. ran_infra.c provides data structures that list RAN implementation specifics, from logging to RAN de-/encoding to SCCP callbacks and timers. A ran_infra pointer hence allows complete abstraction of RAN implementations: - managing connected RAN peers (BSC, RNC) in ran_peer.c, - classifying and de-/encoding RAN PDUs, - recording connected LACs and cell IDs and sending out Paging requests to matching RAN peers. * RAN RESET now also for RANAP ran_peer.c absorbs the reset_fsm from a_reset.c; in consequence, RANAP also supports proper RESET semantics now. Hence osmo-hnbgw now also needs to provide proper RESET handling, which it so far duly ignores. (TODO) * RAN de-/encoding abstraction The RAN abstraction mentioned above serves not only to separate RANAP and BSSAP implementations transparently, but also to be able to optionally handle RAN on distinct levels. Before Handover, all RAN messages are handled by the MSC-A role. However, after an inter-MSC Handover, a standalone MSC-I will need to decode RAN PDUs, at least in order to manage Assignment of RTP streams between BSS/RNC and MNCC call forwarding. ran_msg.h provides a common API with abstraction for: - receiving events from RAN, i.e. passing RAN decode from the BSC/RNC and MS/UE: struct ran_dec_msg represents RAN messages decoded from either BSSMAP or RANAP; - sending RAN events: ran_enc_msg is the counterpart to compose RAN messages that should be encoded to either BSSMAP or RANAP and passed down to the BSC/RNC and MS/UE. The RAN-specific implementations are completely contained by ran_msg_a.c and ran_msg_iu.c. In particular, Assignment and Ciphering have so far been distinct code paths for BSSAP and RANAP, with switch(via_ran){...} statements all over the place. Using RAN_DEC_* and RAN_ENC_* abstractions, these are now completely unified. Note that SGs does not qualify for RAN abstraction: the SGs interface always remains with the MSC-A role, and SGs messages follow quite distinct semantics from the fairly similar GERAN and UTRAN. * MGW and RTP stream management So far, managing MGW endpoints via MGCP was tightly glued in-between GSM-04.08-CC on the one and MNCC on the other side. Prepare for switching RTP streams between different RAN peers by moving to object-oriented implementations: implement struct call_leg and struct rtp_stream with distinct FSMs each. For MGW communication, use the osmo_mgcpc_ep API that has originated from osmo-bsc and recently moved to libosmo-mgcp-client for this purpose. Instead of implementing a sequence of events with code duplication for the RAN and CN sides, the idea is to manage each RTP stream separately by firing and receiving events as soon as codecs and RTP ports are negotiated, and letting the individual FSMs take care of the MGW management "asynchronously". The caller provides event IDs and an FSM instance that should be notified of RTP stream setup progress. Hence it becomes possible to reconnect RTP streams from one GSM-04.08-CC to another (inter-BSC Handover) or between CC and MNCC RTP peers (inter-MSC Handover) without duplicating the MGCP code for each transition. The number of FSM implementations used for MGCP handling may seem a bit of an overkill. But in fact, the number of perspectives on RTP forwarding are far from trivial: - an MGW endpoint is an entity with N connections, and MGCP "sessions" for configuring them by talking to the MGW; - an RTP stream is a remote peer connected to one of the endpoint's connections, which is asynchronously notified of codec and RTP port choices; - a call leg is the higher level view on either an MT or MO side of a voice call, a combination of two RTP streams to forward between two remote peers. BSC MGW PBX CI CI [MGW-endpoint] [--rtp_stream--] [--rtp_stream--] [----------------call_leg----------------] * Use counts Introduce using the new osmo_use_count API added to libosmocore for this purpose. Each use token has a distinct name in the logging, which can be a globally constant name or ad-hoc, like the local __func__ string constant. Use in the new struct msc_a, as well as change vlr_subscr to the new osmo_use_count API. * FSM Timeouts Introduce using the new osmo_tdef API, which provides a common VTY implementation for all timer numbers, and FSM state transitions with the correct timeout. Originated in osmo-bsc, recently moved to libosmocore. Depends: Ife31e6798b4e728a23913179e346552a7dd338c0 (libosmocore) Ib9af67b100c4583342a2103669732dab2e577b04 (libosmocore) Id617265337f09dfb6ddfe111ef5e578cd3dc9f63 (libosmocore) Ie9e2add7bbfae651c04e230d62e37cebeb91b0f5 (libosmo-sccp) I26be5c4b06a680f25f19797407ab56a5a4880ddc (osmo-mgw) Ida0e59f9a1f2dd18efea0a51680a67b69f141efa (osmo-mgw) I9a3effd38e72841529df6c135c077116981dea36 (osmo-mgw) Change-Id: I27e4988e0371808b512c757d2b52ada1615067bd
Diffstat (limited to 'include')
-rw-r--r--include/osmocom/msc/Makefile.am32
-rw-r--r--include/osmocom/msc/a_iface.h83
-rw-r--r--include/osmocom/msc/a_iface_bssap.h41
-rw-r--r--include/osmocom/msc/a_reset.h31
-rw-r--r--include/osmocom/msc/call_leg.h81
-rw-r--r--include/osmocom/msc/cell_id_list.h43
-rw-r--r--include/osmocom/msc/e_link.h36
-rw-r--r--include/osmocom/msc/gsm_04_08.h37
-rw-r--r--include/osmocom/msc/gsm_04_11.h5
-rw-r--r--include/osmocom/msc/gsm_04_11_gsup.h7
-rw-r--r--include/osmocom/msc/gsm_04_14.h14
-rw-r--r--include/osmocom/msc/gsm_04_80.h15
-rw-r--r--include/osmocom/msc/gsm_09_11.h10
-rw-r--r--include/osmocom/msc/gsm_data.h41
-rw-r--r--include/osmocom/msc/gsm_data_shared.h6
-rw-r--r--include/osmocom/msc/gsm_subscriber.h36
-rw-r--r--include/osmocom/msc/gsup_client_mux.h34
-rw-r--r--include/osmocom/msc/iu_dummy.h50
-rw-r--r--include/osmocom/msc/iucs.h14
-rw-r--r--include/osmocom/msc/iucs_ranap.h7
-rw-r--r--include/osmocom/msc/mncc.h14
-rw-r--r--include/osmocom/msc/mncc_call.h140
-rw-r--r--include/osmocom/msc/msc_a.h215
-rw-r--r--include/osmocom/msc/msc_a_remote.h17
-rw-r--r--include/osmocom/msc/msc_common.h39
-rw-r--r--include/osmocom/msc/msc_ho.h104
-rw-r--r--include/osmocom/msc/msc_i.h46
-rw-r--r--include/osmocom/msc/msc_i_remote.h14
-rw-r--r--include/osmocom/msc/msc_ifaces.h39
-rw-r--r--include/osmocom/msc/msc_mgcp.h65
-rw-r--r--include/osmocom/msc/msc_roles.h387
-rw-r--r--include/osmocom/msc/msc_t.h60
-rw-r--r--include/osmocom/msc/msc_t_remote.h14
-rw-r--r--include/osmocom/msc/msub.h79
-rw-r--r--include/osmocom/msc/neighbor_ident.h68
-rw-r--r--include/osmocom/msc/paging.h46
-rw-r--r--include/osmocom/msc/ran_conn.h243
-rw-r--r--include/osmocom/msc/ran_infra.h31
-rw-r--r--include/osmocom/msc/ran_msg.h281
-rw-r--r--include/osmocom/msc/ran_msg_a.h45
-rw-r--r--include/osmocom/msc/ran_msg_iu.h35
-rw-r--r--include/osmocom/msc/ran_peer.h106
-rw-r--r--include/osmocom/msc/rtp_stream.h64
-rw-r--r--include/osmocom/msc/sccp_ran.h280
-rw-r--r--include/osmocom/msc/sgs_iface.h9
-rw-r--r--include/osmocom/msc/signal.h20
-rw-r--r--include/osmocom/msc/silent_call.h13
-rw-r--r--include/osmocom/msc/sms_queue.h1
-rw-r--r--include/osmocom/msc/transaction.h55
-rw-r--r--include/osmocom/msc/vlr.h42
-rw-r--r--include/osmocom/msc/vlr_sgs.h2
51 files changed, 2470 insertions, 727 deletions
diff --git a/include/osmocom/msc/Makefile.am b/include/osmocom/msc/Makefile.am
index 408d710e3..729dae444 100644
--- a/include/osmocom/msc/Makefile.am
+++ b/include/osmocom/msc/Makefile.am
@@ -1,28 +1,42 @@
noinst_HEADERS = \
- a_iface.h \
- a_iface_bssap.h \
+ call_leg.h \
+ cell_id_list.h \
db.h \
debug.h \
+ e_link.h \
gsm_04_08.h \
- gsm_04_11.h \
gsm_04_11_gsup.h \
+ gsm_04_11.h \
gsm_04_14.h \
gsm_04_80.h \
gsm_09_11.h \
gsm_data.h \
gsm_data_shared.h \
gsm_subscriber.h \
- iucs.h \
- iucs_ranap.h \
- iu_dummy.h \
mncc.h \
mncc_int.h \
+ mncc_call.h \
+ msc_a.h \
+ msc_a_remote.h \
msc_common.h \
- msc_ifaces.h \
- msc_mgcp.h \
- a_reset.h \
+ msc_ho.h \
+ msc_i.h \
+ msc_i_remote.h \
+ msc_roles.h \
+ msc_t.h \
+ msc_t_remote.h \
+ msub.h \
+ neighbor_ident.h \
+ paging.h \
ran_conn.h \
+ ran_infra.h \
+ ran_msg.h \
+ ran_msg_a.h \
+ ran_msg_iu.h \
+ ran_peer.h \
rrlp.h \
+ rtp_stream.h \
+ sccp_ran.h \
sgs_iface.h \
sgs_server.h \
sgs_vty.h \
diff --git a/include/osmocom/msc/a_iface.h b/include/osmocom/msc/a_iface.h
deleted file mode 100644
index d8a8aab38..000000000
--- a/include/osmocom/msc/a_iface.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/* (C) 2017 by Sysmocom s.f.m.c. GmbH
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * 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/>.
- *
- */
-
-#pragma once
-
-#include <osmocom/msc/a_reset.h>
-#include <osmocom/msc/transaction.h>
-#include <osmocom/msc/vlr.h>
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-
-/* A struct to keep a context information about the BSCs we are associated with */
-struct bsc_context {
- struct llist_head list;
-
- /* Holds a copy of the sccp address of the BSC,
- * this address will become known as soon as
- * a remote BSC tries to make a connection or
- * sends a RESET request via UNIDATA */
- struct osmo_sccp_addr bsc_addr;
-
- /* Holds a copy of the our local MSC address,
- * this will be the sccp-address that is associated
- * with the A interface */
- struct osmo_sccp_addr msc_addr;
-
- /* A pointer to the reset handler FSM, the
- * state machine is allocated when the BSC
- * is registerd. */
- struct osmo_fsm_inst *reset_fsm;
-
- /* A pointer to the sccp_user that is associated
- * with the A interface. We need this information
- * to send the resets and to send paging requests */
- struct osmo_sccp_user *sccp_user;
-};
-
-/* Initalize A interface connection between to MSC and BSC */
-int a_init(struct osmo_sccp_instance *sccp, struct gsm_network *network);
-
-/* Send DTAP message via A-interface, take ownership of msg */
-int a_iface_tx_dtap(struct msgb *msg);
-
-/* Send Cipher mode command via A-interface */
-int a_iface_tx_cipher_mode(const struct ran_conn *conn,
- struct gsm0808_encrypt_info *ei, int include_imeisv);
-
-/* Page a subscriber via A-interface */
-int a_iface_tx_paging(const char *imsi, uint32_t tmsi, uint16_t lac);
-
-/* Send assignment request via A-interface */
-int a_iface_tx_assignment(const struct gsm_trans *trans);
-
-/* Send clear command via A-interface */
-int a_iface_tx_clear_cmd(const struct ran_conn *conn);
-
-int a_iface_tx_classmark_request(const struct ran_conn *conn);
-
-/* Clear all RAN connections on a specified BSC
- * (Helper function for a_iface_bssap.c) */
-void a_clear_all(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *bsc_addr);
-
-void a_start_reset(struct bsc_context *bsc_ctx, bool already_connected);
-
-/* Delete info of a closed connection from the active connection list
- * (Helper function for a_iface_bssap.c) */
-void a_delete_bsc_con(uint32_t conn_id);
diff --git a/include/osmocom/msc/a_iface_bssap.h b/include/osmocom/msc/a_iface_bssap.h
deleted file mode 100644
index d4b67e3ec..000000000
--- a/include/osmocom/msc/a_iface_bssap.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* (C) 2017 by sysmocom s.f.m.c. GmbH
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * 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/>.
- *
- */
-
-#pragma once
-
-#include <osmocom/msc/a_iface.h>
-
-/* Note: The structs and functions presented in this header file are intended
- * to be used only by a_iface.c. */
-
-/* A structure to hold tha most basic information about a sigtran connection
- * we use this struct internally here to pass connection data around */
-struct a_conn_info {
- struct bsc_context *bsc;
- uint32_t conn_id;
- struct gsm_network *network;
-};
-
-/* Receive incoming connection less data messages via sccp */
-void a_sccp_rx_udt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg);
-
-/* Receive incoming connection oriented data messages via sccp */
-int a_sccp_rx_dt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg);
-
diff --git a/include/osmocom/msc/a_reset.h b/include/osmocom/msc/a_reset.h
deleted file mode 100644
index 8eb3bbfda..000000000
--- a/include/osmocom/msc/a_reset.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* (C) 2017 by sysmocom s.f.m.c. GmbH
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * 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/>.
- *
- */
-
-#pragma once
-
-/* Create and start state machine which handles the reset/reset-ack procedure */
-struct osmo_fsm_inst *a_reset_alloc(void *ctx, const char *name, void *cb,
- void *priv, bool already_connected);
-
-/* Confirm that we sucessfully received a reset acknowlege message */
-void a_reset_ack_confirm(struct osmo_fsm_inst *reset_fsm);
-
-/* Check if we have a connection to a specified msc */
-bool a_reset_conn_ready(struct osmo_fsm_inst *reset_fsm);
diff --git a/include/osmocom/msc/call_leg.h b/include/osmocom/msc/call_leg.h
new file mode 100644
index 000000000..b8126e82d
--- /dev/null
+++ b/include/osmocom/msc/call_leg.h
@@ -0,0 +1,81 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/tdef.h>
+
+struct osmo_fsm_inst;
+struct osmo_sockaddr_str;
+struct osmo_mgcpc_ep;
+struct gsm_network;
+struct gsm_trans;
+struct rtp_stream;
+enum rtp_direction;
+
+extern struct osmo_tdef g_mgw_tdefs[];
+
+/* All sides of an MGW endpoint, connecting remote RTP peers via the MGW.
+ *
+ * BSC MGW PBX
+ * CI CI
+ * [MGW-endpoint]
+ * [--rtp_stream--] [--rtp_stream--]
+ * [----------------call_leg----------------]
+ *
+ */
+struct call_leg {
+ struct osmo_fsm_inst *fi;
+
+ struct osmo_mgcpc_ep *mgw_endpoint;
+
+ /* Array indexed by enum rtp_direction. */
+ struct rtp_stream *rtp[2];
+ /* Array indexed by enum rtp_direction. */
+ enum mgcp_connection_mode crcx_conn_mode[2];
+
+ uint32_t parent_event_rtp_addr_available;
+ uint32_t parent_event_rtp_complete;
+ uint32_t parent_event_rtp_released;
+
+ /* For internal MNCC, if RTP addresses for endpoints become assigned by the MGW, implicitly notify the other
+ * call leg's RTP_TO_CN side rtp_stream with rtp_stream_remote_addr_available(). */
+ struct call_leg *local_bridge;
+
+ /* Prevent events from deallocating for certain release code paths, to prevent use-after-free problems. */
+ bool deallocating;
+};
+
+enum call_leg_event {
+ CALL_LEG_EV_RTP_STREAM_ADDR_AVAILABLE,
+ CALL_LEG_EV_RTP_STREAM_ESTABLISHED,
+ CALL_LEG_EV_RTP_STREAM_GONE,
+ CALL_LEG_EV_MGW_ENDPOINT_GONE,
+};
+
+void call_leg_init(struct gsm_network *net);
+
+struct call_leg *call_leg_alloc(struct osmo_fsm_inst *parent_fi,
+ uint32_t parent_event_term,
+ uint32_t parent_event_rtp_addr_available,
+ uint32_t parent_event_rtp_complete,
+ uint32_t parent_event_rtp_released);
+
+void call_leg_reparent(struct call_leg *cl,
+ struct osmo_fsm_inst *parent_fi,
+ uint32_t parent_event_term,
+ uint32_t parent_event_rtp_addr_available,
+ uint32_t parent_event_rtp_complete,
+ uint32_t parent_event_rtp_released);
+
+int call_leg_local_bridge(struct call_leg *cl1, uint32_t call_id1, struct gsm_trans *trans1,
+ struct call_leg *cl2, uint32_t call_id2, struct gsm_trans *trans2);
+
+int call_leg_ensure_rtp_alloc(struct call_leg *cl, enum rtp_direction dir, uint32_t call_id,
+ struct gsm_trans *for_trans);
+int call_leg_ensure_ci(struct call_leg *cl, enum rtp_direction dir, uint32_t call_id, struct gsm_trans *for_trans,
+ const enum mgcp_codecs *codec_if_known, const struct osmo_sockaddr_str *remote_port_if_known);
+struct osmo_sockaddr_str *call_leg_local_ip(struct call_leg *cl, enum rtp_direction dir);
+
+void call_leg_rtp_stream_gone(struct call_leg *cl, struct rtp_stream *rtps);
+void call_leg_release(struct call_leg *cl);
diff --git a/include/osmocom/msc/cell_id_list.h b/include/osmocom/msc/cell_id_list.h
new file mode 100644
index 000000000..83d05f5da
--- /dev/null
+++ b/include/osmocom/msc/cell_id_list.h
@@ -0,0 +1,43 @@
+/* Manage a list of struct gsm0808_cell_id */
+/*
+ * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/gsm/gsm0808_utils.h>
+
+struct cell_id_list_entry {
+ struct llist_head entry;
+ struct gsm0808_cell_id cell_id;
+};
+
+int cell_id_list_add_cell(void *talloc_ctx, struct llist_head *list, const struct gsm0808_cell_id *cid);
+int cell_id_list_add_list(void *talloc_ctx, struct llist_head *list, const struct gsm0808_cell_id_list2 *cil);
+
+struct cell_id_list_entry *cell_id_list_find(struct llist_head *list,
+ const struct gsm0808_cell_id *id,
+ unsigned int match_nr,
+ bool exact_match);
+
+void cell_id_list_del_entry(struct cell_id_list_entry *e);
diff --git a/include/osmocom/msc/e_link.h b/include/osmocom/msc/e_link.h
new file mode 100644
index 000000000..88e41a7aa
--- /dev/null
+++ b/include/osmocom/msc/e_link.h
@@ -0,0 +1,36 @@
+/* E-interface messaging over a GSUP connection */
+#pragma once
+
+#include <osmocom/gsm/gsup.h>
+#include <osmocom/msc/msc_roles.h>
+
+struct osmo_fsm_inst;
+struct gsm_network;
+struct vlr_instance;
+
+/* E-interface: connection to a remote MSC via GSUP */
+struct e_link {
+ struct osmo_fsm_inst *msc_role;
+ struct gsup_client_mux *gcm;
+ uint8_t *remote_name;
+ size_t remote_name_len;
+};
+
+struct e_link *e_link_alloc(struct gsup_client_mux *gcm, struct osmo_fsm_inst *msc_role,
+ const uint8_t *remote_name, size_t remote_name_len);
+void e_link_assign(struct e_link *e, struct osmo_fsm_inst *msc_role);
+void e_link_free(struct e_link *e);
+
+int e_prep_gsup_msg(struct e_link *e, enum msc_role from_role, struct osmo_gsup_message *gsup_msg);
+int e_tx(struct e_link *e, const struct osmo_gsup_message *gsup_msg);
+
+const char *e_link_name(struct e_link *e);
+
+void msc_a_i_t_gsup_init(struct gsm_network *net);
+
+enum osmo_gsup_entity msc_role_to_gsup_entity(enum msc_role role);
+enum msc_role gsup_entity_to_msc_role(enum osmo_gsup_entity entity);
+int gsup_msg_assign_an_apdu(struct osmo_gsup_message *gsup_msg, struct an_apdu *an_apdu);
+
+struct msgb *gsup_msg_to_msgb(const struct osmo_gsup_message *gsup_msg);
+void gsup_msg_to_an_apdu(struct an_apdu *an_apdu, const struct osmo_gsup_message *gsup_msg);
diff --git a/include/osmocom/msc/gsm_04_08.h b/include/osmocom/msc/gsm_04_08.h
index 2d4a0cd77..47747cbcf 100644
--- a/include/osmocom/msc/gsm_04_08.h
+++ b/include/osmocom/msc/gsm_04_08.h
@@ -4,6 +4,7 @@
#include <osmocom/gsm/gsm48.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/msc/transaction.h>
struct msgb;
struct gsm_bts;
@@ -12,6 +13,7 @@ struct gsm_trans;
struct ran_conn;
struct amr_multirate_conf;
struct amr_mode;
+struct msc_a;
#define GSM48_ALLOC_SIZE 2048
#define GSM48_ALLOC_HEADROOM 256
@@ -22,33 +24,26 @@ static inline struct msgb *gsm48_msgb_alloc_name(const char *name)
name);
}
-void cm_service_request_concludes(struct ran_conn *conn,
- struct msgb *msg);
+void cm_service_request_concludes(struct msc_a *msc_a, struct msgb *msg, enum osmo_cm_service_type type);
/* config options controlling the behaviour of the lower leves */
-void gsm0408_clear_all_trans(struct gsm_network *net, int protocol);
-int gsm0408_dispatch(struct ran_conn *conn, struct msgb *msg);
+void gsm0408_clear_all_trans(struct gsm_network *net, enum trans_type type);
int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id);
/* don't use "enum gsm_chreq_reason_t" to avoid circular dependency */
void gsm_net_update_ctype(struct gsm_network *net);
-int gsm48_tx_simple(struct ran_conn *conn,
- uint8_t pdisc, uint8_t msg_type);
-int gsm48_tx_mm_info(struct ran_conn *conn);
-int gsm48_tx_mm_auth_req(struct ran_conn *conn, uint8_t *rand,
- uint8_t *autn, int key_seq);
-int gsm48_tx_mm_auth_rej(struct ran_conn *conn);
-int gsm48_tx_mm_serv_ack(struct ran_conn *conn);
-int gsm48_tx_mm_serv_rej(struct ran_conn *conn,
- enum gsm48_reject_value value);
+int gsm48_tx_simple(struct msc_a *msc_a, uint8_t pdisc, uint8_t msg_type);
+int gsm48_tx_mm_info(struct msc_a *msc_a);
+int gsm48_tx_mm_auth_req(struct msc_a *msc_a, uint8_t *rand, uint8_t *autn, int key_seq);
+int gsm48_tx_mm_auth_rej(struct msc_a *msc_a);
+int gsm48_tx_mm_serv_ack(struct msc_a *msc_a);
+int gsm48_tx_mm_serv_rej(struct msc_a *msc_a, enum gsm48_reject_value value);
int gsm48_send_rr_release(struct gsm_lchan *lchan);
int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv);
-int gsm48_send_rr_app_info(struct ran_conn *conn, uint8_t apdu_id,
- uint8_t apdu_len, const uint8_t *apdu);
+int gsm48_send_rr_app_info(struct msc_a *msc_a, uint8_t apdu_id, uint8_t apdu_len, const uint8_t *apdu);
int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_class);
-int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
- uint8_t power_command, uint8_t ho_ref);
+int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, uint8_t power_command, uint8_t ho_ref);
int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg);
@@ -79,4 +74,12 @@ int gsm48_tch_rtp_create(struct gsm_trans *trans);
int gsm48_conn_sendmsg(struct msgb *msg, struct ran_conn *conn, struct gsm_trans *trans);
struct msgb *gsm48_create_mm_info(struct gsm_network *net);
+int gsm0408_rcv_cc(struct msc_a *msc_a, struct msgb *msg);
+int gsm0408_rcv_mm(struct msc_a *msc_a, struct msgb *msg);
+int gsm0408_rcv_rr(struct msc_a *msc_a, struct msgb *msg);
+
+int msc_vlr_tx_cm_serv_acc(void *msc_conn_ref, enum osmo_cm_service_type cm_service_type);
+
+int compl_l3_msg_is_r99(const struct msgb *msg);
+
#endif
diff --git a/include/osmocom/msc/gsm_04_11.h b/include/osmocom/msc/gsm_04_11.h
index 4297cdb4a..be8bff3c3 100644
--- a/include/osmocom/msc/gsm_04_11.h
+++ b/include/osmocom/msc/gsm_04_11.h
@@ -7,6 +7,7 @@
struct vlr_subscr;
struct ran_conn;
struct gsm_trans;
+struct msc_a;
#define UM_SAPI_SMS 3 /* See GSM 04.05/04.06 */
@@ -31,7 +32,7 @@ struct sms_deliver {
struct gsm_network;
struct msgb;
-int gsm0411_rcv_sms(struct ran_conn *conn, struct msgb *msg);
+int gsm0411_rcv_sms(struct msc_a *msc_a, struct msgb *msg);
struct gsm_sms *sms_alloc(void);
void sms_free(struct gsm_sms *sms);
@@ -46,7 +47,7 @@ int gsm411_send_rp_data(struct gsm_network *net, struct vlr_subscr *vsub,
size_t sm_rp_oa_len, const uint8_t *sm_rp_oa,
size_t sm_rp_ud_len, const uint8_t *sm_rp_ud);
-void gsm411_sapi_n_reject(struct ran_conn *conn);
+void gsm411_sapi_n_reject(struct msc_a *msc_a);
int gsm411_send_rp_ack(struct gsm_trans *trans, uint8_t msg_ref);
int gsm411_send_rp_error(struct gsm_trans *trans, uint8_t msg_ref,
diff --git a/include/osmocom/msc/gsm_04_11_gsup.h b/include/osmocom/msc/gsm_04_11_gsup.h
index 969eabad2..4034f5e19 100644
--- a/include/osmocom/msc/gsm_04_11_gsup.h
+++ b/include/osmocom/msc/gsm_04_11_gsup.h
@@ -2,6 +2,7 @@
#include <stdint.h>
+struct gsup_client_mux;
struct osmo_gsup_message;
struct vlr_subscr;
struct gsm_trans;
@@ -10,11 +11,9 @@ struct msgb;
int gsm411_gsup_mo_ready_for_sm_req(struct gsm_trans *trans, uint8_t sm_rp_mr);
int gsm411_gsup_mo_fwd_sm_req(struct gsm_trans *trans, struct msgb *msg,
uint8_t sm_rp_mr, uint8_t *sm_rp_da, uint8_t sm_rp_da_len);
-int gsm411_gsup_mo_handler(struct vlr_subscr *vsub,
- struct osmo_gsup_message *gsup_msg);
int gsm411_gsup_mt_fwd_sm_res(struct gsm_trans *trans, uint8_t sm_rp_mr);
int gsm411_gsup_mt_fwd_sm_err(struct gsm_trans *trans,
uint8_t sm_rp_mr, uint8_t cause);
-int gsm411_gsup_mt_handler(struct vlr_subscr *vsub,
- struct osmo_gsup_message *gsup_msg);
+
+int gsm411_gsup_rx(struct gsup_client_mux *gcm, void *data, const struct osmo_gsup_message *gsup_msg);
diff --git a/include/osmocom/msc/gsm_04_14.h b/include/osmocom/msc/gsm_04_14.h
index a6bafce90..3513c68bb 100644
--- a/include/osmocom/msc/gsm_04_14.h
+++ b/include/osmocom/msc/gsm_04_14.h
@@ -2,14 +2,16 @@
#include <osmocom/gsm/protocol/gsm_04_14.h>
-int gsm0414_tx_close_tch_loop_cmd(struct ran_conn *conn,
+struct msc_a;
+
+int gsm0414_tx_close_tch_loop_cmd(struct msc_a *msc_a,
enum gsm414_tch_loop_mode loop_mode);
-int gsm0414_tx_open_loop_cmd(struct ran_conn *conn);
-int gsm0414_tx_act_emmi_cmd(struct ran_conn *conn);
-int gsm0414_tx_test_interface(struct ran_conn *conn,
+int gsm0414_tx_open_loop_cmd(struct msc_a *msc_a);
+int gsm0414_tx_act_emmi_cmd(struct msc_a *msc_a);
+int gsm0414_tx_test_interface(struct msc_a *msc_a,
uint8_t tested_devs);
-int gsm0414_tx_reset_ms_pos_store(struct ran_conn *conn,
+int gsm0414_tx_reset_ms_pos_store(struct msc_a *msc_a,
uint8_t technology);
-int gsm0414_rcv_test(struct ran_conn *conn,
+int gsm0414_rcv_test(struct msc_a *msc_a,
struct msgb *msg);
diff --git a/include/osmocom/msc/gsm_04_80.h b/include/osmocom/msc/gsm_04_80.h
index b786dcc49..bb6573b26 100644
--- a/include/osmocom/msc/gsm_04_80.h
+++ b/include/osmocom/msc/gsm_04_80.h
@@ -2,16 +2,13 @@
#include <stdint.h>
-struct ran_conn;
+struct msc_a;
-int msc_send_ussd_reject(struct ran_conn *conn,
- uint8_t transaction_id, int invoke_id,
- uint8_t problem_tag, uint8_t problem_code);
+int msc_send_ussd_reject(struct msc_a *msc_a, uint8_t transaction_id, int invoke_id,
+ uint8_t problem_tag, uint8_t problem_code);
-int msc_send_ussd_notify(struct ran_conn *conn, int level,
- const char *text);
-int msc_send_ussd_release_complete(struct ran_conn *conn,
- uint8_t transaction_id);
-int msc_send_ussd_release_complete_cause(struct ran_conn *conn,
+int msc_send_ussd_notify(struct msc_a *msc_a, int level, const char *text);
+int msc_send_ussd_release_complete(struct msc_a *msc_a, uint8_t transaction_id);
+int msc_send_ussd_release_complete_cause(struct msc_a *msc_a,
uint8_t transaction_id,
uint8_t cause_loc, uint8_t cause_val);
diff --git a/include/osmocom/msc/gsm_09_11.h b/include/osmocom/msc/gsm_09_11.h
index 8fbe41be3..324befcd1 100644
--- a/include/osmocom/msc/gsm_09_11.h
+++ b/include/osmocom/msc/gsm_09_11.h
@@ -1,7 +1,9 @@
#pragma once
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/gsup.h>
+struct msc_a;
+struct mgsb;
+struct gsup_client_mux;
+struct osmo_gsup_message;
-int gsm0911_rcv_nc_ss(struct ran_conn *conn, struct msgb *msg);
-int gsm0911_gsup_handler(struct vlr_subscr *vsub, struct osmo_gsup_message *gsup);
+int gsm0911_rcv_nc_ss(struct msc_a *msc_a, struct msgb *msg);
+int gsm0911_gsup_rx(struct gsup_client_mux *gcm, void *data, const struct osmo_gsup_message *msg);
diff --git a/include/osmocom/msc/gsm_data.h b/include/osmocom/msc/gsm_data.h
index 1a0d14463..42bb69a06 100644
--- a/include/osmocom/msc/gsm_data.h
+++ b/include/osmocom/msc/gsm_data.h
@@ -16,23 +16,17 @@
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/msc/msc_common.h>
+#include <osmocom/msc/neighbor_ident.h>
#include "gsm_data_shared.h"
-/* TS 48.008 DLCI containing DCCH/ACCH + SAPI */
-#define OMSC_LINKID_CB(__msgb) (__msgb)->cb[3]
-
-#include "../../bscconfig.h"
-#if BUILD_IU
-#include <osmocom/ranap/iu_client.h>
-#endif
-
/** annotations for msgb ownership */
#define __uses
struct mncc_sock_state;
struct vlr_instance;
struct vlr_subscr;
+struct gsup_client_mux;
#define tmsi_from_string(str) strtoul(str, NULL, 10)
@@ -144,6 +138,7 @@ struct gsm_network {
struct mncc_sock_state *mncc_state;
mncc_recv_cb_t mncc_recv;
struct llist_head upqueue;
+ struct osmo_tdef *mncc_tdefs;
/*
* TODO: Move the trans_list into the RAN connection and
* create a pending list for MT transactions. These exist before
@@ -171,9 +166,6 @@ struct gsm_network {
/* control interface */
struct ctrl_handle *ctrl;
- /* all active RAN connections. */
- struct llist_head ran_conns;
-
/* if override is nonzero, this timezone data is used for all MM
* contexts. */
/* TODO: in OsmoNITB, tz-override used to be BTS-specific. To enable
@@ -184,6 +176,7 @@ struct gsm_network {
/* MSC: GSUP server address of the HLR */
const char *gsup_server_addr_str;
uint16_t gsup_server_port;
+ struct gsup_client_mux *gcm;
struct vlr_instance *vlr;
@@ -196,28 +189,30 @@ struct gsm_network {
int ncss_guard_timeout;
struct {
+ struct osmo_tdef *tdefs;
struct mgcp_client_conf conf;
struct mgcp_client *client;
} mgw;
-#if BUILD_IU
struct {
/* CS7 instance id number (set via VTY) */
uint32_t cs7_instance;
- enum ranap_nsap_addr_enc rab_assign_addr_enc;
- struct osmo_sccp_instance *sccp;
+ enum nsap_addr_enc rab_assign_addr_enc;
+
+ struct sccp_ran_inst *sri;
} iu;
-#endif
struct {
/* CS7 instance id number (set via VTY) */
uint32_t cs7_instance;
- /* A list with the context information about
- * all BSCs we have connections with */
- struct llist_head bscs;
- struct osmo_sccp_instance *sccp;
+
+ struct sccp_ran_inst *sri;
} a;
+ /* A list of neighbor BSCs. This list is defined statically via VTY and does not
+ * necessarily correspond to BSCs attached to the A interface at a given moment. */
+ struct neighbor_ident_list *neighbor_list;
+
struct {
/* MSISDN to which to route MO emergency calls */
char *route_to_msisdn;
@@ -228,6 +223,14 @@ struct gsm_network {
* If no name is set, the IPA Serial Number will be the same as the Unit Name,
* and will be of the form 'MSC-00-00-00-00-00-00' */
char *msc_ipa_name;
+
+ struct llist_head neighbor_ident_list;
+
+ struct {
+ uint64_t range_start;
+ uint64_t range_end;
+ uint64_t next;
+ } handover_number;
};
struct osmo_esme;
diff --git a/include/osmocom/msc/gsm_data_shared.h b/include/osmocom/msc/gsm_data_shared.h
index 732607bc1..511d6bca3 100644
--- a/include/osmocom/msc/gsm_data_shared.h
+++ b/include/osmocom/msc/gsm_data_shared.h
@@ -31,10 +31,4 @@ enum gsm_hooks {
GSM_HOOK_RR_SECURITY,
};
-enum gsm_paging_event {
- GSM_PAGING_SUCCEEDED,
- GSM_PAGING_EXPIRED,
- GSM_PAGING_BUSY,
-};
-
#endif
diff --git a/include/osmocom/msc/gsm_subscriber.h b/include/osmocom/msc/gsm_subscriber.h
index f848ac850..31eca6b0f 100644
--- a/include/osmocom/msc/gsm_subscriber.h
+++ b/include/osmocom/msc/gsm_subscriber.h
@@ -12,40 +12,4 @@
struct ran_conn;
struct msgb;
-typedef int gsm_cbfn(unsigned int hooknum, unsigned int event, struct msgb *msg,
- void *data, void *param);
-
-/*
- * Struct for pending channel requests. This is managed in the
- * llist_head requests of each subscriber. The reference counting
- * should work in such a way that a subscriber with a pending request
- * remains in memory.
- */
-struct subscr_request {
- struct llist_head entry;
-
- /* human readable label to be able to log pending request kinds */
- const char *label;
-
- /* the callback data */
- gsm_cbfn *cbfn;
- void *param;
-};
-
-/*
- * Paging handling with authentication
- */
-struct subscr_request *subscr_request_conn(struct vlr_subscr *vsub,
- gsm_cbfn *cbfn, void *param,
- const char *label,
- enum sgsap_service_ind serv_ind);
-void subscr_remove_request(struct subscr_request *req);
-
-void subscr_paging_cancel(struct vlr_subscr *vsub, enum gsm_paging_event event);
-int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *data, void *param);
-
-/* Find an allocated channel for a specified subscriber */
-struct ran_conn *connection_for_subscr(struct vlr_subscr *vsub);
-
#endif /* _GSM_SUBSCR_H */
diff --git a/include/osmocom/msc/gsup_client_mux.h b/include/osmocom/msc/gsup_client_mux.h
new file mode 100644
index 000000000..07f17c260
--- /dev/null
+++ b/include/osmocom/msc/gsup_client_mux.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <osmocom/gsm/gsup.h>
+#include <osmocom/msc/gsup_client_mux.h>
+
+struct gsup_client_mux;
+struct ipaccess_unit;
+
+struct gsup_client_mux_rx_cb {
+ int (* func )(struct gsup_client_mux *gcm, void *data, const struct osmo_gsup_message *gsup_msg);
+ void *data;
+};
+
+/* A GSUP client shared between code paths for various GSUP Message Classes.
+ * The main task is to dispatch GSUP messages to code paths corresponding to the respective Message Class, i.e.
+ * subscriber management, SMS, SS/USSD and inter-MSC messaging.
+ * If a GSUP Message Class IE is present in the message, the received message is dispatched directly to the rx_cb entry
+ * for that Message Class. Otherwise, the Message Class is determined by a switch() on the Message Type.*/
+struct gsup_client_mux {
+ struct osmo_gsup_client *gsup_client;
+
+ /* Target clients by enum osmo_gsup_message_class */
+ struct gsup_client_mux_rx_cb rx_cb[OSMO_GSUP_MESSAGE_CLASS_ARRAYSIZE];
+};
+
+struct gsup_client_mux *gsup_client_mux_alloc(void *talloc_ctx);
+int gsup_client_mux_start(struct gsup_client_mux *gcm, const char *gsup_server_addr_str, uint16_t gsup_server_port,
+ struct ipaccess_unit *ipa_dev);
+
+int gsup_client_mux_tx(struct gsup_client_mux *gcm, const struct osmo_gsup_message *gsup_msg);
+void gsup_client_mux_tx_error_reply(struct gsup_client_mux *gcm, const struct osmo_gsup_message *gsup_orig,
+ enum gsm48_gmm_cause cause);
+
+int gsup_client_mux_rx(struct osmo_gsup_client *gsup_client, struct msgb *msg);
diff --git a/include/osmocom/msc/iu_dummy.h b/include/osmocom/msc/iu_dummy.h
deleted file mode 100644
index 01a8aa608..000000000
--- a/include/osmocom/msc/iu_dummy.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Trivial switch-off of external Iu dependencies,
- * allowing to run full unit tests even when built without Iu support. */
-
-/*
- * (C) 2016,2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- *
- * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
- *
- * All Rights Reserved
- *
- * 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 <stdint.h>
-#include <stdbool.h>
-
-#include <osmocom/core/linuxlist.h>
-
-struct msgb;
-struct RANAP_Cause;
-struct osmo_auth_vector;
-
-struct ranap_ue_conn_ctx {
- struct llist_head list;
- uint32_t conn_id;
-};
-
-int ranap_iu_tx(struct msgb *msg, uint8_t sapi);
-int ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec,
- int send_ck);
-int ranap_iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac);
-int ranap_iu_page_ps(const char *imsi, const uint32_t *ptmsi, uint16_t lac, uint8_t rac);
-struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id, uint32_t rtp_ip,
- uint16_t rtp_port,
- bool use_x213_nsap);
-int ranap_iu_rab_act(struct ranap_ue_conn_ctx *ue_ctx, struct msgb *msg);
-int ranap_iu_tx_common_id(struct ranap_ue_conn_ctx *uectx, const char *imsi);
-int ranap_iu_tx_release(struct ranap_ue_conn_ctx *ctx, const struct RANAP_Cause *cause);
diff --git a/include/osmocom/msc/iucs.h b/include/osmocom/msc/iucs.h
deleted file mode 100644
index 302edc0e6..000000000
--- a/include/osmocom/msc/iucs.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-
-#include <osmocom/msc/transaction.h>
-
-struct ranap_ue_conn_ctx;
-
-int gsm0408_rcvmsg_iucs(struct gsm_network *network, struct msgb *msg,
- uint16_t *lac);
-
-struct ran_conn *ran_conn_lookup_iu(struct gsm_network *network,
- struct ranap_ue_conn_ctx *ue);
-int iu_rab_act_cs(struct gsm_trans *trans);
-
-uint32_t iu_get_conn_id(const struct ranap_ue_conn_ctx *ue);
diff --git a/include/osmocom/msc/iucs_ranap.h b/include/osmocom/msc/iucs_ranap.h
deleted file mode 100644
index c2ff5f90e..000000000
--- a/include/osmocom/msc/iucs_ranap.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-
-struct gsm_network;
-struct ranap_ue_conn_ctx;
-
-int iucs_rx_ranap_event(struct gsm_network *network,
- struct ranap_ue_conn_ctx *ue_ctx, int type, void *data);
diff --git a/include/osmocom/msc/mncc.h b/include/osmocom/msc/mncc.h
index a9be0048c..28ee9b339 100644
--- a/include/osmocom/msc/mncc.h
+++ b/include/osmocom/msc/mncc.h
@@ -1,4 +1,4 @@
-/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
+/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
* 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
@@ -31,6 +31,7 @@
struct gsm_network;
struct msgb;
+struct gsm0808_channel_type;
/* One end of a call */
@@ -196,6 +197,15 @@ struct gsm_mncc_bridge {
uint32_t callref[2];
};
+union mncc_msg {
+ uint32_t msg_type;
+ struct gsm_mncc signal;
+ struct gsm_mncc_hello hello;
+ struct gsm_data_frame data_frame;
+ struct gsm_mncc_rtp rtp;
+ struct gsm_mncc_bridge bridge;
+};
+
const char *get_mncc_name(int value);
void mncc_set_cause(struct gsm_mncc *data, int loc, int val);
void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg);
@@ -217,4 +227,6 @@ int mncc_sock_init(struct gsm_network *net, const char *sock_path);
int mncc_prim_check(const struct gsm_mncc *mncc_prim, unsigned int len);
+int mncc_bearer_cap_to_channel_type(struct gsm0808_channel_type *ct, const struct gsm_mncc_bearer_cap *bc);
+
#endif
diff --git a/include/osmocom/msc/mncc_call.h b/include/osmocom/msc/mncc_call.h
new file mode 100644
index 000000000..ad0f0f841
--- /dev/null
+++ b/include/osmocom/msc/mncc_call.h
@@ -0,0 +1,140 @@
+/* Handle an MNCC managed call (external MNCC). */
+/*
+ * (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/>.
+ */
+#pragma once
+
+#include <osmocom/msc/mncc.h>
+#include <osmocom/msc/mncc_call.h>
+
+struct osmo_fsm_inst;
+struct rtp_stream;
+
+#define LOG_MNCC_CALL(MNCC, LEVEL, FMT, ARGS...) \
+ LOGPFSML((MNCC) ? (MNCC)->fi : NULL, LEVEL, FMT, ##ARGS)
+
+enum mncc_call_fsm_event {
+ /* An MNCC message was received from the MNCC socket. The data argument is a const union mncc_msg* pointing at
+ * the message contents. */
+ MNCC_CALL_EV_RX_MNCC_MSG,
+
+ /* The user has invoked mncc_call_outgoing_start(); this event exists to ensure that the FSM is in a state that
+ * allows starting a new outgoing call. */
+ MNCC_CALL_EV_OUTGOING_START,
+ /* The MNCC server has sent an MNCC_ALERT_REQ. */
+ MNCC_CALL_EV_OUTGOING_ALERTING,
+ /* The MNCC server has confirmed call setup with an MNCC_SETUP_RSP, we have sent an MNCC_SETUP_COMPL_IND. */
+ MNCC_CALL_EV_OUTGOING_SETUP_COMPLETE,
+
+ /* The user has invoked mncc_call_incoming_start(); this event exists to ensure that the FSM is in a state that
+ * allows starting a new incoming call. */
+ MNCC_CALL_EV_INCOMING_START,
+ /* MNCC server sent an MNCC_SETUP_REQ */
+ MNCC_CALL_EV_INCOMING_SETUP,
+ /* MNCC server confirmed call setup with an MNCC_SETUP_COMPL_REQ */
+ MNCC_CALL_EV_INCOMING_SETUP_COMPLETE,
+
+ /* MNCC server requests call release (Rx MNCC_DISC_REQ) */
+ MNCC_CALL_EV_CN_RELEASE,
+ /* osmo-msc should request call release (Tx MNCC_DISC_IND) */
+ MNCC_CALL_EV_MS_RELEASE,
+};
+
+/* The typical progression of outgoing and incoming calls via MNCC is shown by doc/sequence_charts/mncc_call_fsm.msc */
+enum mncc_call_fsm_state {
+ MNCC_CALL_ST_NOT_STARTED = 0,
+
+ MNCC_CALL_ST_OUTGOING_WAIT_PROCEEDING,
+ MNCC_CALL_ST_OUTGOING_WAIT_COMPLETE,
+
+ MNCC_CALL_ST_INCOMING_WAIT_COMPLETE,
+
+ MNCC_CALL_ST_TALKING,
+
+ MNCC_CALL_ST_WAIT_RELEASE_ACK,
+};
+
+struct mncc_call_incoming_req {
+ bool bearer_cap_present;
+ struct gsm_mncc_bearer_cap bearer_cap;
+
+ bool cccap_present;
+ struct gsm_mncc_cccap cccap;
+
+ struct gsm_mncc setup_req_msg;
+};
+
+struct mncc_call;
+typedef void (* mncc_call_message_cb_t )(struct mncc_call *mncc_call, const union mncc_msg *mncc_msg, void *data);
+
+struct mncc_call {
+ struct llist_head entry;
+
+ struct osmo_fsm_inst *fi;
+ struct vlr_subscr *vsub;
+ struct gsm_network *net;
+
+ /* Details originally passed to mncc_call_outgoing_start(), if any. */
+ struct gsm_mncc outgoing_req;
+
+ uint32_t callref;
+ bool remote_msisdn_present;
+ struct gsm_mncc_number remote_msisdn;
+ bool local_msisdn_present;
+ struct gsm_mncc_number local_msisdn;
+ struct rtp_stream *rtps;
+ bool received_rtp_create;
+
+ mncc_call_message_cb_t message_cb;
+ void *forward_cb_data;
+
+ /* Event to dispatch to the FSM inst parent when the call is complete. Omit event dispatch when negative. See
+ * mncc_call_alloc()'s arg of same name. */
+ int parent_event_call_setup_complete;
+};
+
+void mncc_call_fsm_init(struct gsm_network *net);
+struct mncc_call *mncc_call_alloc(struct vlr_subscr *vsub,
+ struct osmo_fsm_inst *parent,
+ int parent_event_call_setup_complete,
+ uint32_t parent_event_call_released,
+ mncc_call_message_cb_t message_cb, void *forward_cb_data);
+void mncc_call_reparent(struct mncc_call *mncc_call,
+ struct osmo_fsm_inst *new_parent,
+ int parent_event_call_setup_complete,
+ uint32_t parent_event_call_released,
+ mncc_call_message_cb_t message_cb, void *forward_cb_data);
+
+int mncc_call_outgoing_start(struct mncc_call *mncc_call, const struct gsm_mncc *outgoing_req);
+
+int mncc_call_incoming_start(struct mncc_call *mncc_call, const struct mncc_call_incoming_req *incoming_req);
+int mncc_call_incoming_tx_setup_cnf(struct mncc_call *mncc_call, const struct gsm_mncc_number *connected_number);
+
+int mncc_call_set_rtp_stream(struct mncc_call *mncc_call, struct rtp_stream *rtps);
+void mncc_call_detach_rtp_stream(struct mncc_call *mncc_call);
+
+void mncc_call_rx(struct mncc_call *mncc_call, const union mncc_msg *mncc_msg);
+int mncc_call_tx(struct mncc_call *mncc_call, union mncc_msg *mncc_msg);
+int mncc_call_tx_msgt(struct mncc_call *mncc_call, uint32_t msg_type);
+
+struct mncc_call *mncc_call_find_by_callref(uint32_t callref);
+
+void mncc_call_release(struct mncc_call *mncc_call);
diff --git a/include/osmocom/msc/msc_a.h b/include/osmocom/msc/msc_a.h
new file mode 100644
index 000000000..c732695a1
--- /dev/null
+++ b/include/osmocom/msc/msc_a.h
@@ -0,0 +1,215 @@
+/* MSC-A role: main subscriber management */
+/*
+ * (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/>.
+ */
+#pragma once
+
+#include <osmocom/core/use_count.h>
+#include <osmocom/core/tdef.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+#include <osmocom/gsm/protocol/gsm_08_08.h>
+#include <osmocom/gsm/gsm23003.h>
+
+#include <osmocom/msc/msc_roles.h>
+#include <osmocom/msc/ran_msg.h>
+#include <osmocom/msc/msc_common.h>
+#include <osmocom/msc/msc_ho.h>
+#include <osmocom/msc/neighbor_ident.h>
+
+struct ran_infra;
+
+#define MSC_A_USE_LOCATION_UPDATING "lu"
+#define MSC_A_USE_CM_SERVICE_CC "cm_service_cc"
+#define MSC_A_USE_CM_SERVICE_SMS "cm_service_sms"
+#define MSC_A_USE_CM_SERVICE_SS "cm_service_ss"
+#define MSC_A_USE_PAGING_RESPONSE "paging-response"
+#define MSC_A_USE_CC "cc"
+#define MSC_A_USE_SMS "sms"
+#define MSC_A_USE_NC_SS "nc_ss"
+#define MSC_A_USE_SILENT_CALL "silent_call"
+
+/* These are macros to use the source file:line information from the caller in a trivial way */
+#define msc_a_get(msc_a, use) \
+ OSMO_ASSERT(osmo_use_count_get_put(&msc_a->use_count, use, 1) == 0)
+#define msc_a_put(msc_a, use) \
+ OSMO_ASSERT(osmo_use_count_get_put(&msc_a->use_count, use, -1) == 0)
+#define msc_a_put_all(msc_a, use) do { \
+ int32_t has_count = osmo_use_count_by(&msc_a->use_count, use); \
+ if (has_count) \
+ OSMO_ASSERT(osmo_use_count_get_put(&msc_a->use_count, use, -has_count) == 0); \
+ } while(0)
+
+
+enum msc_a_action_on_classmark_update_type {
+ MSC_A_CLASSMARK_UPDATE_NOT_EXPECTED = 0,
+ MSC_A_CLASSMARK_UPDATE_THEN_CIPHERING,
+};
+
+/* A Classmark Update might be required for various tasks. At the time of writing, the only use case is to determine A5
+ * capabilities for choosing a ciphering algorithm. This structure anticipates other Classmark Update use cases to be
+ * added in the future. */
+struct msc_a_action_on_classmark_update {
+ enum msc_a_action_on_classmark_update_type type;
+ union {
+ /* State required to resume Ciphering after the Classmark Request / Classmark Update is complete. */
+ struct {
+ bool umts_aka;
+ bool retrieve_imeisv;
+ } ciphering;
+
+ /* Add more use cases here... */
+ };
+};
+
+struct msc_a {
+ /* struct msc_role_common must remain at start */
+ struct msc_role_common c;
+ enum complete_layer3_type complete_layer3_type;
+ struct osmo_cell_global_id via_cell;
+
+ /* Temporary storage for Classmark Information for times when a connection has no VLR subscriber
+ * associated yet. It will get copied to the VLR subscriber upon msc_vlr_subscr_assoc(). */
+ struct osmo_gsm48_classmark temporary_classmark;
+
+ /* See handling of E_MSC_A_CLASSMARK_UPDATE */
+ struct msc_a_action_on_classmark_update action_on_classmark_update;
+ uint32_t state_before_classmark_update;
+
+ /* After Ciphering Mode Complete on GERAN, this reflects the chosen ciphering algorithm and key */
+ struct geran_encr geran_encr;
+
+ /* N(SD) expected in the received frame, per flow (TS 24.007 11.2.3.2.3.2.2) */
+ uint8_t n_sd_next[4];
+
+ /* Call control and MSC-A side of RTP switching. Without inter-MSC handover involved, this manages all of the
+ * MGW and RTP switching; after an inter-MSC handover, the RAN-side of this is redirected via another MNCC
+ * connection to the Handover MSISDN, and a remote MSC-I role takes over RTP switching to the remote BSS.
+ *
+ * Without / before inter-MSC HO:
+ *
+ * BSS [MSC-I MSC-A] MNCC to PBX
+ * <--RTP---------> <--RTP-->
+ *
+ * After inter-MSC HO:
+ *
+ * BSS [MSC-I MSC-A] MNCC to PBX MSC-I BSS-B
+ * /--> <--RTP-->
+ * \-------RTP--> (ISUP) <--RTP--> <--RTP-->
+ */
+ struct {
+ /* All of the RTP stream handling */
+ struct call_leg *call_leg;
+ struct mncc_call *mncc_forwarding_to_remote_ran;
+
+ /* There may be up to 7 incoming calls for this subscriber. This is the currently serviced voice call,
+ * as in, the other person the subscriber is currently talking to. */
+ struct gsm_trans *active_trans;
+ } cc;
+
+ struct msc_ho_state ho;
+
+ struct osmo_use_count use_count;
+ struct osmo_use_count_entry use_count_buf[8];
+ int32_t max_total_use_count;
+};
+
+osmo_static_assert(offsetof(struct msc_a, c) == 0, msc_role_common_first_member_of_msc_a);
+
+struct msc_a_ran_dec_data {
+ enum msc_role from_role;
+ const struct an_apdu *an_apdu;
+ const struct ran_msg *ran_dec;
+};
+
+#define LOG_MSC_A(MSC_A, LEVEL, FMT, ARGS ...) \
+ LOG_MSC_A_CAT(MSC_A, (MSC_A) ? (MSC_A)->c.ran->log_subsys : DMSC, LEVEL, FMT, ## ARGS)
+#define LOG_MSC_A_CAT(MSC_A, SUBSYS, LEVEL, FMT, ARGS ...) \
+ LOGPFSMSL((MSC_A) ? (MSC_A)->c.fi : NULL, SUBSYS, LEVEL, FMT, ## ARGS)
+#define LOG_MSC_A_CAT_SRC(MSC_A, SUBSYS, LEVEL, SRCFILE, LINE, FMT, ARGS ...) \
+ LOGPFSMSLSRC((MSC_A) ? (MSC_A)->c.fi : NULL, SUBSYS, LEVEL, SRCFILE, LINE, FMT, ## ARGS)
+
+enum msc_a_states {
+ MSC_A_ST_VALIDATE_L3,
+ MSC_A_ST_AUTH_CIPH,
+ MSC_A_ST_WAIT_CLASSMARK_UPDATE,
+ MSC_A_ST_AUTHENTICATED,
+ MSC_A_ST_COMMUNICATING,
+ MSC_A_ST_RELEASING,
+ MSC_A_ST_RELEASED,
+};
+
+struct msc_a *msc_a_alloc(struct msub *msub, struct ran_infra *ran);
+
+int msc_a_classmark_request_then_cipher_mode_cmd(struct msc_a *msc_a, bool umts_aka, bool retrieve_imeisv);
+
+bool msc_a_is_establishing_auth_ciph(const struct msc_a *msc_a);
+bool msc_a_is_accepted(const struct msc_a *msc_a);
+bool msc_a_in_release(struct msc_a *msc_a);
+
+struct gsm_network *msc_a_net(const struct msc_a *msc_a);
+struct vlr_subscr *msc_a_vsub(const struct msc_a *msc_a);
+struct msc_i *msc_a_msc_i(const struct msc_a *msc_a);
+struct msc_t *msc_a_msc_t(const struct msc_a *msc_a);
+
+struct msc_a *msc_a_for_vsub(const struct vlr_subscr *vsub, bool valid_conn_only);
+
+void msc_a_pending_cm_service_req_add(struct msc_a *msc_a, enum osmo_cm_service_type type);
+unsigned int msc_a_pending_cm_service_req_count(struct msc_a *msc_a, enum osmo_cm_service_type type);
+void msc_a_pending_cm_service_req_del(struct msc_a *msc_a, enum osmo_cm_service_type type);
+
+#define msc_a_ran_down(A,B,C) \
+ _msc_a_ran_down(A,B,C, __FILE__, __LINE__)
+int _msc_a_ran_down(struct msc_a *msc_a, enum msc_role to_role, const struct ran_msg *ran_enc_msg,
+ const char *file, int line);
+#define msc_a_msg_down(A,B,C,D) \
+ _msc_a_msg_down(A,B,C,D, __FILE__, __LINE__)
+int _msc_a_msg_down(struct msc_a *msc_a, enum msc_role to_role, uint32_t to_role_event,
+ const struct ran_msg *ran_enc_msg,
+ const char *file, int line);
+
+int msc_a_tx_dtap_to_i(struct msc_a *msc_a, struct msgb *dtap);
+int msc_a_tx_common_id(struct msc_a *msc_a);
+int msc_a_tx_mm_serv_ack(struct msc_a *msc_a);
+int msc_a_tx_mm_serv_rej(struct msc_a *msc_a, enum gsm48_reject_value value);
+
+int msc_a_up_l3(struct msc_a *msc_a, struct msgb *msg);
+
+void msc_a_up_ciph_res(struct msc_a *msc_a, bool success, const char *imeisv);
+
+bool msc_a_is_accepted(const struct msc_a *msc_a);
+bool msc_a_is_establishing_auth_ciph(const struct msc_a *msc_a);
+
+int msc_a_try_call_assignment(struct gsm_trans *cc_trans);
+
+const char *msc_a_cm_service_type_to_use(enum osmo_cm_service_type cm_service_type);
+
+void msc_a_release_cn(struct msc_a *msc_a);
+void msc_a_release_mo(struct msc_a *msc_a, enum gsm48_gsm_cause gsm_cause);
+
+int msc_a_ran_decode_cb(struct osmo_fsm_inst *msc_a_fi, void *data, const struct ran_msg *msg);
+
+int msc_a_vlr_set_cipher_mode(void *_msc_a, bool umts_aka, bool retrieve_imeisv);
+
+struct msgb *msc_a_ran_encode(struct msc_a *msc_a, const struct ran_msg *ran_enc_msg);
+
+void msc_a_update_id(struct msc_a *msc_a);
diff --git a/include/osmocom/msc/msc_a_remote.h b/include/osmocom/msc/msc_a_remote.h
new file mode 100644
index 000000000..db7f50773
--- /dev/null
+++ b/include/osmocom/msc/msc_a_remote.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#define LOG_MSC_A_REMOTE(MSC_A_REMOTE, LEVEL, FMT, ARGS ...) \
+ LOG_MSC_A_REMOTE_CAT(MSC_A_REMOTE, (MSC_A_REMOTE) ? (MSC_A_REMOTE)->c.ran->log_subsys : DMSC, LEVEL, FMT, ## ARGS)
+#define LOG_MSC_A_REMOTE_CAT(MSC_A_REMOTE, SUBSYS, LEVEL, FMT, ARGS ...) \
+ LOGPFSMSL((MSC_A_REMOTE) ? (MSC_A_REMOTE)->c.fi : NULL, SUBSYS, LEVEL, FMT, ## ARGS)
+#define LOG_MSC_A_REMOTE_CAT_SRC(MSC_A_REMOTE, SUBSYS, LEVEL, SRCFILE, LINE, FMT, ARGS ...) \
+ LOGPFSMSLSRC((MSC_A_REMOTE) ? (MSC_A_REMOTE)->c.fi : NULL, SUBSYS, LEVEL, SRCFILE, LINE, FMT, ## ARGS)
+
+struct msub;
+struct ran_infra;
+
+struct msc_a *msc_a_remote_alloc(struct msub *msub, struct ran_infra *ran,
+ const uint8_t *remote_msc_name, size_t remote_msc_name_len);
+
+int msc_a_remote_assign_handover_number(struct msc_a *msc_a);
+struct msc_a *msc_a_remote_find_by_handover_number(const char *handover_number);
diff --git a/include/osmocom/msc/msc_common.h b/include/osmocom/msc/msc_common.h
index 3ca34692d..78337f764 100644
--- a/include/osmocom/msc/msc_common.h
+++ b/include/osmocom/msc/msc_common.h
@@ -1,5 +1,8 @@
#pragma once
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm0808.h>
+
struct msgb;
struct gsm_network;
struct vlr_subscr;
@@ -7,17 +10,51 @@ struct vlr_subscr;
#define MSC_HLR_REMOTE_IP_DEFAULT "127.0.0.1"
#define MSC_HLR_REMOTE_PORT_DEFAULT OSMO_GSUP_PORT
+/* TS 48.008 DLCI containing DCCH/ACCH + SAPI */
+#define OMSC_LINKID_CB(__msgb) (__msgb)->cb[3]
+
enum nsap_addr_enc {
NSAP_ADDR_ENC_X213,
NSAP_ADDR_ENC_V4RAW,
};
+#define MAX_A5_KEY_LEN (128/8)
+
+struct geran_encr {
+ /*! alg_id is in encoded format:
+ * alg_id == 1 means A5/0 i.e. no encryption, alg_id == 4 means A5/3.
+ * alg_id == 0 means no such IE was present. */
+ uint8_t alg_id;
+ uint8_t key_len;
+ uint8_t key[MAX_A5_KEY_LEN];
+};
+
+enum complete_layer3_type {
+ COMPLETE_LAYER3_NONE,
+ COMPLETE_LAYER3_LU,
+ COMPLETE_LAYER3_CM_SERVICE_REQ,
+ COMPLETE_LAYER3_PAGING_RESP,
+};
+
+extern const struct value_string complete_layer3_type_names[];
+static inline const char *complete_layer3_type_name(enum complete_layer3_type val)
+{
+ return get_value_string(complete_layer3_type_names, val);
+}
+
+struct cell_ids_entry {
+ struct llist_head entry;
+ struct gsm0808_cell_id_list2 cell_ids;
+};
+
typedef int (*mncc_recv_cb_t)(struct gsm_network *, struct msgb *);
struct gsm_network *gsm_network_init(void *ctx, mncc_recv_cb_t mncc_recv);
void gsm_network_set_mncc_sock_path(struct gsm_network *net, const char *mncc_sock_path);
+extern const struct vlr_ops msc_vlr_ops;
int msc_vlr_alloc(struct gsm_network *net);
int msc_vlr_start(struct gsm_network *net);
+int msc_gsup_client_start(struct gsm_network *net);
-void msc_stop_paging(struct vlr_subscr *vsub);
+uint32_t msc_cc_next_outgoing_callref();
diff --git a/include/osmocom/msc/msc_ho.h b/include/osmocom/msc/msc_ho.h
new file mode 100644
index 000000000..99956f1e6
--- /dev/null
+++ b/include/osmocom/msc/msc_ho.h
@@ -0,0 +1,104 @@
+/* MSC Handover API */
+/*
+ * (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/>.
+ */
+
+#pragma once
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/core/sockaddr_str.h>
+
+#include <osmocom/mgcp_client/mgcp_client.h>
+
+#include <osmocom/msc/neighbor_ident.h>
+#include <osmocom/msc/ran_msg.h>
+#include <osmocom/msc/mncc_call.h>
+
+
+struct gsm0808_handover_required;
+
+struct msc_a;
+struct ran_dec_handover_required;
+
+#define LOG_HO(msc_a, level, fmt, args...) \
+ LOGPFSML((msc_a)? ((msc_a)->ho.fi ? : (msc_a)->c.fi) : NULL, \
+ level, "%s" fmt, (msc_a->ho.fi ? "" : "HO: "), ##args)
+
+enum msc_ho_fsm_state {
+ MSC_HO_ST_REQUIRED,
+ MSC_HO_ST_WAIT_REQUEST_ACK,
+ MSC_HO_ST_WAIT_COMPLETE,
+};
+
+enum msc_ho_fsm_event {
+ MSC_HO_EV_RX_REQUEST_ACK,
+ MSC_HO_EV_RX_DETECT,
+ MSC_HO_EV_RX_COMPLETE,
+ MSC_HO_EV_RX_FAILURE,
+ MSC_HO_EV_MNCC_FORWARDING_COMPLETE,
+ MSC_HO_EV_MNCC_FORWARDING_FAILED,
+};
+
+struct msc_ho_state {
+ struct osmo_fsm_inst *fi;
+ struct ran_handover_required info;
+ unsigned int next_cil_idx;
+ bool subsequent_ho;
+ bool ready_to_switch_rtp;
+ bool rtp_switched_to_new_cell;
+
+ struct {
+ enum osmo_rat_type ran_type;
+ struct gsm0808_cell_id cid;
+ struct osmo_cell_global_id cgi;
+ enum msc_neighbor_type type;
+ union {
+ struct ran_peer *ran_peer;
+ const char *msc_ipa_name;
+ };
+
+ /* The RTP address from Handover Request Acknowledge.
+ * Might be from AoIP Transport Layer Address from a BSC RAN peer,
+ * or from MNCC forwarding for inter-MSC handover. */
+ struct osmo_sockaddr_str ran_remote_rtp;
+ /* The codec from Handover Request Acknowledge. */
+ bool codec_present;
+ enum mgcp_codecs codec;
+
+ /* Inter-MSC voice forwarding via MNCC, to the remote MSC. The Prepare Handover Response sent us the
+ * Handover Number the remote MSC assigned. This is a call to that Handover Number, via PBX.
+ * (NULL if not an inter-MSC Handover) */
+ struct mncc_call *mncc_forwarding_to_remote_ran;
+ } new_cell;
+
+ struct {
+ /* Saved RTP IP:port and codec in case we need to roll back */
+ struct osmo_sockaddr_str ran_remote_rtp;
+ enum mgcp_codecs codec;
+ } old_cell;
+};
+
+void msc_ho_start(struct msc_a *msc_a, const struct ran_handover_required *ho_req);
+
+enum msc_neighbor_type msc_ho_find_target_cell(struct msc_a *msc_a, const struct gsm0808_cell_id *cid,
+ const struct neighbor_ident_entry **remote_msc,
+ struct ran_peer **ran_peer_from_neighbor_ident,
+ struct ran_peer **ran_peer_from_seen_cells);
diff --git a/include/osmocom/msc/msc_i.h b/include/osmocom/msc/msc_i.h
new file mode 100644
index 000000000..a2a5fb133
--- /dev/null
+++ b/include/osmocom/msc/msc_i.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/mncc.h>
+
+#include <osmocom/msc/msc_roles.h>
+
+struct ran_infra;
+struct mncc_call;
+
+#define LOG_MSC_I(MSC_I, LEVEL, FMT, ARGS ...) \
+ LOG_MSC_I_CAT(MSC_I, (MSC_I) ? (MSC_I)->c.ran->log_subsys : DMSC, LEVEL, FMT, ## ARGS)
+#define LOG_MSC_I_CAT(MSC_I, SUBSYS, LEVEL, FMT, ARGS ...) \
+ LOGPFSMSL((MSC_I) ? (MSC_I)->c.fi : NULL, SUBSYS, LEVEL, FMT, ## ARGS)
+#define LOG_MSC_I_CAT_SRC(MSC_I, SUBSYS, LEVEL, SRCFILE, LINE, FMT, ARGS ...) \
+ LOGPFSMSLSRC((MSC_I) ? (MSC_I)->c.fi : NULL, SUBSYS, LEVEL, SRCFILE, LINE, FMT, ## ARGS)
+
+struct msc_i {
+ /* struct msc_role_common must remain at start */
+ struct msc_role_common c;
+ struct ran_conn *ran_conn;
+
+ struct {
+ struct call_leg *call_leg;
+ struct mncc_call *mncc_forwarding_to_remote_cn;
+ } inter_msc;
+};
+
+osmo_static_assert(offsetof(struct msc_i, c) == 0, msc_role_common_first_member_of_msc_i);
+
+enum msc_i_state {
+ MSC_I_ST_READY,
+ MSC_I_ST_CLEARING,
+ MSC_I_ST_CLEARED,
+};
+
+struct msc_i *msc_i_alloc(struct msub *msub, struct ran_infra *ran);
+void msc_i_set_ran_conn(struct msc_i *msc_i, struct ran_conn *ran_conn);
+
+void msc_i_clear(struct msc_i *msc_i);
+void msc_i_cleared(struct msc_i *msc_i);
+
+int msc_i_down_l2(struct msc_i *msc_i, struct msgb *l2);
+
+struct gsm_network *msc_i_net(const struct msc_i *msc_i);
+struct vlr_subscr *msc_i_vsub(const struct msc_i *msc_i);
diff --git a/include/osmocom/msc/msc_i_remote.h b/include/osmocom/msc/msc_i_remote.h
new file mode 100644
index 000000000..526d76f86
--- /dev/null
+++ b/include/osmocom/msc/msc_i_remote.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#define LOG_MSC_I_REMOTE(MSC_I_REMOTE, LEVEL, FMT, ARGS ...) \
+ LOG_MSC_I_REMOTE_CAT(MSC_I_REMOTE, (MSC_I_REMOTE) ? (MSC_I_REMOTE)->c.ran->log_subsys : DMSC, LEVEL, FMT, ## ARGS)
+#define LOG_MSC_I_REMOTE_CAT(MSC_I_REMOTE, SUBSYS, LEVEL, FMT, ARGS ...) \
+ LOGPFSMSL((MSC_I_REMOTE) ? (MSC_I_REMOTE)->c.fi : NULL, SUBSYS, LEVEL, FMT, ## ARGS)
+#define LOG_MSC_I_REMOTE_CAT_SRC(MSC_I_REMOTE, SUBSYS, LEVEL, SRCFILE, LINE, FMT, ARGS ...) \
+ LOGPFSMSLSRC((MSC_I_REMOTE) ? (MSC_I_REMOTE)->c.fi : NULL, SUBSYS, LEVEL, SRCFILE, LINE, FMT, ## ARGS)
+
+struct msub;
+struct ran_infra;
+struct e_link;
+
+struct msc_i *msc_i_remote_alloc(struct msub *msub, struct ran_infra *ran, struct e_link *e);
diff --git a/include/osmocom/msc/msc_ifaces.h b/include/osmocom/msc/msc_ifaces.h
deleted file mode 100644
index 94423caa5..000000000
--- a/include/osmocom/msc/msc_ifaces.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/msc/gsm_data.h>
-#include <osmocom/msc/transaction.h>
-
-/* These are the interfaces of the MSC layer towards (from?) the BSC and RNC,
- * i.e. in the direction towards the mobile device (MS aka UE).
- *
- * 2G will use the A-interface,
- * 3G aka UMTS will use the Iu-interface (for the MSC, it's IuCS).
- *
- * To allow linking parts of the MSC code without having to include entire
- * infrastructures of external libraries, the core transmitting and receiving
- * functions are left unimplemented. For example, a unit test does not need to
- * link against external ASN1 libraries if it is never going to encode actual
- * outgoing messages. It is up to each building scope to implement real world
- * functions or to plug mere dummy implementations.
- *
- * For example, msc_tx_dtap(conn, msg), depending on conn->via_iface, will call
- * either iu_tx() or a_tx() [note: at time of writing, the A-interface is not
- * yet implemented]. When you try to link against libmsc, you will find that
- * the compiler complains about an undefined reference to iu_tx(). If you,
- * however, link against libiu as well as the osmo-iuh libs (etc.), iu_tx() is
- * available. A unit test may instead simply implement a dummy iu_tx() function
- * and not link against osmo-iuh, see tests/libiudummy/.
- */
-
-/* Each main linkage must implement this function (see comment above). */
-extern int iu_tx(struct msgb *msg, uint8_t sapi);
-
-int msc_tx_dtap(struct ran_conn *conn,
- struct msgb *msg);
-
-int msc_gsm48_tx_mm_serv_ack(struct ran_conn *conn);
-int msc_gsm48_tx_mm_serv_rej(struct ran_conn *conn,
- enum gsm48_reject_value value);
-
-int msc_tx_common_id(struct ran_conn *conn);
diff --git a/include/osmocom/msc/msc_mgcp.h b/include/osmocom/msc/msc_mgcp.h
deleted file mode 100644
index 304e96706..000000000
--- a/include/osmocom/msc/msc_mgcp.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * 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/>.
- *
- */
-
-#pragma once
-
-#include <osmocom/mgcp_client/mgcp_client.h>
-#include <osmocom/msc/gsm_data.h>
-
-struct ran_conn;
-
-/* MGCP state handler context. This context information stores all information
- * to handle the direction of the RTP streams via MGCP. There is one instance
- * of this context struct per RAN connection.
- * (see also struct ran_conn) */
-struct mgcp_ctx {
- /* FSM instance, which handles the connection switching procedure */
- struct osmo_fsm_inst *fsm;
-
- /* RTP endpoint string. This string identifies the endpoint
- * on the MGW on which the RAN and CN connection is created. This
- * endpoint number is assigned by the MGW. */
- char rtp_endpoint[MGCP_ENDPOINT_MAXLEN];
-
- /* Call id of the current call. Will be derived from the callref
- * of the transaction that is valid during the first CRCX. (The
- * callref may change throughout the call) */
- unsigned int call_id;
-
- /* Set to true, when the context information is no longer needed */
- bool free_ctx;
-
- /* RTP connection identifiers */
- char conn_id_ran[MGCP_CONN_ID_LENGTH];
- char conn_id_cn[MGCP_CONN_ID_LENGTH];
-
- /* Copy of the pointer and the data with context information
- * needed to process the AoIP and MGCP requests (system data) */
- struct mgcp_client *mgcp;
- struct gsm_trans *trans;
- mgcp_trans_id_t mgw_pending_trans;
-};
-
-int msc_mgcp_try_call_assignment(struct gsm_trans *trans);
-int msc_mgcp_call_assignment(struct gsm_trans *trans);
-int msc_mgcp_ass_complete(struct ran_conn *conn, uint16_t port, char *addr);
-int msc_mgcp_ass_fail(struct ran_conn *conn);
-int msc_mgcp_call_complete(struct gsm_trans *trans, uint16_t port, char *addr);
-int msc_mgcp_call_release(struct gsm_trans *trans);
diff --git a/include/osmocom/msc/msc_roles.h b/include/osmocom/msc/msc_roles.h
new file mode 100644
index 000000000..a1fab2f23
--- /dev/null
+++ b/include/osmocom/msc/msc_roles.h
@@ -0,0 +1,387 @@
+#pragma once
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsup.h>
+
+#include <osmocom/msc/msc_common.h>
+#include <osmocom/msc/ran_infra.h>
+
+/* Each subscriber connection is managed by different roles, as described in 3GPP TS 49.008 '4.3 Roles of MSC-A, MSC-I
+ * and MSC-T':
+ *
+ * MSC-A: subscriber management and control of all transactions (CC, SMS, USSD,...)
+ * MSC-I: "internal": the actual BSSMAP link to the BSS, or RANAP link to the RNC.
+ * MSC-T: "transitory": a new pending RAN link to a BSS or RNC, while handover is in progress.
+ * MSC-T becomes the new MSC-I once handover ends successfully.
+ *
+ * Without inter-MSC handover involved, all of the roles are managed by a single MSC instance. During inter-MSC
+ * handover negotiation, an MSC-T is set up at a remote MSC while MSC-A remains in the original MSC, and when handover
+ * concludes successfully, the remote MSC-T becomes the new remote MSC-I, replacing the local MSC-I role.
+ *
+ * Furthermore, the 3GPP specs use the following terms for naming MSC locations: MSC-A, MSC-B and MSC-B', as well as BSS
+ * or BSS-A, BSS-B and BSS-B':
+ *
+ * MSC-A: the first MSC the subscriber connected to.
+ * MSC-B: a remote MSC (if any).
+ * MSC-B': another remote MSC (if any, during Subsequent Handover).
+ *
+ * The full role assignments are spelled out in 3GPP TS 29.002.
+ *
+ * In Osmocom, the MAP protocol spoken between the MSCs is modeled using GSUP instead.
+ *
+ * Here are some diagrams of the lifecycle of a single subscriber's MSC-A,-I,-T roles at the locations MSC-A, MSC-B and
+ * MSC-B'.
+ *
+ * Initially:
+ *
+ * [MSC-A]
+ * BSS <-> MSC-I
+ *
+ * Then during inter-MSC handover negotiation:
+ *
+ * [MSC-A] <-MAP-> MSC-B
+ * BSS <-> MSC-I MSC-T <-> new BSS
+ *
+ * and when successful:
+ *
+ * [MSC-A] <-MAP-> MSC-B
+ * MSC-I <-> BSS
+ *
+ * Additional subsequent handover:
+ *
+ * [MSC-A] <-MAP-> MSC-B
+ * ^ MSC-I <-> BSS
+ * |
+ * +-------MAP-> MSC-B'
+ * MSC-T <-> new BSS
+ *
+ * (Here, quote, MSC-A "shall act as the target BSS towards the MSC-I and as the MSC towards the MSC-T.")
+ * and when successful:
+ *
+ * [MSC-A]
+ * ^
+ * |
+ * +-------MAP-> MSC-B
+ * MSC-I <-> BSS
+ *
+ * Subsequent handover back to the original MSC:
+ *
+ * [MSC-A] <-MAP-> MSC-B
+ * new BSS <-> MSC-T MSC-I <-> BSS
+ *
+ * and then
+ * [MSC-A]
+ * BSS <-> MSC-I
+ *
+ *
+ * Inter-BSC Handover is just a special case of inter-MSC Handover, where the same MSC-A takes on both MSC-I and MSC-T
+ * roles:
+ *
+ * [MSC-A]
+ * BSS <-> MSC-I
+ * new BSS <-> MSC-T
+ *
+ * The mechanism to take on different roles is implemented by different FSM instances. Each FSM kind has one
+ * implementation that acts locally, and another implementation to forward to a remote MSC. For example, in this
+ * scenario:
+ *
+ * [MSC-A] <-MAP-> MSC-B
+ * MSC-I <-> BSS
+ *
+ * the implementation is
+ *
+ * [MSC-A-----------------] [MSC-B-----------------]
+ * msc_a <-> msc_i_REMOTE <---GSUP---> msc_a_REMOTE <-> msc_i <--BSSMAP--> [BSS]
+ *
+ * MSC-A has a locally acting msc_a FSM implementation. The msc_i FSM implementation at MSC-A receives signals from the
+ * msc_a FSM and "merely" sends the MAP instructions to MSC-B.
+ *
+ * At MSC-B, in turn, the msc_a FSM's "remote" implementation receives the MAP messages and dispatches according events
+ * to the MSC-B's local msc_i FSM instance, which is implemented to directly act towards the BSS.
+ *
+ * To implement single-MSC operation, we have the separate MSC roles' local implementations on the same MSC instance
+ * instead of forwarding.
+ *
+ *
+ * Use of MAP procedures on GSUP towards HLR:
+ *
+ * The MSC <-> VLR communication does still happen locally in the MSC-A only. In other words, there may be MAP message
+ * handling between the MSCs (in the form of GSUP), but no MAP to talk to our internal VLR.
+ *
+ * From the VLR to the HLR, though, we again use GSUP for subscriber related HLR operations such as LU requesting and
+ * retrieving auth tokens.
+ *
+ * To complete the picture, the MSC-A <--GSUP--> MSC-B forwarding happens over the same GSUP connection
+ * as the VLR <--GSUP--> HLR link:
+ *
+ * OsmoMSC
+ * MSC-A <----------E-interface--->+--GSUP--> [IPA routing] ----E--> MSC-B
+ * ^ ^ (in osmo-hlr) \
+ * | (internal API) / \--D--> HLR
+ * v /
+ * VLR <------------D-interface-/
+ */
+
+struct inter_msc_link;
+struct ran_conn;
+
+enum msc_role {
+ MSC_ROLE_A,
+ MSC_ROLE_I,
+ MSC_ROLE_T,
+
+ MSC_ROLES_COUNT
+};
+
+extern const struct value_string msc_role_names[];
+static inline const char *msc_role_name(enum msc_role role)
+{ return get_value_string(msc_role_names, role); }
+
+
+enum msc_common_events {
+ /* Explicitly start with 0 (first real event will be -1 + 1 = 0). */
+ OFFSET_MSC_COMMON_EV = -1,
+
+ MSC_REMOTE_EV_RX_GSUP,
+
+ MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE,
+ MSC_EV_CALL_LEG_RTP_COMPLETE,
+ MSC_EV_CALL_LEG_RTP_RELEASED,
+ MSC_EV_CALL_LEG_TERM,
+
+ /* MNCC has told us to RTP_CREATE, but local RTP port has not yet been set up.
+ * The MSC role should respond by calling mncc_set_rtp_stream() */
+ MSC_MNCC_EV_NEED_LOCAL_RTP,
+ MSC_MNCC_EV_CALL_PROCEEDING,
+ MSC_MNCC_EV_CALL_COMPLETE,
+ MSC_MNCC_EV_CALL_ENDED,
+
+ LAST_MSC_COMMON_EV,
+};
+
+
+/* The events that the msc_a_local and msc_a_remote FSM implementations can receive,
+ * according to specifications. Not all of these are necessarily implemented. */
+enum msc_a_events {
+ OFFSET_MSC_A_EV = LAST_MSC_COMMON_EV - 1,
+
+ /* Establishing Layer 3 happens only at MSC-A (all-local MSC). To distinguish from the inter-MSC DTAP
+ * forwarding, keep this as a separate event. */
+ MSC_A_EV_FROM_I_COMPLETE_LAYER_3,
+
+ /* In inter-MSC situations, DTAP is forwarded transparently in AN-APDU IEs (formerly named
+ * BSS-APDU); see
+ * - 3GPP TS 49.008 4.2 'Transfer of DTAP and BSSMAP layer 3 messages on the * E-interface',
+ * - 3GPP TS 29.010 4.5.4 'BSSAP Messages transfer on E-Interface',
+ * - 3GPP TS 29.002 8.4.3 MAP_PROCESS_ACCESS_SIGNALLING service, 8.4.4 MAP_FORWARD_ACCESS_SIGNALLING service.
+ *
+ * MSC-B ---DTAP--> MSC-A MAP PROCESS ACCESS SIGNALLING request
+ * MSC-B <--DTAP--- MSC-A MAP FORWARD ACCESS SIGNALLING request
+ * (where neither will receive a "response")
+ *
+ * See 3GPP TS 49.008 6. 'BSSMAP messages transferred on the E-interface'.
+ * Depending on the RAN, the AN-APDU contains a BSSMAP or a RANAP encoded message.
+ * MSC-I to MSC-A:
+ * - Managing attach to one BSC+MSC:
+ * - CLASSMARK_UPDATE,
+ * - CIPHER_MODE_COMPLETE,
+ * - CIPHER_MODE_REJECT,
+ * - ASSIGNMENT_COMPLETE,
+ * - ASSIGNMENT_FAILURE,
+ * - CLEAR_REQUEST,
+ * - Handover related messages:
+ * - HANDOVER_REQUEST,
+ * - HANDOVER_PERFORMED,
+ * - HANDOVER_FAILURE,
+ * - Messages we don't need/support yet:
+ * - CHANNEL_MODIFY_REQUEST (MSC assisted codec changing handover),
+ * - SAPI_N_REJECT,
+ * - CONFUSION,
+ * - BSS_INVOKE_TRACE,
+ * - QUEUING_INDICATION,
+ * - PERFORM_LOCATION_REQUEST (*not* related to a Location Updating, but about passing the MS's geological
+ * position)
+ * - PERFORM_LOCATION_ABORT,
+ * - PERFORM_LOCATION_RESPONSE,
+ * - CONNECTION_ORIENTED_INFORMATION is listed in 48.008 3.2.1.70 as "(void)",
+ */
+ MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST,
+ MSC_A_EV_FROM_I_PREPARE_SUBSEQUENT_HANDOVER_REQUEST,
+
+ /* See 3GPP TS 29.002 8.4.2 MAP_SEND_END_SIGNAL service. */
+ MSC_A_EV_FROM_I_SEND_END_SIGNAL_REQUEST,
+
+ /* These BSSMAP messages are relevant for MSC-T -> MSC-A, i.e. from the transitory during inter-MSC handover:
+ *
+ * - Handover related messages:
+ * - HANDOVER_REQUEST_ACKNOWLEDGE,
+ * - HANDOVER_COMPLETE,
+ * - HANDOVER_FAILURE,
+ * - HANDOVER_DETECT,
+ * - CLEAR_REQUEST,
+ * - Messages we don't need/support yet:
+ * - CONFUSION,
+ * - QUEUING_INDICATION,
+ */
+ MSC_A_EV_FROM_T_PROCESS_ACCESS_SIGNALLING_REQUEST,
+
+ /* Essentially the HO Request Ack. 3GPP TS 29.002 8.4.1 MAP_PREPARE_HANDOVER service. */
+ MSC_A_EV_FROM_T_PREPARE_HANDOVER_RESPONSE,
+ MSC_A_EV_FROM_T_PREPARE_HANDOVER_FAILURE,
+
+ /* Done establishing the radio link to the MS, for Handover.
+ * See 3GPP TS 29.002 8.4.2 MAP_SEND_END_SIGNAL service.
+ * Not to be confused with the MSC_I_EV_FROM_A_SEND_END_SIGNAL_RESPONSE that tells MSC-B to release. */
+ MSC_A_EV_FROM_T_SEND_END_SIGNAL_REQUEST,
+
+ /* gsm_04_08.c has successfully received a valid Complete Layer 3 message, i.e. Location Updating, CM Service
+ * Request, Paging Reponse or IMSI Detach. */
+ MSC_A_EV_COMPLETE_LAYER_3_OK,
+
+ /* Received a Classmark Update -- during GERAN ciphering, msc_a may have to wait for Classmark information to
+ * determine supported ciphers. */
+ MSC_A_EV_CLASSMARK_UPDATE,
+
+ /* LU or Process Access FSM have determined that the peer has verified its authenticity. */
+ MSC_A_EV_AUTHENTICATED,
+
+ /* A valid request is starting to be processed on the connection. Upon this event, msc_a moves from
+ * MSC_A_ST_AUTHENTICATED to MSC_A_ST_COMMUNICATING, and enters the only state without an expiry timeout. */
+ MSC_A_EV_TRANSACTION_ACCEPTED,
+
+ /* MSC originated close request, e.g. all done, failed authentication, ... */
+ MSC_A_EV_CN_CLOSE,
+
+ /* Subscriber originated close request */
+ MSC_A_EV_MO_CLOSE,
+
+ /* msc_a->use_count has reached a total of zero. */
+ MSC_A_EV_UNUSED,
+
+ MSC_A_EV_HANDOVER_REQUIRED,
+ MSC_A_EV_HANDOVER_END,
+
+ /* indicates nr of MSC_A events, keep this as last enum value */
+ LAST_MSC_A_EV
+};
+osmo_static_assert(LAST_MSC_A_EV <= 32, not_too_many_msc_a_events);
+
+extern const struct value_string msc_a_fsm_event_names[];
+
+enum msc_from_ran_events {
+ OFFSET_MSC_EV_FROM_RAN = LAST_MSC_COMMON_EV - 1,
+
+ MSC_EV_FROM_RAN_COMPLETE_LAYER_3,
+
+ /* A BSSMAP/RANAP message came in on the RAN conn. */
+ MSC_EV_FROM_RAN_UP_L2,
+
+ /* The RAN connection is gone, or busy going. */
+ MSC_EV_FROM_RAN_CONN_RELEASED,
+
+ LAST_MSC_EV_FROM_RAN
+};
+
+/* The events that the msc_i_local and msc_i_remote FSM implementations can receive.
+ * The MSC-I can also receive all msc_common_events and msc_from_ran_events. */
+enum msc_i_events {
+ OFFSET_E_MSC_I = LAST_MSC_EV_FROM_RAN - 1,
+
+ /* BSSMAP/RANAP comes in from MSC-A to be sent out on the RAN conn.
+ * Depending on the RAN, the AN-APDU contains a BSSMAP or a RANAP encoded message.
+ * Relevant BSSMAP procedures, see 3GPP TS 49.008 6. 'BSSMAP messages transferred on the E-interface':
+ * - Managing attach to one BSC+MSC:
+ * - CLASSMARK_REQUEST,
+ * - CIPHER_MODE_COMMAND,
+ * - COMMON_ID,
+ * - ASSIGNMENT_REQUEST,
+ * - Handover related messages:
+ * - HANDOVER_REQUEST_ACKNOWLEDGE,
+ * - HANDOVER_FAILURE,
+ * - Messages we don't need/support yet:
+ * - CONFUSION,
+ * - MSC_INVOKE_TRACE,
+ * - QUEUING_INDICATION,
+ * - LSA_INFORMATION,
+ * - PERFORM_LOCATION_REQUEST, (*not* related to a Location Updating, but about passing the MS's geological position)
+ * - PERFORM_LOCATION_ABORT,
+ * - PERFORM_LOCATION_RESPONSE,
+ * - CONNECTION_ORIENTED_INFORMATION is listed in 48.008 3.2.1.70 as "(void)"
+ */
+ MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST,
+
+ /* MSC-A tells us to release the RAN connection. */
+ MSC_I_EV_FROM_A_SEND_END_SIGNAL_RESPONSE,
+
+ MSC_I_EV_FROM_A_PREPARE_SUBSEQUENT_HANDOVER_RESULT,
+ MSC_I_EV_FROM_A_PREPARE_SUBSEQUENT_HANDOVER_ERROR,
+
+ LAST_MSC_I_EV
+};
+osmo_static_assert(LAST_MSC_I_EV <= 32, not_too_many_msc_i_events);
+
+extern const struct value_string msc_i_fsm_event_names[];
+
+/* The events that the msc_t_local and msc_t_remote FSM implementations can receive.
+ * The MSC-T can also receive all msc_common_events and msc_from_ran_events. */
+enum msc_t_events {
+ /* sufficient would be to use LAST_MSC_EV_FROM_RAN as offset. But while we have enough numbers
+ * available, it is a good idea to keep MSC-I and MSC-T events separate, to catch errors of
+ * sending wrong event kinds. */
+ OFFSET_MSC_T_EV = LAST_MSC_I_EV - 1,
+
+ /* BSSMAP/RANAP comes in from MSC-A to be sent out on the RAN conn.
+ * Relevant BSSMAP procedures, see 3GPP TS 49.008 6. 'BSSMAP messages transferred on the E-interface':
+ * - Handover related messages:
+ * - HANDOVER_REQUEST,
+ * - CLASSMARK_UPDATE, (?)
+ * - Messages we don't need/support yet:
+ * - CONFUSION,
+ * - MSC_INVOKE_TRACE,
+ * - BSS_INVOKE_TRACE,
+ */
+ MSC_T_EV_FROM_A_PREPARE_HANDOVER_REQUEST,
+ MSC_T_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST,
+
+ /* MSC originated close request, e.g. all done, failed handover, ... */
+ MSC_T_EV_CN_CLOSE,
+
+ /* Subscriber originated close request */
+ MSC_T_EV_MO_CLOSE,
+
+ MSC_T_EV_CLEAR_COMPLETE,
+
+ LAST_MSC_T_EV
+};
+osmo_static_assert(LAST_MSC_T_EV <= 32, not_too_many_msc_t_events);
+
+extern const struct value_string msc_t_fsm_event_names[];
+
+/* All MSC role FSM implementations share this at the start of their fi->priv struct.
+ * See struct msc_a, struct msc_i, struct msc_t in their individual headers. */
+struct msc_role_common {
+ enum msc_role role;
+
+ struct osmo_fsm_inst *fi;
+
+ /* For a local implementation, this is NULL. Otherwise, this identifies how to reach the remote
+ * MSC that this "remote" implementation forwards messages to. */
+ struct e_link *remote_to;
+
+ struct msub *msub;
+ struct gsm_network *net;
+ struct ran_infra *ran;
+};
+
+/* AccessNetworkSignalInfo as in 3GPP TS 29.002. */
+struct an_apdu {
+ /* accessNetworkProtocolId */
+ enum osmo_gsup_access_network_protocol an_proto;
+ /* signalInfo */
+ struct msgb *msg;
+ /* If this AN-APDU is sent between MSCs, additional information from the E-interface messaging, like the
+ * Handover Number, will placed/available here. Otherwise may be left NULL. */
+ const struct osmo_gsup_message *e_info;
+};
diff --git a/include/osmocom/msc/msc_t.h b/include/osmocom/msc/msc_t.h
new file mode 100644
index 000000000..39b3abca0
--- /dev/null
+++ b/include/osmocom/msc/msc_t.h
@@ -0,0 +1,60 @@
+#pragma once
+
+#include <osmocom/msc/msc_roles.h>
+
+struct ran_conn;
+struct ran_infra;
+struct ran_peer;
+struct gsm_mncc;
+struct mncc_call;
+
+#define LOG_MSC_T(MSC_T, LEVEL, FMT, ARGS ...) \
+ LOG_MSC_T_CAT(MSC_T, (MSC_T) ? (MSC_T)->c.ran->log_subsys : DMSC, LEVEL, FMT, ## ARGS)
+#define LOG_MSC_T_CAT(MSC_T, SUBSYS, LEVEL, FMT, ARGS ...) \
+ LOGPFSMSL((MSC_T) ? (MSC_T)->c.fi : NULL, SUBSYS, LEVEL, FMT, ## ARGS)
+#define LOG_MSC_T_CAT_SRC(MSC_T, SUBSYS, LEVEL, SRCFILE, LINE, FMT, ARGS ...) \
+ LOGPFSMSLSRC((MSC_T) ? (MSC_T)->c.fi : NULL, SUBSYS, LEVEL, SRCFILE, LINE, FMT, ## ARGS)
+
+struct msc_t {
+ /* struct msc_role_common must remain at start */
+ struct msc_role_common c;
+
+ struct ran_conn *ran_conn;
+
+ struct {
+ uint8_t chosen_channel;
+ uint8_t chosen_encr_alg;
+ uint8_t chosen_speech_version;
+ } geran;
+
+ struct {
+ struct an_apdu ho_request;
+ struct gsm0808_cell_id cell_id_target;
+ uint32_t callref;
+ char handover_number[16]; /* No libosmocore definition for MSISDN_MAXLEN? */
+ struct call_leg *call_leg;
+ struct mncc_call *mncc_forwarding_to_remote_cn;
+ } inter_msc;
+
+ struct osmo_gsm48_classmark classmark;
+ bool ho_success;
+ bool ho_fail_sent;
+};
+
+enum msc_t_state {
+ MSC_T_ST_PENDING_FIRST_CO_INITIAL_MSG,
+ MSC_T_ST_WAIT_LOCAL_RTP,
+ MSC_T_ST_WAIT_HO_REQUEST_ACK,
+ MSC_T_ST_WAIT_HO_COMPLETE,
+};
+
+struct msc_t *msc_t_alloc_without_ran_peer(struct msub *msub, struct ran_infra *ran);
+int msc_t_set_ran_peer(struct msc_t *msc_t, struct ran_peer *ran_peer);
+struct msc_t *msc_t_alloc(struct msub *msub, struct ran_peer *ran_peer);
+int msc_t_down_l2_co(struct msc_t *msc_t, const struct an_apdu *an_apdu, bool initial);
+void msc_t_clear(struct msc_t *msc_t);
+
+struct gsm_network *msc_t_net(const struct msc_t *msc_t);
+struct vlr_subscr *msc_t_vsub(const struct msc_t *msc_t);
+
+struct mncc_call *msc_t_check_call_to_handover_number(const struct gsm_mncc *msg);
diff --git a/include/osmocom/msc/msc_t_remote.h b/include/osmocom/msc/msc_t_remote.h
new file mode 100644
index 000000000..170505405
--- /dev/null
+++ b/include/osmocom/msc/msc_t_remote.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#define LOG_MSC_T_REMOTE(MSC_T_REMOTE, LEVEL, FMT, ARGS ...) \
+ LOG_MSC_T_REMOTE_CAT(MSC_T_REMOTE, (MSC_T_REMOTE) ? (MSC_T_REMOTE)->c.ran->log_subsys : DMSC, LEVEL, FMT, ## ARGS)
+#define LOG_MSC_T_REMOTE_CAT(MSC_T_REMOTE, SUBSYS, LEVEL, FMT, ARGS ...) \
+ LOGPFSMSL((MSC_T_REMOTE) ? (MSC_T_REMOTE)->c.fi : NULL, SUBSYS, LEVEL, FMT, ## ARGS)
+#define LOG_MSC_T_REMOTE_CAT_SRC(MSC_T_REMOTE, SUBSYS, LEVEL, SRCFILE, LINE, FMT, ARGS ...) \
+ LOGPFSMSLSRC((MSC_T_REMOTE) ? (MSC_T_REMOTE)->c.fi : NULL, SUBSYS, LEVEL, SRCFILE, LINE, FMT, ## ARGS)
+
+struct msub;
+struct ran_infra;
+
+struct msc_t *msc_t_remote_alloc(struct msub *msub, struct ran_infra *ran,
+ const uint8_t *remote_msc_name, size_t remote_msc_name_len);
diff --git a/include/osmocom/msc/msub.h b/include/osmocom/msc/msub.h
new file mode 100644
index 000000000..2418febcf
--- /dev/null
+++ b/include/osmocom/msc/msub.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include <osmocom/msc/debug.h>
+#include <osmocom/msc/msc_roles.h>
+
+struct vlr_subscr;
+struct gsm_network;
+enum gsm48_gsm_cause;
+enum complete_layer3_type;
+enum osmo_gsup_access_network_protocol;
+
+#define VSUB_USE_MSUB "active-conn"
+
+struct msub {
+ struct llist_head entry;
+ struct osmo_fsm_inst *fi;
+
+ struct vlr_subscr *vsub;
+
+ /* role = {MSC_ROLE_A, MSC_ROLE_I, MSC_ROLE_T} */
+ struct osmo_fsm_inst *role[MSC_ROLES_COUNT];
+ struct gsm_network *net;
+};
+
+extern struct llist_head msub_list;
+
+#define LOG_MSUB_CAT_SRC(msub, cat, level, file, line, fmt, args ...) \
+ LOGPSRC(cat, level, file, line, "(%s) " fmt, msub_name(msub), ## args)
+
+#define LOG_MSUB_CAT(msub, cat, level, fmt, args ...) \
+ LOGP(cat, level, "msub(%s) " fmt, msub_name(msub), ## args)
+
+#define LOG_MSUB(msub, level, fmt, args ...) \
+ LOG_MSUB_CAT(msub, DMSC, level, fmt, ## args)
+
+struct msub *msub_alloc(struct gsm_network *net);
+
+#define msub_role_alloc(MSUB, ROLE, FSM, ROLE_STRUCT, RAN) \
+ (ROLE_STRUCT*)_msub_role_alloc(MSUB, ROLE, FSM, sizeof(ROLE_STRUCT), #ROLE_STRUCT ":" #FSM, RAN)
+struct msc_role_common *_msub_role_alloc(struct msub *msub, enum msc_role role, struct osmo_fsm *role_fsm,
+ size_t struct_size, const char *struct_name, struct ran_infra *ran);
+
+const char *msub_name(const struct msub *msub);
+
+struct msub *msub_for_vsub(const struct vlr_subscr *for_vsub);
+
+void msub_set_role(struct msub *msub, struct osmo_fsm_inst *msc_role);
+void msub_remove_role(struct msub *msub, struct osmo_fsm_inst *fi);
+
+struct msc_a *msub_msc_a(const struct msub *msub);
+struct msc_i *msub_msc_i(const struct msub *msub);
+struct msc_t *msub_msc_t(const struct msub *msub);
+struct ran_conn *msub_ran_conn(const struct msub *msub);
+const char *msub_ran_conn_name(const struct msub *msub);
+
+int msub_set_vsub(struct msub *msub, struct vlr_subscr *vsub);
+struct vlr_subscr *msub_vsub(const struct msub *msub);
+struct gsm_network *msub_net(const struct msub *msub);
+
+int msub_role_to_role_event(struct msub *msub, enum msc_role from_role, enum msc_role to_role);
+#define msub_role_dispatch(MSUB, TO_ROLE, TO_ROLE_EVENT, AN_APDU) \
+ _msub_role_dispatch(MSUB, TO_ROLE, TO_ROLE_EVENT, AN_APDU, __FILE__, __LINE__)
+int _msub_role_dispatch(struct msub *msub, enum msc_role to_role, uint32_t to_role_event, const struct an_apdu *an_apdu,
+ const char *file, int line);
+int msub_tx_an_apdu(struct msub *msub, enum msc_role from_role, enum msc_role to_role, struct an_apdu *an_apdu);
+
+void msub_update_id_from_mi(struct msub *msub, const uint8_t mi[], uint8_t mi_len);
+void msub_update_id(struct msub *msub);
+void msub_update_id_for_vsub(struct vlr_subscr *for_vsub);
+
+void msub_pending_cm_service_req_add(struct msub *msub, enum osmo_cm_service_type type);
+unsigned int msub_pending_cm_service_req_count(struct msub *msub, enum osmo_cm_service_type type);
+void msub_pending_cm_service_req_del(struct msub *msub, enum osmo_cm_service_type type);
+
+void msc_role_forget_conn(struct osmo_fsm_inst *role, struct ran_conn *conn);
+
+struct msgb *msc_role_ran_encode(struct osmo_fsm_inst *role, const struct ran_msg *ran_msg);
+int msc_role_ran_decode(struct osmo_fsm_inst *fi, const struct an_apdu *an_apdu,
+ ran_decode_cb_t decode_cb, void *decode_cb_data);
diff --git a/include/osmocom/msc/neighbor_ident.h b/include/osmocom/msc/neighbor_ident.h
new file mode 100644
index 000000000..8cd74ab57
--- /dev/null
+++ b/include/osmocom/msc/neighbor_ident.h
@@ -0,0 +1,68 @@
+/* Manage identity of neighboring BSS cells for inter-BSC handover */
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/sigtran/sccp_sap.h>
+
+struct vty;
+struct gsm_network;
+
+enum msc_neighbor_type {
+ MSC_NEIGHBOR_TYPE_NONE = 0,
+ MSC_NEIGHBOR_TYPE_LOCAL_RAN_PEER,
+ MSC_NEIGHBOR_TYPE_REMOTE_MSC,
+};
+
+struct msc_ipa_name {
+ char buf[64];
+ size_t len;
+};
+
+int msc_ipa_name_from_str(struct msc_ipa_name *min, const char *name);
+int msc_ipa_name_cmp(const struct msc_ipa_name *a, const struct msc_ipa_name *b);
+
+struct neighbor_ident_addr {
+ enum osmo_rat_type ran_type;
+ enum msc_neighbor_type type;
+ union {
+ char local_ran_peer_pc_str[23];
+ struct msc_ipa_name remote_msc_ipa_name;
+ };
+};
+
+struct neighbor_ident_entry {
+ struct llist_head entry;
+
+ struct neighbor_ident_addr addr;
+
+ /* A list of struct cell_ids_entry. A gsm0808_cell_id_list2 would in principle suffice, but to support
+ * storing more than 127 cell ids and to allow storing IDs of differing types, have a list of any number of
+ * gsm0808_cell_id_list2. */
+ struct llist_head cell_ids;
+};
+
+void neighbor_ident_init(struct gsm_network *net);
+const char *neighbor_ident_addr_name(const struct neighbor_ident_addr *nia);
+
+const struct neighbor_ident_entry *neighbor_ident_add(struct llist_head *ni_list,
+ const struct neighbor_ident_addr *nia,
+ const struct gsm0808_cell_id *cid);
+
+const struct neighbor_ident_entry *neighbor_ident_find_by_cell(const struct llist_head *ni_list,
+ enum osmo_rat_type ran_type,
+ const struct gsm0808_cell_id *cell_id);
+
+const struct neighbor_ident_entry *neighbor_ident_find_by_addr(const struct llist_head *ni_list,
+ const struct neighbor_ident_addr *nia);
+
+void neighbor_ident_del(const struct neighbor_ident_entry *nie);
+
+void neighbor_ident_clear(struct llist_head *ni_list);
+
+void neighbor_ident_vty_init(struct gsm_network *net);
+void neighbor_ident_vty_write(struct vty *vty);
+
diff --git a/include/osmocom/msc/paging.h b/include/osmocom/msc/paging.h
new file mode 100644
index 000000000..4de679df7
--- /dev/null
+++ b/include/osmocom/msc/paging.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+
+struct msc_a;
+struct vlr_subscr;
+struct gsm_trans;
+
+/* Modeled after the RANAP PagingCause; translates to enum sgsap_service_ind and BSSMAP Channel Needed (3GPP TS 48.008
+ * 3.2.2.36) by collapsing e.g. all call related paging causes to SGSAP_SERV_IND_CS_CALL, etc. */
+enum paging_cause {
+ PAGING_CAUSE_CALL_CONVERSATIONAL = 0,
+ PAGING_CAUSE_CALL_STREAMING,
+ PAGING_CAUSE_CALL_INTERACTIVE,
+ PAGING_CAUSE_CALL_BACKGROUND,
+ PAGING_CAUSE_SIGNALLING_LOW_PRIO,
+ PAGING_CAUSE_SIGNALLING_HIGH_PRIO,
+ PAGING_CAUSE_UNSPECIFIED,
+};
+
+extern const struct value_string paging_cause_names[];
+static inline const char *paging_cause_name(enum paging_cause val)
+{ return get_value_string(paging_cause_names, val); }
+
+/* A successful Paging will pass a valid msc_a, an expired paging will pass msc_a == NULL. */
+typedef void (* paging_cb_t )(struct msc_a *msc_a, struct gsm_trans *trans);
+
+struct paging_request {
+ struct llist_head entry;
+
+ /* human readable label to be able to log pending request kinds */
+ const char *label;
+ enum paging_cause cause;
+
+ /* the callback data */
+ paging_cb_t paging_cb;
+ struct gsm_trans *trans;
+};
+
+struct paging_request *paging_request_start(struct vlr_subscr *vsub, enum paging_cause cause,
+ paging_cb_t paging_cb, struct gsm_trans *trans,
+ const char *label);
+void paging_request_remove(struct paging_request *pr);
+
+void paging_response(struct msc_a *msc_a);
+void paging_expired(struct vlr_subscr *vsub);
diff --git a/include/osmocom/msc/ran_conn.h b/include/osmocom/msc/ran_conn.h
index 0b99e252c..7aa50df07 100644
--- a/include/osmocom/msc/ran_conn.h
+++ b/include/osmocom/msc/ran_conn.h
@@ -3,238 +3,31 @@
#include <stdint.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/sigtran/sccp_sap.h>
-#include <osmocom/mgcp_client/mgcp_client.h>
-#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/core/linuxlist.h>
-#define LOG_RAN_CONN(conn, level, fmt, args ...) \
- LOG_RAN_CONN_CAT(conn, (conn) ? (conn)->log_subsys : DMSC, level, fmt, ## args)
-
-#define LOG_RAN_CONN_CAT(conn, subsys, level, fmt, args ...) \
- LOGPFSMSL((conn)? (conn)->fi : NULL, subsys, level, fmt, ## args)
-
-#define VSUB_USE_CONN "conn"
-
-enum ran_conn_fsm_event {
- /* Accepted the initial Complete Layer 3 (starting to evaluate Authentication and Ciphering) */
- RAN_CONN_E_COMPLETE_LAYER_3,
- /* Received Classmark Update, typically neede for Ciphering Mode Command */
- RAN_CONN_E_CLASSMARK_UPDATE,
- /* LU or Process Access FSM has determined that this conn is good */
- RAN_CONN_E_ACCEPTED,
- /* received first reply from MS in "real" CC, SMS, USSD communication */
- RAN_CONN_E_COMMUNICATING,
- /* Some async action has completed, check again whether all is done */
- RAN_CONN_E_RELEASE_WHEN_UNUSED,
- /* MS/BTS/BSC originated close request */
- RAN_CONN_E_MO_CLOSE,
- /* MSC originated close request, e.g. failed authentication */
- RAN_CONN_E_CN_CLOSE,
- /* The usage count for the conn has reached zero */
- RAN_CONN_E_UNUSED,
-};
-
-enum ran_conn_fsm_state {
- RAN_CONN_S_NEW,
- RAN_CONN_S_AUTH_CIPH,
- RAN_CONN_S_WAIT_CLASSMARK_UPDATE,
- RAN_CONN_S_ACCEPTED,
- RAN_CONN_S_COMMUNICATING,
- RAN_CONN_S_RELEASING,
- RAN_CONN_S_RELEASED,
-};
-
-enum integrity_protection_state {
- INTEGRITY_PROTECTION_NONE = 0,
- INTEGRITY_PROTECTION_IK = 1,
- INTEGRITY_PROTECTION_IK_CK = 2,
-};
-
-enum complete_layer3_type {
- COMPLETE_LAYER3_NONE,
- COMPLETE_LAYER3_LU,
- COMPLETE_LAYER3_CM_SERVICE_REQ,
- COMPLETE_LAYER3_PAGING_RESP,
-};
-
-#define MAX_A5_KEY_LEN (128/8)
-
-struct geran_encr {
- uint8_t alg_id;
- uint8_t key_len;
- uint8_t key[MAX_A5_KEY_LEN];
-};
-
-extern const struct value_string complete_layer3_type_names[];
-static inline const char *complete_layer3_type_name(enum complete_layer3_type val)
-{
- return get_value_string(complete_layer3_type_names, val);
-}
-
-struct gsm_classmark {
- bool classmark1_set;
- struct gsm48_classmark1 classmark1;
- uint8_t classmark2_len;
- uint8_t classmark2[3];
- uint8_t classmark3_len;
- uint8_t classmark3[14]; /* if cm3 gets extended by spec, it will be truncated */
-};
+struct ran_peer;
+struct osmo_fsm_inst;
+struct msgb;
/* active radio connection of a mobile subscriber */
struct ran_conn {
- /* global linked list of ran_conn instances */
+ /* Entry in sccp_ran_inst->ran_conns */
struct llist_head entry;
- /* FSM instance to control the RAN connection's permissions and lifetime. */
- struct osmo_fsm_inst *fi;
- enum complete_layer3_type complete_layer3_type;
-
- /* usage count. If this drops to zero, we start the release
- * towards A/Iu */
- uint32_t use_count;
- uint32_t use_tokens;
-
- /* The MS has opened the conn with a CM Service Request, and we shall
- * keep it open for an actual request (or until timeout). */
- bool received_cm_service_request;
-
- /* libmsc/libvlr subscriber information (if available) */
- struct vlr_subscr *vsub;
-
- /* LU expiration handling */
- uint8_t expire_timer_stopped;
-
- /* Are we part of a special "silent" call */
- int silent_call;
+ struct ran_peer *ran_peer;
+ uint32_t sccp_conn_id;
- /* back pointers */
- struct gsm_network *network;
+ /* MSC role that this RAN connection belongs to. This will be either an msc_i (currently active
+ * connection) or an msc_t (transitory new connection during Handover). */
+ struct osmo_fsm_inst *msc_role;
- /* connected via 2G or 3G? */
- enum osmo_rat_type via_ran;
- /* whether to log on DBSSAP, DIUCS, ... */
- int log_subsys;
-
- uint16_t lac;
- struct geran_encr geran_encr;
-
- /* "Temporary" storage for the case the VLR asked for Cipher Mode Command, but the MSC still
- * wants to request a Classmark Update first. */
- struct {
- bool umts_aka;
- bool retrieve_imeisv;
- } geran_set_cipher_mode;
-
- /* N(SD) expected in the received frame, per flow (TS 24.007 11.2.3.2.3.2.2) */
- uint8_t n_sd_next[4];
-
- struct {
- struct mgcp_ctx *mgcp_ctx;
- unsigned int mgcp_rtp_endpoint;
-
- uint16_t local_port_ran;
- char local_addr_ran[INET_ADDRSTRLEN];
- uint16_t remote_port_ran;
- char remote_addr_ran[INET_ADDRSTRLEN];
- enum mgcp_codecs codec_ran;
-
- uint16_t local_port_cn;
- char local_addr_cn[INET_ADDRSTRLEN];
- uint16_t remote_port_cn;
- char remote_addr_cn[INET_ADDRSTRLEN];
- enum mgcp_codecs codec_cn;
- } rtp;
-
- /* which Iu-CS connection, if any. */
- struct {
- struct ranap_ue_conn_ctx *ue_ctx;
- uint8_t rab_id;
- bool waiting_for_release_complete;
- } iu;
-
- struct {
- /* A pointer to the SCCP user that handles
- * the SCCP connections for this subscriber
- * connection */
- struct osmo_sccp_user *scu;
-
- /* The address of the BSC that is associated
- * with this RAN connection */
- struct osmo_sccp_addr bsc_addr;
-
- /* The connection identifier that is used
- * to reference the SCCP connection that is
- * associated with this RAN connection */
- uint32_t conn_id;
-
- bool waiting_for_clear_complete;
- } a;
-
- /* Temporary storage for Classmark Information for times when a connection has no VLR subscriber
- * associated yet. It will get copied to the VLR subscriber upon msc_vlr_subscr_assoc(). */
- struct gsm_classmark temporary_classmark;
-};
-
-struct ran_conn *ran_conn_alloc(struct gsm_network *network, enum osmo_rat_type via_ran, uint16_t lac);
-
-void ran_conn_update_id_from_mi(struct ran_conn *conn, const uint8_t *mi, uint8_t mi_len);
-void ran_conn_update_id(struct ran_conn *conn);
-const char *ran_conn_get_conn_id(struct ran_conn *conn);
-void ran_conn_update_id_for_vsub(struct vlr_subscr *for_vsub);
-
-void ran_conn_complete_layer_3(struct ran_conn *conn);
-
-void ran_conn_sapi_n_reject(struct ran_conn *conn, int dlci);
-int ran_conn_clear_request(struct ran_conn *conn, uint32_t cause);
-void ran_conn_compl_l3(struct ran_conn *conn,
- struct msgb *msg, uint16_t chosen_channel);
-void ran_conn_dtap(struct ran_conn *conn, struct msgb *msg);
-int ran_conn_classmark_request_then_cipher_mode_cmd(struct ran_conn *conn, bool umts_aka,
- bool retrieve_imeisv);
-int ran_conn_geran_set_cipher_mode(struct ran_conn *conn, bool umts_aka, bool retrieve_imeisv);
-void ran_conn_cipher_mode_compl(struct ran_conn *conn, struct msgb *msg, uint8_t alg_id);
-void ran_conn_rx_sec_mode_compl(struct ran_conn *conn);
-void ran_conn_classmark_chg(struct ran_conn *conn,
- const uint8_t *cm2, uint8_t cm2_len,
- const uint8_t *cm3, uint8_t cm3_len);
-void ran_conn_assign_fail(struct ran_conn *conn, uint8_t cause, uint8_t *rr_cause);
-
-void ran_conn_init(void);
-bool ran_conn_is_accepted(const struct ran_conn *conn);
-bool ran_conn_is_establishing_auth_ciph(const struct ran_conn *conn);
-void ran_conn_communicating(struct ran_conn *conn);
-void ran_conn_close(struct ran_conn *conn, uint32_t cause);
-void ran_conn_mo_close(struct ran_conn *conn, uint32_t cause);
-bool ran_conn_in_release(struct ran_conn *conn);
-
-void ran_conn_rx_bssmap_clear_complete(struct ran_conn *conn);
-void ran_conn_rx_iu_release_complete(struct ran_conn *conn);
-void ran_conn_sgs_release_sent(struct ran_conn *conn);
-
-enum ran_conn_use {
- RAN_CONN_USE_UNTRACKED = -1,
- RAN_CONN_USE_COMPL_L3,
- RAN_CONN_USE_DTAP,
- RAN_CONN_USE_AUTH_CIPH,
- RAN_CONN_USE_CM_SERVICE,
- RAN_CONN_USE_TRANS_CC,
- RAN_CONN_USE_TRANS_SMS,
- RAN_CONN_USE_TRANS_NC_SS,
- RAN_CONN_USE_SILENT_CALL,
- RAN_CONN_USE_RELEASE,
+ bool closing;
};
-extern const struct value_string ran_conn_use_names[];
-static inline const char *ran_conn_use_name(enum ran_conn_use val)
-{ return get_value_string(ran_conn_use_names, val); }
-
-#define ran_conn_get(conn, balance_token) \
- _ran_conn_get(conn, balance_token, __FILE__, __LINE__)
-#define ran_conn_put(conn, balance_token) \
- _ran_conn_put(conn, balance_token, __FILE__, __LINE__)
-struct ran_conn * _ran_conn_get(struct ran_conn *conn, enum ran_conn_use balance_token,
- const char *file, int line);
-void _ran_conn_put(struct ran_conn *conn, enum ran_conn_use balance_token,
- const char *file, int line);
-bool ran_conn_used_by(struct ran_conn *conn, enum ran_conn_use token);
+struct ran_conn *ran_conn_create_incoming(struct ran_peer *ran_peer, uint32_t sccp_conn_id);
+struct ran_conn *ran_conn_create_outgoing(struct ran_peer *ran_peer);
+const char *ran_conn_name(struct ran_conn *conn);
+int ran_conn_down_l2_co(struct ran_conn *conn, struct msgb *l3, bool initial);
+void ran_conn_msc_role_gone(struct ran_conn *conn, struct osmo_fsm_inst *msc_role);
+void ran_conn_close(struct ran_conn *conn);
+void ran_conn_discard(struct ran_conn *conn);
diff --git a/include/osmocom/msc/ran_infra.h b/include/osmocom/msc/ran_infra.h
new file mode 100644
index 000000000..38c424f09
--- /dev/null
+++ b/include/osmocom/msc/ran_infra.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsup.h>
+#include <osmocom/msc/sccp_ran.h>
+#include <osmocom/msc/ran_msg.h>
+
+struct osmo_tdef;
+
+extern struct osmo_tdef msc_tdefs_geran[];
+extern struct osmo_tdef msc_tdefs_utran[];
+extern struct osmo_tdef msc_tdefs_sgs[];
+
+extern const struct value_string an_proto_names[];
+static inline const char *an_proto_name(enum osmo_gsup_access_network_protocol val)
+{ return get_value_string(an_proto_names, val); }
+
+struct ran_infra {
+ const enum osmo_rat_type type;
+ const enum osmo_gsup_access_network_protocol an_proto;
+ uint32_t ssn;
+ const int log_subsys;
+ struct osmo_tdef * const tdefs;
+ const struct sccp_ran_ops sccp_ran_ops;
+ const ran_dec_l2_t ran_dec_l2;
+ const ran_encode_t ran_encode;
+ struct sccp_ran_inst *sri;
+};
+
+extern struct ran_infra msc_ran_infra[];
+extern const int msc_ran_infra_len;
diff --git a/include/osmocom/msc/ran_msg.h b/include/osmocom/msc/ran_msg.h
new file mode 100644
index 000000000..4d0485d43
--- /dev/null
+++ b/include/osmocom/msc/ran_msg.h
@@ -0,0 +1,281 @@
+/* API to forward upcoming NAS events, e.g. from BSSAP and RANAP, to be handled by MSC-A or MSC-I. */
+/*
+ * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * All Rights Reserved
+ *
+ * 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/>.
+ */
+#pragma once
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/gsm/protocol/gsm_08_08.h>
+#include <osmocom/mgcp_client/mgcp_client.h>
+
+#include <osmocom/msc/msc_common.h>
+
+struct msgb;
+struct osmo_fsm_inst;
+
+#define LOG_RAN_DEC(NAS_DEC, subsys, level, fmt, args...) \
+ LOGPFSMSL((NAS_DEC)? (NAS_DEC)->caller_fi : NULL, subsys, level, "RAN decode: " fmt, ## args)
+
+#define LOG_RAN_ENC(FI, subsys, level, fmt, args...) \
+ LOGPFSMSL(FI, subsys, level, "RAN encode: " fmt, ## args)
+
+/* These message types are named after the BSSAP procedures in nas_a.h; most are also used for RANAP procedures of
+ * similar meaning in nas_iu.h. */
+enum ran_msg_type {
+ RAN_MSG_NONE = 0,
+ RAN_MSG_COMPL_L3,
+ RAN_MSG_DTAP,
+ RAN_MSG_CLEAR_COMMAND,
+ RAN_MSG_CLEAR_REQUEST,
+ RAN_MSG_CLEAR_COMPLETE,
+ RAN_MSG_CLASSMARK_REQUEST,
+ RAN_MSG_CLASSMARK_UPDATE,
+ RAN_MSG_CIPHER_MODE_COMMAND,
+ RAN_MSG_CIPHER_MODE_COMPLETE,
+ RAN_MSG_CIPHER_MODE_REJECT,
+ RAN_MSG_COMMON_ID,
+ RAN_MSG_ASSIGNMENT_COMMAND,
+ RAN_MSG_ASSIGNMENT_COMPLETE,
+ RAN_MSG_ASSIGNMENT_FAILURE,
+ RAN_MSG_SAPI_N_REJECT,
+ RAN_MSG_LCLS_STATUS,
+ RAN_MSG_LCLS_BREAK_REQ,
+ RAN_MSG_HANDOVER_COMMAND,
+ RAN_MSG_HANDOVER_PERFORMED,
+ RAN_MSG_HANDOVER_REQUIRED,
+ RAN_MSG_HANDOVER_REQUIRED_REJECT,
+ RAN_MSG_HANDOVER_REQUEST,
+ RAN_MSG_HANDOVER_REQUEST_ACK,
+ RAN_MSG_HANDOVER_DETECT,
+ RAN_MSG_HANDOVER_SUCCEEDED,
+ RAN_MSG_HANDOVER_COMPLETE,
+ RAN_MSG_HANDOVER_FAILURE,
+};
+
+extern const struct value_string ran_msg_type_names[];
+static inline const char *ran_msg_type_name(enum ran_msg_type val)
+{ return get_value_string(ran_msg_type_names, val); }
+
+struct ran_clear_command {
+ enum gsm0808_cause gsm0808_cause;
+ bool csfb_ind;
+};
+
+struct ran_assignment_command {
+ const struct osmo_sockaddr_str *cn_rtp;
+ const struct gsm0808_channel_type *channel_type;
+ enum nsap_addr_enc rab_assign_addr_enc;
+};
+
+struct ran_cipher_mode_command {
+ const struct osmo_auth_vector *vec;
+ const struct osmo_gsm48_classmark *classmark;
+ struct {
+ bool umts_aka;
+ bool retrieve_imeisv;
+ uint8_t a5_encryption_mask;
+
+ /* out-argument to return the key to the caller, pass NULL if not needed. */
+ struct geran_encr *chosen_key;
+ } geran;
+};
+
+struct ran_handover_request {
+ const char *imsi;
+ const struct osmo_gsm48_classmark *classmark;
+ /* A Handover Request on GERAN-A sends separate IEs for
+ * - permitted algorithms, here composed from the a5_encryption_mask,
+ * - the key, here taken from chosen_encryption->key iff chosen_encryption is present,
+ * - the actually chosen algorithm ("Serving"), here taken from chosen_encryption->alg_id.
+ */
+ struct {
+ struct gsm0808_channel_type *channel_type;
+ uint8_t a5_encryption_mask;
+ /*! chosen_encryption->alg_id is in encoded format:
+ * alg_id == 1 means A5/0 i.e. no encryption, alg_id == 4 means A5/3.
+ * alg_id == 0 means no such IE was present. */
+ struct geran_encr *chosen_encryption;
+ } geran;
+ struct gsm0808_cell_id cell_id_serving;
+ struct gsm0808_cell_id cell_id_target;
+
+ enum gsm0808_cause bssap_cause;
+
+ bool current_channel_type_1_present;
+ uint8_t current_channel_type_1;
+
+ enum gsm0808_permitted_speech speech_version_used;
+
+ const uint8_t *old_bss_to_new_bss_info_raw;
+ uint8_t old_bss_to_new_bss_info_raw_len;
+
+ struct osmo_sockaddr_str *rtp_ran_local;
+
+ struct gsm0808_speech_codec_list *codec_list_msc_preferred;
+
+ bool call_id_present;
+ uint32_t call_id;
+
+ const uint8_t *global_call_reference;
+ uint8_t global_call_reference_len;
+};
+
+struct ran_handover_request_ack {
+ const uint8_t *rr_ho_command;
+ uint8_t rr_ho_command_len;
+ bool chosen_channel_present;
+ uint8_t chosen_channel;
+ /*! chosen_encr_alg is in encoded format:
+ * chosen_encr_alg == 1 means A5/0 i.e. no encryption, chosen_encr_alg == 4 means A5/3.
+ * chosen_encr_alg == 0 means no such IE was present. */
+ uint8_t chosen_encr_alg;
+
+ /* chosen_speech_version == 0 means "not present" */
+ enum gsm0808_permitted_speech chosen_speech_version;
+
+ struct osmo_sockaddr_str remote_rtp;
+ bool codec_present;
+ enum mgcp_codecs codec;
+};
+
+struct ran_handover_command {
+ const uint8_t *rr_ho_command;
+ uint8_t rr_ho_command_len;
+
+ const uint8_t *new_bss_to_old_bss_info_raw;
+ uint8_t new_bss_to_old_bss_info_raw_len;
+};
+
+struct ran_handover_required {
+ uint16_t cause;
+ struct gsm0808_cell_id_list2 cil;
+
+ bool current_channel_type_1_present;
+ /*! See gsm0808_chosen_channel() */
+ uint8_t current_channel_type_1;
+
+ enum gsm0808_permitted_speech speech_version_used;
+
+ uint8_t *old_bss_to_new_bss_info_raw;
+ size_t old_bss_to_new_bss_info_raw_len;
+};
+
+struct ran_msg {
+ enum ran_msg_type msg_type;
+
+ /* Since different RAN implementations feed these messages, they should place here an implementation specific
+ * string constant to name the actual message (e.g. "BSSMAP Assignment Complete" vs. "RANAP RAB Assignment
+ * Response") */
+ const char *msg_name;
+
+ union {
+ struct {
+ const struct gsm0808_cell_id *cell_id;
+ struct msgb *msg;
+ } compl_l3;
+ struct msgb *dtap;
+ struct {
+ enum gsm0808_cause bssap_cause;
+#define RAN_MSG_BSSAP_CAUSE_UNSET 0xffff
+ } clear_request;
+ struct ran_clear_command clear_command;
+ struct {
+ const struct osmo_gsm48_classmark *classmark;
+ } classmark_update;
+ struct ran_cipher_mode_command cipher_mode_command;
+ struct {
+ /*! alg_id is in encoded format:
+ * alg_id == 1 means A5/0 i.e. no encryption, alg_id == 4 means A5/3.
+ * alg_id == 0 means no such IE was present. */
+ uint8_t alg_id;
+ const char *imeisv;
+ } cipher_mode_complete;
+ struct {
+ enum gsm0808_cause bssap_cause;
+ } cipher_mode_reject;
+ struct {
+ const char *imsi;
+ } common_id;
+ struct {
+ enum gsm48_reject_value cause;
+ } cm_service_reject;
+ struct ran_assignment_command assignment_command;
+ struct {
+ struct osmo_sockaddr_str remote_rtp;
+ bool codec_present;
+ enum mgcp_codecs codec;
+ } assignment_complete;
+ struct {
+ enum gsm0808_cause bssap_cause;
+ uint8_t rr_cause;
+ const struct gsm0808_speech_codec_list *scl_bss_supported;
+ } assignment_failure;
+ struct {
+ enum gsm0808_cause bssap_cause;
+ uint8_t dlci;
+ } sapi_n_reject;
+ struct {
+ enum gsm0808_lcls_status status;
+ } lcls_status;
+ struct {
+ int todo;
+ } lcls_break_req;
+ struct ran_handover_required handover_required;
+ struct gsm0808_handover_required_reject handover_required_reject;
+ struct ran_handover_command handover_command;
+ struct {
+ enum gsm0808_cause cause;
+ } handover_failure;
+ struct ran_handover_request handover_request;
+ struct ran_handover_request_ack handover_request_ack;
+ };
+};
+
+/* MSC-A/I/T roles implement this to receive decoded NAS messages, upon feeding an L2 msgb to a ran_dec_l2_t matching the
+ * RAN type implementation. */
+typedef int (* ran_decode_cb_t )(struct osmo_fsm_inst *caller_fi, void *caller_data, const struct ran_msg *msg);
+
+struct ran_dec {
+ /* caller provided osmo_fsm_inst, used both for logging from within decoding of NAS events, as well as caller's
+ * context in decode_cb(). */
+ struct osmo_fsm_inst *caller_fi;
+ void *caller_data;
+
+ /* Callback receives the decoded NAS messages */
+ ran_decode_cb_t decode_cb;
+};
+
+/* NAS decoders (BSSAP/RANAP) implement this to turn a msgb into a struct ran_msg.
+ * An implementation typically calls ran_decoded() when done decoding.
+ * NAS decoding is modeled with a callback instead of a plain decoding, because some L2 messages by design contain more
+ * than one NAS event, e.g. Ciphering Mode Complete may include another L3 message for Identity Response, and LCLS
+ * Information messages can contain Status and Break Req events. */
+typedef int (* ran_dec_l2_t )(struct ran_dec *ran_dec, struct msgb *l2);
+
+int ran_decoded(struct ran_dec *ran_dec, struct ran_msg *msg);
+
+/* An MSC-A/I/T role that receives NAS events containing DTAP buffers may use this to detect DTAP duplicates as in TS
+ * 24.007 11.2.3.2 Message Type Octet / Duplicate Detection */
+bool ran_dec_dtap_undup_is_duplicate(struct osmo_fsm_inst *log_fi, uint8_t *n_sd_next, bool is_r99, struct msgb *l3);
+
+/* Implemented by individual RAN implementations, see ran_a_encode() and ran_iu_encode(). */
+typedef struct msgb *(* ran_encode_t )(struct osmo_fsm_inst *caller_fi, const struct ran_msg *ran_enc_msg);
diff --git a/include/osmocom/msc/ran_msg_a.h b/include/osmocom/msc/ran_msg_a.h
new file mode 100644
index 000000000..3ba081de2
--- /dev/null
+++ b/include/osmocom/msc/ran_msg_a.h
@@ -0,0 +1,45 @@
+/* Abstraction of BSSAP decoding into NAS events, to be handled by MSC-A or MSC-I, and encoding of BSSAP messages
+ * towards the RAN. */
+/*
+ * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * All Rights Reserved
+ *
+ * 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/>.
+ */
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/msc/ran_msg.h>
+#include <osmocom/msc/paging.h>
+
+struct msgb;
+struct sccp_ran_inst;
+struct msub;
+struct gsm_mncc_bearer_cap;
+
+int ran_a_decode_l2(struct ran_dec *ran_a, struct msgb *bssap);
+struct msgb *ran_a_encode(struct osmo_fsm_inst *caller_fi, const struct ran_msg *ran_enc_msg);
+
+enum reset_msg_type bssmap_is_reset_msg(const struct sccp_ran_inst *sri, const struct msgb *l2);
+struct msgb *bssmap_make_reset_msg(const struct sccp_ran_inst *sri, enum reset_msg_type type);
+struct msgb *bssmap_make_paging_msg(const struct sccp_ran_inst *sri, const struct gsm0808_cell_id *page_cell_id,
+ const char *imsi, uint32_t tmsi, enum paging_cause cause);
+const char *bssmap_msg_name(const struct sccp_ran_inst *sri, const struct msgb *l2);
+
+enum mgcp_codecs ran_a_mgcp_codec_from_sc(const struct gsm0808_speech_codec *sc);
+int ran_a_bearer_cap_to_channel_type(struct gsm0808_channel_type *ct, const struct gsm_mncc_bearer_cap *bc);
diff --git a/include/osmocom/msc/ran_msg_iu.h b/include/osmocom/msc/ran_msg_iu.h
new file mode 100644
index 000000000..316a91cdb
--- /dev/null
+++ b/include/osmocom/msc/ran_msg_iu.h
@@ -0,0 +1,35 @@
+/* Abstraction of RANAP decoding into NAS events, to be handled by MSC-A or MSC-I, and encoding of RANAP messages
+ * towards the RAN. */
+/*
+ * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * All Rights Reserved
+ *
+ * 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/>.
+ */
+#pragma once
+
+#include <osmocom/msc/ran_msg.h>
+#include <osmocom/msc/paging.h>
+
+int ran_iu_decode_l2(struct ran_dec *ran_dec_iu, struct msgb *ranap);
+struct msgb *ran_iu_encode(struct osmo_fsm_inst *caller_fi, const struct ran_msg *ran_enc_msg);
+
+enum reset_msg_type ranap_is_reset_msg(const struct sccp_ran_inst *sri, const struct msgb *l2);
+struct msgb *ranap_make_reset_msg(const struct sccp_ran_inst *sri, enum reset_msg_type type);
+struct msgb *ranap_make_paging_msg(const struct sccp_ran_inst *sri, const struct gsm0808_cell_id *page_cell_id,
+ const char *imsi, uint32_t tmsi, enum paging_cause cause);
+const char *ranap_msg_name(const struct sccp_ran_inst *sri, const struct msgb *l2);
diff --git a/include/osmocom/msc/ran_peer.h b/include/osmocom/msc/ran_peer.h
new file mode 100644
index 000000000..e3ff59d9c
--- /dev/null
+++ b/include/osmocom/msc/ran_peer.h
@@ -0,0 +1,106 @@
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/sigtran/sccp_sap.h>
+
+#include <osmocom/msc/debug.h>
+#include <osmocom/msc/paging.h>
+
+struct vlr_subscr;
+struct ran_conn;
+struct neighbor_ident_entry;
+
+#define LOG_RAN_PEER_CAT(RAN_PEER, subsys, loglevel, fmt, args ...) \
+ LOGPFSMSL((RAN_PEER)? (RAN_PEER)->fi : NULL, subsys, loglevel, fmt, ## args)
+
+#define LOG_RAN_PEER(RAN_PEER, loglevel, fmt, args ...) \
+ LOG_RAN_PEER_CAT(RAN_PEER, \
+ (RAN_PEER) && (RAN_PEER)->sri? (RAN_PEER)->sri->ran->log_subsys : DMSC, \
+ loglevel, fmt, ## args)
+
+/* A BSC or RNC with activity on a local SCCP connection.
+ * Here we collect those BSC and RNC peers that are actually connected to the MSC and manage their connection Reset
+ * status.
+ *
+ * Before we had explicit neighbor configuration for inter-BSC and inter-MSC handover, the only way to know which peer
+ * address corresponds to which LAC (for paging a specific LAC) was to collect the LAC from L3 messages coming in on a
+ * subscriber connection. We still continue that practice to support unconfigured operation.
+ *
+ * The neighbor list config extends this by possibly naming LAC and CI that have not seen explicit activity yet, and
+ * allows us to page towards the correct peer's SCCP address from the start.
+ *
+ * So, for paging, the idea is to look for a LAC that is recorded here, and if not found, query the neighbor
+ * configuration for a peer's SCCP address matching that LAC. If found, look for active connections on that SCCP address
+ * here.
+ *
+ * Any valid RAN peer will contact us and initiate a RESET procedure. In turn, on osmo-msc start, we may choose to
+ * initiate a RESET procedure towards every known RAN peer.
+ *
+ * Semantically, it would make sense to keep the list of ran_conn instances in each struct ran_peer, but since
+ * non-Initial Connection-Oriented messages indicate only the conn by id (and identify the ran_peer from that), the conn
+ * list is kept in sccp_ran_inst. For convenience, see ran_peer_for_each_ran_conn().
+ */
+struct ran_peer {
+ /* Entry in sccp_ran_inst->ran_conns */
+ struct llist_head entry;
+
+ struct sccp_ran_inst *sri;
+ struct osmo_sccp_addr peer_addr;
+ struct osmo_fsm_inst *fi;
+
+ /* See cell_id_list.h */
+ struct llist_head cells_seen;
+};
+
+#define ran_peer_for_each_ran_conn(RAN_CONN, RAN_PEER) \
+ llist_for_each_entry(RAN_CONN, &(RAN_PEER)->sri->ran_conns, entry) \
+ if ((RAN_CONN)->ran_peer == (RAN_PEER))
+
+#define ran_peer_for_each_ran_conn_safe(RAN_CONN, RAN_CONN_NEXT, RAN_PEER) \
+ llist_for_each_entry_safe(RAN_CONN, RAN_CONN_NEXT, &(RAN_PEER)->sri->ran_conns, entry) \
+ if ((RAN_CONN)->ran_peer == (RAN_PEER))
+
+enum ran_peer_state {
+ RAN_PEER_ST_WAIT_RX_RESET = 0,
+ RAN_PEER_ST_WAIT_RX_RESET_ACK,
+ RAN_PEER_ST_READY,
+ RAN_PEER_ST_DISCARDING,
+};
+
+enum ran_peer_event {
+ RAN_PEER_EV_MSG_UP_CL = 0,
+ RAN_PEER_EV_MSG_UP_CO_INITIAL,
+ RAN_PEER_EV_MSG_UP_CO,
+ RAN_PEER_EV_MSG_DOWN_CL,
+ RAN_PEER_EV_MSG_DOWN_CO_INITIAL,
+ RAN_PEER_EV_MSG_DOWN_CO,
+ RAN_PEER_EV_RX_RESET,
+ RAN_PEER_EV_RX_RESET_ACK,
+ RAN_PEER_EV_CONNECTION_SUCCESS,
+ RAN_PEER_EV_CONNECTION_TIMEOUT,
+};
+
+struct ran_peer_ev_ctx {
+ uint32_t conn_id;
+ struct ran_conn *conn;
+ struct msgb *msg;
+};
+
+struct ran_peer *ran_peer_find_or_create(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *peer_addr);
+struct ran_peer *ran_peer_find(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *peer_addr);
+
+void ran_peer_cells_seen_add(struct ran_peer *ran_peer, const struct gsm0808_cell_id *id);
+
+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);
+void ran_peer_disconnect(struct sccp_ran_inst *sri, uint32_t conn_id);
+
+int ran_peers_down_paging(struct sccp_ran_inst *sri, enum CELL_IDENT page_where, struct vlr_subscr *vsub,
+ enum paging_cause cause);
+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 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 *ran_peer_find_by_addr(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *addr);
diff --git a/include/osmocom/msc/rtp_stream.h b/include/osmocom/msc/rtp_stream.h
new file mode 100644
index 000000000..794e8066f
--- /dev/null
+++ b/include/osmocom/msc/rtp_stream.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/mgcp_client/mgcp_client.h>
+
+struct gsm_trans;
+
+struct osmo_fsm_inst;
+struct call_leg;
+struct osmo_mgcpc_ep;
+struct osmo_mgcpc_ep_ci;
+
+enum rtp_direction {
+ RTP_TO_RAN,
+ RTP_TO_CN,
+};
+
+extern const struct value_string rtp_direction_names[];
+static inline const char *rtp_direction_name(enum rtp_direction val)
+{ return get_value_string(rtp_direction_names, val); }
+
+/* A single bidirectional RTP hop between remote and MGW's local RTP port. */
+struct rtp_stream {
+ struct osmo_fsm_inst *fi;
+ struct call_leg *parent_call_leg;
+ enum rtp_direction dir;
+
+ uint32_t call_id;
+
+ /* Backpointer for callers (optional) */
+ struct gsm_trans *for_trans;
+
+ struct osmo_sockaddr_str local;
+ struct osmo_sockaddr_str remote;
+ bool remote_sent_to_mgw;
+
+ bool codec_known;
+ enum mgcp_codecs codec;
+ bool codec_sent_to_mgw;
+
+ struct osmo_mgcpc_ep_ci *ci;
+
+ enum mgcp_connection_mode crcx_conn_mode;
+};
+
+#define RTP_STREAM_FMT "local=" RTP_IP_PORT_FMT ",remote=" RTP_IP_PORT_FMT
+#define RTP_STREAM_ARGS(RS) RTP_IP_PORT_ARGS(&(RS)->local), RTP_IP_PORT_ARGS(&(RS)->remote),
+
+struct rtp_stream *rtp_stream_alloc(struct call_leg *parent_call_leg, enum rtp_direction dir,
+ uint32_t call_id, struct gsm_trans *for_trans);
+
+int rtp_stream_ensure_ci(struct rtp_stream *rtps, struct osmo_mgcpc_ep *at_endpoint);
+int rtp_stream_do_mdcx(struct rtp_stream *rtps);
+
+void rtp_stream_set_codec(struct rtp_stream *rtps, enum mgcp_codecs codec);
+void rtp_stream_set_remote_addr(struct rtp_stream *rtps, const struct osmo_sockaddr_str *r);
+int rtp_stream_commit(struct rtp_stream *rtps);
+
+void rtp_stream_release(struct rtp_stream *rtps);
+
+bool rtp_stream_is_established(struct rtp_stream *rtps);
diff --git a/include/osmocom/msc/sccp_ran.h b/include/osmocom/msc/sccp_ran.h
new file mode 100644
index 000000000..b7da314b2
--- /dev/null
+++ b/include/osmocom/msc/sccp_ran.h
@@ -0,0 +1,280 @@
+/* The RAN (Radio Access Network) side of an A- or Iu-connection, which is closely tied to an SCCP connection.
+ * (as opposed to the NAS side.)
+ *
+ * The SCCP connection is located with the MSC-I role, while the MSC-A responsible for subscriber management may be at a
+ * remote MSC behind an E-interface connection. In that case we need to forward the L2 messages over the E-interface and
+ * the BSSAP or RANAP messages get decoded and interpreted at MSC-A.
+ *
+ * The life cycle of a DTAP message from RAN to MSC-A -- starting from the bottom left:
+ *
+ * ------------------>[ 3GPP TS 24.008 ]------------------->|
+ * ^ (Request) (Response) |
+ * | v
+ * msc_a_up_l3() msc_a_tx_dtap_to_i(dtap_msgb)
+ * ^ |
+ * | v
+ * msc_a_nas_decode_cb(struct nas_dec_msg) msc_a_nas_enc(struct nas_enc_msg)
+ * ^ ^ . |
+ * | -Decode NAS- | . NAS v
+ * | | . ran_infra[type]->nas_encode(struct nas_enc_msg)
+ * nas_a_decode_l2() nas_iu_decode_l2() . | |
+ * ^ ^ . v v
+ * | | . nas_a_encode() nas_iu_encode()
+ * ran_infra[type]->nas_dec_l2() | |
+ * ^ | -Encode BSSAP/RANAP- |
+ * | v v
+ * msc_a_nas_dec() msub_tx_an_apdu(from MSC_ROLE_A to MSC_ROLE_I)
+ * ^ |
+ * | MSC-A v
+ * . msc_a FSM . . . . . . . . . . . . . . . . msc_a FSM . . . . . . . . . .
+ * ^ |
+ * | MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST v
+ * | data = an_apdu [possibly
+ * | via GSUP
+ * [possibly from remote MSC-A]
+ * via GSUP |
+ * to remote MSC-A] | MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST
+ * ^ | data = an_apdu
+ * | v
+ * . msc_i FSM . . . . . . . . . . . . . . . . msc_i FSM . . . . . . . . . .
+ * ^ MSC-I |
+ * | MSC_EV_FROM_RAN_UP_L2 V
+ * | data = an_apdu msc_i_down_l2(an_apdu->msg)
+ * | |
+ * ran_peer FSM V
+ * ^ ran_conn_down_l2_co();
+ * | RAN_PEER_EV_MSG_UP_CO |
+ * | data = struct ran_peer_ev_ctx | RAN_PEER_EV_MSG_DOWN_CO
+ * | | data = struct ran_peer_ev_ctx
+ * ran_peer_up_l2() V
+ * (ran_infa->sccp_ran_ops.up_l2) ran_peer FSM
+ * ^ ^ |
+ * | | v
+ * sccp_ran_sap_up() sccp_ran_down_l2_co(conn_id, msg)
+ * ^ ^ | |
+ * | | |SCCP|
+ * |SCCP| v v
+ * | | <------------------------------------------------------
+ * BSC RNC
+ * | |
+ * BTS NodeB
+ * | |
+ * MS UE
+ *
+ * sccp_ran:
+ * - handles receiving of SCCP primitives from the SCCP layer.
+ * - extracts L2 msg
+ * - passes on L2 msg and conn_id by calling sccp_ran_ops.up_l2 == ran_peer_up_l2().
+ *
+ * On Connection-Oriented *Initial* message
+ * ========================================
+ *
+ * ran_peer_up_l2()
+ * - notices an unknown, new osmo_rat_type:conn_id and
+ * - first creates an "empty" msub with new local MSC-I and MSC-A roles;
+ * in this case always a *local* MSC-A (never remote on Initial messages).
+ * - Passes the L2 msgb containing the BSSAP or RANAP as AN-APDU
+ * in MSC_A_EV_FROM_I_COMPLETE_LAYER_3 to the MSC-A role FSM instance.
+ *
+ * MSC-A:
+ * - Receives MSC_A_EV_FROM_I_COMPLETE_LAYER_3 AN-APDU, notices an_proto indicating BSSAP or RANAP.
+ * - Passes L2 message to ran_infra[]->nas_dec_l2(), which decodes the BSSAP or RANAP.
+ * - contained information is passed to msc_a_nas_decode_cb().
+ * - which msc_a starts Complete-L3 and VLR procedures,
+ * - associates msub with a vlr_subscr,
+ * - sends DTAP requests back down by calling msc_a_tx_dtap_to_i() (possibly other more specialized tx functions)
+ * - according to ran_infra[]->nas_encode(), the nas_enc_msg gets encoded as BSSAP or RANAP.
+ * - passes as AN-APDU to MSC-I in MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST signal.
+ *
+ * MSC-I, receiving AN-APDU from local MSC-A:
+ * - feeds L2 msgb to the ran_peer FSM as RAN_PEER_EV_MSG_DOWN_CO, passing the SCCP conn_id.
+ *
+ * sccp_ran_down_l2_co()
+ * - wraps in SCCP prim,
+ * - sends down.
+ *
+ *
+ * On (non-Initial) Connection-Oriented DTAP
+ * =========================================
+ *
+ * ran_peer_up_l2()
+ * - notices an already known conn_id by looking up a matching osmo_rat_type:ran_conn.
+ * - ran_conn already associated with an MSC-I role.
+ * - Now forwards AN-APDU like above, only using MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST.
+ *
+ *
+ * MSC-A and MSC-I roles on separate MSC instances
+ * ===============================================
+ *
+ * After inter-MSC handover, the MSC-I and MSC-A roles can be on separate MSC instances, typically physically distant /
+ * possibly belonging to a different operator. This will never see Complete-L3.
+ * Assuming that both instances are osmo-msc, then:
+ *
+ * At MSC-B:
+ * initially, via GSUP:
+ * - receives Handover Request from remote MSC-A,
+ * - creates msub with local MSC-T role,
+ * - sets up the ran_conn with a new SCCP conn_id, and waits for the MS/UE to show up.
+ * - (fast-forward to successful Handover)
+ * - MSC-T role becomes MSC-I for the remote MSC-A.
+ *
+ * Then for DTAP from the MS:
+ *
+ * sccp_ran:
+ * - receives SCCP,
+ * - extracts L2 and passes on to ran_peer_up_l2().
+ *
+ * ran_peer_up_l2()
+ * - notices an already known conn_id by looking up a matching ran_conn.
+ * - ran_conn already associated with an MSC-I role and an msub.
+ * - forwards AN-APDU in MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST to the MSC-A role.
+ *
+ * At MSC-B, the "MSC-A role" is a *remote* implementation,
+ * meaning there is an msc_a_remote FSM instance in MSC-B's msub:
+ *
+ * MSC-A-Remote:
+ * - msc_a_remote receives MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST,
+ * - wraps AN-APDU in GSUP message,
+ * - sends to remote MSC-A.
+ *
+ * At MSC-A:
+ * Here, msub has a *remote* MSC-I role,
+ * meaning it is an msc_i_remote FSM instance:
+ *
+ * MSC-I-Remote:
+ * - msc_i_remote receives and decodes GSUP message,
+ * - passes AN-APDU to MSC-A FSM instance via MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST.
+ *
+ * MSC-A role:
+ * - Receives MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST, notices an_proto indicating BSSAP or RANAP.
+ * - Passes L2 message to ran_infra[]->nas_dec_l2(), which decodes the BSSAP or RANAP.
+ * - contained information is passed to msc_a_nas_decode_cb().
+ * - sends DTAP requests back down by calling msc_a_tx_dtap_to_i() (possibly other more specialized tx functions)
+ * - according to ran_infra[]->nas_encode(), the nas_enc_msg gets encoded as BSSAP or RANAP.
+ * - passes as AN-APDU to MSC-I in MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST signal.
+ *
+ * MSC-I-Remote:
+ * - msc_i_remote wraps AN-APDU in GSUP message,
+ * - sends to MSC-B
+ *
+ * At MSC-B:
+ * MSC-A-Remote:
+ * - msc_a_remote receives GSUP message,
+ * - passes AN-APDU to msc_i in MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST.
+ *
+ * MSC-I:
+ * - BSSAP or RANAP is indicated both by the AN-APDU an_proto, as well as the ran_conn state for that subscriber.
+ * - feeds L2 msgb to the ran_peer FSM as RAN_PEER_EV_MSG_DOWN_CO, passing the SCCP conn_id.
+ *
+ * sccp_ran_down_l2_co()
+ * - wraps in SCCP prim,
+ * - sends down.
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/tdef.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0808_utils.h>
+#include <osmocom/sigtran/sccp_sap.h>
+
+#include <osmocom/msc/paging.h>
+
+struct msgb;
+struct ran_infra;
+struct sccp_ran_inst;
+
+#define LOG_SCCP_RAN_CO(sri, peer_addr, conn_id, level, fmt, args...) \
+ LOGP((sri) && (sri)->ran? (sri)->ran->log_subsys : DMSC, level, "(%s-%u%s%s) " fmt, \
+ osmo_rat_type_name((sri) && (sri)->ran? (sri)->ran->type : -1), conn_id, \
+ peer_addr ? " from " : "", \
+ peer_addr ? osmo_sccp_inst_addr_name((sri)->sccp, peer_addr) : "", \
+ ## args)
+
+#define LOG_SCCP_RAN_CL_CAT(sri, peer_addr, subsys, level, fmt, args...) \
+ LOGP(subsys, level, "(%s%s%s) " fmt, \
+ osmo_rat_type_name((sri) && (sri)->ran? (sri)->ran->type : -1), \
+ peer_addr ? " from " : "", \
+ peer_addr ? osmo_sccp_inst_addr_name((sri)->sccp, peer_addr) : "", \
+ ## args)
+
+#define LOG_SCCP_RAN_CL(sri, peer_addr, level, fmt, args...) \
+ LOG_SCCP_RAN_CL_CAT(sri, peer_addr, (sri) && (sri)->ran? (sri)->ran->log_subsys : DMSC, level, fmt, ##args)
+
+#define LOG_SCCP_RAN_CAT(sri, subsys, level, fmt, args...) \
+ LOG_SCCP_RAN_CL_CAT(sri, NULL, subsys, level, fmt, ##args)
+
+#define LOG_SCCP_RAN(sri, level, fmt, args...) \
+ LOG_SCCP_RAN_CL(sri, NULL, level, fmt, ##args)
+
+extern struct osmo_tdef g_sccp_tdefs[];
+
+enum reset_msg_type {
+ SCCP_RAN_MSG_NON_RESET = 0,
+ SCCP_RAN_MSG_RESET,
+ SCCP_RAN_MSG_RESET_ACK,
+};
+
+struct sccp_ran_ops {
+ /* Implemented to receive L2 messages (e.g. BSSAP or RANAP passed to ran_peer).
+ * - ConnectionLess messages: co = false, calling_addr != NULL, conn_id == 0;
+ * - ConnectionOriented Initial messages: co = true, calling_addr != NULL;
+ * - ConnectionOriented non-Initial messages: co = true, calling_addr == NULL;
+ */
+ int (* up_l2 )(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *calling_addr, bool co, uint32_t conn_id,
+ struct msgb *l2);
+
+ /* Implemented to finally remove a connection state. Last event in a connection-oriented exchange. If the
+ * N-DISCONNECT contained l2 data, it was dispatched via up_l2() before this is called. */
+ void (* disconnect )(struct sccp_ran_inst *sri, uint32_t conn_id);
+
+ /* Return whether the given l2_cl message is a RESET, RESET ACKNOWLEDGE, or RESET-unrelated message.
+ * This callback is stored in struct sccp_ran_inst to provide RESET handling to the caller (ran_peer),
+ * it is not used in sccp_ran.c. */
+ enum reset_msg_type (* is_reset_msg )(const struct sccp_ran_inst *sri, const struct msgb *l2_cl);
+
+ /* Return a RESET or RESET ACK message for this RAN type.
+ * This callback is stored in struct sccp_ran_inst to provide RESET handling to the caller (ran_peer),
+ * it is not used in sccp_ran.c. */
+ struct msgb* (* make_reset_msg )(const struct sccp_ran_inst *sri, enum reset_msg_type);
+
+ /* Return a PAGING message towards the given Cell Identifier, to page for the given TMSI or IMSI.
+ * Page for TMSI if TMSI != GSM_RESERVED_TMSI, otherwise page for IMSI. */
+ struct msgb* (* make_paging_msg )(const struct sccp_ran_inst *sri, const struct gsm0808_cell_id *page_cell_id,
+ const char *imsi, uint32_t tmsi, enum paging_cause cause);
+
+ /* Return a human printable name for the msgb */
+ const char* (* msg_name )(const struct sccp_ran_inst *sri, const struct msgb *l2);
+};
+
+struct sccp_ran_inst {
+ struct ran_infra *ran;
+
+ struct osmo_sccp_instance *sccp;
+ struct osmo_sccp_user *scu;
+ struct osmo_sccp_addr local_sccp_addr;
+
+ struct llist_head ran_peers;
+ struct llist_head ran_conns;
+
+ void *user_data;
+
+ /* Compatibility with legacy osmo-hnbgw that was unable to properly handle RESET messages. Set to 'false' to
+ * require proper RESET procedures, set to 'true' to implicitly put a ran_peer in RAN_PEER_ST_READY upon the
+ * first CO message. Default is false = be strict. */
+ bool ignore_missing_reset;
+};
+
+struct sccp_ran_inst *sccp_ran_init(void *talloc_ctx, struct osmo_sccp_instance *sccp, enum osmo_sccp_ssn ssn,
+ const char *sccp_user_name, struct ran_infra *ran, void *user_data);
+
+int sccp_ran_down_l2_co_initial(struct sccp_ran_inst *sri,
+ const struct osmo_sccp_addr *called_addr,
+ uint32_t conn_id, struct msgb *l2);
+int sccp_ran_down_l2_co(struct sccp_ran_inst *sri, uint32_t conn_id, struct msgb *l2);
+int sccp_ran_down_l2_cl(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *called_addr, struct msgb *l2);
+
+int sccp_ran_disconnect(struct sccp_ran_inst *ran, uint32_t conn_id, uint32_t cause);
diff --git a/include/osmocom/msc/sgs_iface.h b/include/osmocom/msc/sgs_iface.h
index a167cd6d8..575468e10 100644
--- a/include/osmocom/msc/sgs_iface.h
+++ b/include/osmocom/msc/sgs_iface.h
@@ -24,8 +24,11 @@
#include <osmocom/gsm/protocol/gsm_29_118.h>
#include <osmocom/msc/vlr.h>
#include <osmocom/msc/vlr_sgs.h>
+#include <osmocom/msc/paging.h>
#include <osmocom/core/socket.h>
+struct msc_a;
+
static const unsigned int sgs_state_timer_defaults[_NUM_SGS_STATE_TIMERS] = {
[SGS_STATE_TS5] = SGS_TS5_DEFAULT,
[SGS_STATE_TS6_2] = SGS_TS6_2_DEFAULT,
@@ -82,6 +85,8 @@ extern struct sgs_state *g_sgs;
struct sgs_state *sgs_iface_init(void *ctx, struct gsm_network *network);
int sgs_iface_rx(struct sgs_connection *sgc, struct msgb *msg);
+enum sgsap_service_ind sgs_serv_ind_from_paging_cause(enum paging_cause);
int sgs_iface_tx_paging(struct vlr_subscr *vsub, enum sgsap_service_ind serv_ind);
-int sgs_iface_tx_dtap_ud(struct msgb *msg);
-void sgs_iface_tx_release(struct ran_conn *conn);
+int sgs_iface_tx_dtap_ud(struct msc_a *msc_a, struct msgb *msg);
+void sgs_iface_tx_release(struct vlr_subscr *vsub);
+
diff --git a/include/osmocom/msc/signal.h b/include/osmocom/msc/signal.h
index 51269763e..16b5678db 100644
--- a/include/osmocom/msc/signal.h
+++ b/include/osmocom/msc/signal.h
@@ -28,6 +28,9 @@
#include <osmocom/core/signal.h>
+struct msc_a;
+struct vty;
+
/*
* Signalling subsystems
*/
@@ -63,7 +66,7 @@ enum signal_subscr {
/* SS_SCALL signals */
enum signal_scall {
S_SCALL_SUCCESS,
- S_SCALL_EXPIRED,
+ S_SCALL_FAILED,
S_SCALL_DETACHED,
};
@@ -78,23 +81,18 @@ enum signal_global {
struct paging_signal_data {
struct vlr_subscr *vsub;
- struct gsm_bts *bts;
-
- int paging_result;
-
- /* NULL in case the paging didn't work */
- struct ran_conn *conn;
+ struct msc_a *msc_a;
};
struct scall_signal_data {
- struct ran_conn *conn;
- void *data;
+ struct msc_a *msc_a;
+ struct vty *vty;
};
struct sms_signal_data {
/* The transaction where this occured */
struct gsm_trans *trans;
/* Can be NULL for SMMA */
struct gsm_sms *sms;
- /* int paging result. Only the ones with > 0 */
- int paging_result;
+ /* true when paging was successful */
+ bool paging_result;
};
diff --git a/include/osmocom/msc/silent_call.h b/include/osmocom/msc/silent_call.h
index ca36052ff..fb53e9049 100644
--- a/include/osmocom/msc/silent_call.h
+++ b/include/osmocom/msc/silent_call.h
@@ -1,15 +1,18 @@
#ifndef _SILENT_CALL_H
#define _SILENT_CALL_H
-struct ran_conn;
struct gsm0808_channel_type;
+struct gsm_trans;
+
+int gsm_silent_call_start(struct vlr_subscr *vsub,
+ const struct gsm0808_channel_type *ct,
+ const char *traffic_dst_ip, uint16_t traffic_dst_port,
+ struct vty *vty);
-extern int gsm_silent_call_start(struct vlr_subscr *vsub,
- const struct gsm0808_channel_type *ct,
- const char *traffic_dst_ip, uint16_t traffic_dst_port,
- void *data);
extern int gsm_silent_call_stop(struct vlr_subscr *vsub);
+void trans_silent_call_free(struct gsm_trans *trans);
+
#if 0
extern int silent_call_rx(struct ran_conn *conn, struct msgb *msg);
extern int silent_call_reroute(struct ran_conn *conn, struct msgb *msg);
diff --git a/include/osmocom/msc/sms_queue.h b/include/osmocom/msc/sms_queue.h
index 70cabe287..ef73baf04 100644
--- a/include/osmocom/msc/sms_queue.h
+++ b/include/osmocom/msc/sms_queue.h
@@ -6,6 +6,7 @@ struct gsm_sms_queue;
struct vty;
#define VSUB_USE_SMS_PENDING "SMS-pending"
+#define MSC_A_USE_SMS_PENDING "SMS-pending"
int sms_queue_start(struct gsm_network *, int in_flight);
int sms_queue_trigger(struct gsm_sms_queue *);
diff --git a/include/osmocom/msc/transaction.h b/include/osmocom/msc/transaction.h
index 7ffcf3b78..99aca55ef 100644
--- a/include/osmocom/msc/transaction.h
+++ b/include/osmocom/msc/transaction.h
@@ -6,18 +6,21 @@
#include <osmocom/core/fsm.h>
#include <osmocom/msc/gsm_04_11.h>
#include <osmocom/msc/mncc.h>
+#include <osmocom/msc/msc_a.h>
#include <osmocom/msc/debug.h>
#include <osmocom/gsm/gsm0411_smc.h>
#include <osmocom/gsm/gsm0411_smr.h>
+struct vty;
+
/* Used for late TID assignment */
#define TRANS_ID_UNASSIGNED 0xff
#define LOG_TRANS_CAT(trans, subsys, level, fmt, args...) \
LOGP(subsys, level, \
"trans(%s %s callref-0x%x tid-%u%s) " fmt, \
- (trans) ? gsm48_pdisc_name((trans)->protocol) : "NULL", \
- (trans) ? ((trans)->conn ? (trans)->conn->fi->id : vlr_subscr_name((trans)->vsub)) : "NULL", \
+ (trans) ? trans_type_name((trans)->type) : "NULL", \
+ (trans) ? ((trans)->msc_a ? (trans)->msc_a->c.fi->id : vlr_subscr_name((trans)->vsub)) : "NULL", \
(trans) ? (trans)->callref : 0, \
(trans) ? (trans)->transaction_id : 0, \
(trans) && (trans)->paging_request ? ",PAGING" : "", \
@@ -34,6 +37,19 @@ enum bridge_state {
BRIDGE_STATE_BRIDGE_ESTABLISHED,
};
+enum trans_type {
+ TRANS_CC = GSM48_PDISC_CC,
+ TRANS_SMS = GSM48_PDISC_SMS,
+ TRANS_USSD = GSM48_PDISC_NC_SS,
+ TRANS_SILENT_CALL,
+};
+
+extern const struct value_string trans_type_names[];
+static inline const char *trans_type_name(enum trans_type val)
+{ return get_value_string(trans_type_names, val); }
+
+uint8_t trans_type_to_gsm48_proto(enum trans_type type);
+
/* One transaction */
struct gsm_trans {
/* Entry in list of all transactions */
@@ -42,8 +58,8 @@ struct gsm_trans {
/* Back pointer to the network struct */
struct gsm_network *net;
- /* The protocol within which we live */
- uint8_t protocol;
+ /* What kind of transaction */
+ enum trans_type type;
/* The current transaction ID */
uint8_t transaction_id;
@@ -55,7 +71,7 @@ struct gsm_trans {
struct vlr_subscr *vsub;
/* The associated connection we are using to transmit messages */
- struct ran_conn *conn;
+ struct msc_a *msc_a;
/* reference from MNCC or other application */
uint32_t callref;
@@ -64,7 +80,7 @@ struct gsm_trans {
int tch_recv;
/* is thats one paging? */
- struct subscr_request *paging_request;
+ struct paging_request *paging_request;
/* bearer capabilities (rate and codec) */
struct gsm_mncc_bearer_cap bearer_cap;
@@ -85,7 +101,6 @@ struct gsm_trans {
struct osmo_timer_list timer;
struct osmo_timer_list timer_guard;
struct gsm_mncc msg; /* stores setup/disconnect/release message */
- bool assignment_started;
} cc;
struct {
struct gsm411_smc_inst smc_inst;
@@ -105,6 +120,11 @@ struct gsm_trans {
/* Inactivity timer, triggers transaction release */
struct osmo_timer_list timer_guard;
} ss;
+ struct {
+ struct gsm0808_channel_type ct;
+ struct osmo_sockaddr_str rtp_cn;
+ struct vty *from_vty;
+ } silent_call;
};
struct {
@@ -115,8 +135,9 @@ struct gsm_trans {
-struct gsm_trans *trans_find_by_id(const struct ran_conn *conn,
- uint8_t proto, uint8_t trans_id);
+struct gsm_trans *trans_find_by_type(const struct msc_a *msc_a, enum trans_type type);
+struct gsm_trans *trans_find_by_id(const struct msc_a *msc_a,
+ enum trans_type type, uint8_t trans_id);
struct gsm_trans *trans_find_by_callref(const struct gsm_network *net,
uint32_t callref);
struct gsm_trans *trans_find_by_sm_rp_mr(const struct gsm_network *net,
@@ -125,26 +146,28 @@ struct gsm_trans *trans_find_by_sm_rp_mr(const struct gsm_network *net,
struct gsm_trans *trans_alloc(struct gsm_network *net,
struct vlr_subscr *vsub,
- uint8_t protocol, uint8_t trans_id,
+ enum trans_type type, uint8_t trans_id,
uint32_t callref);
void trans_free(struct gsm_trans *trans);
int trans_assign_trans_id(const struct gsm_network *net, const struct vlr_subscr *vsub,
- uint8_t protocol);
-struct gsm_trans *trans_has_conn(const struct ran_conn *conn);
-void trans_conn_closed(const struct ran_conn *conn);
+ enum trans_type type);
+struct gsm_trans *trans_has_conn(const struct msc_a *msc_a);
+void trans_conn_closed(const struct msc_a *msc_a);
static inline int trans_log_subsys(const struct gsm_trans *trans)
{
if (!trans)
return DMSC;
- switch (trans->protocol) {
- case GSM48_PDISC_CC:
+ switch (trans->type) {
+ case TRANS_CC:
return DCC;
- case GSM48_PDISC_SMS:
+ case TRANS_SMS:
return DLSMS;
default:
break;
}
+ if (trans->msc_a)
+ return trans->msc_a->c.ran->log_subsys;
return DMSC;
}
diff --git a/include/osmocom/msc/vlr.h b/include/osmocom/msc/vlr.h
index ce6a232fa..4c119514a 100644
--- a/include/osmocom/msc/vlr.h
+++ b/include/osmocom/msc/vlr.h
@@ -91,11 +91,6 @@ struct vlr_auth_tuple {
#define VLR_KEY_SEQ_INVAL 7 /* GSM 04.08 - 10.5.1.2 */
-struct vlr_ciph_result {
- enum vlr_ciph_result_cause cause;
- char imeisv[GSM48_MI_SIZE];
-};
-
enum vlr_subscr_security_context {
VLR_SEC_CTX_NONE,
VLR_SEC_CTX_GSM,
@@ -162,7 +157,8 @@ struct vlr_subscr {
bool la_allowed;
struct osmo_use_count use_count;
- struct osmo_use_count_entry use_count_buf[10];
+ struct osmo_use_count_entry use_count_buf[8];
+ int32_t max_total_use_count;
struct osmo_fsm_inst *lu_fsm;
struct osmo_fsm_inst *auth_fsm;
@@ -200,20 +196,19 @@ struct vlr_subscr {
struct osmo_timer_list Ts5;
} sgs;
- struct gsm_classmark classmark;
+ struct osmo_gsm48_classmark classmark;
};
enum vlr_ciph {
- VLR_CIPH_NONE, /*< A5/0, no encryption */
- VLR_CIPH_A5_1, /*< A5/1, encryption */
- VLR_CIPH_A5_2, /*< A5/2, deprecated export-grade encryption */
- VLR_CIPH_A5_3, /*< A5/3, 'new secure' encryption */
+ VLR_CIPH_NONE = 0, /*< A5/0, no encryption */
+ VLR_CIPH_A5_1 = 1, /*< A5/1, encryption */
+ VLR_CIPH_A5_2 = 2, /*< A5/2, deprecated export-grade encryption */
+ VLR_CIPH_A5_3 = 3, /*< A5/3, 'new secure' encryption */
};
static inline uint8_t vlr_ciph_to_gsm0808_alg_id(enum vlr_ciph ciph)
{
switch (ciph) {
- default:
case VLR_CIPH_NONE:
return GSM0808_ALG_ID_A5_0;
case VLR_CIPH_A5_1:
@@ -222,6 +217,8 @@ static inline uint8_t vlr_ciph_to_gsm0808_alg_id(enum vlr_ciph ciph)
return GSM0808_ALG_ID_A5_2;
case VLR_CIPH_A5_3:
return GSM0808_ALG_ID_A5_3;
+ default:
+ return GSM0808_ALG_ID_A5_7;
}
}
@@ -240,12 +237,12 @@ struct vlr_ops {
int (*tx_lu_acc)(void *msc_conn_ref, uint32_t send_tmsi);
int (*tx_lu_rej)(void *msc_conn_ref, enum gsm48_reject_value cause);
- int (*tx_cm_serv_acc)(void *msc_conn_ref);
- int (*tx_cm_serv_rej)(void *msc_conn_ref, enum gsm48_reject_value cause);
+ int (*tx_cm_serv_acc)(void *msc_conn_ref, enum osmo_cm_service_type cm_service_type);
+ int (*tx_cm_serv_rej)(void *msc_conn_ref, enum osmo_cm_service_type cm_service_type,
+ enum gsm48_reject_value cause);
int (*set_ciph_mode)(void *msc_conn_ref, bool umts_aka, bool retrieve_imeisv);
- /* UTRAN: send Common Id (when auth+ciph are complete) */
int (*tx_common_id)(void *msc_conn_ref);
int (*tx_mm_info)(void *msc_conn_ref);
@@ -255,9 +252,6 @@ struct vlr_ops {
/* notify MSC/SGSN that the given subscriber has been associated
* with this msc_conn_ref */
int (*subscr_assoc)(void *msc_conn_ref, struct vlr_subscr *vsub);
-
- /* Forward a parsed GSUP message towards MSC message router */
- int (*forward_gsup_msg)(struct vlr_subscr *vsub, struct osmo_gsup_message *gsup_msg);
};
enum vlr_timer {
@@ -271,7 +265,7 @@ enum vlr_timer {
struct vlr_instance {
struct llist_head subscribers;
struct llist_head operations;
- struct osmo_gsup_client *gsup_client;
+ struct gsup_client_mux *gcm;
struct vlr_ops ops;
struct osmo_timer_list lu_expire_timer;
struct {
@@ -323,13 +317,13 @@ int vlr_subscr_rx_auth_resp(struct vlr_subscr *vsub, bool is_r99, bool is_utran,
const uint8_t *res, uint8_t res_len);
int vlr_subscr_rx_auth_fail(struct vlr_subscr *vsub, const uint8_t *auts);
int vlr_subscr_tx_auth_fail_rep(const struct vlr_subscr *vsub) __attribute__((warn_unused_result));
-void vlr_subscr_rx_ciph_res(struct vlr_subscr *vsub, struct vlr_ciph_result *res);
+void vlr_subscr_rx_ciph_res(struct vlr_subscr *vsub, enum vlr_ciph_result_cause result);
int vlr_subscr_rx_tmsi_reall_compl(struct vlr_subscr *vsub);
int vlr_subscr_rx_imsi_detach(struct vlr_subscr *vsub);
struct vlr_instance *vlr_alloc(void *ctx, const struct vlr_ops *ops);
-int vlr_start(struct ipaccess_unit *ipa_dev, struct vlr_instance *vlr,
- const char *gsup_server_addr_str, uint16_t gsup_server_port);
+int vlr_start(struct vlr_instance *vlr, struct gsup_client_mux *gcm);
+int vlr_gsup_rx(struct gsup_client_mux *gcm, void *data, const struct osmo_gsup_message *gsup_msg);
/* internal use only */
@@ -351,6 +345,7 @@ lu_compl_vlr_proc_start(struct osmo_fsm_inst *parent,
const char *vlr_subscr_name(const struct vlr_subscr *vsub);
+const char *vlr_subscr_short_name(const struct vlr_subscr *vsub, unsigned int maxlen);
const char *vlr_subscr_msisdn_or_name(const struct vlr_subscr *vsub);
#define vlr_subscr_find_by_imsi(vlr, imsi, USE) \
@@ -454,7 +449,8 @@ vlr_proc_acc_req(struct osmo_fsm_inst *parent,
uint32_t parent_event_failure,
void *parent_event_data,
struct vlr_instance *vlr, void *msc_conn_ref,
- enum vlr_parq_type type, const uint8_t *mi_lv,
+ enum vlr_parq_type type, enum osmo_cm_service_type cm_service_type,
+ const uint8_t *mi_lv,
const struct osmo_location_area_id *lai,
bool authentication_required,
bool ciphering_required,
diff --git a/include/osmocom/msc/vlr_sgs.h b/include/osmocom/msc/vlr_sgs.h
index 1cbb771af..00d52f7b4 100644
--- a/include/osmocom/msc/vlr_sgs.h
+++ b/include/osmocom/msc/vlr_sgs.h
@@ -27,7 +27,7 @@ struct vlr_subscr;
struct vlr_instance;
#define VSUB_USE_SGS "SGs"
-#define VSUB_USE_SGS_PAGING "SGs-paging"
+#define VSUB_USE_SGS_PAGING_REQ "SGs-paging-req"
/* See also 3GPP TS 29.118, chapter 4.2.2 States at the VLR */
enum sgs_ue_fsm_state {