diff --git a/configure.ac b/configure.ac index b0d6b236d..90a95e67f 100644 --- a/configure.ac +++ b/configure.ac @@ -46,6 +46,7 @@ PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.9.5) PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.2.0) PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.6.4) PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.0.1) +PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran) # TODO version? PKG_CHECK_MODULES(LIBCRYPTO, libcrypto >= 0.9.5) # Enabke/disable the NAT? @@ -98,7 +99,6 @@ AC_ARG_ENABLE([iu], [AS_HELP_STRING([--enable-iu], [Build 3G support, aka IuPS a if test "x$osmo_ac_iu" = "xyes" ; then PKG_CHECK_MODULES(LIBASN1C, libasn1c) # TODO version? PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap) # TODO version? - PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran) # TODO version? AC_DEFINE(BUILD_IU, 1, [Define if we want to build IuPS and IuCS interfaces support]) fi AM_CONDITIONAL(BUILD_IU, test "x$osmo_ac_iu" = "xyes") diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh index 7734965f4..2b667ad04 100755 --- a/contrib/jenkins.sh +++ b/contrib/jenkins.sh @@ -24,21 +24,16 @@ verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]") export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" export LD_LIBRARY_PATH="$inst/lib" -if [ "x$IU" = "x--enable-iu" ]; then - sccp_branch="old_sua" - osmo_iuh_branch="old_sua" -fi - osmo-build-dep.sh libosmo-abis osmo-build-dep.sh libosmo-netif -osmo-build-dep.sh libosmo-sccp $sccp_branch +osmo-build-dep.sh libosmo-sccp PARALLEL_MAKE="" osmo-build-dep.sh libsmpp34 osmo-build-dep.sh openggsn if [ "x$IU" = "x--enable-iu" ]; then osmo-build-dep.sh libasn1c #osmo-build-dep.sh asn1c aper-prefix # only needed for make regen in osmo-iuh - osmo-build-dep.sh osmo-iuh $osmo_iuh_branch + osmo-build-dep.sh osmo-iuh fi set +x diff --git a/doc/examples/osmo-bsc/osmo-bsc.cfg b/doc/examples/osmo-bsc/osmo-bsc.cfg index 534605a70..7c10e9dea 100644 --- a/doc/examples/osmo-bsc/osmo-bsc.cfg +++ b/doc/examples/osmo-bsc/osmo-bsc.cfg @@ -81,7 +81,15 @@ network timeslot 7 phys_chan_config TCH/F hopping enabled 0 +cs7 instance 1 + point-code 3.0.0 + sccp-address bsc_local + point-code 3.0.0 + sccp-address msc_remote + point-code 1.0.0 msc + bsc-addr bsc_local + msc-addr msc_remote ip.access rtp-base 4000 timeout-ping 20 timeout-pong 5 diff --git a/include/openbsc/Makefile.am b/include/openbsc/Makefile.am index 995f02d09..25709f1d2 100644 --- a/include/openbsc/Makefile.am +++ b/include/openbsc/Makefile.am @@ -2,6 +2,8 @@ noinst_HEADERS = \ abis_nm.h \ abis_om2000.h \ abis_rsl.h \ + a_iface.h \ + a_iface_bssap.h \ arfcn_range_encode.h \ auth.h \ bsc_msc.h \ @@ -66,8 +68,10 @@ noinst_HEADERS = \ openbscdefines.h \ osmo_bsc.h \ osmo_bsc_grace.h \ + a_reset.h \ osmo_bsc_rf.h \ osmo_msc.h \ + osmo_bsc_sigtran.h \ bsc_msc_data.h \ osmux.h \ paging.h \ diff --git a/include/openbsc/a_iface.h b/include/openbsc/a_iface.h new file mode 100644 index 000000000..149f1c71e --- /dev/null +++ b/include/openbsc/a_iface.h @@ -0,0 +1,76 @@ +/* (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 . + * + */ + +#pragma once + +#include + +/* 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 a_reset_ctx *reset; + + /* 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 */ +int a_iface_tx_dtap(struct msgb *msg); + +/* Send Cipher mode command via A-interface */ +int a_iface_tx_cipher_mode(const struct gsm_subscriber_connection *conn, + int cipher, const const uint8_t *key, int len, 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(struct gsm_subscriber_connection *conn); + +/* Clear all subscriber 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); + +/* 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/openbsc/a_iface_bssap.h b/include/openbsc/a_iface_bssap.h new file mode 100644 index 000000000..237c618fd --- /dev/null +++ b/include/openbsc/a_iface_bssap.h @@ -0,0 +1,41 @@ +/* (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 . + * + */ + +#pragma once + +/* 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 osmo_sccp_addr *msc_addr; + struct osmo_sccp_addr *bsc_addr; + uint32_t conn_id; + struct gsm_network *network; + struct a_reset_ctx *reset; +}; + +/* Receive incoming connection less data messages via sccp */ +void 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 sccp_rx_dt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg); + diff --git a/include/openbsc/a_reset.h b/include/openbsc/a_reset.h new file mode 100644 index 000000000..7aaab0620 --- /dev/null +++ b/include/openbsc/a_reset.h @@ -0,0 +1,63 @@ +/* (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 . + * + */ + +#pragma once + + + +/* Reset context data (callbacks, state machine etc...) */ +struct a_reset_ctx { + + /* FSM instance, which handles the reset procedure */ + struct osmo_fsm_inst *fsm; + + /* Connection failure counter. When this counter + * reaches a certain threshold, the reset procedure + * will be triggered */ + int conn_loss_counter; + + /* A human readable name to display in the logs */ + char name[256]; + + /* Callback function to be called when a connection + * failure is detected and a rest must occur */ + void (*cb)(void *priv); + + /* Privated data for the callback function */ + void *priv; +}; + +/* Create and start state machine which handles the reset/reset-ack procedure */ +struct a_reset_ctx *a_reset_alloc(const void *ctx, const char *name, void *cb, void *priv); + +/* Tear down state machine */ +void a_reset_free(struct a_reset_ctx *reset); + +/* Confirm that we sucessfully received a reset acknowlege message */ +void a_reset_ack_confirm(struct a_reset_ctx *reset); + +/* Report a failed connection */ +void a_reset_conn_fail(struct a_reset_ctx *reset); + +/* Report a successful connection */ +void a_reset_conn_success(struct a_reset_ctx *reset); + +/* Check if we have a connection to a specified msc */ +bool a_reset_conn_ready(struct a_reset_ctx *reset); diff --git a/include/openbsc/bsc_msc.h b/include/openbsc/bsc_msc.h index 39258d364..380eb17c1 100644 --- a/include/openbsc/bsc_msc.h +++ b/include/openbsc/bsc_msc.h @@ -24,6 +24,8 @@ #include #include +#include +#include #include @@ -37,6 +39,7 @@ struct bsc_msc_dest { struct bsc_msc_connection { + /* FIXME: Remove stuff that is no longer needed! */ struct osmo_wqueue write_queue; int is_connected; int is_authenticated; @@ -52,6 +55,15 @@ struct bsc_msc_connection { struct osmo_timer_list timeout_timer; struct msgb *pending_msg; + + /* Sigtran connection data */ + struct osmo_sccp_instance *sccp; + struct osmo_sccp_user *sccp_user; + struct osmo_sccp_addr g_calling_addr; + struct osmo_sccp_addr g_called_addr; + struct a_reset_ctx *reset; + + int conn_id_counter; }; struct bsc_msc_connection *bsc_msc_create(void *ctx, struct llist_head *dest); diff --git a/include/openbsc/bsc_msc_data.h b/include/openbsc/bsc_msc_data.h index 38e87cfb9..4a283d165 100644 --- a/include/openbsc/bsc_msc_data.h +++ b/include/openbsc/bsc_msc_data.h @@ -32,6 +32,14 @@ #include #include + +#include +#include +#include +#include +#include +#include + #include struct osmo_bsc_rf; @@ -103,6 +111,30 @@ struct bsc_msc_data { char *ussd_grace_txt; char *acc_lst_name; + + /* Sigtran connection data */ + struct { + uint32_t cs7_instance; + bool cs7_instance_valid; + struct osmo_sccp_instance *sccp; + struct osmo_sccp_user *sccp_user; + + /* Holds a copy of the our local MSC address, + * this will be the sccp-address that is associated + * with the A interface of this particular BSC, + * this address is filled up by the VTY interface */ + struct osmo_sccp_addr bsc_addr; + char *bsc_addr_name; + + /* Holds a copy of the MSC address. This is the + * address of the MSC that handles the calls of + * this BSC. The address is configured via the + * VTY interface */ + struct osmo_sccp_addr msc_addr; + char *msc_addr_name; + + struct a_reset_ctx *reset; + } a; }; /* diff --git a/include/openbsc/debug.h b/include/openbsc/debug.h index de00b2930..65e197d52 100644 --- a/include/openbsc/debug.h +++ b/include/openbsc/debug.h @@ -40,6 +40,7 @@ enum { DPCU, DVLR, DIUCS, + DSIGTRAN, Debug_LastEntry, }; diff --git a/include/openbsc/gsm_04_08.h b/include/openbsc/gsm_04_08.h index 6d6ead183..ca251b00b 100644 --- a/include/openbsc/gsm_04_08.h +++ b/include/openbsc/gsm_04_08.h @@ -80,4 +80,6 @@ void allocate_security_operation(struct gsm_subscriber_connection *conn); int gsm48_multirate_config(uint8_t *lv, const struct amr_multirate_conf *mr, const struct amr_mode *modes); +int gsm48_tch_rtp_create(struct gsm_trans *trans); + #endif diff --git a/include/openbsc/gsm_data.h b/include/openbsc/gsm_data.h index f4de38105..43fc6d330 100644 --- a/include/openbsc/gsm_data.h +++ b/include/openbsc/gsm_data.h @@ -12,12 +12,14 @@ #include #include +#include #include #include #include #include + /** annotations for msgb ownership */ #define __uses @@ -193,14 +195,33 @@ struct gsm_subscriber_connection { uint16_t lac; struct gsm_encr encr; + struct { + unsigned int mgcp_rtp_endpoint; + uint16_t port_subscr; + uint16_t port_cn; + } rtp; + /* which Iu-CS connection, if any. */ struct { struct ue_conn_ctx *ue_ctx; - unsigned int mgcp_rtp_endpoint; - uint16_t mgcp_rtp_port_ue; - uint16_t mgcp_rtp_port_cn; uint8_t rab_id; } 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 subscriber connection */ + struct osmo_sccp_addr bsc_addr; + + /* The connection identifier that is used + * to reference the SCCP connection that is + * associated with this subscriber connection */ + int conn_id; + } a; }; @@ -470,8 +491,20 @@ struct gsm_network { } mgcpgw; struct { + /* CS7 instance id number (set via VTY) */ + uint32_t cs7_instance; enum nsap_addr_enc rab_assign_addr_enc; + struct osmo_sccp_instance *sccp; } iu; + + 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; + } a; }; struct osmo_esme; diff --git a/include/openbsc/gsm_data_shared.h b/include/openbsc/gsm_data_shared.h index 60da2e5c0..bed46d254 100644 --- a/include/openbsc/gsm_data_shared.h +++ b/include/openbsc/gsm_data_shared.h @@ -252,6 +252,16 @@ struct gsm_lchan { uint8_t speech_mode; #ifdef ROLE_BSC struct rtp_socket *rtp_socket; + + /* info we need to postpone the AoIP + * assignment completed message */ + struct { + uint8_t rr_cause; + uint8_t chosen_channel; + uint8_t encr_alg_id; + uint8_t speech_mode; + bool valid; + } ass_compl; #else struct osmo_rtp_socket *rtp_socket; #endif diff --git a/include/openbsc/iu.h b/include/openbsc/iu.h index 5b298301d..08e4cd06b 100644 --- a/include/openbsc/iu.h +++ b/include/openbsc/iu.h @@ -5,13 +5,15 @@ #include #include +#include #include struct sgsn_pdp_ctx; struct msgb; -struct osmo_sccp_link; struct gsm_auth_tuple; +struct osmo_sccp_addr; +struct osmo_ss7_instance; struct RANAP_RAB_SetupOrModifiedItemIEs_s; struct RANAP_GlobalRNC_ID; @@ -23,7 +25,10 @@ extern int asn1_xer_print; struct ue_conn_ctx { struct llist_head list; - struct osmo_sccp_link *link; + /* TODO: It's not needed to store the full SCCP address for each + * UE. Rather than that, a pointer to the RNC should be far + * sufficient */ + struct osmo_sccp_addr sccp_addr; uint32_t conn_id; int integrity_active; struct gprs_ra_id ra_id; @@ -53,11 +58,9 @@ typedef int (* iu_event_cb_t )(struct ue_conn_ctx *ue_ctx, typedef int (* iu_rab_ass_resp_cb_t )(struct ue_conn_ctx *ue_ctx, uint8_t rab_id, struct RANAP_RAB_SetupOrModifiedItemIEs_s *setup_ies); -int iu_init(void *ctx, const char *listen_addr, uint16_t listen_port, +int iu_init(void *ctx, struct osmo_sccp_instance *sccp, iu_recv_cb_t iu_recv_cb, iu_event_cb_t iu_event_cb); -void iu_link_del(struct osmo_sccp_link *link); - int iu_tx(struct msgb *msg, uint8_t sapi); int iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac); diff --git a/include/openbsc/mgcpgw_client.h b/include/openbsc/mgcpgw_client.h index b353db0a4..b1b5fd42b 100644 --- a/include/openbsc/mgcpgw_client.h +++ b/include/openbsc/mgcpgw_client.h @@ -3,11 +3,11 @@ #include #include +#include enum mgcp_connection_mode; struct msgb; -struct mgcpgw_client; struct vty; #define MGCPGW_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0" @@ -24,6 +24,9 @@ struct mgcpgw_client_conf { int local_port; const char *remote_addr; int remote_port; + uint16_t first_endpoint; + uint16_t last_endpoint; + uint16_t bts_base; }; struct mgcp_response_head { @@ -38,6 +41,20 @@ struct mgcp_response { uint16_t audio_port; }; +struct mgcpgw_client { + struct mgcpgw_client_conf actual; + uint32_t remote_addr; + struct osmo_wqueue wq; + mgcp_trans_id_t next_trans_id; + struct llist_head responses_pending; + struct llist_head inuse_endpoints; +}; + +struct mgcp_inuse_endpoint { + struct llist_head entry; + uint16_t id; +}; + /* Invoked when an MGCP response is received or sending failed. When the * response is passed as NULL, this indicates failure during transmission. */ typedef void (* mgcp_response_cb_t )(struct mgcp_response *response, void *priv); @@ -61,7 +78,11 @@ const char *mgcpgw_client_remote_addr_str(struct mgcpgw_client *mgcp); uint16_t mgcpgw_client_remote_port(struct mgcpgw_client *mgcp); uint32_t mgcpgw_client_remote_addr_n(struct mgcpgw_client *mgcp); -unsigned int mgcpgw_client_next_endpoint(struct mgcpgw_client *client); +/* Find and seize an unsused endpoint id */ +int mgcpgw_client_next_endpoint(struct mgcpgw_client *client); + +/* Release a seized endpoint id to make it available again for other calls */ +void mgcpgw_client_release_endpoint(uint16_t id, struct mgcpgw_client *client); int mgcp_response_parse_params(struct mgcp_response *r); @@ -76,6 +97,9 @@ struct msgb *mgcp_msg_mdcx(struct mgcpgw_client *mgcp, uint16_t rtp_endpoint, const char *rtp_conn_addr, uint16_t rtp_port, enum mgcp_connection_mode mode); +struct msgb *mgcp_msg_dlcx(struct mgcpgw_client *mgcp, uint16_t rtp_endpoint, + unsigned int call_id); + void mgcpgw_client_vty_init(int node, struct mgcpgw_client_conf *conf); int mgcpgw_client_config_write(struct vty *vty, const char *indent); diff --git a/include/openbsc/msc_ifaces.h b/include/openbsc/msc_ifaces.h index 2965c72c5..a1071ae9b 100644 --- a/include/openbsc/msc_ifaces.h +++ b/include/openbsc/msc_ifaces.h @@ -28,20 +28,6 @@ /* Each main linkage must implement this function (see comment above). */ extern int iu_tx(struct msgb *msg, uint8_t sapi); -/* So far this is a dummy implemented in libmsc/a_iface.c. When A-interface - * gets implemented, it should be in a separate lib (like libiu), this function - * should move there, and the following comment should remain here: " - * Each main linkage must implement this function (see comment above). - * " */ -extern int a_tx(struct msgb *msg); - -/* So far this is a dummy implemented in libmsc/a_iface.c. When A-interface - * gets implemented, it should be in a separate lib (like libiu), this function - * should move there, and the following comment should remain here: " - * Each main linkage must implement this function (see comment above). - * " */ -extern int a_page(const char *imsi, uint32_t tmsi, uint16_t lac); - int msc_tx_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg); @@ -49,10 +35,8 @@ int msc_gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn); int msc_gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, enum gsm48_reject_value value); -/* TODO: specific to A interface, move this away */ -int msc_gsm0808_tx_cipher_mode(struct gsm_subscriber_connection *conn, int cipher, - const uint8_t *key, int len, int include_imeisv); - int msc_tx_common_id(struct gsm_subscriber_connection *conn); int msc_call_assignment(struct gsm_trans *trans); int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2); +void msc_call_release(struct gsm_trans *trans); +int msc_call_connect(struct gsm_trans *trans, uint16_t port, uint32_t ip); diff --git a/include/openbsc/osmo_bsc.h b/include/openbsc/osmo_bsc.h index 9e688fd59..5ebea5079 100644 --- a/include/openbsc/osmo_bsc.h +++ b/include/openbsc/osmo_bsc.h @@ -26,6 +26,7 @@ struct osmo_bsc_sccp_con { /* for audio handling */ uint16_t cic; + uint32_t rtp_ip; int rtp_port; /* for advanced ping/pong */ @@ -44,6 +45,9 @@ struct osmo_bsc_sccp_con { uint8_t new_subscriber; struct bsc_filter_state filter_state; + + /* Sigtran connection ID */ + int conn_id; }; struct bsc_api *osmo_bsc_api(); @@ -60,7 +64,7 @@ int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg); int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn); int bsc_handle_udt(struct bsc_msc_data *msc, struct msgb *msg, unsigned int length); -int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int len); +int bsc_handle_dt(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int len); int bsc_ctrl_cmds_install(); diff --git a/include/openbsc/osmo_bsc_reset.h b/include/openbsc/osmo_bsc_reset.h new file mode 100644 index 000000000..578f763e6 --- /dev/null +++ b/include/openbsc/osmo_bsc_reset.h @@ -0,0 +1,34 @@ +/* (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 . + * + */ + +/* Create and start state machine which handles the reset/reset-ack procedure */ +void start_reset_fsm(struct bsc_msc_data *msc); + +/* Confirm that we sucessfully received a reset acknowlege message */ +void reset_ack_confirm(struct bsc_msc_data *msc); + +/* Report a failed connection */ +void report_conn_fail(struct bsc_msc_data *msc); + +/* Report a successful connection */ +void report_conn_success(struct bsc_msc_data *msc); + +/* Check if we have a connection to a specified msc */ +bool sccp_conn_ready(struct bsc_msc_data *msc); diff --git a/include/openbsc/osmo_bsc_sigtran.h b/include/openbsc/osmo_bsc_sigtran.h new file mode 100644 index 000000000..fbcfcb3fc --- /dev/null +++ b/include/openbsc/osmo_bsc_sigtran.h @@ -0,0 +1,48 @@ +/* (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 . + * + */ + +#pragma once + +#include +#include + +/* Allocate resources to make a new connection oriented sigtran connection + * (not the connection ittself!) */ +enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, struct bsc_msc_data *msc); + +/* Open a new connection oriented sigtran connection */ +int osmo_bsc_sigtran_open_conn(const struct osmo_bsc_sccp_con *conn, struct msgb *msg); + +/* Send data to MSC */ +int osmo_bsc_sigtran_send(const struct osmo_bsc_sccp_con *conn, struct msgb *msg); + +/* Delete a connection from the list with open connections + * (called by osmo_bsc_api.c on failing open connections and + * locally, when a connection is closed by the MSC */ +int osmo_bsc_sigtran_del_conn(struct osmo_bsc_sccp_con *sccp); + +/* Initalize osmo sigtran backhaul */ +int osmo_bsc_sigtran_init(struct llist_head *mscs); + +/* Close all open sigtran connections and channels */ +void osmo_bsc_sigtran_reset(const struct bsc_msc_data *msc); + +/* Send reset-ack to MSC */ +void osmo_bsc_sigtran_tx_reset_ack(const struct bsc_msc_data *msc); diff --git a/include/openbsc/osmo_msc.h b/include/openbsc/osmo_msc.h index bc96f1d5a..cdfd27f11 100644 --- a/include/openbsc/osmo_msc.h +++ b/include/openbsc/osmo_msc.h @@ -63,6 +63,8 @@ int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id); int msc_vlr_alloc(struct gsm_network *net); int msc_vlr_start(struct gsm_network *net); +void msc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci); +int msc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause); int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, uint16_t chosen_channel); void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, @@ -70,6 +72,11 @@ void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, void msc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t alg_id); void msc_rx_sec_mode_compl(struct gsm_subscriber_connection *conn); +void msc_classmark_chg(struct gsm_subscriber_connection *conn, + const uint8_t *cm2, uint8_t cm2_len, + const uint8_t *cm3, uint8_t cm3_len); +void msc_assign_fail(struct gsm_subscriber_connection *conn, + uint8_t cause, uint8_t *rr_cause); void msc_subscr_conn_init(void); bool msc_subscr_conn_is_accepted(struct gsm_subscriber_connection *conn); diff --git a/include/openbsc/transaction.h b/include/openbsc/transaction.h index 713d878f3..4930fbd32 100644 --- a/include/openbsc/transaction.h +++ b/include/openbsc/transaction.h @@ -46,6 +46,16 @@ struct gsm_trans { /* is thats one paging? */ struct subscr_request *paging_request; + /* bearer capabilities (rate and codec) */ + struct gsm_mncc_bearer_cap bearer_cap; + + /* status of the assignment, true when done */ + bool assignment_done; + + /* if true, TCH_RTP_CREATE is sent after the + * assignment is done */ + bool tch_rtp_create; + union { struct { diff --git a/src/gprs/Makefile.am b/src/gprs/Makefile.am index cb0997902..e05eb79ff 100644 --- a/src/gprs/Makefile.am +++ b/src/gprs/Makefile.am @@ -34,6 +34,7 @@ OSMO_LIBS = \ $(LIBOSMOCTRL_LIBS) \ $(LIBOSMOGB_LIBS) \ $(LIBGTP_LIBS) \ + $(LIBOSMOSIGTRAN_LIBS) \ $(NULL) bin_PROGRAMS = \ @@ -128,5 +129,6 @@ osmo_gtphub_LDADD = \ $(LIBOSMOVTY_LIBS) \ $(LIBCARES_LIBS) \ $(LIBGTP_LIBS) \ + $(LIBOSMOSIGTRAN_LIBS) \ -lrt \ $(NULL) diff --git a/src/gprs/sgsn_main.c b/src/gprs/sgsn_main.c index d5d43ad2a..d56af0ed4 100644 --- a/src/gprs/sgsn_main.c +++ b/src/gprs/sgsn_main.c @@ -63,6 +63,8 @@ #include #include +#include + #include #include "../../bscconfig.h" @@ -326,6 +328,7 @@ int main(int argc, char **argv) { struct ctrl_handle *ctrl; struct gsm_network dummy_network; + struct osmo_sccp_instance *sccp; int rc; srand(time(NULL)); @@ -348,6 +351,7 @@ int main(int argc, char **argv) osmo_stats_vty_add_cmds(&gprs_log_info); sgsn_vty_init(&sgsn_inst.cfg); ctrl_vty_init(tall_bsc_ctx); + osmo_ss7_init(); handle_options(argc, argv); @@ -436,7 +440,18 @@ int main(int argc, char **argv) } #ifdef BUILD_IU - iu_init(tall_bsc_ctx, "127.0.0.2", 14001, gsm0408_gprs_rcvmsg_iu, sgsn_ranap_iu_event); + sccp = osmo_sccp_simple_client(tall_bsc_ctx, "OsmoSGSN", + 2 /* FIXME: configurable */, + OSMO_SS7_ASP_PROT_M3UA, 0, + "127.0.0.4" /* FIXME: configurable */, + M3UA_PORT, + "127.0.0.1" /* FIXME: configurable */); + if (!sccp) { + printf("Setting up SCCP client failed.\n"); + return 8; + } + + iu_init(tall_bsc_ctx, sccp, gsm0408_gprs_rcvmsg_iu, sgsn_ranap_iu_event); #endif if (daemonize) { diff --git a/src/libbsc/abis_rsl.c b/src/libbsc/abis_rsl.c index 4f687a039..66cda8200 100644 --- a/src/libbsc/abis_rsl.c +++ b/src/libbsc/abis_rsl.c @@ -2327,6 +2327,8 @@ static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv) DEBUGPC(DRSL, "REMOTE_PORT=%u ", port); lchan->abis_ip.connect_port = port; } + + DEBUGPC(DRSL, "\n"); } /*! \brief Issue IPA RSL CRCX to configure RTP on BTS side @@ -2558,7 +2560,6 @@ static int abis_rsl_rx_ipacc(struct msgb *msg) rllh->c.msg_type); break; } - DEBUGPC(DRSL, "\n"); return rc; } diff --git a/src/libbsc/bsc_vty.c b/src/libbsc/bsc_vty.c index bd363ae55..d55c6eb30 100644 --- a/src/libbsc/bsc_vty.c +++ b/src/libbsc/bsc_vty.c @@ -57,6 +57,7 @@ #include #include #include +#include #include @@ -1341,6 +1342,83 @@ DEFUN(show_lchan_summary, return lchan_summary(vty, argc, argv, lchan_dump_short_vty); } +DEFUN(show_subscr_conn, + show_subscr_conn_cmd, + "show conns", + SHOW_STR "Display currently active subscriber connections\n") +{ + struct gsm_subscriber_connection *conn; + struct gsm_network *net = gsmnet_from_vty(vty); + bool no_conns = true; + unsigned int count = 0; + + vty_out(vty, "Active subscriber connections: %s", VTY_NEWLINE); + + llist_for_each_entry(conn, &net->subscr_conns, entry) { + vty_out(vty, "conn nr #%u:%s", count, VTY_NEWLINE); + lchan_dump_full_vty(vty, conn->lchan); + no_conns = false; + count++; + } + + if (no_conns) + vty_out(vty, "None%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(handover_subscr_conn, + handover_subscr_conn_cmd, + "handover <0-255> <0-255> <0-7> LCHAN_NR <0-255>", + "Handover subscriber connection to other BTS\n" + "BTS Number (current)\n" "TRX Number\n" "Timeslot Number\n" + LCHAN_NR_STR "BTS Number (new)\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + struct gsm_subscriber_connection *conn; + struct gsm_bts *bts; + struct gsm_bts *new_bts = NULL; + unsigned int bts_nr = atoi(argv[0]); + unsigned int trx_nr = atoi(argv[1]); + unsigned int ts_nr = atoi(argv[2]); + unsigned int ss_nr = atoi(argv[3]); + unsigned int bts_nr_new = atoi(argv[4]); + + /* Lookup the BTS where we want to handover to */ + llist_for_each_entry(bts, &net->bts_list, list) { + if (bts->nr == bts_nr_new) { + new_bts = bts; + break; + } + } + + if (!new_bts) { + vty_out(vty, "Unable to trigger handover," + "specified bts #%u does not exist %s", bts_nr_new, + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Find the connection/lchan that we want to handover */ + llist_for_each_entry(conn, &net->subscr_conns, entry) { + if (conn->bts->nr == bts_nr && + conn->lchan->ts->trx->nr == trx_nr && + conn->lchan->ts->nr == ts_nr && conn->lchan->nr == ss_nr) { + vty_out(vty, "starting handover for lchan %s...%s", + conn->lchan->name, VTY_NEWLINE); + lchan_dump_full_vty(vty, conn->lchan); + bsc_handover_start(conn->lchan, new_bts); + return CMD_SUCCESS; + } + } + + vty_out(vty, "Unable to trigger handover," + "specified connection (bts=%u,trx=%u,ts=%u,ss=%u) does not exist%s", + bts_nr, trx_nr, ts_nr, ss_nr, VTY_NEWLINE); + + return CMD_WARNING; +} + static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag) { vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE); @@ -4153,6 +4231,9 @@ int bsc_vty_init(struct gsm_network *network) install_element_ve(&show_lchan_cmd); install_element_ve(&show_lchan_summary_cmd); + install_element_ve(&show_subscr_conn_cmd); + install_element_ve(&handover_subscr_conn_cmd); + install_element_ve(&show_paging_cmd); install_element_ve(&show_paging_group_cmd); diff --git a/src/libbsc/handover_logic.c b/src/libbsc/handover_logic.c index 57d1dcd31..14566cfa1 100644 --- a/src/libbsc/handover_logic.c +++ b/src/libbsc/handover_logic.c @@ -101,7 +101,8 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) if (bsc_ho_by_old_lchan(old_lchan)) return -EBUSY; - DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n", + DEBUGP(DHO, "Beginning with handover operation" + "(old_lchan on BTS %u, new BTS %u) ...\n", old_lchan->ts->trx->bts->nr, bts->nr); rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED]); diff --git a/src/libcommon-cs/Makefile.am b/src/libcommon-cs/Makefile.am index f3921ba5f..21c27455d 100644 --- a/src/libcommon-cs/Makefile.am +++ b/src/libcommon-cs/Makefile.am @@ -16,5 +16,6 @@ AM_CFLAGS = \ noinst_LIBRARIES = libcommon-cs.a libcommon_cs_a_SOURCES = \ + a_reset.c \ common_cs.c \ common_cs_vty.c diff --git a/src/libcommon-cs/a_reset.c b/src/libcommon-cs/a_reset.c new file mode 100644 index 000000000..c0294c797 --- /dev/null +++ b/src/libcommon-cs/a_reset.c @@ -0,0 +1,224 @@ +/* (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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RESET_RESEND_INTERVAL 2 /* sec */ +#define RESET_RESEND_TIMER_NO 1234 /* FIXME: dig out the real timer number */ +#define BAD_CONNECTION_THRESOLD 3 /* connection failures */ + +enum fsm_states { + ST_DISC, /* Disconnected from remote end */ + ST_CONN, /* We have a confirmed connection */ +}; + +static const struct value_string fsm_state_names[] = { + {ST_DISC, "ST_DISC (disconnected)"}, + {ST_CONN, "ST_CONN (connected)"}, + {0, NULL}, +}; + +enum fsm_evt { + EV_RESET_ACK, /* got reset acknowlegement from remote end */ + EV_N_DISCONNECT, /* lost a connection */ + EV_N_CONNECT, /* made a successful connection */ +}; + +static const struct value_string fsm_evt_names[] = { + {EV_RESET_ACK, "EV_RESET_ACK"}, + {EV_N_DISCONNECT, "EV_N_DISCONNECT"}, + {EV_N_CONNECT, "EV_N_CONNECT"}, + {0, NULL}, +}; + +/* Disconnected state */ +static void fsm_disc_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct a_reset_ctx *reset = (struct a_reset_ctx *)data; + OSMO_ASSERT(reset); + + LOGP(DMSC, LOGL_NOTICE, "(%s) fsm-state (msc-reset): %s, fsm-event: %s\n", reset->name, + get_value_string(fsm_state_names, ST_CONN), get_value_string(fsm_evt_names, event)); + + reset->conn_loss_counter = 0; + osmo_fsm_inst_state_chg(fi, ST_CONN, 0, 0); +} + +/* Connected state */ +static void fsm_conn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct a_reset_ctx *reset = (struct a_reset_ctx *)data; + OSMO_ASSERT(reset); + + LOGP(DMSC, LOGL_NOTICE, "(%s) fsm-state (msc-reset): %s, fsm-event: %s\n", reset->name, + get_value_string(fsm_state_names, ST_CONN), get_value_string(fsm_evt_names, event)); + + switch (event) { + case EV_N_DISCONNECT: + if (reset->conn_loss_counter >= BAD_CONNECTION_THRESOLD) { + LOGP(DMSC, LOGL_NOTICE, "(%s) SIGTRAN connection down, reconnecting...\n", reset->name); + osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); + } else + reset->conn_loss_counter++; + break; + case EV_N_CONNECT: + reset->conn_loss_counter = 0; + break; + } +} + +/* Timer callback to retransmit the reset signal */ +static int fsm_reset_ack_timeout_cb(struct osmo_fsm_inst *fi) +{ + struct a_reset_ctx *reset = (struct a_reset_ctx *)fi->priv; + + LOGP(DMSC, LOGL_NOTICE, "(%s) reset-ack timeout (T%i) in state %s, resending...\n", reset->name, fi->T, + get_value_string(fsm_state_names, fi->state)); + + reset->cb(reset->priv); + + osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); + return 0; +} + +static struct osmo_fsm_state fsm_states[] = { + [ST_DISC] = { + .in_event_mask = (1 << EV_RESET_ACK), + .out_state_mask = (1 << ST_DISC) | (1 << ST_CONN), + .name = "DISC", + .action = fsm_disc_cb, + }, + [ST_CONN] = { + .in_event_mask = (1 << EV_N_DISCONNECT) | (1 << EV_N_CONNECT), + .out_state_mask = (1 << ST_DISC) | (1 << ST_CONN), + .name = "CONN", + .action = fsm_conn_cb, + }, +}; + +/* State machine definition */ +static struct osmo_fsm fsm = { + .name = "FSM RESET", + .states = fsm_states, + .num_states = ARRAY_SIZE(fsm_states), + .log_subsys = DMSC, + .timer_cb = fsm_reset_ack_timeout_cb, +}; + +/* Create and start state machine which handles the reset/reset-ack procedure */ +struct a_reset_ctx *a_reset_alloc(const void *ctx, const char *name, void *cb, void *priv) +{ + OSMO_ASSERT(name); + + struct a_reset_ctx *reset; + + /* Register the fsm description (if not already done) */ + if (osmo_fsm_find_by_name(fsm.name) != &fsm) + osmo_fsm_register(&fsm); + + /* Allocate and configure a new fsm instance */ + reset = talloc_zero(ctx, struct a_reset_ctx); + OSMO_ASSERT(reset); + reset->priv = priv; + reset->cb = cb; + strncpy(reset->name, name, sizeof(reset->name)); + reset->conn_loss_counter = 0; + reset->fsm = osmo_fsm_inst_alloc(&fsm, NULL, NULL, LOGL_DEBUG, "FSM RESET INST"); + OSMO_ASSERT(reset->fsm); + reset->fsm->priv = reset; + LOGP(DMSC, LOGL_NOTICE, "(%s) reset handler fsm created.\n", reset->name); + + /* kick off reset-ack sending mechanism */ + osmo_fsm_inst_state_chg(reset->fsm, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); + + return reset; +} + +/* Tear down state machine */ +void a_reset_free(struct a_reset_ctx *reset) +{ + OSMO_ASSERT(reset); + OSMO_ASSERT(reset->fsm); + + osmo_fsm_inst_free(reset->fsm); + reset->fsm = NULL; + + memset(reset, 0, sizeof(*reset)); + talloc_free(reset); + + LOGP(DMSC, LOGL_NOTICE, "(%s) reset handler fsm destroyed.\n", reset->name); +} + +/* Confirm that we sucessfully received a reset acknowlege message */ +void a_reset_ack_confirm(struct a_reset_ctx *reset) +{ + OSMO_ASSERT(reset); + OSMO_ASSERT(reset->fsm); + + osmo_fsm_inst_dispatch(reset->fsm, EV_RESET_ACK, reset); +} + +/* Report a failed connection */ +void a_reset_conn_fail(struct a_reset_ctx *reset) +{ + /* If no reset context is supplied, just drop the info */ + if (!reset) + return; + + OSMO_ASSERT(reset->fsm); + + osmo_fsm_inst_dispatch(reset->fsm, EV_N_DISCONNECT, reset); +} + +/* Report a successful connection */ +void a_reset_conn_success(struct a_reset_ctx *reset) +{ + /* If no reset context is supplied, just drop the info */ + if (!reset) + return; + + OSMO_ASSERT(reset->fsm); + + osmo_fsm_inst_dispatch(reset->fsm, EV_N_CONNECT, reset); +} + +/* Check if we have a connection to a specified msc */ +bool a_reset_conn_ready(struct a_reset_ctx *reset) +{ + /* If no reset context is supplied, we assume that + * the connection can't be ready! */ + if (!reset) + return false; + + OSMO_ASSERT(reset->fsm); + if (reset->fsm->state == ST_CONN) + return true; + + return false; +} diff --git a/src/libcommon-cs/common_cs.c b/src/libcommon-cs/common_cs.c index 99206c86c..d6dff95df 100644 --- a/src/libcommon-cs/common_cs.c +++ b/src/libcommon-cs/common_cs.c @@ -78,6 +78,8 @@ struct gsm_network *gsm_network_init(void *ctx, net->dyn_ts_allow_tch_f = true; + INIT_LLIST_HEAD(&net->a.bscs); + return net; } diff --git a/src/libcommon/common_vty.c b/src/libcommon/common_vty.c index 6e1c10b00..1443791f0 100644 --- a/src/libcommon/common_vty.c +++ b/src/libcommon/common_vty.c @@ -34,6 +34,7 @@ #include #include #include +#include int bsc_vty_go_parent(struct vty *vty) @@ -117,13 +118,15 @@ int bsc_vty_go_parent(struct vty *vty) case MSC_NODE: case MNCC_INT_NODE: case NITB_NODE: - default: - if (bsc_vty_is_config_node(vty, vty->node)) - vty->node = CONFIG_NODE; - else - vty->node = ENABLE_NODE; - + vty->node = CONFIG_NODE; vty->index = NULL; + break; + case SUBSCR_NODE: + vty->node = ENABLE_NODE; + vty->index = NULL; + break; + default: + osmo_ss7_vty_go_parent(vty); } return vty->node; @@ -131,6 +134,11 @@ int bsc_vty_go_parent(struct vty *vty) int bsc_vty_is_config_node(struct vty *vty, int node) { + /* Check if libosmo-sccp declares the node in + * question as config node */ + if (osmo_ss7_is_config_node(vty, node)) + return 1; + switch (node) { /* add items that are not config */ case OML_NODE: diff --git a/src/libcommon/debug.c b/src/libcommon/debug.c index 7dbbc6ac0..723641335 100644 --- a/src/libcommon/debug.c +++ b/src/libcommon/debug.c @@ -189,6 +189,12 @@ static const struct log_info_cat default_categories[] = { .description = "Iu-CS Protocol", .enabled = 1, .loglevel = LOGL_DEBUG, }, + [DSIGTRAN] = { + .name = "DSIGTRAN", + .description = "SIGTRAN Signalling Transport", + .color = "\033[1;29m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, }; static int filter_fn(const struct log_context *ctx, struct log_target *tar) diff --git a/src/libiu/iu.c b/src/libiu/iu.c index 5d56a4a37..779497745 100644 --- a/src/libiu/iu.c +++ b/src/libiu/iu.c @@ -36,9 +36,10 @@ #include #include -#include +#include #include #include +#include #include #include @@ -63,7 +64,7 @@ struct iu_grnc_id { }; /* A remote RNC (Radio Network Controller, like BSC but for UMTS) that has - * called us and is currently reachable at the given osmo_sccp_link. So, when we + * called us and is currently reachable at the given osmo_sccp_addr. So, when we * know a LAC for a subscriber, we can page it at the RNC matching that LAC or * RAC. An HNB-GW typically presents itself as if it were a single RNC, even * though it may have several RNCs in hNodeBs connected to it. Those will then @@ -75,7 +76,7 @@ struct iu_rnc { uint16_t rnc_id; uint16_t lac; /* Location Area Code (used for CS and PS) */ uint8_t rac; /* Routing Area Code (used for PS only) */ - struct osmo_sccp_link *link; + struct osmo_sccp_addr sccp_addr; }; void *talloc_iu_ctx; @@ -97,6 +98,9 @@ iu_event_cb_t global_iu_event_cb = NULL; static LLIST_HEAD(ue_conn_ctx_list); static LLIST_HEAD(rnc_list); +static struct osmo_sccp_instance *g_sccp; +static struct osmo_sccp_user *g_scu; + const struct value_string iu_event_type_names[] = { OSMO_VALUE_STRING(IU_EVENT_RAB_ASSIGN), OSMO_VALUE_STRING(IU_EVENT_SECURITY_MODE_COMPLETE), @@ -105,38 +109,37 @@ const struct value_string iu_event_type_names[] = { { 0, NULL } }; -struct ue_conn_ctx *ue_conn_ctx_alloc(struct osmo_sccp_link *link, uint32_t conn_id) +struct ue_conn_ctx *ue_conn_ctx_alloc(struct osmo_sccp_addr *addr, uint32_t conn_id) { struct ue_conn_ctx *ctx = talloc_zero(talloc_iu_ctx, struct ue_conn_ctx); - ctx->link = link; + ctx->sccp_addr = *addr; ctx->conn_id = conn_id; llist_add(&ctx->list, &ue_conn_ctx_list); return ctx; } -struct ue_conn_ctx *ue_conn_ctx_find(struct osmo_sccp_link *link, - uint32_t conn_id) +struct ue_conn_ctx *ue_conn_ctx_find(uint32_t conn_id) { struct ue_conn_ctx *ctx; llist_for_each_entry(ctx, &ue_conn_ctx_list, list) { - if (ctx->link == link && ctx->conn_id == conn_id) + if (ctx->conn_id == conn_id) return ctx; } return NULL; } static struct iu_rnc *iu_rnc_alloc(uint16_t rnc_id, uint16_t lac, uint8_t rac, - struct osmo_sccp_link *link) + struct osmo_sccp_addr *addr) { struct iu_rnc *rnc = talloc_zero(talloc_iu_ctx, struct iu_rnc); rnc->rnc_id = rnc_id; rnc->lac = lac; rnc->rac = rac; - rnc->link = link; + rnc->sccp_addr = *addr; llist_add(&rnc->entry, &rnc_list); LOGP(DRANAP, LOGL_NOTICE, "New RNC %d (LAC=%d RAC=%d)\n", @@ -146,7 +149,7 @@ static struct iu_rnc *iu_rnc_alloc(uint16_t rnc_id, uint16_t lac, uint8_t rac, } static struct iu_rnc *iu_rnc_register(uint16_t rnc_id, uint16_t lac, - uint8_t rac, struct osmo_sccp_link *link) + uint8_t rac, struct osmo_sccp_addr *addr) { struct iu_rnc *rnc; llist_for_each_entry(rnc, &rnc_list, entry) { @@ -165,42 +168,16 @@ static struct iu_rnc *iu_rnc_register(uint16_t rnc_id, uint16_t lac, rnc->lac = lac; rnc->rac = rac; - if (link && rnc->link != link) - LOGP(DRANAP, LOGL_NOTICE, "RNC %d on new link" + if (addr && memcmp(&rnc->sccp_addr, addr, sizeof(*addr))) + LOGP(DRANAP, LOGL_NOTICE, "RNC %d on New SCCP Addr %s" " (LAC=%d RAC=%d)\n", - rnc->rnc_id, rnc->lac, rnc->rac); - rnc->link = link; + rnc->rnc_id, osmo_sccp_addr_dump(addr), rnc->lac, rnc->rac); + rnc->sccp_addr = *addr; return rnc; } /* Not found, make a new one. */ - return iu_rnc_alloc(rnc_id, lac, rac, link); -} - -/* Discard/invalidate all ue_conn_ctx and iu_rnc entries that reference the - * given link, since this link is invalid and about to be deallocated. For - * each ue_conn_ctx, invoke the iu_event_cb_t with IU_EVENT_LINK_INVALIDATED. - */ -void iu_link_del(struct osmo_sccp_link *link) -{ - struct iu_rnc *rnc, *rnc_next; - llist_for_each_entry_safe(rnc, rnc_next, &rnc_list, entry) { - if (!rnc->link) - continue; - if (rnc->link != link) - continue; - rnc->link = NULL; - llist_del(&rnc->entry); - talloc_free(rnc); - } - - struct ue_conn_ctx *uec, *uec_next; - llist_for_each_entry_safe(uec, uec_next, &ue_conn_ctx_list, list) { - if (uec->link != link) - continue; - uec->link = NULL; - global_iu_event_cb(uec, IU_EVENT_LINK_INVALIDATED, NULL); - } + return iu_rnc_alloc(rnc_id, lac, rac, addr); } /*********************************************************************** @@ -219,7 +196,7 @@ int iu_rab_act(struct ue_conn_ctx *ue_ctx, struct msgb *msg) OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, msg); - return osmo_sua_user_link_down(ue_ctx->link, &prim->oph); + return osmo_sccp_user_sap_down(g_scu, &prim->oph); } int iu_rab_deact(struct ue_conn_ctx *ue_ctx, uint8_t rab_id) @@ -244,7 +221,7 @@ int iu_tx_sec_mode_cmd(struct ue_conn_ctx *uectx, struct gsm_auth_tuple *tp, osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, msg); - osmo_sua_user_link_down(uectx->link, &prim->oph); + osmo_sccp_user_sap_down(g_scu, &prim->oph); return 0; } @@ -254,8 +231,8 @@ int iu_tx_common_id(struct ue_conn_ctx *uectx, const char *imsi) struct msgb *msg; struct osmo_scu_prim *prim; - LOGP(DRANAP, LOGL_INFO, "Transmitting RANAP CommonID (SUA link %p conn_id %u)\n", - uectx->link, uectx->conn_id); + LOGP(DRANAP, LOGL_INFO, "Transmitting RANAP CommonID (SCCP conn_id %u)\n", + uectx->conn_id); msg = ranap_new_msg_common_id(imsi); msg->l2h = msg->data; @@ -264,7 +241,7 @@ int iu_tx_common_id(struct ue_conn_ctx *uectx, const char *imsi) osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, msg); - osmo_sua_user_link_down(uectx->link, &prim->oph); + osmo_sccp_user_sap_down(g_scu, &prim->oph); return 0; } @@ -325,7 +302,7 @@ static int ranap_handle_co_initial_ue(void *ctx, RANAP_InitialUE_MessageIEs_t *i memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size); /* Make sure we know the RNC Id and LAC+RAC coming in on this connection. */ - iu_rnc_register(grnc_id.rnc_id, ra_id.lac, ra_id.rac, ue_conn->link); + iu_rnc_register(grnc_id.rnc_id, ra_id.lac, ra_id.rac, &ue_conn->sccp_addr); ue_conn->ra_id = ra_id; /* Feed into the MM layer */ @@ -387,8 +364,8 @@ int iu_tx(struct msgb *msg_nas, uint8_t sapi) struct msgb *msg; struct osmo_scu_prim *prim; - LOGP(DRANAP, LOGL_INFO, "Transmitting L3 Message as RANAP DT (SUA link %p conn_id %u)\n", - uectx->link, uectx->conn_id); + LOGP(DRANAP, LOGL_INFO, "Transmitting L3 Message as RANAP DT (SCCP conn_id %u)\n", + uectx->conn_id); msg = ranap_new_msg_dt(sapi, msg_nas->data, msgb_length(msg_nas)); msgb_free(msg_nas); @@ -398,7 +375,7 @@ int iu_tx(struct msgb *msg_nas, uint8_t sapi) osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, msg); - osmo_sua_user_link_down(uectx->link, &prim->oph); + osmo_sccp_user_sap_down(g_scu, &prim->oph); return 0; } @@ -417,6 +394,9 @@ int iu_tx_release(struct ue_conn_ctx *ctx, const struct RANAP_Cause *cause) if (!cause) cause = &default_cause; + LOGP(DRANAP, LOGL_INFO, "Transmitting Iu Release (SCCP conn_id %u)\n", + ctx->conn_id); + msg = ranap_new_msg_iu_rel_cmd(cause); msg->l2h = msg->data; prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); @@ -424,7 +404,7 @@ int iu_tx_release(struct ue_conn_ctx *ctx, const struct RANAP_Cause *cause) osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, msg); - return osmo_sua_user_link_down(ctx->link, &prim->oph); + return osmo_sccp_user_sap_down(g_scu, &prim->oph); } static int ranap_handle_co_iu_rel_req(struct ue_conn_ctx *ctx, RANAP_Iu_ReleaseRequestIEs_t *ies) @@ -595,21 +575,28 @@ static void cn_ranap_handle_cl(void *ctx, ranap_message *message) * Paging ***********************************************************************/ -/* Send a paging command down a given SUA link. tmsi and paging_cause are +struct osmo_sccp_addr local_sccp_addr = { + .presence = OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC, + .ri = OSMO_SCCP_RI_SSN_PC, + .ssn = OSMO_SCCP_SSN_RANAP, + .pc = 1, +}; + +/* Send a paging command down a given SCCP User. tmsi and paging_cause are * optional and may be passed NULL and 0, respectively, to disable their use. * See enum RANAP_PagingCause. * * If TMSI is given, the IMSI is not sent over the air interface. Nevertheless, * the IMSI is still required for resolution in the HNB-GW and/or(?) RNC. */ -static int iu_tx_paging_cmd(struct osmo_sccp_link *link, +static int iu_tx_paging_cmd(struct osmo_sccp_addr *called_addr, const char *imsi, const uint32_t *tmsi, bool is_ps, uint32_t paging_cause) { struct msgb *msg; msg = ranap_new_msg_paging_cmd(imsi, tmsi, is_ps? 1 : 0, paging_cause); msg->l2h = msg->data; - return osmo_sccp_tx_unitdata_ranap(link, 1, 2, msg->data, - msgb_length(msg)); + osmo_sccp_tx_unitdata_msg(g_scu, &local_sccp_addr, called_addr, msg); + return 0; } static int iu_page(const char *imsi, const uint32_t *tmsi_or_ptimsi, @@ -634,22 +621,18 @@ static int iu_page(const char *imsi, const uint32_t *tmsi_or_ptimsi, } llist_for_each_entry(rnc, &rnc_list, entry) { - if (!rnc->link) { - /* Not actually connected, don't count it. */ - continue; - } if (rnc->lac != lac) continue; if (is_ps && rnc->rac != rac) continue; /* Found a match! */ - if (iu_tx_paging_cmd(rnc->link, imsi, tmsi_or_ptimsi, is_ps, 0) + if (iu_tx_paging_cmd(&rnc->sccp_addr, imsi, tmsi_or_ptimsi, is_ps, 0) == 0) { LOGP(DRANAP, LOGL_DEBUG, - "%s: Paged for IMSI %s on RNC %d, on SUA link %p\n", + "%s: Paged for IMSI %s on RNC %d, on SCCP addr %s\n", is_ps? "IuPS" : "IuCS", - imsi, rnc->rnc_id, rnc->link); + imsi, rnc->rnc_id, osmo_sccp_addr_dump(&rnc->sccp_addr)); pagings_sent ++; } } @@ -692,8 +675,8 @@ int iu_page_ps(const char *imsi, const uint32_t *ptmsi, uint16_t lac, uint8_t ra * ***********************************************************************/ -int tx_unitdata(struct osmo_sccp_link *link); -int tx_conn_req(struct osmo_sccp_link *link, uint32_t conn_id); +int tx_unitdata(struct osmo_sccp_user *scu); +int tx_conn_req(struct osmo_sccp_user *scu, uint32_t conn_id); struct osmo_prim_hdr *make_conn_req(uint32_t conn_id); struct osmo_prim_hdr *make_dt1_req(uint32_t conn_id, const uint8_t *data, unsigned int len); @@ -711,8 +694,9 @@ struct osmo_prim_hdr *make_conn_resp(struct osmo_scu_connect_param *param) return &prim->oph; } -static int sccp_sap_up(struct osmo_prim_hdr *oph, void *link) +static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) { + struct osmo_sccp_user *scu = _scu; struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph; struct osmo_prim_hdr *resp = NULL; int rc; @@ -734,10 +718,10 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *link) "Received invalid N-CONNECT.ind\n"); return 0; } - ue = ue_conn_ctx_alloc(link, prim->u.connect.conn_id); - /* first ensure the local SUA/SCCP socket is ACTIVE */ + ue = ue_conn_ctx_alloc(&prim->u.connect.calling_addr, prim->u.connect.conn_id); + /* first ensure the local SCCP socket is ACTIVE */ resp = make_conn_resp(&prim->u.connect); - osmo_sua_user_link_down(link, resp); + osmo_sccp_user_sap_down(scu, resp); /* then handle the RANAP payload */ rc = ranap_cn_rx_co(cn_ranap_handle_co, ue, msgb_l2(oph->msg), msgb_l2len(oph->msg)); break; @@ -745,7 +729,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *link) /* indication of disconnect */ DEBUGP(DRANAP, "N-DISCONNECT.ind(%u)\n", prim->u.disconnect.conn_id); - ue = ue_conn_ctx_find(link, prim->u.disconnect.conn_id); + ue = ue_conn_ctx_find(prim->u.disconnect.conn_id); rc = ranap_cn_rx_co(cn_ranap_handle_co, ue, msgb_l2(oph->msg), msgb_l2len(oph->msg)); break; case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION): @@ -753,14 +737,14 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *link) DEBUGP(DRANAP, "N-DATA.ind(%u, %s)\n", prim->u.data.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); /* resolve UE context */ - ue = ue_conn_ctx_find(link, prim->u.data.conn_id); + ue = ue_conn_ctx_find(prim->u.data.conn_id); rc = ranap_cn_rx_co(cn_ranap_handle_co, ue, msgb_l2(oph->msg), msgb_l2len(oph->msg)); break; case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): /* connection-less data received */ DEBUGP(DRANAP, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); - rc = ranap_cn_rx_cl(cn_ranap_handle_cl, link, msgb_l2(oph->msg), msgb_l2len(oph->msg)); + rc = ranap_cn_rx_cl(cn_ranap_handle_cl, scu, msgb_l2(oph->msg), msgb_l2len(oph->msg)); break; default: rc = -1; @@ -771,17 +755,17 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *link) return rc; } -int iu_init(void *ctx, const char *listen_addr, uint16_t listen_port, +int iu_init(void *ctx, struct osmo_sccp_instance *sccp, iu_recv_cb_t iu_recv_cb, iu_event_cb_t iu_event_cb) { - struct osmo_sccp_user *user; talloc_iu_ctx = talloc_named_const(ctx, 1, "iu"); talloc_asn1_ctx = talloc_named_const(talloc_iu_ctx, 1, "asn1"); global_iu_recv_cb = iu_recv_cb; global_iu_event_cb = iu_event_cb; - osmo_sua_set_log_area(DSUA); - user = osmo_sua_user_create(talloc_iu_ctx, sccp_sap_up, talloc_iu_ctx); - return osmo_sua_server_listen(user, listen_addr, listen_port); + g_sccp = sccp; + g_scu = osmo_sccp_user_bind(g_sccp, "OsmoMSC-Iu", sccp_sap_up, OSMO_SCCP_SSN_RANAP); + + return 0; } diff --git a/src/libiu/iu_vty.c b/src/libiu/iu_vty.c index 73ad126ba..3fd3cd15c 100644 --- a/src/libiu/iu_vty.c +++ b/src/libiu/iu_vty.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include @@ -52,9 +54,10 @@ DEFUN(logging_asn_xer_print, return CMD_SUCCESS; } +#define IU_STR "Iu interface protocol options\n" DEFUN(cfg_iu_rab_assign_addr_enc, cfg_iu_rab_assign_addr_enc_cmd, "iu rab-assign-addr-enc (x213|v4raw)", - "Iu interface protocol options\n" + IU_STR "Choose RAB Assignment's Transport Layer Address encoding\n" "ITU-T X.213 compliant address encoding (default)\n" "32bit length raw IPv4 address (for ip.access nano3G)\n") @@ -72,6 +75,24 @@ DEFUN(cfg_iu_rab_assign_addr_enc, cfg_iu_rab_assign_addr_enc_cmd, return CMD_SUCCESS; } +extern struct osmo_sccp_addr local_sccp_addr; + +/* Note from the future: change-id Ib8c4fcdb4766c5e575618b95ce16dce51063206b will move this file to + * osmo-iuh, and there, change-id I3bb7fc1cd5261d214c6ba0cccfe95f637e6db087 will drop this vty command + * and use the point code defined via libosmo-sccp vty commands instead. */ +DEFUN(cfg_iu_local_addr_pc, cfg_iu_local_addr_pc_cmd, + "iu local-address point-code PC", + IU_STR "Local SCCP Address\n" "Set local point code\n" "point code\n") +{ + local_sccp_addr.presence = OSMO_SCCP_ADDR_T_PC | OSMO_SCCP_ADDR_T_SSN; + local_sccp_addr.ri = OSMO_SCCP_RI_SSN_PC; + local_sccp_addr.pc = osmo_ss7_pointcode_parse(NULL, argv[0]); + + return CMD_SUCCESS; +} + +/* TODO: GT address configuration, in line with 4.5.1.1.1 of TS 25.410 */ + int iu_vty_config_write(struct vty *vty, const char *indent) { if (!g_rab_assign_addr_enc) { @@ -95,6 +116,9 @@ int iu_vty_config_write(struct vty *vty, const char *indent) return CMD_WARNING; } + vty_out(vty, "%siu local-address point-code %s%s", indent, + osmo_ss7_pointcode_print(NULL, local_sccp_addr.pc), VTY_NEWLINE); + return CMD_SUCCESS; } @@ -105,4 +129,5 @@ void iu_vty_init(int iu_parent_node, enum nsap_addr_enc *rab_assign_addr_enc) install_element(CFG_LOG_NODE, &logging_asn_debug_cmd); install_element(CFG_LOG_NODE, &logging_asn_xer_print_cmd); install_element(iu_parent_node, &cfg_iu_rab_assign_addr_enc_cmd); + install_element(iu_parent_node, &cfg_iu_local_addr_pc_cmd); } diff --git a/src/libmgcp/mgcp_protocol.c b/src/libmgcp/mgcp_protocol.c index 96542c5a0..c8b6e8660 100644 --- a/src/libmgcp/mgcp_protocol.c +++ b/src/libmgcp/mgcp_protocol.c @@ -66,6 +66,45 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp); static int mgcp_analyze_header(struct mgcp_parse_data *parse, char *data); +/* Display an mgcp message on the log output */ +void display_mgcp_message(unsigned char *message, unsigned int len, + char *preamble) +{ + unsigned char line[80]; + unsigned char *ptr; + unsigned int consumed = 0; + unsigned int consumed_line = 0; + unsigned int line_count = 0; + + if (!log_check_level(DMGCP, LOGL_DEBUG)) + return; + + while (1) { + memset(line, 0, sizeof(line)); + ptr = line; + consumed_line = 0; + do { + if (*message != '\n' && *message != '\r') { + *ptr = *message; + ptr++; + } + message++; + consumed++; + consumed_line++; + } while (*message != '\n' && consumed < len + && consumed_line < sizeof(line)); + + if (strlen((const char *)line)) { + LOGP(DMGCP, LOGL_DEBUG, "%s: line #%02u: %s\n", + preamble, line_count, line); + line_count++; + } + + if (consumed >= len) + return; + } +} + static int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line) { const size_t line_len = strlen(line); @@ -157,7 +196,8 @@ static struct msgb *create_resp(struct mgcp_endpoint *endp, int code, } res->l2h = msgb_put(res, len); - LOGP(DMGCP, LOGL_DEBUG, "Generated response: code: %d for '%s'\n", code, res->l2h); + LOGP(DMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code); + display_mgcp_message(res->l2h, msgb_l2len(res), "Generated response"); /* * Remember the last transmission per endpoint. @@ -329,6 +369,8 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) if (mgcp_msg_terminate_nul(msg)) return NULL; + display_mgcp_message(msg->l2h, msgb_l2len(msg), "Received message"); + /* attempt to treat it as a response */ if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) { LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code); diff --git a/src/libmgcp/mgcpgw_client.c b/src/libmgcp/mgcpgw_client.c index 9f0c84de2..1910a9f35 100644 --- a/src/libmgcp/mgcpgw_client.c +++ b/src/libmgcp/mgcpgw_client.c @@ -35,15 +35,6 @@ #include #include -struct mgcpgw_client { - struct mgcpgw_client_conf actual; - uint32_t remote_addr; - struct osmo_wqueue wq; - mgcp_trans_id_t next_trans_id; - uint16_t next_endpoint; - struct llist_head responses_pending; -}; - void mgcpgw_client_conf_init(struct mgcpgw_client_conf *conf) { /* NULL and -1 default to MGCPGW_CLIENT_*_DEFAULT values */ @@ -52,12 +43,68 @@ void mgcpgw_client_conf_init(struct mgcpgw_client_conf *conf) .local_port = -1, .remote_addr = NULL, .remote_port = -1, + .first_endpoint = 0, + .last_endpoint = 0, + .bts_base = 0, }; } -unsigned int mgcpgw_client_next_endpoint(struct mgcpgw_client *client) +/* Test if a given endpoint id is currently in use */ +static bool endpoint_in_use(uint16_t id, struct mgcpgw_client *client) { - return client->next_endpoint ++; + struct mgcp_inuse_endpoint *endpoint; + llist_for_each_entry(endpoint, &client->inuse_endpoints, entry) { + if (endpoint->id == id) + return true; + } + + return false; +} + +/* Find and seize an unsused endpoint id */ +int mgcpgw_client_next_endpoint(struct mgcpgw_client *client) +{ + int i; + uint16_t first_endpoint = client->actual.first_endpoint; + uint16_t last_endpoint = client->actual.last_endpoint; + struct mgcp_inuse_endpoint *endpoint; + + /* Use the maximum permitted range if the VTY + * configuration does not specify a range */ + if (client->actual.last_endpoint == 0) { + first_endpoint = 1; + last_endpoint = 65534; + } + + /* Test the permitted endpoint range for an endpoint + * number that is not in use. When a suitable endpoint + * number can be found, seize it by adding it to the + * inuse list. */ + for (i=first_endpoint;iid = i; + llist_add_tail(&endpoint->entry, &client->inuse_endpoints); + return endpoint->id; + } + } + + /* All endpoints are busy! */ + return -EINVAL; +} + +/* Release a seized endpoint id to make it available again for other calls */ +void mgcpgw_client_release_endpoint(uint16_t id, struct mgcpgw_client *client) +{ + struct mgcp_inuse_endpoint *endpoint; + struct mgcp_inuse_endpoint *endpoint_tmp; + llist_for_each_entry_safe(endpoint, endpoint_tmp, &client->inuse_endpoints, entry) { + if (endpoint->id == id) { + llist_del(&endpoint->entry); + talloc_free(endpoint); + } + } } static void mgcpgw_client_handle_response(struct mgcpgw_client *mgcp, @@ -257,9 +304,16 @@ static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg) { int ret; static char strbuf[4096]; - unsigned int l = msg->len < sizeof(strbuf)-1 ? msg->len : sizeof(strbuf)-1; + unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf); + unsigned int i; + strncpy(strbuf, (const char*)msg->data, l); - strbuf[l] = '\0'; + for (i = 0; i < sizeof(strbuf); i++) { + if (strbuf[i] == '\n' || strbuf[i] == '\r') { + strbuf[i] = '\0'; + break; + } + } DEBUGP(DMGCP, "Tx MGCP msg to MGCP GW: '%s'\n", strbuf); LOGP(DMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len); @@ -280,9 +334,9 @@ struct mgcpgw_client *mgcpgw_client_init(void *ctx, mgcp = talloc_zero(ctx, struct mgcpgw_client); INIT_LLIST_HEAD(&mgcp->responses_pending); + INIT_LLIST_HEAD(&mgcp->inuse_endpoints); mgcp->next_trans_id = 1; - mgcp->next_endpoint = 1; mgcp->actual.local_addr = conf->local_addr ? conf->local_addr : MGCPGW_CLIENT_LOCAL_ADDR_DEFAULT; @@ -294,6 +348,10 @@ struct mgcpgw_client *mgcpgw_client_init(void *ctx, mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port : MGCPGW_CLIENT_REMOTE_PORT_DEFAULT; + mgcp->actual.first_endpoint = conf->first_endpoint > 0 ? (uint16_t)conf->first_endpoint : 0; + mgcp->actual.last_endpoint = conf->last_endpoint > 0 ? (uint16_t)conf->last_endpoint : 0; + mgcp->actual.bts_base = conf->bts_base > 0 ? (uint16_t)conf->bts_base : 4000; + return mgcp; } @@ -547,3 +605,12 @@ struct msgb *mgcp_msg_mdcx(struct mgcpgw_client *mgcp, rtp_conn_addr, rtp_port); } + +struct msgb *mgcp_msg_dlcx(struct mgcpgw_client *mgcp, uint16_t rtp_endpoint, + unsigned int call_id) +{ + mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp); + return mgcp_msg_from_str(trans_id, + "DLCX %u %x@mgw MGCP 1.0\r\n" + "C: %x\r\n", trans_id, rtp_endpoint, call_id); +} diff --git a/src/libmgcp/mgcpgw_client_vty.c b/src/libmgcp/mgcpgw_client_vty.c index a42ee4e5d..806800078 100644 --- a/src/libmgcp/mgcpgw_client_vty.c +++ b/src/libmgcp/mgcpgw_client_vty.c @@ -79,10 +79,46 @@ DEFUN(cfg_mgcpgw_remote_port, cfg_mgcpgw_remote_port_cmd, return CMD_SUCCESS; } +DEFUN(cfg_mgcpgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd, + "mgcpgw endpoint-range <1-65534> <1-65534>", + MGCPGW_STR "usable range of endpoint identifiers\n" + "set first useable endpoint identifier\n" + "set the last useable endpoint identifier\n") +{ + uint16_t first_endpoint = atoi(argv[0]); + uint16_t last_endpoint = atoi(argv[1]); + + if (last_endpoint < first_endpoint) { + vty_out(vty, "last endpoint must be greater than first endpoint!%s", + VTY_NEWLINE); + return CMD_SUCCESS; + } + + global_mgcpgw_client_conf->first_endpoint = first_endpoint; + global_mgcpgw_client_conf->last_endpoint = last_endpoint; + return CMD_SUCCESS; +} + +#define BTS_START_STR "First UDP port allocated for the BTS side\n" +#define UDP_PORT_STR "UDP Port number\n" +DEFUN(cfg_mgcp_rtp_bts_base_port, + cfg_mgcp_rtp_bts_base_port_cmd, + "mgcpgw bts-base <0-65534>", + MGCPGW_STR + BTS_START_STR + UDP_PORT_STR) +{ + global_mgcpgw_client_conf->bts_base = atoi(argv[0]); + return CMD_SUCCESS; +} + int mgcpgw_client_config_write(struct vty *vty, const char *indent) { const char *addr; int port; + uint16_t first_endpoint; + uint16_t last_endpoint; + uint16_t bts_base; addr = global_mgcpgw_client_conf->local_addr; if (addr) @@ -102,6 +138,19 @@ int mgcpgw_client_config_write(struct vty *vty, const char *indent) vty_out(vty, "%smgcpgw remote-port %u%s", indent, (uint16_t)port, VTY_NEWLINE); + first_endpoint = global_mgcpgw_client_conf->first_endpoint; + last_endpoint = global_mgcpgw_client_conf->last_endpoint; + if (last_endpoint != 0) { + vty_out(vty, "%smgcpgw endpoint-range %u %u%s", indent, + first_endpoint, last_endpoint, VTY_NEWLINE); + } + + bts_base = global_mgcpgw_client_conf->bts_base; + if (bts_base) { + vty_out(vty, "%smgcpgw bts-base %u%s", indent, + bts_base, VTY_NEWLINE); + } + return CMD_SUCCESS; } @@ -113,4 +162,6 @@ void mgcpgw_client_vty_init(int node, struct mgcpgw_client_conf *conf) install_element(node, &cfg_mgcpgw_local_port_cmd); install_element(node, &cfg_mgcpgw_remote_ip_cmd); install_element(node, &cfg_mgcpgw_remote_port_cmd); + install_element(node, &cfg_mgcpgw_endpoint_range_cmd); + install_element(node, &cfg_mgcp_rtp_bts_base_port_cmd); } diff --git a/src/libmsc/Makefile.am b/src/libmsc/Makefile.am index 4726bbe4b..9f246b3d8 100644 --- a/src/libmsc/Makefile.am +++ b/src/libmsc/Makefile.am @@ -13,6 +13,7 @@ AM_CFLAGS = \ $(LIBCRYPTO_CFLAGS) \ $(LIBSMPP34_CFLAGS) \ $(LIBASN1C_CFLAGS) \ + $(LIBOSMOSIGTRAN_CFLAGS) \ $(NULL) noinst_HEADERS = \ @@ -25,6 +26,7 @@ noinst_LIBRARIES = \ libmsc_a_SOURCES = \ a_iface.c \ + a_iface_bssap.c \ auth.c \ msc_vty.c \ db.c \ diff --git a/src/libmsc/a_iface.c b/src/libmsc/a_iface.c index caf9d4b06..93e8ab5e9 100644 --- a/src/libmsc/a_iface.c +++ b/src/libmsc/a_iface.c @@ -1,9 +1,8 @@ -/* A-interface implementation, from MSC to BSC */ - -/* (C) 2016 by sysmocom s.m.f.c GmbH - * +/* (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 @@ -19,35 +18,574 @@ * */ +#include #include #include - +#include +#include +#include +#include +#include +#include +#include +#include #include - -#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include -int a_tx(struct msgb *msg) +/* A pointer to the GSM network we work with. By the current paradigm, + * there can only be one gsm_network per MSC. The pointer is set once + * when calling a_init() */ +static struct gsm_network *gsm_network = NULL; + +/* A struct to track currently active connections. We need that information + * to handle failure sitautions. In case of a problem, we must know which + * connections are currently open and which BSC is responsible. We also need + * the data to perform our connection checks (a_reset). All other logic will + * look at the connection ids and addresses that are supplied by the + * primitives */ +struct bsc_conn { + struct llist_head list; + uint32_t conn_id; /* Connection identifier */ +}; + +/* Internal list with connections we currently maintain. This + * list is of type struct bsc_conn (see above) */ +static LLIST_HEAD(active_connections); + +/* Record info of a new active connection in the active connection list */ +static void record_bsc_con(const void *ctx, uint32_t conn_id) { - LOGP(DMSC, LOGL_ERROR, "message to be sent to BSC, but A-interface" - " not implemented.\n%s\n", osmo_hexdump(msg->data, msg->len)); - return -1; + struct bsc_conn *conn; + + conn = talloc_zero(ctx, struct bsc_conn); + OSMO_ASSERT(conn); + + conn->conn_id = conn_id; + + llist_add_tail(&conn->list, &active_connections); } -int a_page(const char *imsi, uint32_t tmsi, uint16_t lac) +/* Delete info of a closed connection from the active connection list */ +void a_delete_bsc_con(uint32_t conn_id) { - LOGP(DMSC, LOGL_ERROR, "Paging to be sent to BSC, but A-interface" - " not implemented: IMSI %s TMSI 0x%08x LAC %u\n", - imsi, tmsi, lac); - return -1; + struct bsc_conn *conn; + struct bsc_conn *conn_temp; + + LOGP(DMSC, LOGL_DEBUG, + "Removing connection from active sccp-connection list (conn_id=%i)\n", + conn_id); + + llist_for_each_entry_safe(conn, conn_temp, &active_connections, list) { + if (conn->conn_id == conn_id) { + llist_del(&conn->list); + talloc_free(conn); + } + } } -int msc_gsm0808_tx_cipher_mode(struct gsm_subscriber_connection *conn, int cipher, - const uint8_t *key, int len, int include_imeisv) +/* Check if a specified connection id has an active SCCP connection */ +static bool check_connection_active(uint32_t conn_id) +{ + struct bsc_conn *conn; + + /* Find the address for the current connection id */ + llist_for_each_entry(conn, &active_connections, list) { + if (conn->conn_id == conn_id) { + return true; + } + } + + return false; +} + +/* Get the reset context for a specifiec calling (BSC) address */ +static struct a_reset_ctx *get_reset_ctx_by_sccp_addr(const struct osmo_sccp_addr *addr) +{ + struct bsc_context *bsc_ctx; + struct osmo_ss7_instance *ss7; + + if (!addr) + return NULL; + + llist_for_each_entry(bsc_ctx, &gsm_network->a.bscs, list) { + if (memcmp(&bsc_ctx->bsc_addr, addr, sizeof(*addr)) == 0) + return bsc_ctx->reset; + } + + ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance); + OSMO_ASSERT(ss7); + LOGP(DMSC, LOGL_ERROR, "The calling BSC (%s) is unknown to this MSC ...\n", + osmo_sccp_addr_name(ss7, addr)); + return NULL; +} + +/* Send DTAP message via A-interface */ +int a_iface_tx_dtap(struct msgb *msg) +{ + struct gsm_subscriber_connection *conn; + struct msgb *msg_resp; + + /* FIXME: Set this to some meaninful value! */ + uint8_t link_id = 0x00; + OSMO_ASSERT(msg); + conn = (struct gsm_subscriber_connection *)msg->dst; + OSMO_ASSERT(conn); + OSMO_ASSERT(conn->a.scu); + + LOGP(DMSC, LOGL_DEBUG, "Passing DTAP message from MSC to BSC (conn_id=%i)\n", conn->a.conn_id); + + msg->l3h = msg->data; + msg_resp = gsm0808_create_dtap(msg, link_id); + if (!msg_resp) { + LOGP(DMSC, LOGL_ERROR, "Unable to generate BSSMAP DTAP message!\n"); + return -EINVAL; + } else + LOGP(DMSC, LOGL_DEBUG, "Massage will be sent as BSSMAP DTAP message!\n"); + + LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg_resp->data, msg_resp->len)); + return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg_resp); +} + +/* Send Cipher mode command via A-interface */ +int a_iface_tx_cipher_mode(const struct gsm_subscriber_connection *conn, + int cipher, const const uint8_t *key, int len, int include_imeisv) { /* TODO generalize for A- and Iu interfaces, don't name after 08.08 */ - LOGP(DMSC, LOGL_ERROR, "gsm0808_cipher_mode(): message to be sent to" - " BSC, but A interface not yet implemented.\n"); - return -1; + struct msgb *msg_resp; + struct gsm0808_encrypt_info ei; + + OSMO_ASSERT(conn); + + LOGP(DMSC, LOGL_DEBUG, "Passing Cipher mode command message from MSC to BSC (conn_id=%i)\n", conn->a.conn_id); + uint8_t crm = 0x01; + uint8_t *crm_ptr = NULL; + + /* Setup encryption information */ + if (len > ENCRY_INFO_KEY_MAXLEN || !key) { + LOGP(DMSC, LOGL_ERROR, + "Cipher mode command message could not be generated due to invalid key! (conn_id=%i)\n", + conn->a.conn_id); + return -EINVAL; + } else { + memcpy(&ei.key, key, len); + ei.key_len = len; + } + + if (include_imeisv) + crm_ptr = &crm; + + ei.perm_algo[0] = (uint8_t) (1 << cipher); + ei.perm_algo_len = 1; + + msg_resp = gsm0808_create_cipher(&ei, crm_ptr); + LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg_resp->data, msg_resp->len)); + + return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg_resp); +} + +/* Page a subscriber via A-interface */ +int a_iface_tx_paging(const char *imsi, uint32_t tmsi, uint16_t lac) +{ + struct bsc_context *bsc_ctx; + struct gsm0808_cell_id_list cil; + struct msgb *msg; + int page_count = 0; + struct osmo_ss7_instance *ss7; + + OSMO_ASSERT(imsi); + + cil.id_discr = CELL_IDENT_LAC; + cil.id_list_lac[0] = lac; + cil.id_list_len = 1; + + ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance); + OSMO_ASSERT(ss7); + + /* Deliver paging request to all known BSCs */ + llist_for_each_entry(bsc_ctx, &gsm_network->a.bscs, list) { + if (a_reset_conn_ready(bsc_ctx->reset)) { + LOGP(DMSC, LOGL_DEBUG, + "Passing paging message from MSC %s to BSC %s (imsi=%s, tmsi=0x%08x, lac=%u)\n", + osmo_sccp_addr_name(ss7, &bsc_ctx->msc_addr), + osmo_sccp_addr_name(ss7, &bsc_ctx->bsc_addr), imsi, tmsi, lac); + msg = gsm0808_create_paging(imsi, &tmsi, &cil, NULL); + osmo_sccp_tx_unitdata_msg(bsc_ctx->sccp_user, + &bsc_ctx->msc_addr, &bsc_ctx->bsc_addr, msg); + page_count++; + } else { + LOGP(DMSC, LOGL_DEBUG, + "Connection down, dropping paging from MSC %s to BSC %s (imsi=%s, tmsi=0x%08x, lac=%u)\n", + osmo_sccp_addr_name(ss7, &bsc_ctx->msc_addr), + osmo_sccp_addr_name(ss7, &bsc_ctx->bsc_addr), imsi, tmsi, lac); + } + } + + if (page_count <= 0) + LOGP(DMSC, LOGL_ERROR, "Could not deliver paging because none of the associated BSCs is available!\n"); + + return page_count; +} + +/* Convert speech version field */ +static uint8_t convert_Abis_sv_to_A_sv(int speech_ver) +{ + /* The speech versions that are transmitted in the Bearer capability + * information element, that is transmitted on the Abis interfece + * use a different encoding than the permitted speech version + * identifier, that is signalled in the channel type element on the A + * interface. (See also 3GPP TS 48.008, 3.2.2.1 and 3GPP TS 24.008, + * 10.5.103 */ + + switch (speech_ver) { + case GSM48_BCAP_SV_FR: + return GSM0808_PERM_FR1; + break; + case GSM48_BCAP_SV_HR: + return GSM0808_PERM_HR1; + break; + case GSM48_BCAP_SV_EFR: + return GSM0808_PERM_FR2; + break; + case GSM48_BCAP_SV_AMR_F: + return GSM0808_PERM_FR3; + break; + case GSM48_BCAP_SV_AMR_H: + return GSM0808_PERM_HR3; + break; + case GSM48_BCAP_SV_AMR_OFW: + return GSM0808_PERM_FR4; + break; + case GSM48_BCAP_SV_AMR_OHW: + return GSM0808_PERM_HR4; + break; + case GSM48_BCAP_SV_AMR_FW: + return GSM0808_PERM_FR5; + break; + case GSM48_BCAP_SV_AMR_OH: + return GSM0808_PERM_HR6; + break; + } + + /* If nothing matches, tag the result as invalid */ + LOGP(DMSC, LOGL_ERROR, "Invalid permitted speech version / rate detected, discarding.\n"); + return 0xFF; +} + +/* Convert speech preference field */ +static uint8_t convert_Abis_prev_to_A_pref(int radio) +{ + /* The Radio channel requirement field that is transmitted in the + * Bearer capability information element, that is transmitted on the + * Abis interfece uses a different encoding than the Channel rate and + * type field that is signalled in the channel type element on the A + * interface. (See also 3GPP TS 48.008, 3.2.2.1 and 3GPP TS 24.008, + * 10.5.102 */ + + switch (radio) { + case GSM48_BCAP_RRQ_FR_ONLY: + return GSM0808_SPEECH_FULL_BM; + case GSM48_BCAP_RRQ_DUAL_FR: + return GSM0808_SPEECH_FULL_PREF; + case GSM48_BCAP_RRQ_DUAL_HR: + return GSM0808_SPEECH_HALF_PREF; + } + + LOGP(DMSC, LOGL_ERROR, "Invalid speech version / rate combination preference, defaulting to full rate.\n"); + return GSM0808_SPEECH_FULL_BM; +} + +/* Assemble the channel type field */ +static int enc_channel_type(struct gsm0808_channel_type *ct, const struct gsm_mncc_bearer_cap *bc) +{ + unsigned int i; + uint8_t sv; + unsigned int count = 0; + bool only_gsm_hr = true; + + OSMO_ASSERT(ct); + OSMO_ASSERT(bc); + + ct->ch_indctr = GSM0808_CHAN_SPEECH; + + for (i = 0; i < ARRAY_SIZE(bc->speech_ver); i++) { + if (bc->speech_ver[i] == -1) + break; + sv = convert_Abis_sv_to_A_sv(bc->speech_ver[i]); + if (sv != 0xFF) { + /* Detect if something else than + * GSM HR V1 is supported */ + if (sv == GSM0808_PERM_HR2 || + sv == GSM0808_PERM_HR3 || sv == GSM0808_PERM_HR4 || sv == GSM0808_PERM_HR6) + only_gsm_hr = false; + + ct->perm_spch[count] = sv; + count++; + } + } + ct->perm_spch_len = count; + + if (only_gsm_hr) + /* Note: We must avoid the usage of GSM HR1 as this + * codec only offers very poor audio quality. If the + * MS only supports GSM HR1 (and full rate), and has + * a preference for half rate. Then we will ignore the + * preference and assume a preference for full rate. */ + ct->ch_rate_type = GSM0808_SPEECH_FULL_BM; + else + ct->ch_rate_type = convert_Abis_prev_to_A_pref(bc->radio); + + if (count) + return 0; + else + return -EINVAL; +} + +/* Assemble the speech codec field */ +static int enc_speech_codec_list(struct gsm0808_speech_codec_list *scl, const struct gsm0808_channel_type *ct) +{ + unsigned int i; + int rc; + + memset(scl, 0, sizeof(*scl)); + for (i = 0; i < ct->perm_spch_len; i++) { + rc = gsm0808_speech_codec_from_chan_type(&scl->codec[i], ct->perm_spch[i]); + if (rc != 0) + return -EINVAL; + } + scl->len = i; + + return 0; +} + +/* Send assignment request via A-interface */ +int a_iface_tx_assignment(const struct gsm_trans *trans) +{ + struct gsm_subscriber_connection *conn; + struct gsm0808_channel_type ct; + struct gsm0808_speech_codec_list scl; + uint32_t *ci_ptr = NULL; + struct msgb *msg; + struct sockaddr_storage rtp_addr; + struct sockaddr_in rtp_addr_in; + int rc; + + OSMO_ASSERT(trans); + conn = trans->conn; + OSMO_ASSERT(conn); + + LOGP(DMSC, LOGL_ERROR, "Sending assignment command to BSC (conn_id %u)\n", conn->a.conn_id); + + /* Channel type */ + rc = enc_channel_type(&ct, &trans->bearer_cap); + if (rc < 0) { + LOGP(DMSC, LOGL_ERROR, "Faild to generate channel type -- assignment not sent!\n"); + return -EINVAL; + } + + /* Speech codec list */ + rc = enc_speech_codec_list(&scl, &ct); + if (rc < 0) { + LOGP(DMSC, LOGL_ERROR, "Faild to generate Speech codec list -- assignment not sent!\n"); + return -EINVAL; + } + + /* Package RTP-Address data */ + memset(&rtp_addr_in, 0, sizeof(rtp_addr_in)); + rtp_addr_in.sin_family = AF_INET; + rtp_addr_in.sin_port = osmo_htons(conn->rtp.port_subscr); + rtp_addr_in.sin_addr.s_addr = osmo_htonl(mgcpgw_client_remote_addr_n(gsm_network->mgcpgw.client)); + + memset(&rtp_addr, 0, sizeof(rtp_addr)); + memcpy(&rtp_addr, &rtp_addr_in, sizeof(rtp_addr_in)); + + msg = gsm0808_create_ass(&ct, NULL, &rtp_addr, &scl, ci_ptr); + + LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg->data, msg->len)); + return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg); +} + +/* Send clear command via A-interface */ +int a_iface_tx_clear_cmd(struct gsm_subscriber_connection *conn) +{ + struct msgb *msg; + + LOGP(DMSC, LOGL_NOTICE, "Sending clear command to BSC (conn_id=%u)\n", conn->a.conn_id); + + msg = gsm0808_create_clear_command(GSM0808_CAUSE_CALL_CONTROL); + return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg); +} + +/* Callback function: Close all open connections */ +static void a_reset_cb(const void *priv) +{ + struct msgb *msg; + struct bsc_context *bsc_ctx = (struct bsc_context*) priv; + struct osmo_ss7_instance *ss7; + + /* Skip if the A interface is not properly initalized yet */ + if (!gsm_network) + return; + + /* Clear all now orphaned subscriber connections */ + a_clear_all(bsc_ctx->sccp_user, &bsc_ctx->bsc_addr); + + /* Send reset to the remote BSC */ + ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance); + OSMO_ASSERT(ss7); + LOGP(DMSC, LOGL_NOTICE, "Sending RESET to BSC %s\n", osmo_sccp_addr_name(ss7, &bsc_ctx->bsc_addr)); + msg = gsm0808_create_reset(); + osmo_sccp_tx_unitdata_msg(bsc_ctx->sccp_user, &bsc_ctx->msc_addr, + &bsc_ctx->bsc_addr, msg); +} + +/* Add a new BSC connection to our internal list with known BSCs */ +static void add_bsc(const struct osmo_sccp_addr *msc_addr, const struct osmo_sccp_addr *bsc_addr, + struct osmo_sccp_user *scu) +{ + struct bsc_context *bsc_ctx; + struct osmo_ss7_instance *ss7; + + OSMO_ASSERT(bsc_addr); + OSMO_ASSERT(msc_addr); + OSMO_ASSERT(scu); + + /* Check if we already know this BSC, if yes, skip adding it. */ + if (get_reset_ctx_by_sccp_addr(bsc_addr)) + return; + + ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance); + OSMO_ASSERT(ss7); + LOGP(DMSC, LOGL_NOTICE, "Adding new BSC connection for BSC %s...\n", osmo_sccp_addr_name(ss7, bsc_addr)); + + /* Generate and fill up a new bsc context */ + bsc_ctx = talloc_zero(gsm_network, struct bsc_context); + OSMO_ASSERT(bsc_ctx); + memcpy(&bsc_ctx->bsc_addr, bsc_addr, sizeof(*bsc_addr)); + memcpy(&bsc_ctx->msc_addr, msc_addr, sizeof(*msc_addr)); + bsc_ctx->sccp_user = scu; + llist_add_tail(&bsc_ctx->list, &gsm_network->a.bscs); + + /* Start reset procedure to make the new connection active */ + bsc_ctx->reset = a_reset_alloc(bsc_ctx, osmo_sccp_addr_name(ss7, bsc_addr), a_reset_cb, bsc_ctx); +} + +/* Callback function, called by the SSCP stack when data arrives */ +static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) +{ + struct osmo_sccp_user *scu = _scu; + struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph; + int rc = 0; + struct a_conn_info a_conn_info; + memset(&a_conn_info, 0, sizeof(a_conn_info)); + a_conn_info.network = gsm_network; + a_conn_info.reset = NULL; + + switch (OSMO_PRIM_HDR(&scu_prim->oph)) { + case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION): + /* Handle inbound connection indication */ + add_bsc(&scu_prim->u.connect.called_addr, &scu_prim->u.connect.calling_addr, scu); + a_conn_info.conn_id = scu_prim->u.connect.conn_id; + a_conn_info.msc_addr = &scu_prim->u.connect.called_addr; + a_conn_info.bsc_addr = &scu_prim->u.connect.calling_addr; + a_conn_info.reset = get_reset_ctx_by_sccp_addr(&scu_prim->u.unitdata.calling_addr); + + if (a_reset_conn_ready(a_conn_info.reset) == false) { + rc = osmo_sccp_tx_disconn(scu, a_conn_info.conn_id, a_conn_info.msc_addr, + SCCP_RETURN_CAUSE_UNQUALIFIED); + break; + } + + osmo_sccp_tx_conn_resp(scu, scu_prim->u.connect.conn_id, &scu_prim->u.connect.called_addr, NULL, 0); + if (msgb_l2len(oph->msg) > 0) { + LOGP(DMSC, LOGL_DEBUG, "N-CONNECT.ind(%u, %s)\n", + scu_prim->u.connect.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + rc = sccp_rx_dt(scu, &a_conn_info, oph->msg); + } else + LOGP(DMSC, LOGL_DEBUG, "N-CONNECT.ind(%u)\n", scu_prim->u.connect.conn_id); + + record_bsc_con(scu, scu_prim->u.connect.conn_id); + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION): + /* Handle incoming connection oriented data */ + a_conn_info.conn_id = scu_prim->u.data.conn_id; + LOGP(DMSC, LOGL_DEBUG, "N-DATA.ind(%u, %s)\n", + scu_prim->u.data.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + sccp_rx_dt(scu, &a_conn_info, oph->msg); + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): + /* Handle inbound UNITDATA */ + add_bsc(&scu_prim->u.unitdata.called_addr, &scu_prim->u.unitdata.calling_addr, scu); + a_conn_info.msc_addr = &scu_prim->u.unitdata.called_addr; + a_conn_info.bsc_addr = &scu_prim->u.unitdata.calling_addr; + a_conn_info.reset = get_reset_ctx_by_sccp_addr(&scu_prim->u.unitdata.calling_addr); + DEBUGP(DMSC, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + sccp_rx_udt(scu, &a_conn_info, oph->msg); + break; + + default: + LOGP(DMSC, LOGL_ERROR, "Unhandled SIGTRAN primitive: %u:%u\n", oph->primitive, oph->operation); + break; + } + + return rc; +} + +/* Clear all subscriber connections on a specified BSC */ +void a_clear_all(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *bsc_addr) +{ + struct gsm_subscriber_connection *conn; + struct gsm_subscriber_connection *conn_temp; + struct gsm_network *network = gsm_network; + + OSMO_ASSERT(scu); + OSMO_ASSERT(bsc_addr); + + llist_for_each_entry_safe(conn, conn_temp, &network->subscr_conns, entry) { + /* Clear only A connections and connections that actually + * belong to the specified BSC */ + if (conn->via_ran == RAN_GERAN_A && memcmp(bsc_addr, &conn->a.bsc_addr, sizeof(conn->a.bsc_addr)) == 0) { + LOGP(DMSC, LOGL_NOTICE, "Dropping orphaned subscriber connection (conn_id %i)\n", + conn->a.conn_id); + msc_clear_request(conn, GSM48_CC_CAUSE_SWITCH_CONG); + + /* If there is still an SCCP connection active, remove it now */ + if (check_connection_active(conn->a.conn_id)) { + osmo_sccp_tx_disconn(scu, conn->a.conn_id, bsc_addr, + SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); + a_delete_bsc_con(conn->a.conn_id); + } + } + } +} + +/* Initalize A interface connection between to MSC and BSC */ +int a_init(struct osmo_sccp_instance *sccp, struct gsm_network *network) +{ + OSMO_ASSERT(sccp); + OSMO_ASSERT(network); + + /* FIXME: Remove hardcoded parameters, use parameters in parameter list */ + LOGP(DMSC, LOGL_NOTICE, "Initalizing SCCP connection to stp...\n"); + + /* Set GSM network variable, there can only be + * one network by design */ + if (gsm_network != NULL) { + OSMO_ASSERT(gsm_network == network); + } else + gsm_network = network; + + /* SCCP Protocol stack */ + osmo_sccp_user_bind(sccp, "OsmoMSC-A", sccp_sap_up, SCCP_SSN_BSSAP); + + return 0; } diff --git a/src/libmsc/a_iface_bssap.c b/src/libmsc/a_iface_bssap.c new file mode 100644 index 000000000..561ccdeb0 --- /dev/null +++ b/src/libmsc/a_iface_bssap.c @@ -0,0 +1,717 @@ +/* (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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IP_V4_ADDR_LEN 4 + +/* + * Helper functions to lookup and allocate subscribers + */ + +/* Allocate a new subscriber connection */ +static struct gsm_subscriber_connection *subscr_conn_allocate_a(const struct a_conn_info *a_conn_info, + struct gsm_network *network, + uint16_t lac, struct osmo_sccp_user *scu, int conn_id) +{ + struct gsm_subscriber_connection *conn; + + LOGP(DMSC, LOGL_NOTICE, "Allocating A-Interface subscriber conn: lac %i, conn_id %i\n", lac, conn_id); + + conn = talloc_zero(network, struct gsm_subscriber_connection); + if (!conn) + return NULL; + + conn->network = network; + conn->via_ran = RAN_GERAN_A; + conn->lac = lac; + + conn->a.conn_id = conn_id; + conn->a.scu = scu; + + /* Also backup the calling address of the BSC, this allows us to + * identify later which BSC is responsible for this subscriber connection */ + memcpy(&conn->a.bsc_addr, a_conn_info->bsc_addr, sizeof(conn->a.bsc_addr)); + + llist_add_tail(&conn->entry, &network->subscr_conns); + LOGP(DMSC, LOGL_NOTICE, "A-Interface subscriber connection successfully allocated!\n"); + return conn; +} + +/* Return an existing A subscriber connection record for the given + * connection IDs, or return NULL if not found. */ +static struct gsm_subscriber_connection *subscr_conn_lookup_a(const struct gsm_network *network, int conn_id) +{ + struct gsm_subscriber_connection *conn; + + OSMO_ASSERT(network); + + DEBUGP(DMSC, "Looking for A subscriber: conn_id %i\n", conn_id); + + /* FIXME: log_subscribers() is defined in iucs.c as static inline, if + * maybe this function should be public to reach it from here? */ + /* log_subscribers(network); */ + + llist_for_each_entry(conn, &network->subscr_conns, entry) { + if (conn->via_ran == RAN_GERAN_A && conn->a.conn_id == conn_id) { + DEBUGP(DIUCS, "Found A subscriber for conn_id %i\n", conn_id); + return conn; + } + } + DEBUGP(DMSC, "No A subscriber found for conn_id %i\n", conn_id); + return NULL; +} + +/* + * BSSMAP handling for UNITDATA + */ + +/* Endpoint to handle BSSMAP reset */ +static void bssmap_rx_reset(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) +{ + struct gsm_network *network = a_conn_info->network; + struct osmo_ss7_instance *ss7; + + ss7 = osmo_ss7_instance_find(network->a.cs7_instance); + OSMO_ASSERT(ss7); + + LOGP(DMSC, LOGL_NOTICE, "Rx RESET from BSC %s, sending RESET ACK\n", + osmo_sccp_addr_name(ss7, a_conn_info->bsc_addr)); + osmo_sccp_tx_unitdata_msg(scu, a_conn_info->msc_addr, a_conn_info->bsc_addr, gsm0808_create_reset_ack()); + + /* Make sure all orphand subscriber connections will be cleard */ + a_clear_all(scu, a_conn_info->bsc_addr); + + msgb_free(msg); +} + +/* Endpoint to handle BSSMAP reset acknowlegement */ +static void bssmap_rx_reset_ack(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, + struct msgb *msg) +{ + + struct gsm_network *network = a_conn_info->network; + struct osmo_ss7_instance *ss7; + + ss7 = osmo_ss7_instance_find(network->a.cs7_instance); + OSMO_ASSERT(ss7); + + if (a_conn_info->reset == NULL) { + LOGP(DMSC, LOGL_ERROR, "Received RESET ACK from an unknown BSC %s, ignoring...\n", + osmo_sccp_addr_name(ss7, a_conn_info->bsc_addr)); + goto fail; + } + + LOGP(DMSC, LOGL_NOTICE, "Received RESET ACK from BSC %s\n", osmo_sccp_addr_name(ss7, a_conn_info->bsc_addr)); + + /* Confirm that we managed to get the reset ack message + * towards the connection reset logic */ + a_reset_ack_confirm(a_conn_info->reset); + +fail: + msgb_free(msg); +} + +/* Handle UNITDATA BSSMAP messages */ +static void bssmap_rcvmsg_udt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) +{ + /* Note: When in the MSC role, RESET ACK is the only valid message that + * can be received via UNITDATA */ + + if (msgb_l3len(msg) < 1) { + LOGP(DMSC, LOGL_NOTICE, "Error: No data received -- discarding message!\n"); + return; + } + + LOGP(DMSC, LOGL_NOTICE, "Rx BSC UDT BSSMAP %s\n", gsm0808_bssmap_name(msg->l3h[0])); + + switch (msg->l3h[0]) { + case BSS_MAP_MSG_RESET: + bssmap_rx_reset(scu, a_conn_info, msg); + break; + case BSS_MAP_MSG_RESET_ACKNOWLEDGE: + bssmap_rx_reset_ack(scu, a_conn_info, msg); + break; + default: + LOGP(DMSC, LOGL_NOTICE, "Unimplemented message format: %s -- message discarded!\n", + gsm0808_bssmap_name(msg->l3h[0])); + msgb_free(msg); + } +} + +/* Receive incoming connection less data messages via sccp */ +void sccp_rx_udt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) +{ + /* Note: The only valid message type that can be received + * via UNITDATA are BSS Management messages */ + struct bssmap_header *bs; + + OSMO_ASSERT(scu); + OSMO_ASSERT(a_conn_info); + OSMO_ASSERT(msg); + + LOGP(DMSC, LOGL_NOTICE, "Rx BSC UDT: %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); + + if (msgb_l2len(msg) < sizeof(*bs)) { + LOGP(DMSC, LOGL_ERROR, "Error: Header is too short -- discarding message!\n"); + msgb_free(msg); + return; + } + + bs = (struct bssmap_header *)msgb_l2(msg); + if (bs->length < msgb_l2len(msg) - sizeof(*bs)) { + LOGP(DMSC, LOGL_ERROR, "Error: Message is too short -- discarding message!\n"); + msgb_free(msg); + return; + } + + switch (bs->type) { + case BSSAP_MSG_BSS_MANAGEMENT: + msg->l3h = &msg->l2h[sizeof(struct bssmap_header)]; + bssmap_rcvmsg_udt(scu, a_conn_info, msg); + break; + default: + LOGP(DMSC, LOGL_ERROR, + "Error: Unimplemented message type: %s -- message discarded!\n", gsm0808_bssmap_name(bs->type)); + msgb_free(msg); + } +} + +/* + * BSSMAP handling for connection oriented data + */ + +/* Endpoint to handle BSSMAP clear request */ +static int bssmap_rx_clear_rqst(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) +{ + struct gsm_network *network = a_conn_info->network; + struct tlv_parsed tp; + int rc; + struct msgb *msg_resp; + uint8_t cause; + struct gsm_subscriber_connection *conn; + + LOGP(DMSC, LOGL_NOTICE, "BSC requested to clear connection (conn_id=%i)\n", a_conn_info->conn_id); + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); + if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE)) { + LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n"); + goto fail; + } + cause = TLVP_VAL(&tp, GSM0808_IE_CAUSE)[0]; + + /* Respond with clear command */ + msg_resp = gsm0808_create_clear_command(GSM0808_CAUSE_CALL_CONTROL); + rc = osmo_sccp_tx_data_msg(scu, a_conn_info->conn_id, msg_resp); + + /* If possible, inform the MSC about the clear request */ + conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); + if (!conn) + goto fail; + msc_clear_request(conn, cause); + + msgb_free(msg); + return rc; + +fail: + msgb_free(msg); + return -EINVAL; +} + +/* Endpoint to handle BSSMAP clear complete */ +static int bssmap_rx_clear_complete(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) +{ + int rc; + + LOGP(DMSC, LOGL_NOTICE, "Releasing connection (conn_id=%i)\n", a_conn_info->conn_id); + rc = osmo_sccp_tx_disconn(scu, a_conn_info->conn_id, + a_conn_info->msc_addr, SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); + + /* Remove the record from the list with active connections. */ + a_delete_bsc_con(a_conn_info->conn_id); + + msgb_free(msg); + return rc; +} + +/* Endpoint to handle layer 3 complete messages */ +static int bssmap_rx_l3_compl(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) +{ + struct tlv_parsed tp; + struct { + uint8_t ident; + struct gsm48_loc_area_id lai; + uint16_t ci; + } __attribute__ ((packed)) lai_ci; + uint16_t mcc; + uint16_t mnc; + uint16_t lac; + uint8_t data_length; + const uint8_t *data; + int rc; + + struct gsm_network *network = a_conn_info->network; + struct gsm_subscriber_connection *conn; + + LOGP(DMSC, LOGL_NOTICE, "BSC has completed layer 3 connection (conn_id=%i)\n", a_conn_info->conn_id); + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); + if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER)) { + LOGP(DMSC, LOGL_ERROR, "Mandatory CELL IDENTIFIER not present -- discarding message!\n"); + goto fail; + } + if (!TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_INFORMATION)) { + LOGP(DMSC, LOGL_ERROR, "Mandatory LAYER 3 INFORMATION not present -- discarding message!\n"); + goto fail; + } + + /* Parse Cell ID element */ + /* FIXME: Encapsulate this in a parser/generator function inside + * libosmocore, add support for all specified cell identification + * discriminators (see 3GPP ts 3.2.2.17 Cell Identifier) */ + data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER); + data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER); + if (sizeof(lai_ci) != data_length) { + LOGP(DMSC, LOGL_ERROR, + "Unable to parse element CELL IDENTIFIER (wrong field length) -- discarding message!\n"); + goto fail; + } + memcpy(&lai_ci, data, sizeof(lai_ci)); + if (lai_ci.ident != CELL_IDENT_WHOLE_GLOBAL) { + LOGP(DMSC, LOGL_ERROR, + "Unable to parse element CELL IDENTIFIER (wrong cell identification discriminator) -- discarding message!\n"); + goto fail; + } + if (gsm48_decode_lai(&lai_ci.lai, &mcc, &mnc, &lac) != 0) { + LOGP(DMSC, LOGL_ERROR, + "Unable to parse element CELL IDENTIFIER (lai decoding failed) -- discarding message!\n"); + goto fail; + } + + /* Parse Layer 3 Information element */ + /* FIXME: This is probably to hackish, compiler also complains "assignment discards ‘const’ qualifier..." */ + msg->l3h = TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION); + msg->tail = msg->l3h + TLVP_LEN(&tp, GSM0808_IE_LAYER_3_INFORMATION); + + /* Create new subscriber context */ + conn = subscr_conn_allocate_a(a_conn_info, network, lac, scu, a_conn_info->conn_id); + + /* Handover location update to the MSC code */ + /* msc_compl_l3() takes ownership of dtap_msg + * message buffer */ + rc = msc_compl_l3(conn, msg, 0); + if (rc == MSC_CONN_ACCEPT) { + LOGP(DMSC, LOGL_NOTICE, "User has been accepted by MSC.\n"); + return 0; + } else if (rc == MSC_CONN_REJECT) + LOGP(DMSC, LOGL_NOTICE, "User has been rejected by MSC.\n"); + else + LOGP(DMSC, LOGL_NOTICE, "User has been rejected by MSC (unknown error)\n"); + + return -EINVAL; + +fail: + msgb_free(msg); + return -EINVAL; +} + +/* Endpoint to handle BSSMAP classmark update */ +static int bssmap_rx_classmark_upd(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) +{ + struct gsm_network *network = a_conn_info->network; + struct gsm_subscriber_connection *conn; + struct tlv_parsed tp; + const uint8_t *cm2 = NULL; + const uint8_t *cm3 = NULL; + uint8_t cm2_len = 0; + uint8_t cm3_len = 0; + + conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); + if (!conn) + goto fail; + + LOGP(DMSC, LOGL_NOTICE, "BSC sends clasmark update (conn_id=%i)\n", conn->a.conn_id); + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); + if (!TLVP_PRESENT(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T2)) { + LOGP(DMSC, LOGL_ERROR, "Mandatory Classmark Information Type 2 not present -- discarding message!\n"); + goto fail; + } + + cm2 = TLVP_VAL(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T2); + cm2_len = TLVP_LEN(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T2); + + if (TLVP_PRESENT(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T3)) { + cm3 = TLVP_VAL(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T3); + cm3_len = TLVP_LEN(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T3); + } + + /* Inform MSC about the classmark change */ + msc_classmark_chg(conn, cm2, cm2_len, cm3, cm3_len); + + msgb_free(msg); + return 0; + +fail: + msgb_free(msg); + return -EINVAL; +} + +/* Endpoint to handle BSSMAP cipher mode complete */ +static int bssmap_rx_ciph_compl(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, + struct msgb *msg) +{ + /* FIXME: The field GSM0808_IE_LAYER_3_MESSAGE_CONTENTS is optional by + * means of the specification. So there can be messages without L3 info. + * In this case, the code will crash becrause msc_cipher_mode_compl() + * is not able to deal with msg = NULL and apperently + * msc_cipher_mode_compl() was never meant to be used without L3 data. + * This needs to be discussed further! */ + + struct gsm_network *network = a_conn_info->network; + struct gsm_subscriber_connection *conn; + struct tlv_parsed tp; + uint8_t alg_id = 1; + + conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); + if (!conn) + goto fail; + + LOGP(DMSC, LOGL_NOTICE, "BSC sends cipher mode complete (conn_id=%i)\n", conn->a.conn_id); + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); + + if (TLVP_PRESENT(&tp, GSM0808_IE_CHOSEN_ENCR_ALG)) { + alg_id = TLVP_VAL(&tp, GSM0808_IE_CHOSEN_ENCR_ALG)[0] - 1; + } + + if (TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS)) { + msg->l3h = TLVP_VAL(&tp, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS); + msg->tail = msg->l3h + TLVP_LEN(&tp, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS); + } else { + msgb_free(msg); + msg = NULL; + } + + /* Hand over cipher mode complete message to the MSC, + * msc_cipher_mode_compl() takes ownership for msg */ + msc_cipher_mode_compl(conn, msg, alg_id); + + return 0; +fail: + msgb_free(msg); + return -EINVAL; +} + +/* Endpoint to handle BSSMAP cipher mode reject */ +static int bssmap_rx_ciph_rej(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) +{ + struct gsm_network *network = a_conn_info->network; + struct gsm_subscriber_connection *conn; + struct tlv_parsed tp; + uint8_t cause; + + conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); + if (!conn) + goto fail; + + LOGP(DMSC, LOGL_NOTICE, "BSC sends cipher mode reject (conn_id=%i)\n", conn->a.conn_id); + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); + if (!TLVP_PRESENT(&tp, BSS_MAP_MSG_CIPHER_MODE_REJECT)) { + LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n"); + goto fail; + } + + cause = TLVP_VAL(&tp, BSS_MAP_MSG_CIPHER_MODE_REJECT)[0]; + LOGP(DMSC, LOGL_NOTICE, "Cipher mode rejection cause: %i\n", cause); + + /* FIXME: Can we do something meaningful here? e.g. report to the + * msc code somehow that the cipher mode command has failed. */ + + msgb_free(msg); + return 0; +fail: + msgb_free(msg); + return -EINVAL; +} + +/* Endpoint to handle BSSMAP assignment failure */ +static int bssmap_rx_ass_fail(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) +{ + struct gsm_network *network = a_conn_info->network; + struct gsm_subscriber_connection *conn; + struct tlv_parsed tp; + uint8_t cause; + uint8_t *rr_cause_ptr = NULL; + uint8_t rr_cause; + + conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); + if (!conn) + goto fail; + + LOGP(DMSC, LOGL_NOTICE, "BSC sends assignment failure message (conn_id=%i)\n", conn->a.conn_id); + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); + if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE)) { + LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n"); + goto fail; + } + cause = TLVP_VAL(&tp, GSM0808_IE_CAUSE)[0]; + + if (TLVP_PRESENT(&tp, GSM0808_IE_RR_CAUSE)) { + rr_cause = TLVP_VAL(&tp, GSM0808_IE_RR_CAUSE)[0]; + rr_cause_ptr = &rr_cause; + } + + /* FIXME: In AoIP, the Assignment failure will carry also an optional + * Codec List (BSS Supported) element. It has to be discussed if we + * can ignore this element. If not, The msc_assign_fail() function + * call has to change. However msc_assign_fail() does nothing in the + * end. So probably we can just leave it as it is. Even for AoIP */ + + /* Inform the MSC about the assignment failure event */ + msc_assign_fail(conn, cause, rr_cause_ptr); + + msgb_free(msg); + return 0; +fail: + msgb_free(msg); + return -EINVAL; +} + +/* Endpoint to handle sapi "n" reject */ +static int bssmap_rx_sapi_n_rej(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, + struct msgb *msg) +{ + struct gsm_network *network = a_conn_info->network; + struct gsm_subscriber_connection *conn; + struct tlv_parsed tp; + uint8_t dlci; + + conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); + if (!conn) + goto fail; + + LOGP(DMSC, LOGL_NOTICE, "BSC sends sapi \"n\" reject message (conn_id=%i)\n", conn->a.conn_id); + + /* Note: The MSC code seems not to care about the cause code, but by + * the specification it is mandatory, so we check its presence. See + * also 3GPP TS 48.008 3.2.1.34 SAPI "n" REJECT */ + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); + if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE)) { + LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n"); + goto fail; + } + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); + if (!TLVP_PRESENT(&tp, GSM0808_IE_DLCI)) { + LOGP(DMSC, LOGL_ERROR, "DLCI is missing -- discarding message!\n"); + goto fail; + } + dlci = TLVP_VAL(&tp, GSM0808_IE_DLCI)[0]; + + /* Inform the MSC about the sapi "n" reject event */ + msc_sapi_n_reject(conn, dlci); + + msgb_free(msg); + return 0; +fail: + msgb_free(msg); + return -EINVAL; +} + +/* Endpoint to handle assignment complete */ +static int bssmap_rx_ass_compl(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, + struct msgb *msg) +{ + struct gsm_network *network = a_conn_info->network; + struct gsm_subscriber_connection *conn; + struct mgcpgw_client *mgcp; + struct tlv_parsed tp; + struct sockaddr_storage rtp_addr; + struct sockaddr_in *rtp_addr_in; + int rc; + + conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); + if (!conn) + goto fail; + + mgcp = conn->network->mgcpgw.client; + OSMO_ASSERT(mgcp); + + LOGP(DMSC, LOGL_NOTICE, "BSC sends assignment complete message (conn_id=%i)\n", conn->a.conn_id); + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); + + if (!TLVP_PRESENT(&tp, GSM0808_IE_AOIP_TRASP_ADDR)) { + LOGP(DMSC, LOGL_ERROR, "AoIP transport identifier missing -- discarding message!\n"); + goto fail; + } + + /* Decode AoIP transport address element */ + rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr, TLVP_VAL(&tp, GSM0808_IE_AOIP_TRASP_ADDR), + TLVP_LEN(&tp, GSM0808_IE_AOIP_TRASP_ADDR)); + if (rc < 0) { + LOGP(DMSC, LOGL_ERROR, "Unable to decode aoip transport address.\n"); + goto fail; + } + + /* use address / port supplied with the AoIP + * transport address element */ + if (rtp_addr.ss_family == AF_INET) { + rtp_addr_in = (struct sockaddr_in *)&rtp_addr; + conn->rtp.port_subscr = osmo_ntohs(rtp_addr_in->sin_port); + /* FIXME: We also get the IP-Address of the remote (e.g. BTS) + * end with the response. Currently we just ignore that address. + * Instead we expect that our local MGCP gateway and the code + * controlling it, magically knows the IP of the remote end. */ + } else { + LOGP(DMSC, LOGL_ERROR, "Unsopported addressing scheme. (supports only IPV4)\n"); + goto fail; + } + + /* FIXME: Seems to be related to authentication or, + encryption. Is this really in the right place? */ + msc_rx_sec_mode_compl(conn); + + msgb_free(msg); + return 0; +fail: + msgb_free(msg); + return -EINVAL; +} + +/* Handle incoming connection oriented BSSMAP messages */ +static int rx_bssmap(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) +{ + if (msgb_l3len(msg) < 1) { + LOGP(DMSC, LOGL_NOTICE, "Error: No data received -- discarding message!\n"); + msgb_free(msg); + return -1; + } + + LOGP(DMSC, LOGL_NOTICE, "Rx MSC DT1 BSSMAP %s\n", gsm0808_bssmap_name(msg->l3h[0])); + + switch (msg->l3h[0]) { + case BSS_MAP_MSG_CLEAR_RQST: + return bssmap_rx_clear_rqst(scu, a_conn_info, msg); + break; + case BSS_MAP_MSG_CLEAR_COMPLETE: + return bssmap_rx_clear_complete(scu, a_conn_info, msg); + break; + case BSS_MAP_MSG_COMPLETE_LAYER_3: + return bssmap_rx_l3_compl(scu, a_conn_info, msg); + break; + case BSS_MAP_MSG_CLASSMARK_UPDATE: + return bssmap_rx_classmark_upd(scu, a_conn_info, msg); + break; + case BSS_MAP_MSG_CIPHER_MODE_COMPLETE: + return bssmap_rx_ciph_compl(scu, a_conn_info, msg); + break; + case BSS_MAP_MSG_CIPHER_MODE_REJECT: + return bssmap_rx_ciph_rej(scu, a_conn_info, msg); + break; + case BSS_MAP_MSG_ASSIGMENT_FAILURE: + return bssmap_rx_ass_fail(scu, a_conn_info, msg); + break; + case BSS_MAP_MSG_SAPI_N_REJECT: + return bssmap_rx_sapi_n_rej(scu, a_conn_info, msg); + break; + case BSS_MAP_MSG_ASSIGMENT_COMPLETE: + return bssmap_rx_ass_compl(scu, a_conn_info, msg); + break; + default: + LOGP(DMSC, LOGL_ERROR, "Unimplemented msg type: %s\n", gsm0808_bssmap_name(msg->l3h[0])); + msgb_free(msg); + return -EINVAL; + } + + return -EINVAL; +} + +/* Endpoint to handle regular BSSAP DTAP messages */ +static int rx_dtap(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) +{ + struct gsm_network *network = a_conn_info->network; + struct gsm_subscriber_connection *conn; + + conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); + if (!conn) { + msgb_free(msg); + return -EINVAL; + } + + LOGP(DMSC, LOGL_NOTICE, "BSC sends layer 3 dtap (conn_id=%i)\n", conn->a.conn_id); + + /* msc_dtap expects the dtap payload in l3h */ + msg->l3h = msg->l2h + 3; + + /* Forward dtap payload into the msc, + * msc_dtap() takes ownership for msg */ + msc_dtap(conn, conn->a.conn_id, msg); + + return 0; +} + +/* Handle incoming connection oriented messages */ +int sccp_rx_dt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) +{ + OSMO_ASSERT(scu); + OSMO_ASSERT(a_conn_info); + OSMO_ASSERT(msg); + + LOGP(DMSC, LOGL_NOTICE, "Rx BSC DT: %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); + + if (msgb_l2len(msg) < sizeof(struct bssmap_header)) { + LOGP(DMSC, LOGL_NOTICE, "The header is too short -- discarding message!\n"); + msgb_free(msg); + } + + switch (msg->l2h[0]) { + case BSSAP_MSG_BSS_MANAGEMENT: + msg->l3h = &msg->l2h[sizeof(struct bssmap_header)]; + return rx_bssmap(scu, a_conn_info, msg); + break; + case BSSAP_MSG_DTAP: + return rx_dtap(scu, a_conn_info, msg); + break; + default: + LOGP(DMSC, LOGL_ERROR, "Unimplemented BSSAP msg type: %s\n", gsm0808_bssap_name(msg->l2h[0])); + msgb_free(msg); + return -EINVAL; + } + + return -EINVAL; +} diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c index 28cba5b85..b6746a564 100644 --- a/src/libmsc/gsm_04_08.c +++ b/src/libmsc/gsm_04_08.c @@ -79,6 +79,8 @@ #include #endif +#include + #include @@ -1267,6 +1269,8 @@ static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans, struct msgb *msg; unsigned char *data; + DEBUGP(DMNCC, "transmit message %s\n", get_mncc_name(msg_type)); + #if BEFORE_MSCSPLIT /* Re-enable this log output once we can obtain this information via * A-interface, see OS#2391. */ @@ -1324,6 +1328,9 @@ void _gsm48_cc_trans_free(struct gsm_trans *trans) { gsm48_stop_cc_timer(trans); + /* Make sure call also gets released on the mgcp side */ + msc_call_release(trans); + /* send release to L4, if callref still exists */ if (trans->callref) { /* Ressource unavailable */ @@ -1549,6 +1556,12 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) setup.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&setup.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + + /* Create a copy of the bearer capability + * in the transaction struct, so we can use + * this information later */ + memcpy(&trans->bearer_cap,&setup.bearer_cap, + sizeof(trans->bearer_cap)); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { @@ -1682,6 +1695,7 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc call_conf; + int rc; gsm48_stop_cc_timer(trans); gsm48_start_cc_timer(trans, 0x310, GSM48_T310); @@ -1702,6 +1716,12 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) call_conf.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&call_conf.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + + /* Create a copy of the bearer capability + * in the transaction struct, so we can use + * this information later */ + memcpy(&trans->bearer_cap,&call_conf.bearer_cap, + sizeof(trans->bearer_cap)); } /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { @@ -1721,7 +1741,18 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); - msc_call_assignment(trans); + /* Assign call (if not done yet) */ + if (trans->assignment_done == false) { + rc = msc_call_assignment(trans); + trans->assignment_done = true; + } + else + rc = 0; + + /* don't continue, if there were problems with + * the call assignment. */ + if (rc) + return rc; return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND, &call_conf); @@ -1752,7 +1783,15 @@ static int gsm48_cc_tx_call_proc_and_assign(struct gsm_trans *trans, void *arg) if (rc) return rc; - return msc_call_assignment(trans); + /* Assign call (if not done yet) */ + if (trans->assignment_done == false) { + rc = msc_call_assignment(trans); + trans->assignment_done = true; + } + else + rc = 0; + + return rc; } static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) @@ -2398,6 +2437,12 @@ static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) modify.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + + /* Create a copy of the bearer capability + * in the transaction struct, so we can use + * this information later */ + memcpy(&trans->bearer_cap,&modify.bearer_cap, + sizeof(trans->bearer_cap)); } new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); @@ -2440,6 +2485,12 @@ static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg modify.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + + /* Create a copy of the bearer capability + * in the transaction struct, so we can use + * this information later */ + memcpy(&trans->bearer_cap,&modify.bearer_cap, + sizeof(trans->bearer_cap)); } new_cc_state(trans, GSM_CSTATE_ACTIVE); @@ -2480,6 +2531,12 @@ static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) modify.fields |= GSM48_IE_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + + /* Create a copy of the bearer capability + * in the transaction struct, so we can use + * this information later */ + memcpy(&trans->bearer_cap,&modify.bearer_cap, + sizeof(trans->bearer_cap)); } /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { @@ -2582,6 +2639,139 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) return mncc_recvmsg(trans->net, trans, MNCC_USERINFO_IND, &user); } +static void mncc_recv_rtp(struct gsm_network *net, uint32_t callref, + int cmd, uint32_t addr, uint16_t port, uint32_t payload_type, + uint32_t payload_msg_type) +{ + uint8_t data[sizeof(struct gsm_mncc)]; + struct gsm_mncc_rtp *rtp; + + memset(&data, 0, sizeof(data)); + rtp = (struct gsm_mncc_rtp *) &data[0]; + + rtp->callref = callref; + rtp->msg_type = cmd; + rtp->ip = addr; + rtp->port = port; + rtp->payload_type = payload_type; + rtp->payload_msg_type = payload_msg_type; + mncc_recvmsg(net, NULL, cmd, (struct gsm_mncc *)data); +} + +static void mncc_recv_rtp_sock(struct gsm_network *net, struct gsm_trans *trans, int cmd) +{ + int msg_type; + + /* FIXME This has to be set to some meaningful value. + * Possible options are: + * GSM_TCHF_FRAME, GSM_TCHF_FRAME_EFR, + * GSM_TCHH_FRAME, GSM_TCH_FRAME_AMR + * (0 if unknown) */ + msg_type = GSM_TCHF_FRAME; + + uint32_t addr = mgcpgw_client_remote_addr_n(net->mgcpgw.client); + uint16_t port = trans->conn->rtp.port_cn; + + /* FIXME: This has to be set to some meaningful value, + * before the MSC-Split, this value was pulled from + * lchan->abis_ip.rtp_payload */ + uint32_t payload_type = 0; + + return mncc_recv_rtp(net, trans->callref, cmd, + addr, + port, + payload_type, + msg_type); +} + +static void mncc_recv_rtp_err(struct gsm_network *net, uint32_t callref, int cmd) +{ + return mncc_recv_rtp(net, callref, cmd, 0, 0, 0, 0); +} + +static int tch_rtp_create(struct gsm_network *net, uint32_t callref) +{ + struct gsm_trans *trans; + int rc; + + /* Find callref */ + trans = trans_find_by_callref(net, callref); + if (!trans) { + LOGP(DMNCC, LOGL_ERROR, "RTP create for non-existing trans\n"); + mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE); + return -EIO; + } + log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); + if (!trans->conn) { + LOGP(DMNCC, LOGL_NOTICE, "RTP create for trans without conn\n"); + mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE); + return 0; + } + + trans->conn->mncc_rtp_bridge = 1; + + /* When we call msc_call_assignment() we will trigger, depending + * on the RAN type the call assignment on the A or Iu interface. + * msc_call_assignment() also takes care about sending the CRCX + * command to the MGCP-GW. The CRCX will return the port number, + * where the PBX (e.g. Asterisk) will send its RTP stream to. We + * have to return this port number back to the MNCC by sending + * it back with the TCH_RTP_CREATE message. To make sure that + * this message is sent AFTER the response to CRCX from the + * MGCP-GW has arrived, we need will instruct msc_call_assignment() + * to take care of this by setting trans->tch_rtp_create to true. + * This will make sure that gsm48_tch_rtp_create() (below) is + * called as soon as the local port number has become known. */ + trans->tch_rtp_create = true; + + /* Assign call (if not done yet) */ + if (trans->assignment_done == false) { + rc = msc_call_assignment(trans); + trans->assignment_done = true; + } + else + rc = 0; + + return rc; +} + +/* Trigger TCH_RTP_CREATE acknowledgement */ +int gsm48_tch_rtp_create(struct gsm_trans *trans) +{ + /* This function is called as soon as the port, on which the + * mgcp-gw expects the incoming RTP stream from the remote + * end (e.g. Asterisk) is known. */ + + struct gsm_subscriber_connection *conn = trans->conn; + struct gsm_network *network = conn->network; + + mncc_recv_rtp_sock(network, trans, MNCC_RTP_CREATE); + return 0; +} + +static int tch_rtp_connect(struct gsm_network *net, void *arg) +{ + struct gsm_trans *trans; + struct gsm_mncc_rtp *rtp = arg; + + /* Find callref */ + trans = trans_find_by_callref(net, rtp->callref); + if (!trans) { + LOGP(DMNCC, LOGL_ERROR, "RTP connect for non-existing trans\n"); + mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT); + return -EIO; + } + log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); + if (!trans->conn) { + LOGP(DMNCC, LOGL_ERROR, "RTP connect for trans without conn\n"); + mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT); + return 0; + } + + msc_call_connect(trans,rtp->port,rtp->ip); + return 0; +} + static struct downstate { uint32_t states; int type; @@ -2657,11 +2847,16 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) if (rc < 0) disconnect_bridge(net, arg, -rc); return rc; + case MNCC_RTP_CREATE: + return tch_rtp_create(net, data->callref); + case MNCC_RTP_CONNECT: + return tch_rtp_connect(net, arg); + case MNCC_RTP_FREE: + /* unused right now */ + return -EIO; + case MNCC_FRAME_DROP: case MNCC_FRAME_RECV: - case MNCC_RTP_CREATE: - case MNCC_RTP_CONNECT: - case MNCC_RTP_FREE: case GSM_TCHF_FRAME: case GSM_TCHF_FRAME_EFR: case GSM_TCHH_FRAME: @@ -3211,8 +3406,8 @@ static int msc_vlr_set_ciph_mode(void *msc_conn_ref, case RAN_GERAN_A: DEBUGP(DMM, "-> CIPHER MODE COMMAND %s\n", vlr_subscr_name(conn->vsub)); - return msc_gsm0808_tx_cipher_mode(conn, ciph, tuple->vec.kc, 8, - retrieve_imeisv); + return a_iface_tx_cipher_mode(conn, ciph, tuple->vec.kc, 8, + retrieve_imeisv); case RAN_UTRAN_IU: #ifdef BUILD_IU DEBUGP(DMM, "-> SECURITY MODE CONTROL %s\n", diff --git a/src/libmsc/gsm_subscriber.c b/src/libmsc/gsm_subscriber.c index ac6c96a88..73361a169 100644 --- a/src/libmsc/gsm_subscriber.c +++ b/src/libmsc/gsm_subscriber.c @@ -43,6 +43,7 @@ #include #include #include +#include int subscr_paging_dispatch(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param) @@ -105,7 +106,7 @@ int msc_paging_request(struct vlr_subscr *vsub) * Need to add BSC paging at some point. */ switch (vsub->cs.attached_via_ran) { case RAN_GERAN_A: - return a_page(vsub->imsi, vsub->tmsi, vsub->lac); + return a_iface_tx_paging(vsub->imsi, vsub->tmsi, vsub->lac); case RAN_UTRAN_IU: return iu_page_cs(vsub->imsi, vsub->tmsi == GSM_RESERVED_TMSI? diff --git a/src/libmsc/iucs.c b/src/libmsc/iucs.c index aeda1406a..be026c857 100644 --- a/src/libmsc/iucs.c +++ b/src/libmsc/iucs.c @@ -40,8 +40,8 @@ static struct gsm_subscriber_connection *subscr_conn_allocate_iu(struct gsm_netw { struct gsm_subscriber_connection *conn; - DEBUGP(DIUCS, "Allocating IuCS subscriber conn: lac %d, link_id %p, conn_id %" PRIx32 "\n", - lac, ue->link, ue->conn_id); + DEBUGP(DIUCS, "Allocating IuCS subscriber conn: lac %d, conn_id %" PRIx32 "\n", + lac, ue->conn_id); conn = talloc_zero(network, struct gsm_subscriber_connection); if (!conn) @@ -61,8 +61,7 @@ static int same_ue_conn(struct ue_conn_ctx *a, struct ue_conn_ctx *b) { if (a == b) return 1; - return (a->link == b->link) - && (a->conn_id == b->conn_id); + return (a->conn_id == b->conn_id); } static inline void log_subscribers(struct gsm_network *network) @@ -78,8 +77,7 @@ static inline void log_subscribers(struct gsm_network *network) case RAN_UTRAN_IU: DEBUGPC(DIUCS, " Iu"); if (conn->iu.ue_ctx) { - DEBUGPC(DIUCS, " link %p, conn_id %d", - conn->iu.ue_ctx->link, + DEBUGPC(DIUCS, " conn_id %d", conn->iu.ue_ctx->conn_id ); } @@ -101,7 +99,7 @@ static inline void log_subscribers(struct gsm_network *network) DEBUGP(DIUCS, "subscribers registered: %d\n", i); } -/* Return an existing IuCS subscriber connection record for the given link and +/* Return an existing IuCS subscriber connection record for the given * connection IDs, or return NULL if not found. */ struct gsm_subscriber_connection *subscr_conn_lookup_iu( struct gsm_network *network, @@ -109,8 +107,8 @@ struct gsm_subscriber_connection *subscr_conn_lookup_iu( { struct gsm_subscriber_connection *conn; - DEBUGP(DIUCS, "Looking for IuCS subscriber: link_id %p, conn_id %" PRIx32 "\n", - ue->link, ue->conn_id); + DEBUGP(DIUCS, "Looking for IuCS subscriber: conn_id %" PRIx32 "\n", + ue->conn_id); log_subscribers(network); llist_for_each_entry(conn, &network->subscr_conns, entry) { @@ -118,12 +116,12 @@ struct gsm_subscriber_connection *subscr_conn_lookup_iu( continue; if (!same_ue_conn(conn->iu.ue_ctx, ue)) continue; - DEBUGP(DIUCS, "Found IuCS subscriber for link_id %p, conn_id %" PRIx32 "\n", - ue->link, ue->conn_id); + DEBUGP(DIUCS, "Found IuCS subscriber for conn_id %" PRIx32 "\n", + ue->conn_id); return conn; } - DEBUGP(DIUCS, "No IuCS subscriber found for link_id %p, conn_id %" PRIx32 "\n", - ue->link, ue->conn_id); + DEBUGP(DIUCS, "No IuCS subscriber found for conn_id %" PRIx32 "\n", + ue->conn_id); return NULL; } diff --git a/src/libmsc/msc_ifaces.c b/src/libmsc/msc_ifaces.c index 56cbd49d4..7d2e8981b 100644 --- a/src/libmsc/msc_ifaces.c +++ b/src/libmsc/msc_ifaces.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "../../bscconfig.h" @@ -41,13 +42,18 @@ extern struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id, static int msc_tx(struct gsm_subscriber_connection *conn, struct msgb *msg) { + if (!conn) + return -EINVAL; + if (!msg) + return -EINVAL; + DEBUGP(DMSC, "msc_tx %u bytes to %s via %s\n", msg->len, vlr_subscr_name(conn->vsub), ran_type_name(conn->via_ran)); switch (conn->via_ran) { case RAN_GERAN_A: msg->dst = conn; - return a_tx(msg); + return a_iface_tx_dtap(msg); case RAN_UTRAN_IU: msg->dst = conn->iu.ue_ctx; @@ -72,9 +78,15 @@ int msc_tx_dtap(struct gsm_subscriber_connection *conn, /* 9.2.5 CM service accept */ int msc_gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn) { - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERV ACC"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct msgb *msg; + struct gsm48_hdr *gh; + if (!conn) + return -EINVAL; + + msg = gsm48_msgb_alloc_name("GSM 04.08 SERV ACC"); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_CM_SERV_ACC; @@ -89,6 +101,10 @@ int msc_gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, enum gsm48_reject_value value) { struct msgb *msg; + + if (!conn) + return -EINVAL; + conn->received_cm_service_request = false; msg = gsm48_create_mm_serv_rej(value); @@ -104,6 +120,9 @@ int msc_gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, int msc_tx_common_id(struct gsm_subscriber_connection *conn) { + if (!conn) + return -EINVAL; + /* Common ID is only sent over IuCS */ if (conn->via_ran != RAN_UTRAN_IU) { LOGP(DMM, LOGL_INFO, @@ -118,10 +137,10 @@ int msc_tx_common_id(struct gsm_subscriber_connection *conn) return iu_tx_common_id(conn->iu.ue_ctx, conn->vsub->imsi); } -#ifdef BUILD_IU -static void iu_rab_act_cs(struct ue_conn_ctx *uectx, uint8_t rab_id, - uint32_t rtp_ip, uint16_t rtp_port) +static int iu_rab_act_cs(struct ue_conn_ctx *uectx, uint8_t rab_id, + uint32_t rtp_ip, uint16_t rtp_port) { +#ifdef BUILD_IU struct msgb *msg; bool use_x213_nsap; uint32_t conn_id = uectx->conn_id; @@ -140,6 +159,11 @@ static void iu_rab_act_cs(struct ue_conn_ctx *uectx, uint8_t rab_id, LOGP(DIUCS, LOGL_ERROR, "Failed to send RAB Assignment:" " conn_id=%d rab_id=%d rtp=%x:%u\n", conn_id, rab_id, rtp_ip, rtp_port); + return 0; +#else + LOGP(DMSC, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu support\n"); + return -ENOTSUP; +#endif } static void mgcp_response_rab_act_cs_crcx(struct mgcp_response *r, void *priv) @@ -165,71 +189,75 @@ static void mgcp_response_rab_act_cs_crcx(struct mgcp_response *r, void *priv) goto rab_act_cs_error; } - conn->iu.mgcp_rtp_port_cn = r->audio_port; + conn->rtp.port_cn = r->audio_port; rtp_ip = mgcpgw_client_remote_addr_n(conn->network->mgcpgw.client); - iu_rab_act_cs(uectx, conn->iu.rab_id, rtp_ip, - conn->iu.mgcp_rtp_port_ue); - /* use_x213_nsap == 0 for ip.access nano3G */ + + if (trans->conn->via_ran == RAN_UTRAN_IU) { + /* Assign a voice channel via RANAP on 3G */ + if (iu_rab_act_cs(uectx, conn->iu.rab_id, rtp_ip, conn->rtp.port_subscr)) + goto rab_act_cs_error; + } else if (trans->conn->via_ran == RAN_GERAN_A) { + /* Assign a voice channel via A on 2G */ + if (a_iface_tx_assignment(trans)) + goto rab_act_cs_error; + } else + goto rab_act_cs_error; + + /* Respond back to MNCC (if requested) */ + if (trans->tch_rtp_create) { + if (gsm48_tch_rtp_create(trans)) + goto rab_act_cs_error; + } + return; rab_act_cs_error: /* FIXME abort call, invalidate conn, ... */ + LOGP(DMSC, LOGL_ERROR, "%s: failure during assignment\n", + vlr_subscr_name(trans->vsub)); return; } -static int conn_iu_rab_act_cs(struct gsm_trans *trans) +int msc_call_assignment(struct gsm_trans *trans) { - struct gsm_subscriber_connection *conn = trans->conn; - struct mgcpgw_client *mgcp = conn->network->mgcpgw.client; + struct gsm_subscriber_connection *conn; + struct mgcpgw_client *mgcp; struct msgb *msg; + uint16_t bts_base; - /* HACK. where to scope the RAB Id? At the conn / subscriber / - * ue_conn_ctx? */ - static uint8_t next_rab_id = 1; - conn->iu.rab_id = next_rab_id ++; + if (!trans) + return -EINVAL; + if (!trans->conn) + return -EINVAL; - conn->iu.mgcp_rtp_endpoint = + conn = trans->conn; + mgcp = conn->network->mgcpgw.client; + +#ifdef BUILD_IU + /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / ue_conn_ctx? */ + static uint8_t next_iu_rab_id = 1; + if (conn->via_ran == RAN_UTRAN_IU) + conn->iu.rab_id = next_iu_rab_id ++; +#endif + + conn->rtp.mgcp_rtp_endpoint = mgcpgw_client_next_endpoint(conn->network->mgcpgw.client); - /* HACK: the addresses should be known from CRCX response - * and config. */ - conn->iu.mgcp_rtp_port_ue = 4000 + 2 * conn->iu.mgcp_rtp_endpoint; + + /* This will calculate the port we assign to the BTS via AoIP + * assignment command (or rab-assignment on 3G) The BTS will send + * its RTP traffic to that port on the MGCPGW side. The MGCPGW only + * gets the endpoint ID via the CRCX. It will do the same calculation + * on his side too to get knowledge of the rtp port. */ + bts_base = mgcp->actual.bts_base; + conn->rtp.port_subscr = bts_base + 2 * conn->rtp.mgcp_rtp_endpoint; /* Establish the RTP stream first as looping back to the originator. * The MDCX will patch through to the counterpart. TODO: play a ring * tone instead. */ - msg = mgcp_msg_crcx(mgcp, conn->iu.mgcp_rtp_endpoint, trans->callref, - MGCP_CONN_LOOPBACK); + msg = mgcp_msg_crcx(mgcp, conn->rtp.mgcp_rtp_endpoint, + conn->rtp.mgcp_rtp_endpoint, MGCP_CONN_LOOPBACK); return mgcpgw_client_tx(mgcp, msg, mgcp_response_rab_act_cs_crcx, trans); } -#endif - -int msc_call_assignment(struct gsm_trans *trans) -{ - struct gsm_subscriber_connection *conn = trans->conn; - - switch (conn->via_ran) { - case RAN_GERAN_A: - LOGP(DMSC, LOGL_ERROR, - "msc_call_assignment(): A-interface BSSMAP Assignment" - " Request not yet implemented\n"); - return -ENOTSUP; - - case RAN_UTRAN_IU: -#ifdef BUILD_IU - return conn_iu_rab_act_cs(trans); -#else - LOGP(DMSC, LOGL_ERROR, - "msc_call_assignment(): cannot send RAB Activation, built without Iu support\n"); - return -ENOTSUP; -#endif - - default: - LOGP(DMSC, LOGL_ERROR, - "msc_tx(): conn->via_ran invalid (%d)\n", - conn->via_ran); - return -EINVAL; - } -} static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv); @@ -252,8 +280,8 @@ static void mgcp_bridge(struct gsm_trans *from, struct gsm_trans *to, ip = mgcpgw_client_remote_addr_str(mgcp); msg = mgcp_msg_mdcx(mgcp, - conn1->iu.mgcp_rtp_endpoint, - ip, conn2->iu.mgcp_rtp_port_cn, + conn1->rtp.mgcp_rtp_endpoint, + ip, conn2->rtp.port_cn, mode); if (mgcpgw_client_tx(mgcp, msg, mgcp_response_bridge_mdcx, from)) LOGP(DMGCP, LOGL_ERROR, @@ -302,8 +330,58 @@ static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv) } } +int msc_call_connect(struct gsm_trans *trans, uint16_t port, uint32_t ip) +{ + /* With this function we inform the MGCP-GW where (ip/port) it + * has to send its outgoing voic traffic. The receiving end will + * usually be a PBX (e.g. Asterisk). The IP-Address we tell, will + * not only be used to direct the traffic, it will also be used + * as a filter to make sure only RTP packets from the right + * remote end will reach the BSS. This is also the reason why + * inbound audio will not work until this step is performed */ + + /* NOTE: This function is used when msc_call_bridge(), is not + * applicable. This is usually the case when an external MNCC + * is in use */ + + struct gsm_subscriber_connection *conn; + struct mgcpgw_client *mgcp; + struct msgb *msg; + + if (!trans) + return -EINVAL; + if (!trans->conn) + return -EINVAL; + if (!trans->conn->network) + return -EINVAL; + if (!trans->conn->network->mgcpgw.client) + return -EINVAL; + + mgcp = trans->conn->network->mgcpgw.client; + + struct in_addr ip_addr; + ip_addr.s_addr = ntohl(ip); + + conn = trans->conn; + + msg = mgcp_msg_mdcx(mgcp, + conn->rtp.mgcp_rtp_endpoint, + inet_ntoa(ip_addr), port, MGCP_CONN_RECV_SEND); + if (mgcpgw_client_tx(mgcp, msg, NULL, trans)) + LOGP(DMGCP, LOGL_ERROR, + "Failed to send MDCX message for %s\n", + vlr_subscr_name(trans->vsub)); + + return 0; +} + int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2) { + if (!trans1) + return -EINVAL; + if (!trans2) + return -EINVAL; + /* First setup as loopback and configure the counterparts' endpoints, * so that when transmission starts the originating addresses are * already known to be valid. The mgcp callback will continue. */ @@ -314,3 +392,31 @@ int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2) return 0; } + +void msc_call_release(struct gsm_trans *trans) +{ + struct msgb *msg; + struct gsm_subscriber_connection *conn; + struct mgcpgw_client *mgcp; + + if (!trans) + return; + if (!trans->conn) + return; + if (!trans->conn->network) + return; + + conn = trans->conn; + mgcp = conn->network->mgcpgw.client; + + /* Send DLCX */ + msg = mgcp_msg_dlcx(mgcp, conn->rtp.mgcp_rtp_endpoint, + conn->rtp.mgcp_rtp_endpoint); + if (mgcpgw_client_tx(mgcp, msg, NULL, NULL)) + LOGP(DMGCP, LOGL_ERROR, + "Failed to send DLCX message for %s\n", + vlr_subscr_name(trans->vsub)); + + /* Release endpoint id */ + mgcpgw_client_release_endpoint(conn->rtp.mgcp_rtp_endpoint, mgcp); +} diff --git a/src/libmsc/msc_vty.c b/src/libmsc/msc_vty.c index 82dc7d679..01e7e8295 100644 --- a/src/libmsc/msc_vty.c +++ b/src/libmsc/msc_vty.c @@ -64,6 +64,26 @@ DEFUN(cfg_msc_no_assign_tmsi, cfg_msc_no_assign_tmsi_cmd, return CMD_SUCCESS; } +DEFUN(cfg_msc_cs7_instance_a, + cfg_msc_cs7_instance_a_cmd, + "cs7-instance-a <0-15>", + "Set SS7 to be used by the A-Interface.\n" "SS7 instance reference number\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->a.cs7_instance = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_msc_cs7_instance_iu, + cfg_msc_cs7_instance_iu_cmd, + "cs7-instance-iu <0-15>", + "Set SS7 to be used by the Iu-Interface.\n" "SS7 instance reference number\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->iu.cs7_instance = atoi(argv[0]); + return CMD_SUCCESS; +} + static int config_write_msc(struct vty *vty) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); @@ -72,6 +92,11 @@ static int config_write_msc(struct vty *vty) vty_out(vty, " %sassign-tmsi%s", gsmnet->vlr->cfg.assign_tmsi? "" : "no ", VTY_NEWLINE); + vty_out(vty, " cs7-instance-a %u%s", gsmnet->a.cs7_instance, + VTY_NEWLINE); + vty_out(vty, " cs7-instance-iu %u%s", gsmnet->iu.cs7_instance, + VTY_NEWLINE); + mgcpgw_client_config_write(vty, " "); #ifdef BUILD_IU iu_vty_config_write(vty, " "); @@ -123,6 +148,9 @@ void msc_vty_init(struct gsm_network *msc_network) vty_install_default(MSC_NODE); install_element(MSC_NODE, &cfg_msc_assign_tmsi_cmd); install_element(MSC_NODE, &cfg_msc_no_assign_tmsi_cmd); + install_element(MSC_NODE, &cfg_msc_cs7_instance_a_cmd); + install_element(MSC_NODE, &cfg_msc_cs7_instance_iu_cmd); + mgcpgw_client_vty_init(MSC_NODE, &msc_network->mgcpgw.conf); #ifdef BUILD_IU iu_vty_init(MSC_NODE, &msc_network->iu.rab_assign_addr_enc); diff --git a/src/libmsc/osmo_msc.c b/src/libmsc/osmo_msc.c index ddc383612..866cfbd07 100644 --- a/src/libmsc/osmo_msc.c +++ b/src/libmsc/osmo_msc.c @@ -29,11 +29,12 @@ #include #include #include +#include #include /* Receive a SAPI-N-REJECT from BSC */ -static void msc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci) +void msc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci) { int sapi = dlci & 0x7; @@ -106,24 +107,24 @@ void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct ms } /* Receive an ASSIGNMENT COMPLETE from BSC */ -static void msc_assign_compl(struct gsm_subscriber_connection *conn, - uint8_t rr_cause, uint8_t chosen_channel, - uint8_t encr_alg_id, uint8_t speec) +void msc_assign_compl(struct gsm_subscriber_connection *conn, + uint8_t rr_cause, uint8_t chosen_channel, + uint8_t encr_alg_id, uint8_t speec) { LOGP(DRR, LOGL_DEBUG, "MSC assign complete (do nothing).\n"); } /* Receive an ASSIGNMENT FAILURE from BSC */ -static void msc_assign_fail(struct gsm_subscriber_connection *conn, - uint8_t cause, uint8_t *rr_cause) +void msc_assign_fail(struct gsm_subscriber_connection *conn, + uint8_t cause, uint8_t *rr_cause) { LOGP(DRR, LOGL_DEBUG, "MSC assign failure (do nothing).\n"); } /* Receive a CLASSMARK CHANGE from BSC */ -static void msc_classmark_chg(struct gsm_subscriber_connection *conn, - const uint8_t *cm2, uint8_t cm2_len, - const uint8_t *cm3, uint8_t cm3_len) +void msc_classmark_chg(struct gsm_subscriber_connection *conn, + const uint8_t *cm2, uint8_t cm2_len, + const uint8_t *cm3, uint8_t cm3_len) { if (cm2 && cm2_len) { if (cm2_len > sizeof(conn->classmark.classmark2)) { @@ -250,7 +251,7 @@ void msc_subscr_con_free(struct gsm_subscriber_connection *conn) } /* Receive a CLEAR REQUEST from BSC */ -static int msc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) +int msc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) { msc_subscr_conn_close(conn, cause); return 1; @@ -290,7 +291,7 @@ static void msc_subscr_conn_release_all(struct gsm_subscriber_connection *conn, * says "unknown UE" for each release outcome. */ break; case RAN_GERAN_A: - /* future: a_iface_tx_clear_cmd(conn); */ + a_iface_tx_clear_cmd(conn); break; default: LOGP(DMM, LOGL_ERROR, "%s: Unknown RAN type, cannot tx release/clear\n", diff --git a/src/libmsc/subscr_conn.c b/src/libmsc/subscr_conn.c index 31decc7b3..cdeeae903 100644 --- a/src/libmsc/subscr_conn.c +++ b/src/libmsc/subscr_conn.c @@ -31,6 +31,8 @@ #include #include #include +#include + #define SUBSCR_CONN_TIMEOUT 5 /* seconds */ @@ -223,7 +225,6 @@ static void subscr_conn_fsm_cleanup(struct osmo_fsm_inst *fi, if (!conn) return; - conn->conn_fsm = NULL; msc_subscr_conn_close(conn, cause); msc_subscr_conn_put(conn); diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am index ae9410c9d..5642fb2ed 100644 --- a/src/osmo-bsc/Makefile.am +++ b/src/osmo-bsc/Makefile.am @@ -14,6 +14,7 @@ AM_CFLAGS = \ $(LIBOSMOSCCP_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ + $(LIBOSMOSIGTRAN_CFLAGS) \ $(NULL) AM_LDFLAGS = \ @@ -30,7 +31,7 @@ osmo_bsc_SOURCES = \ osmo_bsc_api.c \ osmo_bsc_grace.c \ osmo_bsc_msc.c \ - osmo_bsc_sccp.c \ + osmo_bsc_sigtran.c \ osmo_bsc_filter.c \ osmo_bsc_bssap.c \ osmo_bsc_audio.c \ @@ -52,4 +53,5 @@ osmo_bsc_LDADD = \ $(LIBOSMOCTRL_LIBS) \ $(COVERAGE_LDFLAGS) \ $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOSIGTRAN_LIBS) \ $(NULL) diff --git a/src/osmo-bsc/osmo_bsc_api.c b/src/osmo-bsc/osmo_bsc_api.c index 8c33e2b57..f7343f743 100644 --- a/src/osmo-bsc/osmo_bsc_api.c +++ b/src/osmo-bsc/osmo_bsc_api.c @@ -27,6 +27,7 @@ #include #include +#include #define return_when_not_connected(conn) \ if (!conn->sccp_con) {\ @@ -45,7 +46,7 @@ LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); \ return; \ } \ - bsc_queue_for_msc(conn->sccp_con, resp); + osmo_bsc_sigtran_send(conn->sccp_con, resp); static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause); static int complete_layer3(struct gsm_subscriber_connection *conn, @@ -263,7 +264,8 @@ static int complete_layer3(struct gsm_subscriber_connection *conn, } /* allocate resource for a new connection */ - ret = bsc_create_new_connection(conn, msc, send_ping); + //ret = bsc_create_new_connection(conn, msc, send_ping); + ret = osmo_bsc_sigtran_new_conn(conn, msc); if (ret != BSC_CON_SUCCESS) { /* allocation has failed */ @@ -292,13 +294,13 @@ static int complete_layer3(struct gsm_subscriber_connection *conn, if (!resp) { LOGP(DMSC, LOGL_DEBUG, "Failed to create layer3 message.\n"); sccp_connection_free(conn->sccp_con->sccp); - bsc_delete_connection(conn->sccp_con); + osmo_bsc_sigtran_del_conn(conn->sccp_con); return BSC_API_CONN_POL_REJECT; } - if (bsc_open_connection(conn->sccp_con, resp) != 0) { + if (osmo_bsc_sigtran_open_conn(conn->sccp_con, resp) != 0) { sccp_connection_free(conn->sccp_con->sccp); - bsc_delete_connection(conn->sccp_con); + osmo_bsc_sigtran_del_conn(conn->sccp_con); msgb_free(resp); return BSC_API_CONN_POL_REJECT; } @@ -433,11 +435,28 @@ static void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_ struct msgb *resp; return_when_not_connected(conn); - LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN COMPL\n"); + if (is_ipaccess_bts(conn->bts) && conn->sccp_con->rtp_ip) { + /* NOTE: In a network that makes use of an IPA base station + * and AoIP, we have to wait until the BTS reports its RTP + * IP/Port combination back to BSC via RSL. Unfortunately, the + * IPA protocol sends its Abis assignment complete message + * before it sends its RTP IP/Port via IPACC. So we will now + * postpone the AoIP assignment completed message until we + * know the RTP IP/Port combination. */ + LOGP(DMSC, LOGL_INFO, "POSTPONE MSC ASSIGN COMPL\n"); + conn->lchan->abis_ip.ass_compl.rr_cause = rr_cause; + conn->lchan->abis_ip.ass_compl.chosen_channel = chosen_channel; + conn->lchan->abis_ip.ass_compl.encr_alg_id = encr_alg_id; + conn->lchan->abis_ip.ass_compl.speech_mode = speech_model; + conn->lchan->abis_ip.ass_compl.valid = true; - resp = gsm0808_create_assignment_completed(rr_cause, chosen_channel, - encr_alg_id, speech_model); - queue_msg_or_return(resp); + } else { + /* NOTE: Send the A assignment complete message immediately. */ + LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN COMPL\n"); + resp = gsm0808_create_assignment_completed(rr_cause, chosen_channel, + encr_alg_id, speech_model); + queue_msg_or_return(resp); + } } static void bsc_assign_fail(struct gsm_subscriber_connection *conn, @@ -474,7 +493,7 @@ static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t ca return 1; } - bsc_queue_for_msc(sccp, resp); + osmo_bsc_sigtran_send(sccp, resp); return 1; } diff --git a/src/osmo-bsc/osmo_bsc_audio.c b/src/osmo-bsc/osmo_bsc_audio.c index 116020900..b4ffa88f1 100644 --- a/src/osmo-bsc/osmo_bsc_audio.c +++ b/src/osmo-bsc/osmo_bsc_audio.c @@ -26,15 +26,57 @@ #include #include #include +#include +#include +#include #include +/* Generate and send assignment complete message */ +static int send_aoip_ass_compl(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan) +{ + struct msgb *resp; + struct sockaddr_storage rtp_addr; + struct sockaddr_in rtp_addr_in; + struct gsm0808_speech_codec sc; + + OSMO_ASSERT(lchan->abis_ip.ass_compl.valid == true); + + /* Package RTP-Address data */ + memset(&rtp_addr_in, 0, sizeof(rtp_addr_in)); + rtp_addr_in.sin_family = AF_INET; + rtp_addr_in.sin_port = htons(lchan->abis_ip.bound_port); + rtp_addr_in.sin_addr.s_addr = htonl(lchan->abis_ip.bound_ip); + memset(&rtp_addr, 0, sizeof(rtp_addr)); + memcpy(&rtp_addr, &rtp_addr_in, sizeof(rtp_addr_in)); + + /* Extrapolate speech codec from speech mode */ + gsm0808_speech_codec_from_chan_type(&sc, lchan->abis_ip.ass_compl.speech_mode); + + /* Generate message */ + resp = gsm0808_create_ass_compl(lchan->abis_ip.ass_compl.rr_cause, + lchan->abis_ip.ass_compl.chosen_channel, + lchan->abis_ip.ass_compl.encr_alg_id, + lchan->abis_ip.ass_compl.speech_mode, + &rtp_addr, + &sc, + NULL); + + if (!resp) { + LOGP(DMSC, LOGL_ERROR, "Failed to generate assignment completed message!\n"); \ + return -EINVAL; + } + + return osmo_bsc_sigtran_send(conn->sccp_con, resp); +} + static int handle_abisip_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_subscriber_connection *con; struct gsm_lchan *lchan = signal_data; int rc; + uint32_t rtp_ip; if (subsys != SS_ABISIP) return 0; @@ -49,11 +91,19 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal, * TODO: handle handover here... then the audio should go to * the old mgcp port.. */ + /* we can ask it to connect now */ LOGP(DMSC, LOGL_DEBUG, "Connecting BTS to port: %d conn: %d\n", con->sccp_con->rtp_port, lchan->abis_ip.conn_id); - rc = rsl_ipacc_mdcx(lchan, ntohl(INADDR_ANY), + /* If AoIP is in use, the rtp_ip, which has been communicated + * via the A interface as connect_ip */ + if(con->sccp_con->rtp_ip) + rtp_ip = con->sccp_con->rtp_ip; + else + rtp_ip = ntohl(INADDR_ANY); + + rc = rsl_ipacc_mdcx(lchan, rtp_ip, con->sccp_con->rtp_port, lchan->abis_ip.rtp_payload2); if (rc < 0) { @@ -61,6 +111,24 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal, return rc; } break; + + case S_ABISIP_MDCX_ACK: + if (con->ho_lchan) { + /* NOTE: When an ho_lchan exists, the MDCX is part of an + * handover operation (intra-bsc). This means we will not + * inform the MSC about the event, which means that no + * assignment complete message is transmitted */ + LOGP(DMSC, LOGL_INFO," RTP connection handover complete\n"); + } else if (is_ipaccess_bts(con->bts) && con->sccp_con->rtp_ip) { + /* NOTE: This is only relevant on AoIP networks with + * IPA based base stations. See also osmo_bsc_api.c, + * function bsc_assign_compl() */ + LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN COMPL (POSTPONED)\n"); + if (send_aoip_ass_compl(con, lchan) != 0) + return -EINVAL; + } + break; + break; } return 0; diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c index 100f66441..4353b9ad0 100644 --- a/src/osmo-bsc/osmo_bsc_bssap.c +++ b/src/osmo-bsc/osmo_bsc_bssap.c @@ -29,11 +29,21 @@ #include #include +#include +#include +#include +#include + +#define IP_V4_ADDR_LEN 4 /* * helpers for the assignment command */ -enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio) + +/* Helper function for match_codec_pref(), looks up a matching permitted speech + * value for a given msc audio codec pref */ +enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support + *audio) { if (audio->hr) { switch (audio->ver) { @@ -47,8 +57,9 @@ enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *a return GSM0808_PERM_HR3; break; default: - LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); - return GSM0808_PERM_FR1; + LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", + audio->ver); + return GSM0808_PERM_FR1; } } else { switch (audio->ver) { @@ -62,12 +73,15 @@ enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *a return GSM0808_PERM_FR3; break; default: - LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); + LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", + audio->ver); return GSM0808_PERM_HR1; } } } +/* Helper function for match_codec_pref(), looks up a matching chan mode for + * a given permitted speech value */ enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech) { switch (speech) { @@ -83,16 +97,130 @@ enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech) case GSM0808_PERM_FR3: return GSM48_CMODE_SPEECH_AMR; break; + default: + LOGP(DMSC, LOGL_FATAL, + "Unsupported permitted speech selected, assuming AMR as channel mode...\n"); + return GSM48_CMODE_SPEECH_AMR; + } +} + +/* Helper function for match_codec_pref(), tests if a given audio support + * matches one of the permitted speech settings of the channel type element. + * The matched permitted speech value is then also compared against the + * speech codec list. (optional, only relevant for AoIP) */ +static bool test_codec_pref(const struct gsm0808_channel_type *ct, + const struct gsm0808_speech_codec_list *scl, + uint8_t perm_spch) +{ + unsigned int i; + bool match = false; + struct gsm0808_speech_codec sc; + int rc; + + /* Try to finde the given permitted speech value in the + * codec list of the channel type element */ + for (i = 0; i < ct->perm_spch_len; i++) { + if (ct->perm_spch[i] == perm_spch) { + match = true; + break; + } } - LOGP(DMSC, LOGL_FATAL, "Should not be reached.\n"); - return GSM48_CMODE_SPEECH_AMR; + /* If we do not have a speech codec list to test against, + * we just exit early (will be always the case in non-AoIP networks) */ + if (!scl) + return match; + + /* If we failed to match until here, there is no + * point in testing further */ + if (match == false) + return false; + + /* Extrapolate speech codec data */ + rc = gsm0808_speech_codec_from_chan_type(&sc, perm_spch); + if (rc < 0) + return false; + + /* Try to find extrapolated speech codec data in + * the speech codec list */ + for (i = 0; i < scl->len; i++) { + if (memcmp(&sc, &scl->codec[i], sizeof(sc)) == 0) + return true; + } + + return false; +} + +/* Helper function for bssmap_handle_assignm_req(), matches the codec + * preferences from the MSC with the codec preferences */ +static int match_codec_pref(int *full_rate, enum gsm48_chan_mode *chan_mode, + const struct gsm0808_channel_type *ct, + const struct gsm0808_speech_codec_list *scl, + const struct bsc_msc_data *msc) +{ + unsigned int i; + uint8_t perm_spch; + bool match = false; + + for (i = 0; i < msc->audio_length; i++) { + perm_spch = audio_support_to_gsm88(msc->audio_support[i]); + if (test_codec_pref(ct, scl, perm_spch)) { + match = true; + break; + } + } + + /* Exit without result, in case no match can be deteched */ + if (!match) { + *full_rate = -1; + *chan_mode = GSM48_CMODE_SIGN; + return -1; + } + + /* Check if the result is a half or full rate codec */ + if (perm_spch == GSM0808_PERM_HR1 || perm_spch == GSM0808_PERM_HR2 + || perm_spch == GSM0808_PERM_HR3 || perm_spch == GSM0808_PERM_HR4 + || perm_spch == GSM0808_PERM_HR6) + *full_rate = 0; + else + *full_rate = 1; + + /* Lookup a channel mode for the selected codec */ + *chan_mode = gsm88_to_chan_mode(perm_spch); + + return 0; } static int bssmap_handle_reset_ack(struct bsc_msc_data *msc, struct msgb *msg, unsigned int length) { - LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n"); + LOGP(DMSC, LOGL_NOTICE, "RESET ACK from MSC: %s\n", + osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), + &msc->a.msc_addr)); + + /* Inform the FSM that controls the RESET/RESET-ACK procedure + * that we have successfully received the reset-ack message */ + a_reset_ack_confirm(msc->a.reset); + + return 0; +} + +/* Handle MSC sided reset */ +static int bssmap_handle_reset(struct bsc_msc_data *msc, + struct msgb *msg, unsigned int length) +{ + LOGP(DMSC, LOGL_NOTICE, "RESET from MSC: %s\n", + osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), + &msc->a.msc_addr)); + + /* Instruct the bsc to close all open sigtran connections and to + * close all active channels on the BTS side as well */ + osmo_bsc_sigtran_reset(msc); + + /* Inform the MSC that we have received the reset request and + * that we acted accordingly */ + osmo_bsc_sigtran_tx_reset_ack(msc); + return 0; } @@ -199,7 +327,7 @@ static int bssmap_handle_clear_command(struct osmo_bsc_sccp_con *conn, return -1; } - bsc_queue_for_msc(conn, resp); + osmo_bsc_sigtran_send(conn, resp); return 0; } @@ -276,7 +404,7 @@ reject: return -1; } - bsc_queue_for_msc(conn, resp); + osmo_bsc_sigtran_send(conn, resp); return -1; } @@ -291,99 +419,141 @@ static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn, struct msgb *resp; struct bsc_msc_data *msc; struct tlv_parsed tp; - uint8_t *data; - uint8_t timeslot; - uint8_t multiplex; + uint8_t timeslot = 0; + uint8_t multiplex = 0; enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN; - int i, supported, port, full_rate = -1; + int port, full_rate = -1; + bool aoip = false; + struct sockaddr_storage rtp_addr; + struct sockaddr_in *rtp_addr_in; + struct gsm0808_channel_type ct; + struct gsm0808_speech_codec_list scl; + struct gsm0808_speech_codec_list *scl_ptr = NULL; + int rc; + const uint8_t *data; + char len; if (!conn->conn) { - LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n"); + LOGP(DMSC, LOGL_ERROR, + "No lchan/msc_data in cipher mode command.\n"); return -1; } + msc = conn->msc; + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0); + /* Check for channel type element, if its missing, immediately reject */ if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) { LOGP(DMSC, LOGL_ERROR, "Mandatory channel type not present.\n"); goto reject; } - if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) { - LOGP(DMSC, LOGL_ERROR, "Identity code missing. Audio routing will not work.\n"); - goto reject; - } - - conn->cic = osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); - timeslot = conn->cic & 0x1f; - multiplex = (conn->cic & ~0x1f) >> 5; - - /* - * Currently we only support a limited subset of all - * possible channel types. The limitation ends by not using - * multi-slot, limiting the channel coding, speech... - */ - if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) { - LOGP(DMSC, LOGL_ERROR, "ChannelType len !=3 not supported: %d\n", - TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE)); - goto reject; - } - - /* - * Try to figure out if we support the proposed speech codecs. For - * now we will always pick the full rate codecs. - */ - - data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE); - if ((data[0] & 0xf) != 0x1) { - LOGP(DMSC, LOGL_ERROR, "ChannelType != speech: %d\n", data[0]); - goto reject; - } - - /* - * go through the list of preferred codecs of our gsm network - * and try to find it among the permitted codecs. If we found - * it we will send chan_mode to the right mode and break the - * inner loop. The outer loop will exit due chan_mode having - * the correct value. - */ - full_rate = 0; - msc = conn->msc; - for (supported = 0; - chan_mode == GSM48_CMODE_SIGN && supported < msc->audio_length; - ++supported) { - - int perm_val = audio_support_to_gsm88(msc->audio_support[supported]); - for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) { - if ((data[i] & 0x7f) == perm_val) { - chan_mode = gsm88_to_chan_mode(perm_val); - full_rate = (data[i] & 0x4) == 0; - break; - } else if ((data[i] & 0x80) == 0x00) { - break; - } + /* Detect if a CIC code is present, if so, we use the classic ip.access + * method to calculate the RTP port */ + if (TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) { + conn->cic = + osmo_load16be(TLVP_VAL + (&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); + timeslot = conn->cic & 0x1f; + multiplex = (conn->cic & ~0x1f) >> 5; + } else if (TLVP_PRESENT(&tp, GSM0808_IE_AOIP_TRASP_ADDR)) { + /* Decode AoIP transport address element */ + data = TLVP_VAL(&tp, GSM0808_IE_AOIP_TRASP_ADDR); + len = TLVP_LEN(&tp, GSM0808_IE_AOIP_TRASP_ADDR); + rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr, data, len); + if (rc < 0) { + LOGP(DMSC, LOGL_ERROR, + "Unable to decode aoip transport address.\n"); + goto reject; } + aoip = true; + } else { + LOGP(DMSC, LOGL_ERROR, + "transport address missing. Audio routing will not work.\n"); + goto reject; } - if (chan_mode == GSM48_CMODE_SIGN) { + /* Decode speech codec list (AoIP) */ + if (aoip) { + /* Check for speech codec list element */ + if (!TLVP_PRESENT(&tp, GSM0808_IE_SPEECH_CODEC_LIST)) { + LOGP(DMSC, LOGL_ERROR, + "Mandatory speech codec list not present.\n"); + goto reject; + } + + /* Decode Speech Codec list */ + data = TLVP_VAL(&tp, GSM0808_IE_SPEECH_CODEC_LIST); + len = TLVP_LEN(&tp, GSM0808_IE_SPEECH_CODEC_LIST); + rc = gsm0808_dec_speech_codec_list(&scl, data, len); + if (rc < 0) { + LOGP(DMSC, LOGL_ERROR, + "Unable to decode speech codec list\n"); + goto reject; + } + scl_ptr = &scl; + } + + /* Decode Channel Type element */ + data = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE); + len = TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); + rc = gsm0808_dec_channel_type(&ct, data, len); + if (rc < 0) { + LOGP(DMSC, LOGL_ERROR, "unable to decode channel type.\n"); + goto reject; + } + + /* Currently we only support a limited subset of all + * possible channel types. The limitation ends by not using + * multi-slot, limiting the channel coding to speech */ + if (ct.ch_indctr != GSM0808_CHAN_SPEECH) { + LOGP(DMSC, LOGL_ERROR, + "Unsupported channel type, currently only speech is supported!\n"); + goto reject; + } + + /* Match codec information from the assignment command against the + * local preferences of the BSC */ + rc = match_codec_pref(&full_rate, &chan_mode, &ct, scl_ptr, msc); + if (rc < 0) { LOGP(DMSC, LOGL_ERROR, "No supported audio type found.\n"); goto reject; } - /* map it to a MGCP Endpoint and a RTP port */ - port = mgcp_timeslot_to_endpoint(multiplex, timeslot); - conn->rtp_port = rtp_calculate_port(port, msc->rtp_base); + if (aoip == false) { + /* map it to a MGCP Endpoint and a RTP port */ + port = mgcp_timeslot_to_endpoint(multiplex, timeslot); + conn->rtp_port = rtp_calculate_port(port, msc->rtp_base); + conn->rtp_ip = 0; + } else { + /* use address / port supplied with the AoIP + * transport address element */ + if (rtp_addr.ss_family == AF_INET) { + rtp_addr_in = (struct sockaddr_in *)&rtp_addr; + conn->rtp_port = osmo_ntohs(rtp_addr_in->sin_port); + memcpy(&conn->rtp_ip, &rtp_addr_in->sin_addr.s_addr, + IP_V4_ADDR_LEN); + conn->rtp_ip = osmo_ntohl(conn->rtp_ip); + } else { + LOGP(DMSC, LOGL_ERROR, + "Unsopported addressing scheme. (supports only IPV4)\n"); + goto reject; + } + } return gsm0808_assign_req(conn->conn, chan_mode, full_rate); reject: - resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); + resp = + gsm0808_create_assignment_failure + (GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); if (!resp) { LOGP(DMSC, LOGL_ERROR, "Channel allocation failure.\n"); return -1; } - bsc_queue_for_msc(conn, resp); + osmo_bsc_sigtran_send(conn, resp); return -1; } @@ -404,6 +574,9 @@ static int bssmap_rcvmsg_udt(struct bsc_msc_data *msc, case BSS_MAP_MSG_RESET_ACKNOWLEDGE: ret = bssmap_handle_reset_ack(msc, msg, length); break; + case BSS_MAP_MSG_RESET: + ret = bssmap_handle_reset(msc, msg, length); + break; case BSS_MAP_MSG_PAGING: ret = bssmap_handle_paging(msc, msg, length); break; @@ -524,8 +697,8 @@ int bsc_handle_udt(struct bsc_msc_data *msc, return 0; } -int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int len) +int bsc_handle_dt(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int len) { if (len < sizeof(struct bssmap_header)) { LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c index 90651b95e..cf188a9e0 100644 --- a/src/osmo-bsc/osmo_bsc_main.c +++ b/src/osmo-bsc/osmo_bsc_main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -217,6 +218,10 @@ int main(int argc, char **argv) bsc_msg_lst_vty_init(tall_bsc_ctx, &access_lists, BSC_NODE); ctrl_vty_init(tall_bsc_ctx); + /* Initalize SS7 */ + osmo_ss7_init(); + osmo_ss7_vty_init_asp(tall_bsc_ctx); + INIT_LLIST_HEAD(&access_lists); /* parse options */ @@ -269,9 +274,8 @@ int main(int argc, char **argv) } } - - if (osmo_bsc_sccp_init(bsc_gsmnet) != 0) { - LOGP(DNM, LOGL_ERROR, "Failed to register SCCP.\n"); + if (osmo_bsc_sigtran_init(&bsc_gsmnet->bsc_data->mscs) != 0) { + LOGP(DNM, LOGL_ERROR, "Failed to initalize sigtran backhaul.\n"); exit(1); } diff --git a/src/osmo-bsc/osmo_bsc_msc.c b/src/osmo-bsc/osmo_bsc_msc.c index 8d02624b4..351fd2ced 100644 --- a/src/osmo-bsc/osmo_bsc_msc.c +++ b/src/osmo-bsc/osmo_bsc_msc.c @@ -42,7 +42,7 @@ #include #include - +#if 0 static void initialize_if_needed(struct bsc_msc_connection *conn); static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn); static void send_id_get_response(struct bsc_msc_data *data, int fd, struct msgb *inp); @@ -52,6 +52,8 @@ static void schedule_ping_pong(struct bsc_msc_data *data); /* * MGCP forwarding code */ + +#endif static int mgcp_do_read(struct osmo_fd *fd) { struct bsc_msc_data *data = (struct bsc_msc_data *) fd->data; @@ -93,6 +95,7 @@ static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg) return ret; } +#if 0 static void mgcp_forward(struct bsc_msc_data *data, struct msgb *msg) { struct msgb *mgcp; @@ -115,6 +118,7 @@ static void mgcp_forward(struct bsc_msc_data *data, struct msgb *msg) msgb_free(mgcp); } } +#endif static int mgcp_create_port(struct bsc_msc_data *data) { @@ -168,6 +172,7 @@ static int mgcp_create_port(struct bsc_msc_data *data) return 0; } + /* * Send data to the network */ @@ -183,6 +188,7 @@ int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto return 0; } +#if 0 int msc_queue_write_with_ping(struct bsc_msc_connection *conn, struct msgb *msg, int proto) { @@ -356,10 +362,10 @@ static void msc_ping_timeout_cb(void *_data) static void msc_pong_timeout_cb(void *_data) { - struct bsc_msc_data *data = (struct bsc_msc_data *) _data; +// struct bsc_msc_data *data = (struct bsc_msc_data *) _data; LOGP(DMSC, LOGL_ERROR, "MSC didn't answer PING. Closing connection.\n"); - bsc_msc_lost(data->msc_con); +// bsc_msc_lost(data->msc_con); } static void msc_connection_connected(struct bsc_msc_connection *con) @@ -368,12 +374,12 @@ static void msc_connection_connected(struct bsc_msc_connection *con) struct bsc_msc_data *data; int ret, on; on = 1; - ret = setsockopt(con->write_queue.bfd.fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); - if (ret != 0) - LOGP(DMSC, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); +// ret = setsockopt(con->write_queue.bfd.fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); +// if (ret != 0) +// LOGP(DMSC, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); - data = (struct bsc_msc_data *) con->write_queue.bfd.data; - msc_ping_timeout_cb(data); +// data = (struct bsc_msc_data *) con->write_queue.bfd.data; +// msc_ping_timeout_cb(data); sig.data = data; osmo_signal_dispatch(SS_MSC, S_MSC_CONNECTED, &sig); @@ -385,20 +391,20 @@ static void msc_connection_connected(struct bsc_msc_connection *con) */ static void msc_connection_was_lost(struct bsc_msc_connection *msc) { - struct msc_signal_data sig; - struct bsc_msc_data *data; +// struct msc_signal_data sig; +// struct bsc_msc_data *data; LOGP(DMSC, LOGL_ERROR, "Lost MSC connection. Freing stuff.\n"); - data = (struct bsc_msc_data *) msc->write_queue.bfd.data; - osmo_timer_del(&data->ping_timer); - osmo_timer_del(&data->pong_timer); - - sig.data = data; - osmo_signal_dispatch(SS_MSC, S_MSC_LOST, &sig); +// data = (struct bsc_msc_data *) msc->write_queue.bfd.data; +// osmo_timer_del(&data->ping_timer); +// osmo_timer_del(&data->pong_timer); +// +// sig.data = data; +// osmo_signal_dispatch(SS_MSC, S_MSC_LOST, &sig); msc->is_authenticated = 0; - bsc_msc_schedule_connect(msc); +// bsc_msc_schedule_connect(msc); } static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn) @@ -515,6 +521,8 @@ static void send_id_get_response(struct bsc_msc_data *data, int fd, struct msgb osmo_signal_dispatch(SS_MSC, S_MSC_AUTHENTICATED, &sig); } +#endif + int osmo_bsc_msc_init(struct bsc_msc_data *data) { if (mgcp_create_port(data) != 0) @@ -526,19 +534,24 @@ int osmo_bsc_msc_init(struct bsc_msc_data *data) return -1; } - osmo_timer_setup(&data->ping_timer, msc_ping_timeout_cb, data); - osmo_timer_setup(&data->pong_timer, msc_pong_timeout_cb, data); +// osmo_timer_setup(&data->ping_timer, msc_ping_timeout_cb, data); +// osmo_timer_setup(&data->pong_timer, msc_pong_timeout_cb, data); data->msc_con->write_queue.bfd.data = data; - data->msc_con->connection_loss = msc_connection_was_lost; - data->msc_con->connected = msc_connection_connected; - data->msc_con->write_queue.read_cb = ipaccess_a_fd_cb; - data->msc_con->write_queue.write_cb = msc_alink_do_write; - bsc_msc_connect(data->msc_con); +// data->msc_con->connection_loss = msc_connection_was_lost; +// data->msc_con->connected = msc_connection_connected; +// data->msc_con->write_queue.read_cb = ipaccess_a_fd_cb; +// data->msc_con->write_queue.write_cb = msc_alink_do_write; +// bsc_msc_connect(data->msc_con); + + data->msc_con->is_connected = 1; + data->msc_con->is_authenticated = 1; + return 0; } + struct bsc_msc_data *osmo_msc_data_find(struct gsm_network *net, int nr) { struct bsc_msc_data *msc_data; @@ -584,3 +597,4 @@ struct bsc_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr) return msc_data; } + diff --git a/src/osmo-bsc/osmo_bsc_reset.c b/src/osmo-bsc/osmo_bsc_reset.c new file mode 100644 index 000000000..0baf08089 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_reset.c @@ -0,0 +1,190 @@ +/* (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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RESET_RESEND_INTERVAL 2 /* sec */ +#define RESET_RESEND_TIMER_NO 1234 /* FIXME: dig out the real timer number */ +#define BAD_CONNECTION_THRESOLD 3 /* connection failures */ + +enum fsm_states { + ST_DISC, /* Disconnected from MSC */ + ST_CONN, /* We have a confirmed connection to the MSC */ +}; + +static const struct value_string fsm_state_names[] = { + {ST_DISC, "ST_DISC (disconnected)"}, + {ST_CONN, "ST_CONN (connected)"}, + {0, NULL}, +}; + +enum fsm_evt { + EV_RESET_ACK, /* got reset acknowlegement from the MSC */ + EV_N_DISCONNECT, /* lost a connection */ + EV_N_CONNECT, /* made a successful connection */ +}; + +static const struct value_string fsm_evt_names[] = { + {EV_RESET_ACK, "EV_RESET_ACK"}, + {EV_N_DISCONNECT, "EV_N_DISCONNECT"}, + {EV_N_CONNECT, "EV_N_CONNECT"}, + {0, NULL}, +}; + +/* Disconnected state */ +static void fsm_disc_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct bsc_msc_data *msc = (struct bsc_msc_data *)data; + + LOGP(DMSC, LOGL_NOTICE, "fsm-state (msc-reset): %s, fsm-event: %s, MSC No.: %i\n", + get_value_string(fsm_state_names, ST_DISC), get_value_string(fsm_evt_names, event), msc->nr); + msc->msc_con->msc_conn_loss_count = 0; + osmo_fsm_inst_state_chg(fi, ST_CONN, 0, 0); +} + +/* Connected state */ +static void fsm_conn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct bsc_msc_data *msc = (struct bsc_msc_data *)data; + + LOGP(DMSC, LOGL_NOTICE, "fsm-state (msc-reset): %s, fsm-event: %s, MSC No.: %i\n", + get_value_string(fsm_state_names, ST_CONN), get_value_string(fsm_evt_names, event), msc->nr); + + OSMO_ASSERT(msc); + + switch (event) { + case EV_N_DISCONNECT: + if (msc->msc_con->msc_conn_loss_count >= BAD_CONNECTION_THRESOLD) { + LOGP(DMSC, LOGL_NOTICE, "SIGTRAN connection to MSC No.: %i down, reconnecting...\n", msc->nr); + osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); + } else + msc->msc_con->msc_conn_loss_count++; + break; + case EV_N_CONNECT: + msc->msc_con->msc_conn_loss_count = 0; + break; + } +} + +/* Timer callback to retransmit the reset signal */ +static int fsm_reset_ack_timeout_cb(struct osmo_fsm_inst *fi) +{ + struct bsc_msc_data *msc = (struct bsc_msc_data *)fi->priv; + + LOGP(DMSC, LOGL_NOTICE, "reset-ack timeout (T%i) in state %s, MSC No.: %i, resending...\n", fi->T, + get_value_string(fsm_state_names, fi->state), msc->nr); + + osmo_bsc_sigtran_reset(msc); + osmo_bsc_sigtran_tx_reset(msc); + + osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); + return 0; +} + +static struct osmo_fsm_state fsm_states[] = { + [ST_DISC] = { + .in_event_mask = (1 << EV_RESET_ACK), + .out_state_mask = (1 << ST_DISC) | (1 << ST_CONN), + .name = "DISC", + .action = fsm_disc_cb, + }, + [ST_CONN] = { + .in_event_mask = (1 << EV_N_DISCONNECT) | (1 << EV_N_CONNECT), + .out_state_mask = (1 << ST_DISC) | (1 << ST_CONN), + .name = "CONN", + .action = fsm_conn_cb, + }, +}; + +/* State machine definition */ +static struct osmo_fsm fsm = { + .name = "FSM RESET", + .states = fsm_states, + .num_states = ARRAY_SIZE(fsm_states), + .log_subsys = DMSC, + .timer_cb = fsm_reset_ack_timeout_cb, +}; + +/* Create and start state machine which handles the reset/reset-ack procedure */ +void start_reset_fsm(struct bsc_msc_data *msc) +{ + OSMO_ASSERT(msc); + OSMO_ASSERT(msc->msc_con); + + osmo_fsm_register(&fsm); + msc->msc_con->fsm_reset = osmo_fsm_inst_alloc(&fsm, NULL, NULL, LOGL_DEBUG, "FSM RESET INST"); + OSMO_ASSERT(msc->msc_con->fsm_reset); + + msc->msc_con->fsm_reset->priv = msc; + + /* kick off reset-ack sending mechanism */ + osmo_fsm_inst_state_chg(msc->msc_con->fsm_reset, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); +} + +/* Confirm that we sucessfully received a reset acknowlege message */ +void reset_ack_confirm(struct bsc_msc_data *msc) +{ + OSMO_ASSERT(msc); + OSMO_ASSERT(msc->msc_con); + OSMO_ASSERT(msc->msc_con->fsm_reset); + + osmo_fsm_inst_dispatch(msc->msc_con->fsm_reset, EV_RESET_ACK, msc); +} + +/* Report a failed connection */ +void report_conn_fail(struct bsc_msc_data *msc) +{ + OSMO_ASSERT(msc); + OSMO_ASSERT(msc->msc_con); + OSMO_ASSERT(msc->msc_con->fsm_reset); + + osmo_fsm_inst_dispatch(msc->msc_con->fsm_reset, EV_N_DISCONNECT, msc); +} + +/* Report a successful connection */ +void report_conn_success(struct bsc_msc_data *msc) +{ + OSMO_ASSERT(msc); + OSMO_ASSERT(msc->msc_con); + OSMO_ASSERT(msc->msc_con->fsm_reset); + + osmo_fsm_inst_dispatch(msc->msc_con->fsm_reset, EV_N_CONNECT, msc); +} + +/* Check if we have a connection to a specified msc */ +bool sccp_conn_ready(struct bsc_msc_data *msc) +{ + OSMO_ASSERT(msc); + OSMO_ASSERT(msc->msc_con); + OSMO_ASSERT(msc->msc_con->fsm_reset); + if (msc->msc_con->fsm_reset->state == ST_CONN) + return true; + + return false; +} diff --git a/src/osmo-bsc/osmo_bsc_sccp.c b/src/osmo-bsc/osmo_bsc_sccp.c deleted file mode 100644 index e242390ef..000000000 --- a/src/osmo-bsc/osmo_bsc_sccp.c +++ /dev/null @@ -1,328 +0,0 @@ -/* Interaction with the SCCP subsystem */ -/* - * (C) 2009-2014 by Holger Hans Peter Freyther - * (C) 2009-2014 by On-Waves - * 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 . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -/* SCCP helper */ -#define SCCP_IT_TIMER 60 - -static LLIST_HEAD(active_connections); - -static void free_queued(struct osmo_bsc_sccp_con *conn) -{ - struct msgb *msg; - - while (!llist_empty(&conn->sccp_queue)) { - /* this is not allowed to fail */ - msg = msgb_dequeue(&conn->sccp_queue); - msgb_free(msg); - } - - conn->sccp_queue_size = 0; -} - -static void send_queued(struct osmo_bsc_sccp_con *conn) -{ - struct msgb *msg; - - while (!llist_empty(&conn->sccp_queue)) { - /* this is not allowed to fail */ - msg = msgb_dequeue(&conn->sccp_queue); - sccp_connection_write(conn->sccp, msg); - msgb_free(msg); - conn->sccp_queue_size -= 1; - } -} - -static void msc_outgoing_sccp_data(struct sccp_connection *conn, - struct msgb *msg, unsigned int len) -{ - struct osmo_bsc_sccp_con *bsc_con = - (struct osmo_bsc_sccp_con *) conn->data_ctx; - - bsc_handle_dt1(bsc_con, msg, len); -} - -static void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state) -{ - struct osmo_bsc_sccp_con *con_data; - - if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) { - con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx; - if (con_data->conn) { - LOGP(DMSC, LOGL_ERROR, - "ERROR: The lchan is still associated.\n"); - gsm0808_clear(con_data->conn); - bsc_subscr_con_free(con_data->conn); - con_data->conn = NULL; - } - - con_data->sccp = NULL; - free_queued(con_data); - sccp_connection_free(conn); - bsc_delete_connection(con_data); - } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) { - LOGP(DMSC, LOGL_DEBUG, "Connection established: %p\n", conn); - con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx; - - osmo_timer_del(&con_data->sccp_cc_timeout); - osmo_timer_schedule(&con_data->sccp_it_timeout, SCCP_IT_TIMER, 0); - - send_queued(con_data); - } -} - -static void bsc_sccp_force_free(struct osmo_bsc_sccp_con *data) -{ - if (data->conn) { - gsm0808_clear(data->conn); - bsc_subscr_con_free(data->conn); - data->conn = NULL; - } - - free_queued(data); - sccp_connection_force_free(data->sccp); - data->sccp = NULL; - bsc_delete_connection(data); -} - -static void sccp_it_timeout(void *_data) -{ - struct osmo_bsc_sccp_con *data = - (struct osmo_bsc_sccp_con *) _data; - - sccp_connection_send_it(data->sccp); - osmo_timer_schedule(&data->sccp_it_timeout, SCCP_IT_TIMER, 0); -} - -static void sccp_cc_timeout(void *_data) -{ - struct osmo_bsc_sccp_con *data = - (struct osmo_bsc_sccp_con *) _data; - - if (data->sccp->connection_state >= SCCP_CONNECTION_STATE_ESTABLISHED) - return; - - LOGP(DMSC, LOGL_ERROR, "The connection was never established.\n"); - bsc_sccp_force_free(data); -} - -static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg, - void *global_ctx, void *ctx) -{ - struct bsc_msc_connection *msc_con; - - if (conn) { - struct osmo_bsc_sccp_con *bsc_con = conn->data_ctx; - msc_con = bsc_con->msc->msc_con; - if (bsc_con->send_ping) { - bsc_con->send_ping = 0; - msc_queue_write_with_ping(msc_con, msg, IPAC_PROTO_SCCP); - return; - } - } else { - msc_con = ctx; - } - - msc_queue_write(msc_con, msg, IPAC_PROTO_SCCP); -} - -static int msc_sccp_accept(struct sccp_connection *connection, void *data) -{ - LOGP(DMSC, LOGL_DEBUG, "Rejecting incoming SCCP connection.\n"); - return -1; -} - -static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data) -{ - struct bsc_msc_data *msc = (struct bsc_msc_data *) msgb->cb[0]; - return bsc_handle_udt(msc, msgb, length); -} - -int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg) -{ - struct sccp_connection *sccp = conn->sccp; - - if (sccp->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { - LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp); - msgb_free(msg); - } else if (sccp->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED - && conn->sccp_queue_size == 0) { - sccp_connection_write(sccp, msg); - msgb_free(msg); - } else if (conn->sccp_queue_size > 10) { - LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp); - msgb_free(msg); - } else { - LOGP(DMSC, LOGL_DEBUG, "Queueing packet on %p. Queue size: %d\n", sccp, conn->sccp_queue_size); - conn->sccp_queue_size += 1; - msgb_enqueue(&conn->sccp_queue, msg); - } - - return 0; -} - -enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn, - struct bsc_msc_data *msc, int send_ping) -{ - struct osmo_bsc_sccp_con *bsc_con; - struct sccp_connection *sccp; - - /* This should not trigger */ - if (!msc || !msc->msc_con->is_authenticated) { - LOGP(DMSC, LOGL_ERROR, - "How did this happen? MSC is not connected. Dropping.\n"); - return BSC_CON_REJECT_NO_LINK; - } - - if (!bsc_grace_allow_new_connection(conn->bts->network, conn->bts)) { - LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n"); - return BSC_CON_REJECT_RF_GRACE; - } - - sccp = sccp_connection_socket(); - if (!sccp) { - LOGP(DMSC, LOGL_ERROR, "Failed to allocate memory.\n"); - return BSC_CON_NO_MEM; - } - - bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con); - if (!bsc_con) { - LOGP(DMSC, LOGL_ERROR, "Failed to allocate.\n"); - sccp_connection_free(sccp); - return BSC_CON_NO_MEM; - } - - /* callbacks */ - sccp->state_cb = msc_outgoing_sccp_state; - sccp->data_cb = msc_outgoing_sccp_data; - sccp->data_ctx = bsc_con; - - bsc_con->send_ping = send_ping; - - /* prepare the timers */ - osmo_timer_setup(&bsc_con->sccp_it_timeout, sccp_it_timeout, bsc_con); - osmo_timer_setup(&bsc_con->sccp_cc_timeout, sccp_cc_timeout, bsc_con); - - INIT_LLIST_HEAD(&bsc_con->sccp_queue); - - bsc_con->sccp = sccp; - bsc_con->msc = msc; - bsc_con->conn = conn; - llist_add_tail(&bsc_con->entry, &active_connections); - conn->sccp_con = bsc_con; - return BSC_CON_SUCCESS; -} - -int bsc_open_connection(struct osmo_bsc_sccp_con *conn, struct msgb *msg) -{ - osmo_timer_schedule(&conn->sccp_cc_timeout, 10, 0); - sccp_connection_connect(conn->sccp, &sccp_ssn_bssap, msg); - msgb_free(msg); - return 0; -} - -int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp) -{ - if (!sccp) - return 0; - - if (sccp->conn) - LOGP(DMSC, LOGL_ERROR, "Should have been cleared.\n"); - - llist_del(&sccp->entry); - osmo_timer_del(&sccp->sccp_it_timeout); - osmo_timer_del(&sccp->sccp_cc_timeout); - talloc_free(sccp); - return 0; -} - -static void bsc_notify_msc_lost(struct osmo_bsc_sccp_con *con) -{ - struct gsm_subscriber_connection *conn = con->conn; - - /* send USSD notification if string configured and con->data is set */ - if (!conn) - return; - - /* check for config string */ - if (!con->msc->ussd_msc_lost_txt) - return; - if (con->msc->ussd_msc_lost_txt[0] == '\0') - return; - - /* send USSD notification */ - bsc_send_ussd_notify(conn, 1, conn->sccp_con->msc->ussd_msc_lost_txt); - bsc_send_ussd_release_complete(conn); -} - -static void bsc_notify_and_close_conns(struct bsc_msc_connection *msc_con) -{ - struct osmo_bsc_sccp_con *con, *tmp; - - llist_for_each_entry_safe(con, tmp, &active_connections, entry) { - if (con->msc->msc_con != msc_con) - continue; - - bsc_notify_msc_lost(con); - bsc_sccp_force_free(con); - } -} - -static int handle_msc_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct msc_signal_data *msc; - - if (subsys != SS_MSC) - return 0; - - msc = signal_data; - if (signal == S_MSC_LOST) - bsc_notify_and_close_conns(msc->data->msc_con); - - return 0; -} - -int osmo_bsc_sccp_init(struct gsm_network *gsmnet) -{ - sccp_set_log_area(DSCCP); - sccp_system_init(msc_sccp_write_ipa, gsmnet); - sccp_connection_set_incoming(&sccp_ssn_bssap, msc_sccp_accept, NULL); - sccp_set_read(&sccp_ssn_bssap, msc_sccp_read, gsmnet); - - osmo_signal_register_handler(SS_MSC, handle_msc_signal, gsmnet); - - return 0; -} diff --git a/src/osmo-bsc/osmo_bsc_sigtran.c b/src/osmo-bsc/osmo_bsc_sigtran.c new file mode 100644 index 000000000..0f6ca334f --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_sigtran.c @@ -0,0 +1,561 @@ +/* (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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* A pointer to a list with all involved MSCs + * (a copy of the pointer location submitted with osmo_bsc_sigtran_init() */ +static struct llist_head *msc_list; + +#define RESET_INTERVAL 1 /* sek */ +#define SCCP_MSG_MAXSIZE 1024 +#define CS7_POINTCODE_DEFAULT_OFFSET 2 + +/* Internal list with connections we currently maintain. This + * list is of type struct osmo_bsc_sccp_con */ +static LLIST_HEAD(active_connections); + +/* The SCCP stack will not assign connection IDs to us automatically, we + * will do this ourselves using a counter variable, that counts one up + * for every new connection */ +static uint32_t conn_id_counter; + +/* Helper function to Check if the given connection id is already assigned */ +static struct osmo_bsc_sccp_con *get_bsc_conn_by_conn_id(int conn_id) +{ + conn_id &= 0xFFFFFF; + struct osmo_bsc_sccp_con *bsc_con; + + llist_for_each_entry(bsc_con, &active_connections, entry) { + if (bsc_con->conn_id == conn_id) + return bsc_con; + } + + return NULL; +} + +/* Pick a free connection id */ +static int pick_free_conn_id(const struct bsc_msc_data *msc) +{ + int conn_id = conn_id_counter; + int i; + + for (i = 0; i < 0xFFFFFF; i++) { + conn_id++; + conn_id &= 0xFFFFFF; + if (get_bsc_conn_by_conn_id(conn_id) == false) { + conn_id_counter = conn_id; + return conn_id; + } + } + + return -1; +} + +/* Send reset to MSC */ +static void osmo_bsc_sigtran_tx_reset(const struct bsc_msc_data *msc) +{ + struct osmo_ss7_instance *ss7; + struct msgb *msg; + + ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); + OSMO_ASSERT(ss7); + LOGP(DMSC, LOGL_NOTICE, "Sending RESET to MSC: %s\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); + msg = gsm0808_create_reset(); + osmo_sccp_tx_unitdata_msg(msc->a.sccp_user, &msc->a.bsc_addr, + &msc->a.msc_addr, msg); +} + +/* Send reset-ack to MSC */ +void osmo_bsc_sigtran_tx_reset_ack(const struct bsc_msc_data *msc) +{ + struct osmo_ss7_instance *ss7; + struct msgb *msg; + OSMO_ASSERT(msc); + + ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); + OSMO_ASSERT(ss7); + LOGP(DMSC, LOGL_NOTICE, "Sending RESET ACK to MSC: %s\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); + msg = gsm0808_create_reset_ack(); + osmo_sccp_tx_unitdata_msg(msc->a.sccp_user, &msc->a.bsc_addr, + &msc->a.msc_addr, msg); +} + +/* Find an MSC by its sigtran point code */ +static struct bsc_msc_data *get_msc_by_addr(const struct osmo_sccp_addr *msc_addr) +{ + struct osmo_ss7_instance *ss7; + struct bsc_msc_data *msc; + llist_for_each_entry(msc, msc_list, entry) { + if (memcmp(msc_addr, &msc->a.msc_addr, sizeof(*msc_addr)) == 0) + return msc; + } + + ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); + OSMO_ASSERT(ss7); + LOGP(DMSC, LOGL_ERROR, "Unable to find MSC data under address: %s\n", osmo_sccp_addr_name(ss7, msc_addr)); + return NULL; +} + +/* Send data to MSC, use the connection id which MSC it is */ +static int handle_data_from_msc(int conn_id, struct msgb *msg) +{ + struct osmo_bsc_sccp_con *bsc_con = get_bsc_conn_by_conn_id(conn_id); + int rc = -EINVAL; + + if (bsc_con) { + msg->l3h = msgb_l2(msg); + rc = bsc_handle_dt(bsc_con, msg, msgb_l2len(msg)); + } else + LOGP(DMSC, LOGL_NOTICE, "incoming data from unknown connection id: %i\n", conn_id); + + return rc; +} + +/* Sent unitdata to MSC, use the point code to determine which MSC it is */ +static int handle_unitdata_from_msc(const struct osmo_sccp_addr *msc_addr, struct msgb *msg, + const struct osmo_sccp_user *scu) +{ + struct osmo_ss7_instance *ss7; + struct bsc_msc_data *msc = get_msc_by_addr(msc_addr); + int rc = -EINVAL; + + if (msc) { + msg->l3h = msgb_l2(msg); + rc = bsc_handle_udt(msc, msg, msgb_l2len(msg)); + } else { + ss7 = osmo_sccp_get_ss7(osmo_sccp_get_sccp(scu)); + OSMO_ASSERT(ss7); + LOGP(DMSC, LOGL_NOTICE, "incoming unitdata data from unknown remote address: %s\n", + osmo_sccp_addr_name(ss7, msc_addr)); + } + return rc; +} + +/* Callback function, called by the SSCP stack when data arrives */ +static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) +{ + struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph; + struct osmo_sccp_user *scu = _scu; + struct osmo_bsc_sccp_con *bsc_con; + int rc = 0; + + switch (OSMO_PRIM_HDR(&scu_prim->oph)) { + case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): + /* Handle inbound UNITDATA */ + DEBUGP(DMSC, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + rc = handle_unitdata_from_msc(&scu_prim->u.unitdata.calling_addr, oph->msg, scu); + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION): + /* Handle (Reject) inbound connections */ + DEBUGP(DMSC, "N-CONNECT.ind(X->%u)\n", scu_prim->u.connect.conn_id); + LOGP(DMSC, LOGL_DEBUG, "Rejecting inbound SCCP connection...\n"); + rc = osmo_sccp_tx_disconn(scu, scu_prim->u.connect.conn_id, &scu_prim->u.connect.called_addr, 0); + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM): + /* Handle outbound connection confirmation */ + if (msgb_l2len(oph->msg) > 0) { + DEBUGP(DMSC, "N-CONNECT.cnf(%u, %s)\n", scu_prim->u.connect.conn_id, + osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + rc = handle_data_from_msc(scu_prim->u.connect.conn_id, oph->msg); + } else + DEBUGP(DRANAP, "N-CONNECT.cnf(%u)\n", scu_prim->u.connect.conn_id); + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION): + /* Handle incoming connection oriented data */ + DEBUGP(DMSC, "N-DATA.ind(%u, %s)\n", scu_prim->u.data.conn_id, + osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + + /* Incoming data is a sign of a vital connection */ + bsc_con = get_bsc_conn_by_conn_id(scu_prim->u.disconnect.conn_id); + if (bsc_con) + a_reset_conn_success(bsc_con->msc->a.reset); + + rc = handle_data_from_msc(scu_prim->u.data.conn_id, oph->msg); + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION): + /* indication of disconnect */ + if (msgb_l2len(oph->msg) > 0) { + DEBUGP(DMSC, "N-DISCONNECT.ind(%u, %s, cause=%i)\n", scu_prim->u.disconnect.conn_id, + osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)), scu_prim->u.disconnect.cause); + handle_data_from_msc(scu_prim->u.disconnect.conn_id, oph->msg); + } else + DEBUGP(DRANAP, "N-DISCONNECT.ind(%u, cause=%i)\n", scu_prim->u.disconnect.conn_id, + scu_prim->u.disconnect.cause); + + bsc_con = get_bsc_conn_by_conn_id(scu_prim->u.disconnect.conn_id); + if (bsc_con) { + /* We might have a connectivity problem. Maybe we need to go + * through the reset procedure again? */ + if (scu_prim->u.disconnect.cause == 0) + a_reset_conn_fail(bsc_con->msc->a.reset); + + rc = osmo_bsc_sigtran_del_conn(bsc_con); + } + break; + + default: + LOGP(DMSC, LOGL_ERROR, "Unhandled SIGTRAN primitive: %u:%u\n", oph->primitive, oph->operation); + break; + } + + msgb_free(oph->msg); + return rc; +} + +/* Allocate resources to make a new connection oriented sigtran connection + * (not the connection ittself!) */ +enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, struct bsc_msc_data *msc) +{ + struct osmo_ss7_instance *ss7; + struct osmo_bsc_sccp_con *bsc_con; + int conn_id; + + OSMO_ASSERT(conn); + OSMO_ASSERT(msc); + + ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); + OSMO_ASSERT(ss7); + LOGP(DMSC, LOGL_NOTICE, "Initializing resources for new SIGTRAN connection to MSC: %s...\n", + osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); + + if (a_reset_conn_ready(msc->a.reset) == false) { + LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n"); + return BSC_CON_REJECT_NO_LINK; + } + + if (!bsc_grace_allow_new_connection(conn->bts->network, conn->bts)) { + LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n"); + return BSC_CON_REJECT_RF_GRACE; + } + + bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con); + if (!bsc_con) { + LOGP(DMSC, LOGL_ERROR, "Failed to allocate new SIGTRAN connection.\n"); + return BSC_CON_NO_MEM; + } + + bsc_con->msc = msc; + bsc_con->conn = conn; + llist_add_tail(&bsc_con->entry, &active_connections); + conn->sccp_con = bsc_con; + + /* Pick a free connection id */ + conn_id = pick_free_conn_id(msc); + if (conn_id < 0) + return BSC_CON_REJECT_NO_LINK; + bsc_con->conn_id = conn_id; + + LOGP(DMSC, LOGL_NOTICE, "Allocated new connection id: %i\n", conn_id); + + return BSC_CON_SUCCESS; +} + +/* Open a new connection oriented sigtran connection */ +int osmo_bsc_sigtran_open_conn(const struct osmo_bsc_sccp_con *conn, struct msgb *msg) +{ + struct osmo_ss7_instance *ss7; + struct bsc_msc_data *msc; + int conn_id; + int rc; + + OSMO_ASSERT(conn); + OSMO_ASSERT(msg); + OSMO_ASSERT(conn->msc); + + msc = conn->msc; + + if (a_reset_conn_ready(msc->a.reset) == false) { + LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n"); + return -EINVAL; + } + + conn_id = conn->conn_id; + ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); + OSMO_ASSERT(ss7); + LOGP(DMSC, LOGL_NOTICE, "Opening new SIGTRAN connection (id=%i) to MSC: %s\n", conn_id, + osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); + + rc = osmo_sccp_tx_conn_req_msg(msc->a.sccp_user, conn_id, &msc->a.bsc_addr, + &msc->a.msc_addr, msg); + + return rc; +} + +/* Send data to MSC */ +int osmo_bsc_sigtran_send(const struct osmo_bsc_sccp_con *conn, struct msgb *msg) +{ + struct osmo_ss7_instance *ss7; + int conn_id; + int rc; + struct bsc_msc_data *msc; + + OSMO_ASSERT(conn); + OSMO_ASSERT(msg); + OSMO_ASSERT(conn->msc); + + msc = conn->msc; + + if (a_reset_conn_ready(msc->a.reset) == false) { + LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n"); + return -EINVAL; + } + + conn_id = conn->conn_id; + + ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); + OSMO_ASSERT(ss7); + LOGP(DMSC, LOGL_DEBUG, "Sending connection (id=%i) oriented data to MSC: %si\n", + conn_id, osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); + + rc = osmo_sccp_tx_data_msg(msc->a.sccp_user, conn_id, msg); + + return rc; +} + +/* Delete a connection from the list with open connections + * (called by osmo_bsc_api.c on failing open connections and + * locally, when a connection is closed by the MSC */ +int osmo_bsc_sigtran_del_conn(struct osmo_bsc_sccp_con *conn) +{ + if (!conn) + return 0; + + if (conn->conn) { + LOGP(DMSC, LOGL_ERROR, + "sccp connection (id=%i) not cleared (gsm subscriber connection still active) -- forcefully clearing it now!\n", + conn->conn_id); + bsc_subscr_con_free(conn->conn); + conn->conn = NULL; + + /* This bahaviour might be caused by a bad connection. Maybe we + * will have to go through the reset procedure again */ + a_reset_conn_fail(conn->msc->a.reset); + } + + llist_del(&conn->entry); + talloc_free(conn); + + return 0; +} + +/* Send an USSD notification in case we loose the connection to the MSC */ +static void bsc_notify_msc_lost(const struct osmo_bsc_sccp_con *conn) +{ + struct gsm_subscriber_connection *subscr_conn; + + /* Check if sccp conn is still present */ + if (!conn) + return; + subscr_conn = conn->conn; + + /* send USSD notification if string configured and conn->data is set */ + if (!subscr_conn) + return; + + /* check for config string */ + if (!conn->msc->ussd_msc_lost_txt) + return; + if (conn->msc->ussd_msc_lost_txt[0] == '\0') + return; + + /* send USSD notification */ + bsc_send_ussd_notify(subscr_conn, 1, subscr_conn->sccp_con->msc->ussd_msc_lost_txt); + bsc_send_ussd_release_complete(subscr_conn); +} + +/* Close all open sigtran connections and channels */ +void osmo_bsc_sigtran_reset(const struct bsc_msc_data *msc) +{ + struct osmo_bsc_sccp_con *conn; + struct osmo_bsc_sccp_con *conn_temp; + OSMO_ASSERT(msc); + + /* Close all open connections */ + llist_for_each_entry_safe(conn, conn_temp, &active_connections, entry) { + + /* We only may close connections which actually belong to this + * MSC. All other open connections are left untouched */ + if (conn->msc == msc) { + /* Notify active connection users via USSD that the MSC is down */ + bsc_notify_msc_lost(conn); + + /* Take down all occopied RF channels */ + if (conn->conn) + gsm0808_clear(conn->conn); + + /* Disconnect all Sigtran connections */ + osmo_sccp_tx_disconn(msc->a.sccp_user, conn->conn_id, &msc->a.bsc_addr, 0); + + /* Delete subscriber connection */ + osmo_bsc_sigtran_del_conn(conn); + } + } +} + +/* Callback function: Close all open connections */ +static void osmo_bsc_sigtran_reset_cb(const void *priv) +{ + struct bsc_msc_data *msc = (struct bsc_msc_data*) priv; + + /* Shut down all ongoing traffic */ + osmo_bsc_sigtran_reset(msc); + + /* Send reset to MSC */ + osmo_bsc_sigtran_tx_reset(msc); +} + +/* Default point-code to be used as local address (BSC) */ +#define BSC_DEFAULT_PC "0.23.3" + +/* Default point-code to be used as remote address (MSC) */ +#define MSC_DEFAULT_PC "0.23.1" + +/* Initalize osmo sigtran backhaul */ +int osmo_bsc_sigtran_init(struct llist_head *mscs) +{ + bool free_attempt_used = false; + bool fail_on_next_invalid_cfg = false; + + struct bsc_msc_data *msc; + char msc_name[32]; + uint32_t default_pc; + + OSMO_ASSERT(mscs); + msc_list = mscs; + + llist_for_each_entry(msc, msc_list, entry) { + snprintf(msc_name, sizeof(msc_name), "msc-%u", msc->nr); + LOGP(DMSC, LOGL_NOTICE, "Initializing SCCP connection to MSC %s\n", msc_name); + + /* Check if the VTY could determine a valid CS7 instance, + * use safe default in case none is set */ + if (msc->a.cs7_instance_valid == false) { + msc->a.cs7_instance = 0; + if (fail_on_next_invalid_cfg) + goto fail_auto_cofiguration; + free_attempt_used = true; + } + LOGP(DMSC, LOGL_NOTICE, "CS7 Instance identifier, A-Interface: %u\n", msc->a.cs7_instance); + + /* Pre-Check if there is an ss7 instance present */ + if (osmo_ss7_instance_find(msc->a.cs7_instance) == NULL) { + if (fail_on_next_invalid_cfg) + goto fail_auto_cofiguration; + free_attempt_used = true; + } + + /* SS7 Protocol stack */ + default_pc = osmo_ss7_pointcode_parse(NULL, BSC_DEFAULT_PC); + msc->a.sccp = + osmo_sccp_simple_client_on_ss7_id(msc, msc->a.cs7_instance, msc_name, default_pc, + OSMO_SS7_ASP_PROT_M3UA, 0, NULL, 0, NULL); + if (!msc->a.sccp) + return -EINVAL; + + /* Check if the sccp-address fullfills minimum requirements (SSN+PC is present, + * automatically recover addresses if the addresses are not set up properly) */ + if (!osmo_sccp_check_addr(&msc->a.bsc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) { + if (fail_on_next_invalid_cfg) + goto fail_auto_cofiguration; + free_attempt_used = true; + + LOGP(DMSC, LOGL_NOTICE, + "A-interface: invalid or missing local (BSC) SCCP address (a.bsc_addr=%s)\n", + osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.bsc_addr)); + osmo_sccp_local_addr_by_instance(&msc->a.bsc_addr, msc->a.sccp, SCCP_SSN_BSSAP); + LOGP(DMSC, LOGL_NOTICE, + "A-interface: using automatically generated local (BSC) SCCP address (a.bsc_addr=%s)\n", + osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.bsc_addr)); + } else { + LOGP(DMSC, LOGL_NOTICE, + "A-interface: using local (BSC) automatically SCCP address (a.msc_addr=%s)\n", + osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.bsc_addr)); + } + + if (!osmo_sccp_check_addr(&msc->a.msc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) { + if (fail_on_next_invalid_cfg) + goto fail_auto_cofiguration; + free_attempt_used = true; + + LOGP(DMSC, LOGL_NOTICE, + "A-interface: invalid or missing remote (MSC) SCCP address for the MSC (a.msc_addr=%s)\n", + osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.msc_addr)); + osmo_sccp_local_addr_by_instance(&msc->a.msc_addr, msc->a.sccp, SCCP_SSN_BSSAP); + msc->a.msc_addr.pc = osmo_ss7_pointcode_parse(NULL, MSC_DEFAULT_PC); + LOGP(DMSC, LOGL_NOTICE, + "A-interface: using automatically generated remote (MSC) SCCP address (a.msc_addr=%s)\n", + osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.msc_addr)); + free_attempt_used = true; + } else { + LOGP(DMSC, LOGL_NOTICE, + "A-interface: using remote (MSC) automatically SCCP address (a.msc_addr=%s)\n", + osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.msc_addr)); + } + + /* Bind SCCP user */ + msc->a.sccp_user = osmo_sccp_user_bind(msc->a.sccp, msc_name, sccp_sap_up, msc->a.bsc_addr.ssn); + if (!msc->a.sccp_user) + return -EINVAL; + + /* Start MSC-Reset procedure */ + msc->a.reset = a_reset_alloc(msc, msc_name, osmo_bsc_sigtran_reset_cb, msc); + if (!msc->a.reset) + return -EINVAL; + + /* If we have detected that the SS7 configuration of the MSC we have just initalized + * was incomplete or completely missing, we can not tolerate another incomplete + * configuration. The reson for this is that we do only specify exactly one default + * pointcode pair. We also specify localhost as default IP-Address. If we have wanted + * to support multiple MSCs with automatic configuration we would be forced to invent + * a complex ruleset how to allocate the pointcodes and respective IP-Addresses. + * Furthermore, the situation where a single BSC is connected to multiple MSCs + * is a very rare situation anyway. In this case we expect the user to experienced + * enough to create a valid SS7/CS7 VTY configuration that does not lack any + * components */ + if (free_attempt_used) + fail_on_next_invalid_cfg = true; + } + + return 0; + +fail_auto_cofiguration: + LOGP(DMSC, LOGL_ERROR, + "A-interface: More than one invalid/inclomplete configuration detected, unable to revover - check config file!\n"); + return -EINVAL; +} diff --git a/src/osmo-bsc/osmo_bsc_vty.c b/src/osmo-bsc/osmo_bsc_vty.c index 2e2e99bfd..8edcbf390 100644 --- a/src/osmo-bsc/osmo_bsc_vty.c +++ b/src/osmo-bsc/osmo_bsc_vty.c @@ -28,6 +28,7 @@ #include #include +#include #include @@ -184,6 +185,16 @@ static void write_msc(struct vty *vty, struct bsc_msc_data *msc) /* write amr options */ write_msc_amr_options(vty, msc); + + /* write sccp connection configuration */ + if (msc->a.bsc_addr_name) { + vty_out(vty, " bsc-addr %s%s", + msc->a.bsc_addr_name, VTY_NEWLINE); + } + if (msc->a.msc_addr_name) { + vty_out(vty, " msc-addr %s%s", + msc->a.msc_addr_name, VTY_NEWLINE); + } } static int config_write_msc(struct vty *vty) @@ -685,6 +696,87 @@ DEFUN(cfg_msc_no_acc_lst_name, return CMD_SUCCESS; } +/* Make sure only standard SSN numbers are used. If no ssn number is + * configured, silently apply the default SSN */ +static void enforce_standard_ssn(struct vty *vty, struct osmo_sccp_addr *addr) +{ + if (addr->presence & OSMO_SCCP_ADDR_T_SSN) { + if (addr->ssn != SCCP_SSN_BSSAP) + vty_out(vty, + "setting an SSN (%u) different from the standard (%u) is not allowd, will use standard SSN for address: %s%s", + addr->ssn, SCCP_SSN_BSSAP, osmo_sccp_addr_dump(addr), VTY_NEWLINE); + } + + addr->presence |= OSMO_SCCP_ADDR_T_SSN; + addr->ssn = SCCP_SSN_BSSAP; +} + +DEFUN(cfg_msc_cs7_bsc_addr, + cfg_msc_cs7_bsc_addr_cmd, + "bsc-addr NAME", + "Calling Address (local address of this BSC)\n" "SCCP address name\n") +{ + struct bsc_msc_data *msc = bsc_msc_data(vty); + const char *bsc_addr_name = argv[0]; + struct osmo_ss7_instance *ss7; + + ss7 = osmo_sccp_addr_by_name(&msc->a.bsc_addr, bsc_addr_name); + if (!ss7) { + vty_out(vty, "No sccp address %s found%s", bsc_addr_name, + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Prevent mixing addresses from different CS7/SS7 instances */ + if (msc->a.cs7_instance_valid) { + if (msc->a.cs7_instance != ss7->cfg.id) { + vty_out(vty, + "SCCP address %s from different CS7 instance%s", + bsc_addr_name, VTY_NEWLINE); + return CMD_WARNING; + } + } + + msc->a.cs7_instance = ss7->cfg.id; + msc->a.cs7_instance_valid = true; + enforce_standard_ssn(vty, &msc->a.bsc_addr); + msc->a.bsc_addr_name = talloc_strdup(msc, bsc_addr_name); + return CMD_SUCCESS; +} + +DEFUN(cfg_msc_cs7_msc_addr, + cfg_msc_cs7_msc_addr_cmd, + "msc-addr NAME", + "Called Address (remote address of the MSC)\n" "SCCP address name\n") +{ + struct bsc_msc_data *msc = bsc_msc_data(vty); + const char *msc_addr_name = argv[0]; + struct osmo_ss7_instance *ss7; + + ss7 = osmo_sccp_addr_by_name(&msc->a.msc_addr, msc_addr_name); + if (!ss7) { + vty_out(vty, "No sccp address %s found%s", msc_addr_name, + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Prevent mixing addresses from different CS7/SS7 instances */ + if (msc->a.cs7_instance_valid) { + if (msc->a.cs7_instance != ss7->cfg.id) { + vty_out(vty, + "SCCP address %s from different CS7 instance%s", + msc_addr_name, VTY_NEWLINE); + return CMD_WARNING; + } + } + + msc->a.cs7_instance = ss7->cfg.id; + msc->a.cs7_instance_valid = true; + enforce_standard_ssn(vty, &msc->a.msc_addr); + msc->a.msc_addr_name = talloc_strdup(msc, msc_addr_name); + return CMD_SUCCESS; +} + DEFUN(cfg_net_bsc_mid_call_text, cfg_net_bsc_mid_call_text_cmd, "mid-call-text .TEXT", @@ -931,6 +1023,8 @@ int bsc_vty_init_extra(void) install_element(MSC_NODE, &cfg_net_msc_amr_4_75_cmd); install_element(MSC_NODE, &cfg_msc_acc_lst_name_cmd); install_element(MSC_NODE, &cfg_msc_no_acc_lst_name_cmd); + install_element(MSC_NODE, &cfg_msc_cs7_bsc_addr_cmd); + install_element(MSC_NODE, &cfg_msc_cs7_msc_addr_cmd); install_element_ve(&show_statistics_cmd); install_element_ve(&show_mscs_cmd); diff --git a/src/osmo-bsc_mgcp/Makefile.am b/src/osmo-bsc_mgcp/Makefile.am index a19a4ebc0..b4e0d8564 100644 --- a/src/osmo-bsc_mgcp/Makefile.am +++ b/src/osmo-bsc_mgcp/Makefile.am @@ -31,5 +31,6 @@ osmo_bsc_mgcp_LDADD = \ $(LIBOSMONETIF_LIBS) \ $(LIBBCG729_LIBS) \ $(LIBRARY_GSM) \ + $(LIBOSMOSIGTRAN_LIBS) \ -lrt \ $(NULL) diff --git a/src/osmo-bsc_nat/Makefile.am b/src/osmo-bsc_nat/Makefile.am index be33d289d..b7c13ad3e 100644 --- a/src/osmo-bsc_nat/Makefile.am +++ b/src/osmo-bsc_nat/Makefile.am @@ -53,6 +53,7 @@ osmo_bsc_nat_LDADD = \ $(LIBOSMOCTRL_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(LIBOSMONETIF_LIBS) \ + $(LIBOSMOSIGTRAN_LIBS) \ $(LIBCRYPTO_LIBS) \ -lrt \ $(NULL) diff --git a/src/osmo-msc/msc_main.c b/src/osmo-msc/msc_main.c index 723c0e6e8..cd713eee6 100644 --- a/src/osmo-msc/msc_main.c +++ b/src/osmo-msc/msc_main.c @@ -63,6 +63,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,7 @@ #include #include #include +#include static const char * const osmomsc_copyright = "OsmoMSC - Osmocom Circuit-Switched Core Network implementation\r\n" @@ -310,8 +312,7 @@ static struct vty_app_info msc_vty_info = { .is_config_node = bsc_vty_is_config_node, }; -static int rcvmsg_iu_cs(struct msgb *msg, struct gprs_ra_id *ra_id, - uint16_t *sai) +static int rcvmsg_iu_cs(struct msgb *msg, struct gprs_ra_id *ra_id, uint16_t *sai) { DEBUGP(DIUCS, "got IuCS message" " %d bytes: %s\n", @@ -334,6 +335,68 @@ static int rx_iu_event(struct ue_conn_ctx *ctx, enum iu_event_type type, return iucs_rx_ranap_event(msc_network, ctx, type, data); } +#define DEFAULT_M3UA_REMOTE_IP "127.0.0.1" +#define DEFAULT_PC_A "0.23.1" +#define DEFAULT_PC_IU "0.23.2" +#define DEFAULT_PC_A_IU DEFAULT_PC_A + +static struct osmo_sccp_instance *sccp_setup(void *ctx, uint32_t cs7_instance, + const char *label, const char *default_pc_str) +{ + int default_pc = osmo_ss7_pointcode_parse(NULL, default_pc_str); + if (default_pc < 0) + return NULL; + + return osmo_sccp_simple_client_on_ss7_id(ctx, cs7_instance, label, default_pc, + OSMO_SS7_ASP_PROT_M3UA, + 0, NULL, /* local: use arbitrary port and 0.0.0.0. */ + 0, /* remote: use protocol default port */ + DEFAULT_M3UA_REMOTE_IP); + /* Note: If a differing remote IP is to be used, it was already entered in the vty config at + * 'cs7' / 'asp' / 'remote-ip', and this default remote IP has no effect. + * Similarly, 'cs7' / 'listen' can specify the local IP address. */ +} + +static int ss7_setup(void *ctx) +{ + uint32_t cs7_instance_a = msc_network->a.cs7_instance; +#if BUILD_IU + uint32_t cs7_instance_iu = msc_network->iu.cs7_instance; + + if (cs7_instance_a == cs7_instance_iu) { + /* Create one single SCCP instance which will be used for both, + * Iu and A at the same time, under the same point-code */ + LOGP(DMSC, LOGL_NOTICE, "CS7 Instance identifiers: A = Iu = %u\n", cs7_instance_a); + + msc_network->a.sccp = sccp_setup(ctx, cs7_instance_a, "OsmoMSC-A-Iu", DEFAULT_PC_A_IU); + if (!msc_network->a.sccp) + return -EINVAL; + + msc_network->iu.sccp = msc_network->a.sccp; + } else { + /* Create two separate SCCP instances to run A and Iu independently on different + * pointcodes */ + LOGP(DMSC, LOGL_NOTICE, "CS7 Instance identifiers: A = %u, Iu = %u\n", + cs7_instance_a, cs7_instance_iu); + + msc_network->a.sccp = sccp_setup(ctx, cs7_instance_a, "OsmoMSC-A", DEFAULT_PC_A); + if (!msc_network->a.sccp) + return -EINVAL; + + msc_network->iu.sccp = sccp_setup(ctx, cs7_instance_iu, "OsmoMSC-Iu", DEFAULT_PC_IU); + if (!msc_network->iu.sccp) + return -EINVAL; + } +#else + /* No Iu support, just open up an A instance */ + msc_network->a.sccp = sccp_setup(ctx, cs7_instance_a, "OsmoMSC-A", DEFAULT_PC_A); + if (!msc_network->a.sccp) + return -EINVAL; +#endif + + return 0; +} + int main(int argc, char **argv) { int rc; @@ -349,6 +412,9 @@ int main(int argc, char **argv) /* For --version, vty_init() must be called before handling options */ vty_init(&msc_vty_info); + osmo_ss7_init(); + osmo_ss7_vty_init_asp(tall_msc_ctx); + /* Parse options */ handle_options(argc, argv); @@ -491,14 +557,19 @@ TODO: we probably want some of the _net_ ctrl commands from bsc_base_ctrl_cmds_i return 7; } - /* Set up A-Interface */ - /* TODO: implement A-Interface and remove above legacy stuff. */ + if (ss7_setup(tall_msc_ctx)) { + printf("Setting up SCCP client failed.\n"); + return 8; + } #ifdef BUILD_IU /* Set up IuCS */ - iu_init(tall_msc_ctx, "127.0.0.1", 14001, rcvmsg_iu_cs, rx_iu_event); + iu_init(tall_msc_ctx, msc_network->iu.sccp, rcvmsg_iu_cs, rx_iu_event); #endif + /* Set up A interface */ + a_init(msc_network->a.sccp, msc_network); + if (msc_cmdline_config.daemonize) { rc = osmo_daemonize(); if (rc < 0) { diff --git a/tests/msc_vlr/Makefile.am b/tests/msc_vlr/Makefile.am index 17bfbdfa8..6d51d6eba 100644 --- a/tests/msc_vlr/Makefile.am +++ b/tests/msc_vlr/Makefile.am @@ -43,8 +43,8 @@ EXTRA_DIST = \ $(NULL) COMMON_LDADD = \ - $(top_builddir)/src/libcommon-cs/libcommon-cs.a \ $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libcommon-cs/libcommon-cs.a \ $(top_builddir)/src/libvlr/libvlr.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ @@ -73,8 +73,9 @@ COMMON_LDADD += \ COMMON_LDFLAGS = \ -Wl,--wrap=gsup_client_create \ -Wl,--wrap=gsup_client_send \ - -Wl,--wrap=a_tx \ - -Wl,--wrap=a_page \ + -Wl,--wrap=a_iface_tx_dtap \ + -Wl,--wrap=a_iface_tx_clear_cmd \ + -Wl,--wrap=a_iface_tx_paging \ -Wl,--wrap=iu_tx \ -Wl,--wrap=iu_tx_release \ -Wl,--wrap=iu_tx_common_id \ diff --git a/tests/msc_vlr/msc_vlr_test_gsm_authen.c b/tests/msc_vlr/msc_vlr_test_gsm_authen.c index 22e549b2f..44e405232 100644 --- a/tests/msc_vlr/msc_vlr_test_gsm_authen.c +++ b/tests/msc_vlr/msc_vlr_test_gsm_authen.c @@ -83,7 +83,9 @@ void test_gsm_authen() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -111,8 +113,10 @@ void test_gsm_authen() btw("a USSD request is serviced"); dtap_expect_tx_ussd("Your extension is 46071\r"); + expect_bssap_clear(); ms_sends_msg("0b3b1c15a11302010002013b300b04010f0406aa510c061b017f0100"); OSMO_ASSERT(dtap_tx_confirmed); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("all requests serviced, conn has been released"); EXPECT_CONN_COUNT(0); @@ -191,14 +195,18 @@ void test_gsm_authen() btw("MS also sends RP-ACK, MSC in turn sends CP-ACK for that"); dtap_expect_tx("0904"); + expect_bssap_clear(); ms_sends_msg("890106020041020000"); VERBOSE_ASSERT(dtap_tx_confirmed, == true, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("SMS is done, conn is gone"); EXPECT_CONN_COUNT(0); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -283,7 +291,9 @@ void test_gsm_authen_tmsi() vlr_subscr_put(vsub); btw("MS sends TMSI Realloc Complete"); + expect_bssap_clear(); ms_sends_msg("055b"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); EXPECT_CONN_COUNT(0); @@ -318,8 +328,10 @@ void test_gsm_authen_tmsi() btw("a USSD request is serviced"); dtap_expect_tx_ussd("Your extension is 46071\r"); + expect_bssap_clear(); ms_sends_msg("0b3b1c15a11302010002013b300b04010f0406aa510c061b017f0100"); OSMO_ASSERT(dtap_tx_confirmed); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("all requests serviced, conn has been released"); EXPECT_CONN_COUNT(0); @@ -398,8 +410,10 @@ void test_gsm_authen_tmsi() btw("MS also sends RP-ACK, MSC in turn sends CP-ACK for that"); dtap_expect_tx("0904"); + expect_bssap_clear(); ms_sends_msg("890106020041020000"); VERBOSE_ASSERT(dtap_tx_confirmed, == true, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("SMS is done, conn is gone"); EXPECT_CONN_COUNT(0); @@ -408,7 +422,9 @@ void test_gsm_authen_tmsi() * deallocated and we no longer know the TMSI. This case is covered by * test_lu_unknown_tmsi(), so here I'd like to still have the TMSI. BTW("subscriber detaches, using TMSI"); + expect_bssap_clear(); ms_sends_msg("050130" "05f4" "03020100"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); */ @@ -449,7 +465,9 @@ void test_gsm_authen_tmsi() vlr_subscr_put(vsub); btw("MS sends TMSI Realloc Complete"); + expect_bssap_clear(); ms_sends_msg("055b"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); EXPECT_CONN_COUNT(0); @@ -463,7 +481,9 @@ void test_gsm_authen_tmsi() vlr_subscr_put(vsub); BTW("subscriber detaches, using new TMSI"); + expect_bssap_clear(); ms_sends_msg("050130" "05f4" "07060504"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -542,7 +562,9 @@ void test_gsm_authen_imei() thwart_rx_non_initial_requests(); btw("MS replies with an Identity Response"); + expect_bssap_clear(); ms_sends_msg("0559084a32244332244332"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -555,7 +577,9 @@ void test_gsm_authen_imei() vlr_subscr_put(vsub); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -654,7 +678,9 @@ void test_gsm_authen_tmsi_imei() vlr_subscr_put(vsub); btw("MS sends TMSI Realloc Complete"); + expect_bssap_clear(); ms_sends_msg("055b"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); EXPECT_CONN_COUNT(0); @@ -667,7 +693,9 @@ void test_gsm_authen_tmsi_imei() vlr_subscr_put(vsub); BTW("subscriber detaches, using TMSI"); + expect_bssap_clear(); ms_sends_msg("050130" "05f4" "03020100"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -749,7 +777,9 @@ void test_gsm_milenage_authen() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000000156f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -780,8 +810,10 @@ void test_gsm_milenage_authen() btw("a USSD request is serviced"); dtap_expect_tx_ussd("Your extension is 42342\r"); + expect_bssap_clear(); ms_sends_msg("0b3b1c15a11302010002013b300b04010f0406aa510c061b017f0100"); OSMO_ASSERT(dtap_tx_confirmed); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("all requests serviced, conn has been released"); EXPECT_CONN_COUNT(0); @@ -863,15 +895,19 @@ void test_gsm_milenage_authen() btw("MS also sends RP-ACK, MSC in turn sends CP-ACK for that"); dtap_expect_tx("0904"); + expect_bssap_clear(); ms_sends_msg("890106020041020000"); VERBOSE_ASSERT(dtap_tx_confirmed, == true, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("SMS is done, conn is gone"); EXPECT_CONN_COUNT(0); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130" "089910070000106005" /* IMSI */); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); diff --git a/tests/msc_vlr/msc_vlr_test_gsm_authen.err b/tests/msc_vlr/msc_vlr_test_gsm_authen.err index b6b84edec..bca64a453 100644 --- a/tests/msc_vlr/msc_vlr_test_gsm_authen.err +++ b/tests/msc_vlr/msc_vlr_test_gsm_authen.err @@ -150,6 +150,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -157,6 +158,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -262,12 +264,14 @@ DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - all requests serviced, conn has been released llist_count(&net->subscr_conns) == 0 --- @@ -407,6 +411,7 @@ DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -414,6 +419,7 @@ DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 dtap_tx_confirmed == 1 + bssap_clear_sent == 1 - SMS is done, conn is gone llist_count(&net->subscr_conns) == 0 --- @@ -429,8 +435,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_gsm_authen: SUCCESS @@ -620,12 +628,14 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed llist_count(&net->subscr_conns) == 0 - Subscriber has the new TMSI @@ -737,12 +747,14 @@ DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Removing from parent S DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - all requests serviced, conn has been released llist_count(&net->subscr_conns) == 0 --- @@ -882,6 +894,7 @@ DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Removing from parent S DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -889,6 +902,7 @@ DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 dtap_tx_confirmed == 1 + bssap_clear_sent == 1 - SMS is done, conn is gone llist_count(&net->subscr_conns) == 0 --- @@ -1041,12 +1055,14 @@ DVLR vlr_lu_fsm(50462976){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cause OSMO DVLR vlr_lu_fsm(50462976){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(50462976){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed llist_count(&net->subscr_conns) == 0 - subscriber has the new TMSI @@ -1069,8 +1085,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_gsm_authen_tmsi: SUCCESS @@ -1258,12 +1276,14 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -1284,8 +1304,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_gsm_authen_imei: SUCCESS @@ -1504,12 +1526,14 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed llist_count(&net->subscr_conns) == 0 - Subscriber has the IMEI and TMSI @@ -1530,8 +1554,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_gsm_authen_tmsi_imei: SUCCESS @@ -1671,6 +1697,7 @@ DVLR vlr_lu_fsm(901700000010650){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000010650){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000010650){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:42342, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:42342: MSC conn use - 1 == 0 DRLL subscr MSISDN:42342: Freeing subscriber connection DREF VLR subscr MSISDN:42342 usage decreases to: 2 @@ -1678,6 +1705,7 @@ DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:42342 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -1783,12 +1811,14 @@ DVLR Process_Access_Request_VLR(901700000010650){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000010650){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000010650){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:42342, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:42342: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:42342: MSC conn use - 1 == 0 DRLL subscr MSISDN:42342: Freeing subscriber connection DREF VLR subscr MSISDN:42342 usage decreases to: 1 + bssap_clear_sent == 1 - all requests serviced, conn has been released llist_count(&net->subscr_conns) == 0 --- @@ -1928,6 +1958,7 @@ DVLR Process_Access_Request_VLR(901700000010650){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000010650){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000010650){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:42342, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:42342: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -1935,6 +1966,7 @@ DREF MSISDN:42342: MSC conn use - 1 == 0 DRLL subscr MSISDN:42342: Freeing subscriber connection DREF VLR subscr MSISDN:42342 usage decreases to: 1 dtap_tx_confirmed == 1 + bssap_clear_sent == 1 - SMS is done, conn is gone llist_count(&net->subscr_conns) == 0 --- @@ -1950,8 +1982,10 @@ DREF VLR subscr MSISDN:42342 usage decreases to: 1 DREF VLR subscr MSISDN:42342 usage decreases to: 0 DREF freeing VLR subscr MSISDN:42342 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_gsm_milenage_authen: SUCCESS diff --git a/tests/msc_vlr/msc_vlr_test_gsm_ciph.c b/tests/msc_vlr/msc_vlr_test_gsm_ciph.c index 589f7da94..42e7c737c 100644 --- a/tests/msc_vlr/msc_vlr_test_gsm_ciph.c +++ b/tests/msc_vlr/msc_vlr_test_gsm_ciph.c @@ -87,7 +87,9 @@ void test_ciph() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -124,8 +126,10 @@ void test_ciph() btw("a USSD request is serviced"); dtap_expect_tx_ussd("Your extension is 46071\r"); + expect_bssap_clear(); ms_sends_msg("0b3b1c15a11302010002013b300b04010f0406aa510c061b017f0100"); OSMO_ASSERT(dtap_tx_confirmed); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("all requests serviced, conn has been released"); EXPECT_CONN_COUNT(0); @@ -213,14 +217,18 @@ void test_ciph() btw("MS also sends RP-ACK, MSC in turn sends CP-ACK for that"); dtap_expect_tx("0904"); + expect_bssap_clear(); ms_sends_msg("890106020041020000"); VERBOSE_ASSERT(dtap_tx_confirmed, == true, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("SMS is done, conn is gone"); EXPECT_CONN_COUNT(0); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -310,7 +318,9 @@ void test_ciph_tmsi() vlr_subscr_put(vsub); btw("MS sends TMSI Realloc Complete"); + expect_bssap_clear(); ms_sends_msg("055b"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); EXPECT_CONN_COUNT(0); @@ -355,8 +365,10 @@ void test_ciph_tmsi() btw("a USSD request is serviced"); dtap_expect_tx_ussd("Your extension is 46071\r"); + expect_bssap_clear(); ms_sends_msg("0b3b1c15a11302010002013b300b04010f0406aa510c061b017f0100"); OSMO_ASSERT(dtap_tx_confirmed); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("all requests serviced, conn has been released"); EXPECT_CONN_COUNT(0); @@ -444,14 +456,18 @@ void test_ciph_tmsi() btw("MS also sends RP-ACK, MSC in turn sends CP-ACK for that"); dtap_expect_tx("0904"); + expect_bssap_clear(); ms_sends_msg("890106020041020000"); VERBOSE_ASSERT(dtap_tx_confirmed, == true, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("SMS is done, conn is gone"); EXPECT_CONN_COUNT(0); BTW("subscriber detaches, using TMSI"); + expect_bssap_clear(); ms_sends_msg("050130" "05f4" "03020100"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -536,7 +552,9 @@ void test_ciph_imei() thwart_rx_non_initial_requests(); btw("MS replies with an Identity Response"); + expect_bssap_clear(); ms_sends_msg("0559084a32244332244332"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -549,7 +567,9 @@ void test_ciph_imei() vlr_subscr_put(vsub); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -639,14 +659,18 @@ void test_ciph_imeisv() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); EXPECT_CONN_COUNT(0); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -749,7 +773,9 @@ void test_ciph_tmsi_imei() vlr_subscr_put(vsub); btw("MS sends TMSI Realloc Complete"); + expect_bssap_clear(); ms_sends_msg("055b"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); EXPECT_CONN_COUNT(0); @@ -762,7 +788,9 @@ void test_ciph_tmsi_imei() vlr_subscr_put(vsub); BTW("subscriber detaches, using TMSI"); + expect_bssap_clear(); ms_sends_msg("050130" "05f4" "03020100"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -799,7 +827,9 @@ void test_lu_unknown_tmsi() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); diff --git a/tests/msc_vlr/msc_vlr_test_gsm_ciph.err b/tests/msc_vlr/msc_vlr_test_gsm_ciph.err index bd8d1b9be..c219b496f 100644 --- a/tests/msc_vlr/msc_vlr_test_gsm_ciph.err +++ b/tests/msc_vlr/msc_vlr_test_gsm_ciph.err @@ -150,6 +150,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -157,6 +158,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -279,12 +281,14 @@ DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - all requests serviced, conn has been released llist_count(&net->subscr_conns) == 0 --- @@ -441,6 +445,7 @@ DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -448,6 +453,7 @@ DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 dtap_tx_confirmed == 1 + bssap_clear_sent == 1 - SMS is done, conn is gone llist_count(&net->subscr_conns) == 0 --- @@ -463,8 +469,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_ciph: SUCCESS @@ -655,12 +663,14 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed llist_count(&net->subscr_conns) == 0 - Subscriber has the new TMSI @@ -789,12 +799,14 @@ DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Removing from parent S DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - all requests serviced, conn has been released llist_count(&net->subscr_conns) == 0 --- @@ -951,6 +963,7 @@ DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Removing from parent S DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -958,6 +971,7 @@ DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 dtap_tx_confirmed == 1 + bssap_clear_sent == 1 - SMS is done, conn is gone llist_count(&net->subscr_conns) == 0 --- @@ -973,8 +987,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_ciph_tmsi: SUCCESS @@ -1162,12 +1178,14 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -1188,8 +1206,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_ciph_imei: SUCCESS @@ -1379,6 +1399,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -1386,6 +1407,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -1402,8 +1424,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_ciph_imeisv: SUCCESS @@ -1622,12 +1646,14 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed llist_count(&net->subscr_conns) == 0 - Subscriber has the IMEI and TMSI @@ -1648,8 +1674,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_ciph_tmsi_imei: SUCCESS diff --git a/tests/msc_vlr/msc_vlr_test_hlr_reject.c b/tests/msc_vlr/msc_vlr_test_hlr_reject.c index c4cf3534a..095da8172 100644 --- a/tests/msc_vlr/msc_vlr_test_hlr_reject.c +++ b/tests/msc_vlr/msc_vlr_test_hlr_reject.c @@ -38,9 +38,11 @@ void test_hlr_rej_auth_info_unknown_imsi() btw("HLR sends _SEND_AUTH_INFO_ERROR = unknown IMSI"); auth_request_sent = false; + expect_bssap_clear(); gsup_rx("09" "010809710000004026f0" "020102", NULL); VERBOSE_ASSERT(auth_request_sent, == false, "%d"); VERBOSE_ASSERT(lu_result_sent, == RES_REJECT, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -62,9 +64,11 @@ void test_hlr_rej_auth_info_net_fail() btw("HLR sends _SEND_AUTH_INFO_ERROR = net fail"); auth_request_sent = false; + expect_bssap_clear(); gsup_rx("09" "010809710000004026f0" "020111", NULL); VERBOSE_ASSERT(auth_request_sent, == false, "%d"); VERBOSE_ASSERT(lu_result_sent, == RES_REJECT, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -118,7 +122,9 @@ void test_hlr_rej_auth_info_net_fail_no_reuse_tuples() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -145,9 +151,11 @@ void test_hlr_rej_auth_info_net_fail_no_reuse_tuples() btw("HLR sends _SEND_AUTH_INFO_ERROR = net fail"); auth_request_sent = false; + expect_bssap_clear(); gsup_rx("09" "010809710000004026f0" "020111", NULL); VERBOSE_ASSERT(auth_request_sent, == false, "%d"); VERBOSE_ASSERT(lu_result_sent, == RES_REJECT, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -201,7 +209,9 @@ void test_hlr_rej_auth_info_unkown_imsi_no_reuse_tuples() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -229,9 +239,11 @@ void test_hlr_rej_auth_info_unkown_imsi_no_reuse_tuples() btw("HLR sends _SEND_AUTH_INFO_ERROR = unknown IMSI"); auth_request_sent = false; + expect_bssap_clear(); gsup_rx("09" "010809710000004026f0" "020102", NULL); VERBOSE_ASSERT(auth_request_sent, == false, "%d"); VERBOSE_ASSERT(lu_result_sent, == RES_REJECT, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -255,6 +267,7 @@ void test_hlr_acc_but_no_auth_tuples() btw("from HLR, rx _SEND_AUTH_INFO_RESULT but it lacks auth tuples"); auth_request_sent = false; + expect_bssap_clear(); gsup_rx("0a" /* imsi */ "0108" "09710000004026f0" @@ -262,6 +275,7 @@ void test_hlr_acc_but_no_auth_tuples() ,NULL); VERBOSE_ASSERT(auth_request_sent, == false, "%d"); VERBOSE_ASSERT(lu_result_sent, == RES_REJECT, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -315,7 +329,9 @@ void test_hlr_rej_auth_info_net_fail_reuse_tuples() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -357,7 +373,9 @@ void test_hlr_rej_auth_info_net_fail_reuse_tuples() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -379,9 +397,11 @@ void test_hlr_rej_lu() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR sends UPDATE_LOCATION_ERROR"); + expect_bssap_clear(); gsup_rx("05" "010809710000004026f0" "020102", NULL); VERBOSE_ASSERT(lu_result_sent, == RES_REJECT, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -400,7 +420,9 @@ void test_hlr_no_insert_data() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR sends only _UPDATE_LOCATION_RESULT, no INSERT DATA"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); /* TODO should we wait for OSMO_GSUP_MSGT_INSERT_DATA_REQUEST? */ diff --git a/tests/msc_vlr/msc_vlr_test_hlr_reject.err b/tests/msc_vlr/msc_vlr_test_hlr_reject.err index 9650df9de..c946c1efa 100644 --- a/tests/msc_vlr/msc_vlr_test_hlr_reject.err +++ b/tests/msc_vlr/msc_vlr_test_hlr_reject.err @@ -56,6 +56,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=IMSI:901700000004620, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF IMSI:901700000004620: MSC conn use - 1 == 0 DRLL subscr IMSI:901700000004620: Freeing subscriber connection DREF VLR subscr IMSI:901700000004620 usage decreases to: 1 @@ -66,6 +67,7 @@ DREF freeing VLR subscr IMSI:901700000004620 <-- GSUP rx OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR: vlr_gsupc_read_cb() returns 0 auth_request_sent == 0 lu_result_sent == 2 + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_hlr_rej_auth_info_unknown_imsi: SUCCESS @@ -130,6 +132,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=IMSI:901700000004620, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF IMSI:901700000004620: MSC conn use - 1 == 0 DRLL subscr IMSI:901700000004620: Freeing subscriber connection DREF VLR subscr IMSI:901700000004620 usage decreases to: 1 @@ -140,6 +143,7 @@ DREF freeing VLR subscr IMSI:901700000004620 <-- GSUP rx OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR: vlr_gsupc_read_cb() returns 0 auth_request_sent == 0 lu_result_sent == 2 + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_hlr_rej_auth_info_net_fail: SUCCESS @@ -281,6 +285,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -288,6 +293,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -427,6 +433,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -434,6 +441,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -578,6 +586,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -585,6 +594,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -650,6 +660,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -659,6 +670,7 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR: vlr_gsupc_read_cb() returns 0 auth_request_sent == 0 lu_result_sent == 2 + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 DREF freeing VLR subscr MSISDN:46071 ===== test_hlr_rej_auth_info_net_fail_no_reuse_tuples: SUCCESS @@ -801,6 +813,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -808,6 +821,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -874,6 +888,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -883,6 +898,7 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR: vlr_gsupc_read_cb() returns 0 auth_request_sent == 0 lu_result_sent == 2 + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 DREF freeing VLR subscr MSISDN:46071 ===== test_hlr_rej_auth_info_unkown_imsi_no_reuse_tuples: SUCCESS @@ -947,6 +963,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=IMSI:901700000004620, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF IMSI:901700000004620: MSC conn use - 1 == 0 DRLL subscr IMSI:901700000004620: Freeing subscriber connection DREF VLR subscr IMSI:901700000004620 usage decreases to: 1 @@ -957,6 +974,7 @@ DREF freeing VLR subscr IMSI:901700000004620 <-- GSUP rx OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT: vlr_gsupc_read_cb() returns 0 auth_request_sent == 0 lu_result_sent == 2 + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_hlr_acc_but_no_auth_tuples: SUCCESS @@ -1024,6 +1042,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=IMSI:901700000004620, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF IMSI:901700000004620: MSC conn use - 1 == 0 DRLL subscr IMSI:901700000004620: Freeing subscriber connection DREF VLR subscr IMSI:901700000004620 usage decreases to: 1 @@ -1033,6 +1052,7 @@ DREF VLR subscr IMSI:901700000004620 usage decreases to: 0 DREF freeing VLR subscr IMSI:901700000004620 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: vlr_gsupc_read_cb() returns 0 lu_result_sent == 2 + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_hlr_rej_lu: SUCCESS @@ -1122,6 +1142,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=IMSI:901700000004620, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF IMSI:901700000004620: MSC conn use - 1 == 0 DRLL subscr IMSI:901700000004620: Freeing subscriber connection DREF VLR subscr IMSI:901700000004620 usage decreases to: 2 @@ -1129,6 +1150,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr IMSI:901700000004620 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 diff --git a/tests/msc_vlr/msc_vlr_test_hlr_timeout.c b/tests/msc_vlr/msc_vlr_test_hlr_timeout.c index 2872f0cf4..c2f17c85f 100644 --- a/tests/msc_vlr/msc_vlr_test_hlr_timeout.c +++ b/tests/msc_vlr/msc_vlr_test_hlr_timeout.c @@ -53,8 +53,10 @@ void test_hlr_timeout_lu_auth_info() EXPECT_CONN_COUNT(1); fake_time_passes(1, 235); EXPECT_CONN_COUNT(1); + expect_bssap_clear(); fake_time_passes(1, 235); btw("SUBSCR_CONN_TIMEOUT has passed, conn is gone."); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); VERBOSE_ASSERT(lu_result_sent, == RES_REJECT, "%d"); @@ -98,8 +100,10 @@ void test_hlr_timeout_lu_upd_loc_result() fake_time_passes(1, 235); EXPECT_CONN_COUNT(1); VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); + expect_bssap_clear(); fake_time_passes(1, 235); btw("SUBSCR_CONN_TIMEOUT has passed, conn is gone."); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); VERBOSE_ASSERT(lu_result_sent, == RES_REJECT, "%d"); diff --git a/tests/msc_vlr/msc_vlr_test_hlr_timeout.err b/tests/msc_vlr/msc_vlr_test_hlr_timeout.err index f83cc2976..e34490d8c 100644 --- a/tests/msc_vlr/msc_vlr_test_hlr_timeout.err +++ b/tests/msc_vlr/msc_vlr_test_hlr_timeout.err @@ -64,6 +64,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=IMSI:901700000004620, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF IMSI:901700000004620: MSC conn use - 1 == 0 DRLL subscr IMSI:901700000004620: Freeing subscriber connection DREF VLR subscr IMSI:901700000004620 usage decreases to: 0 @@ -73,6 +74,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Received Event SUBSCR_CONN_E_CN_CLOSE DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Event SUBSCR_CONN_E_CN_CLOSE not permitted - SUBSCR_CONN_TIMEOUT has passed, conn is gone. + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 lu_result_sent == 2 ===== test_hlr_timeout_lu_auth_info: SUCCESS @@ -164,6 +166,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 0 @@ -173,6 +176,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Received Event SUBSCR_CONN_E_CN_CLOSE DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Event SUBSCR_CONN_E_CN_CLOSE not permitted - SUBSCR_CONN_TIMEOUT has passed, conn is gone. + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 lu_result_sent == 2 ===== test_hlr_timeout_lu_upd_loc_result: SUCCESS diff --git a/tests/msc_vlr/msc_vlr_test_ms_timeout.c b/tests/msc_vlr/msc_vlr_test_ms_timeout.c index 47ca9678f..d8a3a314e 100644 --- a/tests/msc_vlr/msc_vlr_test_ms_timeout.c +++ b/tests/msc_vlr/msc_vlr_test_ms_timeout.c @@ -73,8 +73,10 @@ void test_ms_timeout_lu_auth_resp() fake_time_passes(1, 235); EXPECT_CONN_COUNT(1); VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); + expect_bssap_clear(); fake_time_passes(1, 235); btw("SUBSCR_CONN_TIMEOUT has passed, conn is gone."); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); VERBOSE_ASSERT(lu_result_sent, == RES_REJECT, "%d"); @@ -132,7 +134,9 @@ void test_ms_timeout_cm_auth_resp() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -167,8 +171,10 @@ void test_ms_timeout_cm_auth_resp() fake_time_passes(1, 235); EXPECT_CONN_COUNT(1); VERBOSE_ASSERT(cm_service_result_sent, == RES_NONE, "%d"); + expect_bssap_clear(); fake_time_passes(1, 235); btw("SUBSCR_CONN_TIMEOUT has passed, conn is gone."); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); VERBOSE_ASSERT(cm_service_result_sent, == RES_REJECT, "%d"); diff --git a/tests/msc_vlr/msc_vlr_test_ms_timeout.err b/tests/msc_vlr/msc_vlr_test_ms_timeout.err index 1574ef9ee..e267c5b0c 100644 --- a/tests/msc_vlr/msc_vlr_test_ms_timeout.err +++ b/tests/msc_vlr/msc_vlr_test_ms_timeout.err @@ -84,6 +84,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=IMSI:901700000004620, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF IMSI:901700000004620: MSC conn use - 1 == 0 DRLL subscr IMSI:901700000004620: Freeing subscriber connection DREF VLR subscr IMSI:901700000004620 usage decreases to: 0 @@ -93,6 +94,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Received Event SUBSCR_CONN_E_CN_CLOSE DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Event SUBSCR_CONN_E_CN_CLOSE not permitted - SUBSCR_CONN_TIMEOUT has passed, conn is gone. + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 lu_result_sent == 2 ===== test_ms_timeout_lu_auth_resp: SUCCESS @@ -234,6 +236,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -241,6 +244,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -315,6 +319,7 @@ DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 @@ -323,6 +328,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Received Event SUBSCR_CONN_E_CN_CLOSE DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Event SUBSCR_CONN_E_CN_CLOSE not permitted - SUBSCR_CONN_TIMEOUT has passed, conn is gone. + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 cm_service_result_sent == 2 DREF freeing VLR subscr MSISDN:46071 diff --git a/tests/msc_vlr/msc_vlr_test_no_authen.c b/tests/msc_vlr/msc_vlr_test_no_authen.c index f5e2e2b62..34e392e72 100644 --- a/tests/msc_vlr/msc_vlr_test_no_authen.c +++ b/tests/msc_vlr/msc_vlr_test_no_authen.c @@ -53,10 +53,12 @@ void test_no_authen() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); BTW("after a while, a new conn sends a CM Service Request"); @@ -70,8 +72,10 @@ void test_no_authen() btw("a USSD request is serviced"); dtap_expect_tx_ussd("Your extension is 46071\r"); + expect_bssap_clear(); ms_sends_msg("0b3b1c15a11302010002013b300b04010f0406aa510c061b017f0100"); VERBOSE_ASSERT(dtap_tx_confirmed, == true, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("all requests serviced, conn has been released"); EXPECT_CONN_COUNT(0); @@ -140,14 +144,18 @@ void test_no_authen() btw("MS also sends RP-ACK, MSC in turn sends CP-ACK for that"); dtap_expect_tx("0904"); + expect_bssap_clear(); ms_sends_msg("890106020041020000"); VERBOSE_ASSERT(dtap_tx_confirmed, == true, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("SMS is done, conn is gone"); EXPECT_CONN_COUNT(0); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -200,7 +208,9 @@ void test_no_authen_tmsi() vlr_subscr_put(vsub); btw("MS sends TMSI Realloc Complete"); + expect_bssap_clear(); ms_sends_msg("055b"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); EXPECT_CONN_COUNT(0); @@ -224,7 +234,9 @@ void test_no_authen_tmsi() btw("a USSD request is serviced"); dtap_expect_tx_ussd("Your extension is 46071\r"); + expect_bssap_clear(); ms_sends_msg("0b3b1c15a11302010002013b300b04010f0406aa510c061b017f0100"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("all requests serviced, conn has been released"); EXPECT_CONN_COUNT(0); @@ -293,8 +305,10 @@ void test_no_authen_tmsi() btw("MS also sends RP-ACK, MSC in turn sends CP-ACK for that"); dtap_expect_tx("0904"); + expect_bssap_clear(); ms_sends_msg("890106020041020000"); VERBOSE_ASSERT(dtap_tx_confirmed, == true, "%d"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("SMS is done, conn is gone"); EXPECT_CONN_COUNT(0); @@ -343,7 +357,9 @@ void test_no_authen_tmsi() vlr_subscr_put(vsub); btw("MS sends TMSI Realloc Complete"); + expect_bssap_clear(); ms_sends_msg("055b"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); EXPECT_CONN_COUNT(0); @@ -357,7 +373,9 @@ void test_no_authen_tmsi() vlr_subscr_put(vsub); BTW("subscriber detaches, using new TMSI"); + expect_bssap_clear(); ms_sends_msg("050130" "05f4" "07060504"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -406,7 +424,9 @@ void test_no_authen_imei() thwart_rx_non_initial_requests(); btw("MS replies with an Identity Response"); + expect_bssap_clear(); ms_sends_msg("0559084a32244332244332"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -419,7 +439,9 @@ void test_no_authen_imei() vlr_subscr_put(vsub); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -478,7 +500,9 @@ void test_no_authen_tmsi_imei() thwart_rx_non_initial_requests(); btw("MS sends TMSI Realloc Complete"); + expect_bssap_clear(); ms_sends_msg("055b"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -492,7 +516,9 @@ void test_no_authen_tmsi_imei() vlr_subscr_put(vsub); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); diff --git a/tests/msc_vlr/msc_vlr_test_no_authen.err b/tests/msc_vlr/msc_vlr_test_no_authen.err index 015e55b9d..4f4073683 100644 --- a/tests/msc_vlr/msc_vlr_test_no_authen.err +++ b/tests/msc_vlr/msc_vlr_test_no_authen.err @@ -103,6 +103,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -112,6 +113,7 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 - LU was successful, and the conn has already been closed lu_result_sent == 1 + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 --- - after a while, a new conn sends a CM Service Request @@ -176,6 +178,7 @@ DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -183,6 +186,7 @@ DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 dtap_tx_confirmed == 1 + bssap_clear_sent == 1 - all requests serviced, conn has been released llist_count(&net->subscr_conns) == 0 --- @@ -283,6 +287,7 @@ DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -290,6 +295,7 @@ DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 dtap_tx_confirmed == 1 + bssap_clear_sent == 1 - SMS is done, conn is gone llist_count(&net->subscr_conns) == 0 --- @@ -305,8 +311,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_no_authen: SUCCESS @@ -449,12 +457,14 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed llist_count(&net->subscr_conns) == 0 - Subscriber has the new TMSI @@ -527,12 +537,14 @@ DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Removing from parent S DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - all requests serviced, conn has been released llist_count(&net->subscr_conns) == 0 --- @@ -633,6 +645,7 @@ DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Removing from parent S DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(50462976){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -640,6 +653,7 @@ DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 dtap_tx_confirmed == 1 + bssap_clear_sent == 1 - SMS is done, conn is gone llist_count(&net->subscr_conns) == 0 --- @@ -776,12 +790,14 @@ DVLR vlr_lu_fsm(50462976){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cause OSMO DVLR vlr_lu_fsm(50462976){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(50462976){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(50462976){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed llist_count(&net->subscr_conns) == 0 - subscriber has the new TMSI @@ -804,8 +820,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_no_authen_tmsi: SUCCESS @@ -946,12 +964,14 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -972,8 +992,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_no_authen_imei: SUCCESS @@ -1138,12 +1160,14 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -1165,8 +1189,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_no_authen_tmsi_imei: SUCCESS diff --git a/tests/msc_vlr/msc_vlr_test_reject_concurrency.c b/tests/msc_vlr/msc_vlr_test_reject_concurrency.c index 15988584a..2377c19b3 100644 --- a/tests/msc_vlr/msc_vlr_test_reject_concurrency.c +++ b/tests/msc_vlr/msc_vlr_test_reject_concurrency.c @@ -39,7 +39,9 @@ void test_reject_2nd_conn() btw("Another Location Update Request from the same subscriber on another connection is rejected"); conn1 = g_conn; g_conn = NULL; + expect_bssap_clear(); ms_sends_msg("050802008168000130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); VERBOSE_ASSERT(lu_result_sent, == RES_REJECT, "%d"); EXPECT_CONN_COUNT(1); @@ -53,7 +55,9 @@ void test_reject_2nd_conn() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -83,7 +87,9 @@ void _normal_lu_part2() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -164,7 +170,7 @@ void _paging_resp_part1() EXPECT_CONN_COUNT(1); } -void _paging_resp_part2(int expect_conn_count) +void _paging_resp_part2(int expect_conn_count, bool expect_clear) { btw("MS replies with CP-ACK for received SMS"); ms_sends_msg("8904"); @@ -172,8 +178,12 @@ void _paging_resp_part2(int expect_conn_count) btw("MS also sends RP-ACK, MSC in turn sends CP-ACK for that"); dtap_expect_tx("0904"); + if (expect_clear) + expect_bssap_clear(); ms_sends_msg("890106020041020000"); VERBOSE_ASSERT(dtap_tx_confirmed, == true, "%d"); + if (expect_clear) + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("SMS is done"); EXPECT_CONN_COUNT(expect_conn_count); @@ -251,7 +261,9 @@ void test_reject_lu_during_cm() EXPECT_CONN_COUNT(1); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -272,7 +284,9 @@ void test_reject_cm_during_cm() EXPECT_CONN_COUNT(1); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -293,7 +307,9 @@ void test_reject_paging_resp_during_cm() BTW("The original CM Service Request can conclude"); btw("a USSD request is serviced"); dtap_expect_tx_ussd("Your extension is 46071\r"); + expect_bssap_clear(); ms_sends_msg("0b3b1c15a11302010002013b300b04010f0406aa510c061b017f0100"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("all requests serviced, conn has been released"); EXPECT_CONN_COUNT(0); @@ -313,7 +329,7 @@ void test_reject_paging_resp_during_paging_resp() BTW("MS sends another erratic Paging Response which is dropped silently"); ms_sends_msg("06270703305882089910070000006402"); - _paging_resp_part2(0); + _paging_resp_part2(0, true); clear_vlr(); comment_end(); @@ -333,13 +349,13 @@ void test_reject_lu_during_paging_resp() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); EXPECT_CONN_COUNT(1); - _paging_resp_part2(0); + _paging_resp_part2(0, true); clear_vlr(); comment_end(); } -void test_reject_cm_during_paging_resp() +void test_accept_cm_during_paging_resp() { comment_start(); @@ -354,10 +370,12 @@ void test_reject_cm_during_paging_resp() EXPECT_CONN_COUNT(1); VERBOSE_ASSERT(g_conn->received_cm_service_request, == true, "%d"); - _paging_resp_part2(1); + _paging_resp_part2(1, false); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -373,7 +391,7 @@ msc_vlr_test_func_t msc_vlr_tests[] = { test_reject_cm_during_cm, test_reject_paging_resp_during_cm, test_reject_lu_during_paging_resp, - test_reject_cm_during_paging_resp, + test_accept_cm_during_paging_resp, test_reject_paging_resp_during_paging_resp, NULL }; diff --git a/tests/msc_vlr/msc_vlr_test_reject_concurrency.err b/tests/msc_vlr/msc_vlr_test_reject_concurrency.err index 7b0954fdc..00945cb89 100644 --- a/tests/msc_vlr/msc_vlr_test_reject_concurrency.err +++ b/tests/msc_vlr/msc_vlr_test_reject_concurrency.err @@ -64,6 +64,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=unknown, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -71,6 +72,7 @@ DREF VLR subscr IMSI:901700000004620 usage decreases to: 1 DRR 901700000004620: internal error during Location Updating attempt DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 lu_result_sent == 2 llist_count(&net->subscr_conns) == 1 --- @@ -133,6 +135,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -140,6 +143,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -255,6 +259,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -262,6 +267,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -383,6 +389,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -390,6 +397,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -506,6 +514,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -513,6 +522,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -618,6 +628,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -625,6 +636,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -694,6 +706,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_ACCEPTED}: Received Event SUBSCR_ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_ACCEPTED}: state_chg to SUBSCR_CONN_S_RELEASED DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Terminating (cause = OSMO_FSM_TERM_REGULAR) DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -704,6 +717,7 @@ DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_reject_lu_during_cm: SUCCESS @@ -806,6 +820,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -813,6 +828,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -884,6 +900,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_ACCEPTED}: Received Event SUBSCR_ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_ACCEPTED}: state_chg to SUBSCR_CONN_S_RELEASED DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Terminating (cause = OSMO_FSM_TERM_REGULAR) DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -894,6 +911,7 @@ DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_reject_cm_during_cm: SUCCESS @@ -996,6 +1014,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -1003,6 +1022,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -1082,12 +1102,14 @@ DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 + bssap_clear_sent == 1 - all requests serviced, conn has been released llist_count(&net->subscr_conns) == 0 DREF freeing VLR subscr MSISDN:46071 @@ -1192,6 +1214,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -1199,6 +1222,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -1303,6 +1327,7 @@ DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -1310,6 +1335,7 @@ DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 dtap_tx_confirmed == 1 + bssap_clear_sent == 1 - SMS is done llist_count(&net->subscr_conns) == 0 DREF freeing VLR subscr MSISDN:46071 @@ -1318,7 +1344,7 @@ DREF freeing VLR subscr MSISDN:46071 full talloc report on 'msgb' (total 0 bytes in 1 blocks) talloc_total_blocks(tall_bsc_ctx) == 9 -===== test_reject_cm_during_paging_resp +===== test_accept_cm_during_paging_resp --- - Subscriber does a normal LU - Location Update Request @@ -1414,6 +1440,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -1421,6 +1448,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -1541,6 +1569,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_COMMUNICATING}: Received Event SU DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_COMMUNICATING}: state_chg to SUBSCR_CONN_S_RELEASED DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Terminating (cause = OSMO_FSM_TERM_REGULAR) DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -1551,8 +1580,9 @@ DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 -===== test_reject_cm_during_paging_resp: SUCCESS +===== test_accept_cm_during_paging_resp: SUCCESS full talloc report on 'msgb' (total 0 bytes in 1 blocks) talloc_total_blocks(tall_bsc_ctx) == 9 @@ -1653,6 +1683,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -1660,6 +1691,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -1763,6 +1795,7 @@ DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -1770,6 +1803,7 @@ DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 1 dtap_tx_confirmed == 1 + bssap_clear_sent == 1 - SMS is done llist_count(&net->subscr_conns) == 0 DREF freeing VLR subscr MSISDN:46071 diff --git a/tests/msc_vlr/msc_vlr_test_rest.c b/tests/msc_vlr/msc_vlr_test_rest.c index 5ccc951be..c5f7fcfb9 100644 --- a/tests/msc_vlr/msc_vlr_test_rest.c +++ b/tests/msc_vlr/msc_vlr_test_rest.c @@ -45,6 +45,7 @@ void test_early_stage() btw("fake: acceptance"); g_conn->vsub = vlr_subscr_alloc(net->vlr); + g_conn->via_ran = RAN_GERAN_A; OSMO_ASSERT(g_conn->vsub); /* mark as silent call so it sticks around */ g_conn->silent_call = 1; @@ -53,7 +54,9 @@ void test_early_stage() EXPECT_ACCEPTED(true); btw("CLOSE event marks conn_fsm as released and frees the conn"); + expect_bssap_clear(); osmo_fsm_inst_dispatch(g_conn->conn_fsm, SUBSCR_CONN_E_CN_CLOSE, NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -65,7 +68,9 @@ void test_cm_service_without_lu() comment_start(); btw("CM Service Request without a prior Location Updating"); + expect_bssap_clear(); ms_sends_msg("05247803305886089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("conn was released"); EXPECT_CONN_COUNT(0); @@ -98,7 +103,9 @@ void test_two_lu() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); @@ -126,14 +133,18 @@ void test_two_lu() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); EXPECT_CONN_COUNT(0); BTW("subscriber detaches"); + expect_bssap_clear(); ms_sends_msg("050130089910070000006402"); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -170,7 +181,9 @@ void test_lu_unknown_tmsi() VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d"); btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT"); + expect_bssap_clear(); gsup_rx("06010809710000004026f0", NULL); + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); btw("LU was successful, and the conn has already been closed"); VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d"); diff --git a/tests/msc_vlr/msc_vlr_test_rest.err b/tests/msc_vlr/msc_vlr_test_rest.err index 6122040c9..1b5f3406d 100644 --- a/tests/msc_vlr/msc_vlr_test_rest.err +++ b/tests/msc_vlr/msc_vlr_test_rest.err @@ -28,13 +28,14 @@ DMM Subscr_Conn(test){SUBSCR_CONN_S_ACCEPTED}: Received Event SUBSCR_CONN_E_CN_C DMM Subscr_Conn(test){SUBSCR_CONN_S_ACCEPTED}: state_chg to SUBSCR_CONN_S_RELEASED DMM Subscr_Conn(test){SUBSCR_CONN_S_RELEASED}: Terminating (cause = OSMO_FSM_TERM_REGULAR) DMM msc_subscr_conn_close(vsub=unknown, cause=2): no conn fsm, releasing directly without release event. -DMM unknown: Unknown RAN type, cannot tx release/clear +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL subscr unknown: Freeing subscriber connection DREF VLR subscr unknown usage decreases to: 0 DREF freeing VLR subscr unknown DMM Subscr_Conn(test){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(test){SUBSCR_CONN_S_RELEASED}: Deallocated + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_early_stage: SUCCESS @@ -70,11 +71,13 @@ DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000004620){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=unknown, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 - conn was released llist_count(&net->subscr_conns) == 0 ===== test_cm_service_without_lu: SUCCESS @@ -187,6 +190,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -194,6 +198,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -300,6 +305,7 @@ DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000004620){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -307,6 +313,7 @@ DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000004620){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 @@ -323,8 +330,10 @@ DREF VLR subscr MSISDN:46071 usage decreases to: 1 DREF VLR subscr MSISDN:46071 usage decreases to: 0 DREF freeing VLR subscr MSISDN:46071 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_two_lu: SUCCESS @@ -461,6 +470,7 @@ DVLR vlr_lu_fsm(591536962){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cause OSM DVLR vlr_lu_fsm(591536962){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(591536962){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:46071, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:46071: MSC conn use - 1 == 0 DRLL subscr MSISDN:46071: Freeing subscriber connection DREF VLR subscr MSISDN:46071 usage decreases to: 2 @@ -468,6 +478,7 @@ DMM Subscr_Conn(591536962){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(591536962){SUBSCR_CONN_S_RELEASED}: Deallocated DREF VLR subscr MSISDN:46071 usage decreases to: 1 <-- GSUP rx OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: vlr_gsupc_read_cb() returns 0 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed lu_result_sent == 1 llist_count(&net->subscr_conns) == 0 diff --git a/tests/msc_vlr/msc_vlr_test_umts_authen.c b/tests/msc_vlr/msc_vlr_test_umts_authen.c index d026f0229..1175bf875 100644 --- a/tests/msc_vlr/msc_vlr_test_umts_authen.c +++ b/tests/msc_vlr/msc_vlr_test_umts_authen.c @@ -23,6 +23,19 @@ #include "msc_vlr_tests.h" +#define ASSERT_RELEASE_CLEAR(via_ran) \ + switch (via_ran) { \ + case RAN_GERAN_A: \ + VERBOSE_ASSERT(bssap_clear_sent, == true, "%d"); \ + break; \ + case RAN_UTRAN_IU: \ + VERBOSE_ASSERT(iu_release_sent, == true, "%d"); \ + break; \ + default: \ + OSMO_ASSERT(false); \ + break; \ + } + void _test_umts_authen(enum ran_type via_ran) { struct vlr_subscr *vsub; @@ -167,7 +180,9 @@ void _test_umts_authen(enum ran_type via_ran) vlr_subscr_put(vsub); btw("MS sends TMSI Realloc Complete"); + expect_release_clear(via_ran); ms_sends_msg("055b"); + ASSERT_RELEASE_CLEAR(via_ran); btw("LU was successful, and the conn has already been closed"); EXPECT_CONN_COUNT(0); @@ -210,8 +225,10 @@ void _test_umts_authen(enum ran_type via_ran) btw("a USSD request is serviced"); dtap_expect_tx_ussd("Your extension is 42342\r"); + expect_release_clear(via_ran); ms_sends_msg("0b3b1c15a11302010002013b300b04010f0406aa510c061b017f0100"); OSMO_ASSERT(dtap_tx_confirmed); + ASSERT_RELEASE_CLEAR(via_ran); btw("all requests serviced, conn has been released"); EXPECT_CONN_COUNT(0); @@ -287,15 +304,19 @@ void _test_umts_authen(enum ran_type via_ran) btw("MS also sends RP-ACK, MSC in turn sends CP-ACK for that"); dtap_expect_tx("0904"); + expect_release_clear(via_ran); ms_sends_msg("890106020041020000"); VERBOSE_ASSERT(dtap_tx_confirmed, == true, "%d"); + ASSERT_RELEASE_CLEAR(via_ran); btw("SMS is done, conn is gone"); EXPECT_CONN_COUNT(0); BTW("subscriber detaches"); + expect_release_clear(via_ran); ms_sends_msg("050130" "089910070000106005" /* IMSI */); + ASSERT_RELEASE_CLEAR(via_ran); EXPECT_CONN_COUNT(0); clear_vlr(); @@ -527,7 +548,9 @@ void _test_umts_authen_resync(enum ran_type via_ran) vlr_subscr_put(vsub); btw("MS sends TMSI Realloc Complete"); + expect_release_clear(via_ran); ms_sends_msg("055b"); + ASSERT_RELEASE_CLEAR(via_ran); btw("LU was successful, and the conn has already been closed"); EXPECT_CONN_COUNT(0); diff --git a/tests/msc_vlr/msc_vlr_test_umts_authen.err b/tests/msc_vlr/msc_vlr_test_umts_authen.err index f59612cef..57f5e8e85 100644 --- a/tests/msc_vlr/msc_vlr_test_umts_authen.err +++ b/tests/msc_vlr/msc_vlr_test_umts_authen.err @@ -164,12 +164,14 @@ DVLR vlr_lu_fsm(901700000010650){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000010650){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000010650){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:42342, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:42342: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:42342: MSC conn use - 1 == 0 DRLL subscr MSISDN:42342: Freeing subscriber connection DREF VLR subscr MSISDN:42342 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed llist_count(&net->subscr_conns) == 0 --- @@ -275,12 +277,14 @@ DVLR Process_Access_Request_VLR(901700000010650){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000010650){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000010650){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:42342, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:42342: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:42342: MSC conn use - 1 == 0 DRLL subscr MSISDN:42342: Freeing subscriber connection DREF VLR subscr MSISDN:42342 usage decreases to: 1 + bssap_clear_sent == 1 - all requests serviced, conn has been released llist_count(&net->subscr_conns) == 0 --- @@ -421,6 +425,7 @@ DVLR Process_Access_Request_VLR(901700000010650){PR_ARQ_S_DONE}: Removing from p DVLR Process_Access_Request_VLR(901700000010650){PR_ARQ_S_DONE}: Freeing instance DVLR Process_Access_Request_VLR(901700000010650){PR_ARQ_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:42342, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:42342: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Deallocated @@ -428,6 +433,7 @@ DREF MSISDN:42342: MSC conn use - 1 == 0 DRLL subscr MSISDN:42342: Freeing subscriber connection DREF VLR subscr MSISDN:42342 usage decreases to: 1 dtap_tx_confirmed == 1 + bssap_clear_sent == 1 - SMS is done, conn is gone llist_count(&net->subscr_conns) == 0 --- @@ -443,8 +449,10 @@ DREF VLR subscr MSISDN:42342 usage decreases to: 1 DREF VLR subscr MSISDN:42342 usage decreases to: 0 DREF freeing VLR subscr MSISDN:42342 DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + bssap_clear_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_umts_authen_geran: SUCCESS @@ -633,6 +641,7 @@ DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:42342: MSC conn use - 1 == 0 DRLL subscr MSISDN:42342: Freeing subscriber connection DREF VLR subscr MSISDN:42342 usage decreases to: 1 + iu_release_sent == 1 - LU was successful, and the conn has already been closed llist_count(&net->subscr_conns) == 0 --- @@ -752,6 +761,7 @@ DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:42342: MSC conn use - 1 == 0 DRLL subscr MSISDN:42342: Freeing subscriber connection DREF VLR subscr MSISDN:42342 usage decreases to: 1 + iu_release_sent == 1 - all requests serviced, conn has been released llist_count(&net->subscr_conns) == 0 --- @@ -907,6 +917,7 @@ DREF MSISDN:42342: MSC conn use - 1 == 0 DRLL subscr MSISDN:42342: Freeing subscriber connection DREF VLR subscr MSISDN:42342 usage decreases to: 1 dtap_tx_confirmed == 1 + iu_release_sent == 1 - SMS is done, conn is gone llist_count(&net->subscr_conns) == 0 --- @@ -925,6 +936,7 @@ DMM msc_subscr_conn_close(vsub=unknown, cause=0): no conn fsm, releasing directl - Iu Release --RAN_UTRAN_IU--> MS DREF unknown: MSC conn use - 1 == 0 DRLL Freeing subscriber connection with NULL subscriber + iu_release_sent == 1 llist_count(&net->subscr_conns) == 0 ===== test_umts_authen_utran: SUCCESS @@ -1127,12 +1139,14 @@ DVLR vlr_lu_fsm(901700000010650){VLR_ULA_S_DONE}: fsm_lu_cleanup called with cau DVLR vlr_lu_fsm(901700000010650){VLR_ULA_S_DONE}: Freeing instance DVLR vlr_lu_fsm(901700000010650){VLR_ULA_S_DONE}: Deallocated DMM msc_subscr_conn_close(vsub=MSISDN:42342, cause=2): no conn fsm, releasing directly without release event. +- BSSAP Clear --RAN_GERAN_A--> MS DREF MSISDN:42342: MSC conn use - 1 == 1 DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Freeing instance DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:42342: MSC conn use - 1 == 0 DRLL subscr MSISDN:42342: Freeing subscriber connection DREF VLR subscr MSISDN:42342 usage decreases to: 1 + bssap_clear_sent == 1 - LU was successful, and the conn has already been closed llist_count(&net->subscr_conns) == 0 DREF freeing VLR subscr MSISDN:42342 @@ -1353,6 +1367,7 @@ DMM Subscr_Conn(901700000010650){SUBSCR_CONN_S_RELEASED}: Deallocated DREF MSISDN:42342: MSC conn use - 1 == 0 DRLL subscr MSISDN:42342: Freeing subscriber connection DREF VLR subscr MSISDN:42342 usage decreases to: 1 + iu_release_sent == 1 - LU was successful, and the conn has already been closed llist_count(&net->subscr_conns) == 0 DREF freeing VLR subscr MSISDN:42342 diff --git a/tests/msc_vlr/msc_vlr_tests.c b/tests/msc_vlr/msc_vlr_tests.c index 6da34aed6..a45b10fbb 100644 --- a/tests/msc_vlr/msc_vlr_tests.c +++ b/tests/msc_vlr/msc_vlr_tests.c @@ -59,6 +59,11 @@ const char *auth_request_expect_autn; bool cipher_mode_cmd_sent; bool cipher_mode_cmd_sent_with_imeisv; +bool iu_release_expected = false; +bool iu_release_sent = false; +bool bssap_clear_expected = false; +bool bssap_clear_sent = false; + struct msgb *msgb_from_hex(const char *label, uint16_t size, const char *hex) { struct msgb *msg = msgb_alloc(size, label); @@ -149,7 +154,6 @@ struct gsm_subscriber_connection *conn_new(void) if (conn->via_ran == RAN_UTRAN_IU) { struct ue_conn_ctx *ue_ctx = talloc_zero(conn, struct ue_conn_ctx); *ue_ctx = (struct ue_conn_ctx){ - .link = (void*)0x23, .conn_id = 42, }; conn->iu.ue_ctx = ue_ctx; @@ -298,9 +302,9 @@ int __wrap_iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac) return _paging_sent(RAN_UTRAN_IU, imsi, tmsi ? *tmsi : GSM_RESERVED_TMSI, lac); } -/* override, requires '-Wl,--wrap=a_page' */ -int __real_a_page(const char *imsi, uint32_t tmsi, uint16_t lac); -int __wrap_a_page(const char *imsi, uint32_t tmsi, uint16_t lac) +/* override, requires '-Wl,--wrap=a_iface_tx_paging' */ +int __real_a_iface_tx_paging(const char *imsi, uint32_t tmsi, uint16_t lac); +int __wrap_a_iface_tx_paging(const char *imsi, uint32_t tmsi, uint16_t lac) { return _paging_sent(RAN_GERAN_A, imsi, tmsi, lac); } @@ -333,6 +337,11 @@ void clear_vlr() next_rand_byte = 0; + iu_release_expected = false; + iu_release_sent = false; + bssap_clear_expected = false; + bssap_clear_sent = false; + osmo_gettimeofday_override = false; } @@ -475,6 +484,9 @@ int __real_iu_tx_release(struct ue_conn_ctx *ctx, const struct RANAP_Cause *caus int __wrap_iu_tx_release(struct ue_conn_ctx *ctx, const struct RANAP_Cause *cause) { btw("Iu Release --%s--> MS", ran_type_name(RAN_UTRAN_IU)); + OSMO_ASSERT(iu_release_expected); + iu_release_expected = false; + iu_release_sent = true; return 0; } @@ -486,13 +498,24 @@ int __wrap_iu_tx_common_id(struct ue_conn_ctx *ue_ctx, const char *imsi) return 0; } -/* override, requires '-Wl,--wrap=a_tx' */ -int __real_a_tx(struct msgb *msg, uint8_t sapi); -int __wrap_a_tx(struct msgb *msg, uint8_t sapi) +/* override, requires '-Wl,--wrap=a_iface_tx_dtap' */ +int __real_a_iface_tx_dtap(struct msgb *msg); +int __wrap_a_iface_tx_dtap(struct msgb *msg) { return _validate_dtap(msg, RAN_GERAN_A); } +/* override, requires '-Wl,--wrap=a_iface_tx_clear_cmd' */ +int __real_a_iface_tx_clear_cmd(struct gsm_subscriber_connection *conn); +int __wrap_a_iface_tx_clear_cmd(struct gsm_subscriber_connection *conn) +{ + btw("BSSAP Clear --%s--> MS", ran_type_name(RAN_GERAN_A)); + OSMO_ASSERT(bssap_clear_expected); + bssap_clear_expected = false; + bssap_clear_sent = true; + return 0; +} + static int fake_vlr_tx_lu_acc(void *msc_conn_ref, uint32_t send_tmsi) { struct gsm_subscriber_connection *conn = msc_conn_ref; diff --git a/tests/msc_vlr/msc_vlr_tests.h b/tests/msc_vlr/msc_vlr_tests.h index ae0f83c9a..c2c5a58a7 100644 --- a/tests/msc_vlr/msc_vlr_tests.h +++ b/tests/msc_vlr/msc_vlr_tests.h @@ -79,6 +79,38 @@ extern bool cipher_mode_cmd_sent_with_imeisv; extern bool paging_sent; extern bool paging_stopped; +extern bool iu_release_expected; +extern bool iu_release_sent; +extern bool bssap_clear_expected; +extern bool bssap_clear_sent; + +static inline void expect_iu_release() +{ + iu_release_expected = true; + iu_release_sent = false; +} + +static inline void expect_bssap_clear() +{ + bssap_clear_expected = true; + bssap_clear_sent = false; +} + +static inline void expect_release_clear(enum ran_type via_ran) +{ + switch (via_ran) { + case RAN_GERAN_A: + expect_bssap_clear(); + return; + case RAN_UTRAN_IU: + expect_iu_release(); + return; + default: + OSMO_ASSERT(false); + break; + } +} + struct msc_vlr_test_cmdline_opts { bool verbose; int run_test_nr;