diff options
Diffstat (limited to 'include')
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 { |