aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/osmocom/msc/Makefile.am31
-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, 726 deletions
diff --git a/include/osmocom/msc/Makefile.am b/include/osmocom/msc/Makefile.am
index 408d710e3..9ca4c3851 100644
--- a/include/osmocom/msc/Makefile.am
+++ b/include/osmocom/msc/Makefile.am
@@ -1,8 +1,9 @@
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 \
@@ -12,17 +13,31 @@ noinst_HEADERS = \
gsm_data.h \
gsm_data_shared.h \
gsm_subscriber.h \
- iucs.h \
- iucs_ranap.h \
- iu_dummy.h \
+ gsup_client_mux.h \
mncc.h \
+ mncc_call.h \
mncc_int.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..2516bab13
--- /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, 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 {