aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rwxr-xr-xcontrib/jenkins.sh9
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc.cfg8
-rw-r--r--include/openbsc/Makefile.am4
-rw-r--r--include/openbsc/a_iface.h76
-rw-r--r--include/openbsc/a_iface_bssap.h41
-rw-r--r--include/openbsc/a_reset.h63
-rw-r--r--include/openbsc/bsc_msc.h12
-rw-r--r--include/openbsc/bsc_msc_data.h32
-rw-r--r--include/openbsc/debug.h1
-rw-r--r--include/openbsc/gsm_04_08.h2
-rw-r--r--include/openbsc/gsm_data.h39
-rw-r--r--include/openbsc/gsm_data_shared.h10
-rw-r--r--include/openbsc/iu.h13
-rw-r--r--include/openbsc/mgcpgw_client.h28
-rw-r--r--include/openbsc/msc_ifaces.h20
-rw-r--r--include/openbsc/osmo_bsc.h6
-rw-r--r--include/openbsc/osmo_bsc_reset.h34
-rw-r--r--include/openbsc/osmo_bsc_sigtran.h48
-rw-r--r--include/openbsc/osmo_msc.h7
-rw-r--r--include/openbsc/transaction.h10
-rw-r--r--src/gprs/Makefile.am2
-rw-r--r--src/gprs/sgsn_main.c17
-rw-r--r--src/libbsc/abis_rsl.c3
-rw-r--r--src/libbsc/bsc_vty.c81
-rw-r--r--src/libbsc/handover_logic.c3
-rw-r--r--src/libcommon-cs/Makefile.am1
-rw-r--r--src/libcommon-cs/a_reset.c224
-rw-r--r--src/libcommon-cs/common_cs.c2
-rw-r--r--src/libcommon/common_vty.c20
-rw-r--r--src/libcommon/debug.c6
-rw-r--r--src/libiu/iu.c138
-rw-r--r--src/libiu/iu_vty.c27
-rw-r--r--src/libmgcp/mgcp_protocol.c44
-rw-r--r--src/libmgcp/mgcpgw_client.c95
-rw-r--r--src/libmgcp/mgcpgw_client_vty.c51
-rw-r--r--src/libmsc/Makefile.am2
-rw-r--r--src/libmsc/a_iface.c582
-rw-r--r--src/libmsc/a_iface_bssap.c717
-rw-r--r--src/libmsc/gsm_04_08.c207
-rw-r--r--src/libmsc/gsm_subscriber.c3
-rw-r--r--src/libmsc/iucs.c24
-rw-r--r--src/libmsc/msc_ifaces.c214
-rw-r--r--src/libmsc/msc_vty.c28
-rw-r--r--src/libmsc/osmo_msc.c23
-rw-r--r--src/libmsc/subscr_conn.c3
-rw-r--r--src/osmo-bsc/Makefile.am4
-rw-r--r--src/osmo-bsc/osmo_bsc_api.c41
-rw-r--r--src/osmo-bsc/osmo_bsc_audio.c70
-rw-r--r--src/osmo-bsc/osmo_bsc_bssap.c307
-rw-r--r--src/osmo-bsc/osmo_bsc_main.c10
-rw-r--r--src/osmo-bsc/osmo_bsc_msc.c62
-rw-r--r--src/osmo-bsc/osmo_bsc_reset.c190
-rw-r--r--src/osmo-bsc/osmo_bsc_sccp.c328
-rw-r--r--src/osmo-bsc/osmo_bsc_sigtran.c561
-rw-r--r--src/osmo-bsc/osmo_bsc_vty.c94
-rw-r--r--src/osmo-bsc_mgcp/Makefile.am1
-rw-r--r--src/osmo-bsc_nat/Makefile.am1
-rw-r--r--src/osmo-msc/msc_main.c81
-rw-r--r--tests/msc_vlr/Makefile.am7
-rw-r--r--tests/msc_vlr/msc_vlr_test_gsm_authen.c36
-rw-r--r--tests/msc_vlr/msc_vlr_test_gsm_authen.err34
-rw-r--r--tests/msc_vlr/msc_vlr_test_gsm_ciph.c30
-rw-r--r--tests/msc_vlr/msc_vlr_test_gsm_ciph.err28
-rw-r--r--tests/msc_vlr/msc_vlr_test_hlr_reject.c22
-rw-r--r--tests/msc_vlr/msc_vlr_test_hlr_reject.err22
-rw-r--r--tests/msc_vlr/msc_vlr_test_hlr_timeout.c4
-rw-r--r--tests/msc_vlr/msc_vlr_test_hlr_timeout.err4
-rw-r--r--tests/msc_vlr/msc_vlr_test_ms_timeout.c6
-rw-r--r--tests/msc_vlr/msc_vlr_test_ms_timeout.err6
-rw-r--r--tests/msc_vlr/msc_vlr_test_no_authen.c26
-rw-r--r--tests/msc_vlr/msc_vlr_test_no_authen.err26
-rw-r--r--tests/msc_vlr/msc_vlr_test_reject_concurrency.c30
-rw-r--r--tests/msc_vlr/msc_vlr_test_reject_concurrency.err38
-rw-r--r--tests/msc_vlr/msc_vlr_test_rest.c13
-rw-r--r--tests/msc_vlr/msc_vlr_test_rest.err13
-rw-r--r--tests/msc_vlr/msc_vlr_test_umts_authen.c23
-rw-r--r--tests/msc_vlr/msc_vlr_test_umts_authen.err15
-rw-r--r--tests/msc_vlr/msc_vlr_tests.c37
-rw-r--r--tests/msc_vlr/msc_vlr_tests.h32
80 files changed, 4478 insertions, 706 deletions
diff --git a/configure.ac b/configure.ac
index b0d6b236..90a95e67 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 7734965f..2b667ad0 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 534605a7..7c10e9de 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 995f02d0..25709f1d 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 00000000..149f1c71
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <openbsc/a_reset.h>
+
+/* A struct to keep a context information about the BSCs we are associated with */
+struct bsc_context {
+ struct llist_head list;
+
+ /* Holds a copy of the sccp address of the BSC,
+ * this address will become known as soon as
+ * a remote BSC tries to make a connection or
+ * sends a RESET request via UNIDATA */
+ struct osmo_sccp_addr bsc_addr;
+
+ /* Holds a copy of the our local MSC address,
+ * this will be the sccp-address that is associated
+ * with the A interface */
+ struct osmo_sccp_addr msc_addr;
+
+ /* A pointer to the reset handler FSM, the
+ * state machine is allocated when the BSC
+ * is registerd. */
+ struct 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 00000000..237c618f
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 00000000..7aaab062
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 39258d36..380eb17c 100644
--- a/include/openbsc/bsc_msc.h
+++ b/include/openbsc/bsc_msc.h
@@ -24,6 +24,8 @@
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
+#include <osmocom/sigtran/sccp_sap.h>
+#include <openbsc/a_reset.h>
#include <netinet/in.h>
@@ -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 38e87cfb..4a283d16 100644
--- a/include/openbsc/bsc_msc_data.h
+++ b/include/openbsc/bsc_msc_data.h
@@ -32,6 +32,14 @@
#include <osmocom/core/timer.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+#include <osmocom/sigtran/osmo_ss7.h>
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/sigtran/sccp_helpers.h>
+#include <osmocom/sigtran/protocol/sua.h>
+#include <osmocom/sigtran/protocol/m3ua.h>
+#include <osmocom/core/fsm.h>
+
#include <regex.h>
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 de00b293..65e197d5 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 6d6ead18..ca251b00 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 f4de3810..43fc6d33 100644
--- a/include/openbsc/gsm_data.h
+++ b/include/openbsc/gsm_data.h
@@ -12,12 +12,14 @@
#include <osmocom/core/stats.h>
#include <osmocom/crypt/auth.h>
+#include <osmocom/sigtran/sccp_sap.h>
#include <openbsc/common.h>
#include <openbsc/rest_octets.h>
#include <openbsc/common_cs.h>
#include <openbsc/mgcpgw_client.h>
+
/** 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 60da2e5c..bed46d25 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 5b298301..08e4cd06 100644
--- a/include/openbsc/iu.h
+++ b/include/openbsc/iu.h
@@ -5,13 +5,15 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/gsm48.h>
+#include <osmocom/sigtran/sccp_sap.h>
#include <openbsc/common.h>
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 b353db0a..b1b5fd42 100644
--- a/include/openbsc/mgcpgw_client.h
+++ b/include/openbsc/mgcpgw_client.h
@@ -3,11 +3,11 @@
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/write_queue.h>
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 2965c72c..a1071ae9 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 9e688fd5..5ebea507 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 00000000..578f763e
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* 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 00000000..fbcfcb3f
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/bsc_msc_data.h>
+
+/* 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 bc96f1d5..cdfd27f1 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 713d878f..4930fbd3 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 cb099790..e05eb79f 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 d5d43ad2..d56af0ed 100644
--- a/src/gprs/sgsn_main.c
+++ b/src/gprs/sgsn_main.c
@@ -63,6 +63,8 @@
#include <osmocom/ctrl/control_if.h>
#include <osmocom/ctrl/ports.h>
+#include <osmocom/sigtran/protocol/m3ua.h>
+
#include <gtp.h>
#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 4f687a03..66cda820 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 bd363ae5..d55c6eb3 100644
--- a/src/libbsc/bsc_vty.c
+++ b/src/libbsc/bsc_vty.c
@@ -57,6 +57,7 @@
#include <openbsc/pcu_if.h>
#include <openbsc/common_cs.h>
#include <openbsc/vlr.h>
+#include <openbsc/handover.h>
#include <inttypes.h>
@@ -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 57d1dcd3..14566cfa 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 f3921ba5..21c27455 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 00000000..c0294c79
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/fsm.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <openbsc/debug.h>
+#include <openbsc/bsc_msc_data.h>
+#include <openbsc/osmo_bsc_sigtran.h>
+
+#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 99206c86..d6dff95d 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 6e1c10b0..1443791f 100644
--- a/src/libcommon/common_vty.c
+++ b/src/libcommon/common_vty.c
@@ -34,6 +34,7 @@
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
#include <osmocom/vty/vty.h>
+#include <osmocom/sigtran/osmo_ss7.h>
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 7dbbc6ac..72364133 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 5d56a4a3..77949774 100644
--- a/src/libiu/iu.c
+++ b/src/libiu/iu.c
@@ -36,9 +36,10 @@
#include <osmocom/gsm/gsm48.h>
#include <osmocom/gprs/gprs_msgb.h>
-#include <osmocom/sigtran/sua.h>
+#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sigtran/sccp_helpers.h>
+#include <osmocom/sigtran/protocol/m3ua.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/iu.h>
@@ -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 73ad126b..3fd3cd15 100644
--- a/src/libiu/iu_vty.c
+++ b/src/libiu/iu_vty.c
@@ -23,6 +23,8 @@
#include <osmocom/core/logging.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/logging.h>
+#include <osmocom/sigtran/osmo_ss7.h>
+#include <osmocom/sigtran/sccp_sap.h>
#include <openbsc/iu.h>
@@ -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 96542c5a..c8b6e866 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 9f0c84de..1910a9f3 100644
--- a/src/libmgcp/mgcpgw_client.c
+++ b/src/libmgcp/mgcpgw_client.c
@@ -35,15 +35,6 @@
#include <unistd.h>
#include <string.h>
-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;i<last_endpoint;i++)
+ {
+ if (endpoint_in_use(i,client) == false) {
+ endpoint = talloc_zero(client, struct mgcp_inuse_endpoint);
+ endpoint->id = 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 a42ee4e5..80680007 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 4726bbe4..9f246b3d 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 caf9d4b0..93e8ab5e 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 <info@sysmocom.de>
- *
+/* (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 <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
-
+#include <osmocom/sigtran/sccp_helpers.h>
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/sigtran/osmo_ss7.h>
+#include <osmocom/sigtran/protocol/m3ua.h>
+#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/gsm/protocol/gsm_08_08.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm0808_utils.h>
#include <openbsc/debug.h>
-
-#include <openbsc/gsm_data.h>
#include <openbsc/msc_ifaces.h>
-#include <openbsc/debug.h>
+#include <openbsc/a_iface.h>
+#include <openbsc/a_iface_bssap.h>
+#include <openbsc/transaction.h>
+#include <openbsc/mgcpgw_client.h>
+#include <osmocom/core/byteswap.h>
+#include <osmocom/sccp/sccp_types.h>
+#include <openbsc/a_reset.h>
+#include <openbsc/osmo_msc.h>
+
+/* 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)
+{
+ 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);
+}
+
+/* Delete info of a closed connection from the active connection list */
+void a_delete_bsc_con(uint32_t conn_id)
+{
+ 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);
+ }
+ }
+}
+
+/* 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;
+}
-int a_tx(struct msgb *msg)
+/* 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)
{
- 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_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;
}
-int a_page(const char *imsi, uint32_t tmsi, uint16_t lac)
+/* Send DTAP message via A-interface */
+int a_iface_tx_dtap(struct msgb *msg)
{
- 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 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);
}
-int msc_gsm0808_tx_cipher_mode(struct gsm_subscriber_connection *conn, int cipher,
- const uint8_t *key, int len, int include_imeisv)
+/* 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 00000000..561ccdeb
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/sigtran/sccp_helpers.h>
+#include <osmocom/sccp/sccp_types.h>
+#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/gsm/gsm0808_utils.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/a_iface_bssap.h>
+#include <openbsc/a_iface.h>
+#include <openbsc/iu.h>
+#include <openbsc/osmo_msc.h>
+#include <osmocom/core/byteswap.h>
+#include <openbsc/a_reset.h>
+
+#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 28cba5b8..b6746a56 100644
--- a/src/libmsc/gsm_04_08.c
+++ b/src/libmsc/gsm_04_08.c
@@ -79,6 +79,8 @@
#include <openbsc/iu.h>
#endif
+#include <openbsc/a_iface.h>
+
#include <assert.h>
@@ -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_FRAME_DROP:
- case MNCC_FRAME_RECV:
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 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 ac6c96a8..73361a16 100644
--- a/src/libmsc/gsm_subscriber.c
+++ b/src/libmsc/gsm_subscriber.c
@@ -43,6 +43,7 @@
#include <openbsc/iu.h>
#include <openbsc/osmo_msc.h>
#include <openbsc/msc_ifaces.h>
+#include <openbsc/a_iface.h>
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 aeda1406..be026c85 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 56cbd49d..7d2e8981 100644
--- a/src/libmsc/msc_ifaces.c
+++ b/src/libmsc/msc_ifaces.c
@@ -29,6 +29,7 @@
#include <openbsc/mgcp.h>
#include <openbsc/mgcpgw_client.h>
#include <openbsc/vlr.h>
+#include <openbsc/a_iface.h>
#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;
+
+ if (!trans)
+ return -EINVAL;
+ if (!trans->conn)
+ return -EINVAL;
- /* 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 ++;
+ conn = trans->conn;
+ mgcp = conn->network->mgcpgw.client;
- conn->iu.mgcp_rtp_endpoint =
+#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 82dc7d67..01e7e829 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 ddc38361..866cfbd0 100644
--- a/src/libmsc/osmo_msc.c
+++ b/src/libmsc/osmo_msc.c
@@ -29,11 +29,12 @@
#include <openbsc/vlr.h>
#include <openbsc/osmo_msc.h>
#include <openbsc/iu.h>
+#include <openbsc/a_iface.h>
#include <openbsc/gsm_04_11.h>
/* 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 31decc7b..cdeeae90 100644
--- a/src/libmsc/subscr_conn.c
+++ b/src/libmsc/subscr_conn.c
@@ -31,6 +31,8 @@
#include <openbsc/transaction.h>
#include <openbsc/signal.h>
#include <openbsc/iu.h>
+#include <openbsc/a_iface.h>
+
#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 ae9410c9..5642fb2e 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 8c33e2b5..f7343f74 100644
--- a/src/osmo-bsc/osmo_bsc_api.c
+++ b/src/osmo-bsc/osmo_bsc_api.c
@@ -27,6 +27,7 @@
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/sccp/sccp.h>
+#include <openbsc/osmo_bsc_sigtran.h>
#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");
-
- resp = gsm0808_create_assignment_completed(rr_cause, chosen_channel,
- encr_alg_id, speech_model);
- queue_msg_or_return(resp);
+ 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;
+
+ } 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 11602090..b4ffa88f 100644
--- a/src/osmo-bsc/osmo_bsc_audio.c
+++ b/src/osmo-bsc/osmo_bsc_audio.c
@@ -26,15 +26,57 @@
#include <openbsc/gsm_data.h>
#include <openbsc/debug.h>
#include <openbsc/signal.h>
+#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/gsm/gsm0808_utils.h>
+#include <openbsc/osmo_bsc_sigtran.h>
#include <arpa/inet.h>
+/* 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 100f6644..4353b9ad 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -29,11 +29,21 @@
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/gsm/gsm0808_utils.h>
+#include <openbsc/osmo_bsc_sigtran.h>
+#include <openbsc/a_reset.h>
+#include <osmocom/core/byteswap.h>
+
+#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;
+ }
+ }
+
+ /* 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;
}
- LOGP(DMSC, LOGL_FATAL, "Should not be reached.\n");
- return GSM48_CMODE_SPEECH_AMR;
+ 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");
+ /* 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;
}
- conn->cic = osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE));
- timeslot = conn->cic & 0x1f;
- multiplex = (conn->cic & ~0x1f) >> 5;
+ /* 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;
+ }
- /*
- * 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;
+ /* 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;
}
- /*
- * 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]);
+ /* 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;
}
- /*
- * 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;
- }
- }
+ /* 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;
}
- if (chan_mode == GSM48_CMODE_SIGN) {
+ /* 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 90651b95..cf188a9e 100644
--- a/src/osmo-bsc/osmo_bsc_main.c
+++ b/src/osmo-bsc/osmo_bsc_main.c
@@ -28,6 +28,7 @@
#include <openbsc/vty.h>
#include <openbsc/ipaccess.h>
#include <openbsc/ctrl.h>
+#include <openbsc/osmo_bsc_sigtran.h>
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/ctrl/control_if.h>
@@ -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 8d02624b..351fd2ce 100644
--- a/src/osmo-bsc/osmo_bsc_msc.c
+++ b/src/osmo-bsc/osmo_bsc_msc.c
@@ -42,7 +42,7 @@
#include <netinet/tcp.h>
#include <unistd.h>
-
+#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 00000000..0baf0808
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/fsm.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <openbsc/debug.h>
+#include <openbsc/bsc_msc_data.h>
+#include <openbsc/osmo_bsc_sigtran.h>
+
+#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 e242390e..00000000
--- 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 <zecke@selfish.org>
- * (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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/osmo_bsc.h>
-#include <openbsc/osmo_bsc_grace.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/gsm_04_80.h>
-#include <openbsc/debug.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/signal.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/gsm0808.h>
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-
-#include <osmocom/sccp/sccp.h>
-
-/* 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 00000000..0f6ca334
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/sigtran/osmo_ss7.h>
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/sccp/sccp_types.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/core/msgb.h>
+#include <openbsc/bsc_msc_data.h>
+#include <openbsc/debug.h>
+#include <openbsc/osmo_bsc.h>
+#include <openbsc/osmo_bsc_grace.h>
+#include <openbsc/osmo_bsc_sigtran.h>
+#include <openbsc/a_reset.h>
+#include <openbsc/gsm_04_80.h>
+
+/* 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 2e2e99bf..8edcbf39 100644
--- a/src/osmo-bsc/osmo_bsc_vty.c
+++ b/src/osmo-bsc/osmo_bsc_vty.c
@@ -28,6 +28,7 @@
#include <osmocom/core/talloc.h>
#include <osmocom/vty/logging.h>
+#include <osmocom/sccp/sccp_types.h>
#include <time.h>
@@ -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 a19a4ebc..b4e0d856 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 be33d289..b7c13ad3 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 723c0e6e..cd713eee 100644
--- a/src/osmo-msc/msc_main.c
+++ b/src/osmo-msc/msc_main.c
@@ -63,6 +63,7 @@
#include <openbsc/ctrl.h>
#include <openbsc/osmo_bsc_rf.h>
#include <openbsc/smpp.h>
+#include <osmocom/sigtran/osmo_ss7.h>
#include <openbsc/mgcpgw_client.h>
#include <openbsc/msc_ifaces.h>
@@ -70,6 +71,7 @@
#include <openbsc/iu.h>
#include <openbsc/iucs.h>
#include <openbsc/iucs_ranap.h>
+#include <openbsc/a_iface.h>
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 17bfbdfa..6d51d6eb 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 22e549b2..44e40523 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 b6b84ede..bca64a45 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 589f7da9..42e7c737 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 bd8d1b9b..c219b496 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 c4cf3534..095da817 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 9650df9d..c946c1ef 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 2872f0cf..c2f17c85 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 f83cc297..e34490d8 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 47ca9678..d8a3a314 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 1574ef9e..e267c5b0 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 f5e2e2b6..34e392e7 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 015e55b9..4f407368 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 15988584..2377c19b 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 7b0954fd..00945cb8 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 5ccc951b..c5f7fcfb 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 6122040c..1b5f3406 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 d026f022..1175bf87 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 f59612ce..57f5e8e8 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 6da34aed..a45b10fb 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 ae0f83c9..c2c5a58a 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;