aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/libmsc
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src/libmsc')
-rw-r--r--openbsc/src/libmsc/Makefile.am4
-rw-r--r--openbsc/src/libmsc/a_iface.c8
-rw-r--r--openbsc/src/libmsc/gsm_04_08.c135
-rw-r--r--openbsc/src/libmsc/gsm_04_11.c7
-rw-r--r--openbsc/src/libmsc/gsm_04_80.c10
-rw-r--r--openbsc/src/libmsc/gsm_subscriber.c130
-rw-r--r--openbsc/src/libmsc/iucs.c191
-rw-r--r--openbsc/src/libmsc/iucs_ranap.c106
-rw-r--r--openbsc/src/libmsc/msc_ifaces.c240
-rw-r--r--openbsc/src/libmsc/msc_vty.c181
-rw-r--r--openbsc/src/libmsc/osmo_msc.c72
-rw-r--r--openbsc/src/libmsc/silent_call.c3
-rw-r--r--openbsc/src/libmsc/subscr_conn.c62
-rw-r--r--openbsc/src/libmsc/transaction.c24
-rw-r--r--openbsc/src/libmsc/vty_interface_layer3.c102
15 files changed, 1008 insertions, 267 deletions
diff --git a/openbsc/src/libmsc/Makefile.am b/openbsc/src/libmsc/Makefile.am
index 7ab30d029..a320f7d28 100644
--- a/openbsc/src/libmsc/Makefile.am
+++ b/openbsc/src/libmsc/Makefile.am
@@ -12,6 +12,7 @@ AM_CFLAGS = \
$(COVERAGE_CFLAGS) \
$(LIBCRYPTO_CFLAGS) \
$(LIBSMPP34_CFLAGS) \
+ $(LIBASN1C_CFLAGS) \
$(NULL)
noinst_HEADERS = \
@@ -25,11 +26,14 @@ noinst_LIBRARIES = \
libmsc_a_SOURCES = \
a_iface.c \
auth.c \
+ msc_vty.c \
db.c \
gsm_04_08.c \
gsm_04_11.c \
gsm_04_80.c \
gsm_subscriber.c \
+ iucs.c \
+ iucs_ranap.c \
mncc.c \
mncc_builtin.c \
mncc_sock.c \
diff --git a/openbsc/src/libmsc/a_iface.c b/openbsc/src/libmsc/a_iface.c
index 1f471f97b..caf9d4b06 100644
--- a/openbsc/src/libmsc/a_iface.c
+++ b/openbsc/src/libmsc/a_iface.c
@@ -35,6 +35,14 @@ int a_tx(struct msgb *msg)
return -1;
}
+int a_page(const char *imsi, uint32_t tmsi, uint16_t lac)
+{
+ 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;
+}
+
int msc_gsm0808_tx_cipher_mode(struct gsm_subscriber_connection *conn, int cipher,
const uint8_t *key, int len, int include_imeisv)
{
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
index f8d47e4da..67eb5c135 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -31,6 +31,7 @@
#include <netinet/in.h>
#include <regex.h>
#include <sys/types.h>
+#include <openssl/rand.h>
#include "bscconfig.h"
@@ -69,6 +70,10 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/tlv.h>
+#include <osmocom/crypt/auth.h>
+
+#include <openbsc/msc_ifaces.h>
+#include <openbsc/iu.h>
#include <assert.h>
@@ -113,7 +118,7 @@ static int gsm48_conn_sendmsg(struct msgb *msg, struct gsm_subscriber_connection
gh->proto_discr = trans->protocol | (trans->transaction_id << 4);
}
- return gsm0808_submit_dtap(conn, msg, 0, 0);
+ return msc_tx_dtap(conn, msg);
}
int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message)
@@ -149,7 +154,7 @@ void gsm0408_clear_all_trans(struct gsm_network *net, int protocol)
}
/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
-int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause)
+static int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause)
{
struct msgb *msg;
@@ -192,12 +197,17 @@ static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn,
len = gsm48_generate_mid_from_imsi(mi, conn->vsub->imsi);
mid = msgb_put(msg, len);
memcpy(mid, mi, len);
+ DEBUGP(DMM, "-> %s LOCATION UPDATE ACCEPT\n",
+ vlr_subscr_name(conn->vsub));
} else {
/* Include the TMSI, which means that the MS will send a
* TMSI REALLOCATION COMPLETE, and we should wait for
* that until T3250 expiration */
mid = msgb_put(msg, GSM48_MID_TMSI_LEN);
gsm48_generate_mid_from_tmsi(mid, send_tmsi);
+ DEBUGP(DMM, "-> %s LOCATION UPDATE ACCEPT (TMSI = 0x%08x)\n",
+ vlr_subscr_name(conn->vsub),
+ send_tmsi);
}
/* TODO: Follow-on proceed */
/* TODO: CTS permission */
@@ -205,7 +215,6 @@ static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn,
/* TODO: Emergency Number List */
/* TODO: Per-MS T3312 */
- DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
return gsm48_conn_sendmsg(msg, conn, NULL);
}
@@ -265,11 +274,11 @@ int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg)
uint8_t mi_type;
char mi_string[GSM48_MI_SIZE];
enum vlr_lu_type vlr_lu_type = VLR_LU_TYPE_REGULAR;
-
uint32_t tmsi;
char *imsi;
struct osmo_location_area_id old_lai, new_lai;
struct osmo_fsm_inst *lu_fsm;
+ bool is_utran;
int rc;
lu = (struct gsm48_loc_upd_req *) gh->data;
@@ -332,19 +341,21 @@ int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg)
&old_lai.plmn.mnc, &old_lai.lac);
new_lai.plmn.mcc = conn->network->country_code;
new_lai.plmn.mnc = conn->network->network_code;
- new_lai.lac = conn->bts->location_area_code;
+ new_lai.lac = conn->lac;
DEBUGP(DMM, "LU/new-LAC: %u/%u\n", old_lai.lac, new_lai.lac);
+ is_utran = (conn->via_ran == RAN_UTRAN_IU);
lu_fsm = vlr_loc_update(conn->conn_fsm,
SUBSCR_CONN_E_ACCEPTED,
SUBSCR_CONN_E_CN_CLOSE,
(void*)&conn_from_lu,
net->vlr, conn, vlr_lu_type, tmsi, imsi,
&old_lai, &new_lai,
- conn->network->authentication_required,
- conn->network->a5_encryption,
+ is_utran || conn->network->authentication_required,
+ is_utran? VLR_CIPH_A5_3
+ : conn->network->a5_encryption,
classmark_is_r99(&conn->classmark),
- conn->via_ran == RAN_UTRAN_IU,
+ is_utran,
net->vlr->cfg.assign_tmsi);
if (!lu_fsm) {
DEBUGP(DRR, "%s: Can't start LU FSM\n", mi_string);
@@ -643,11 +654,12 @@ int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *ms
uint8_t mi_len = *(classmark2 + classmark2_len);
uint8_t *mi = (classmark2 + classmark2_len + 1);
struct osmo_location_area_id lai;
+ bool is_utran;
int rc;
lai.plmn.mcc = conn->network->country_code;
lai.plmn.mnc = conn->network->network_code;
- lai.lac = conn->bts->location_area_code;
+ lai.lac = conn->lac;
DEBUGP(DMM, "<- CM SERVICE REQUEST ");
if (msg->data_len < sizeof(struct gsm48_service_request*)) {
@@ -708,16 +720,18 @@ int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *ms
send_siemens_mrpci(msg->lchan, classmark2-1);
#endif
+ is_utran = (conn->via_ran == RAN_UTRAN_IU);
vlr_proc_acc_req(conn->conn_fsm,
SUBSCR_CONN_E_ACCEPTED,
SUBSCR_CONN_E_CN_CLOSE,
(void*)&conn_from_cm_service_req,
net->vlr, conn,
VLR_PR_ARQ_T_CM_SERV_REQ, mi-1, &lai,
- conn->network->authentication_required,
- conn->network->a5_encryption,
+ is_utran || conn->network->authentication_required,
+ is_utran? VLR_CIPH_A5_3
+ : conn->network->a5_encryption,
classmark_is_r99(&conn->classmark),
- conn->via_ran == RAN_UTRAN_IU);
+ is_utran);
return 0;
}
@@ -1062,6 +1076,7 @@ static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct m
char mi_string[GSM48_MI_SIZE];
int rc = 0;
struct osmo_location_area_id lai;
+ bool is_utran;
lai.plmn.mcc = conn->network->country_code;
lai.plmn.mnc = conn->network->network_code;
@@ -1087,18 +1102,20 @@ static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct m
memcpy(conn->classmark.classmark2, classmark2_lv+1, *classmark2_lv);
conn->classmark.classmark2_len = *classmark2_lv;
+ is_utran = (conn->via_ran == RAN_UTRAN_IU);
vlr_proc_acc_req(conn->conn_fsm,
SUBSCR_CONN_E_ACCEPTED,
SUBSCR_CONN_E_CN_CLOSE,
(void*)&conn_from_paging_resp,
net->vlr, conn,
VLR_PR_ARQ_T_PAGING_RESP, mi_lv, &lai,
- conn->network->authentication_required,
- conn->network->a5_encryption,
+ is_utran || conn->network->authentication_required,
+ is_utran? VLR_CIPH_A5_3
+ : conn->network->a5_encryption,
classmark_is_r99(&conn->classmark),
- conn->via_ran == RAN_UTRAN_IU);
+ is_utran);
- return rc;
+ return 0;
}
static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct msgb *msg)
@@ -1553,8 +1570,7 @@ static int tch_bridge(struct gsm_network *net, struct gsm_mncc_bridge *bridge)
/* through-connect channel */
return tch_map(trans1->conn->lchan, trans2->conn->lchan);
#else
- /* not implemented yet! */
- return -1;
+ return msc_call_bridge(trans1, trans2);
#endif
}
@@ -1969,15 +1985,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);
+
return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND,
&call_conf);
}
-static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg)
+static int gsm48_cc_tx_call_proc_and_assign(struct gsm_trans *trans, void *arg)
{
struct gsm_mncc *proceeding = arg;
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC PROC");
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ int rc;
gh->msg_type = GSM48_MT_CC_CALL_PROC;
@@ -1993,7 +2012,11 @@ static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg)
if (proceeding->fields & MNCC_F_PROGRESS)
gsm48_encode_progress(msg, 0, &proceeding->progress);
- return gsm48_conn_sendmsg(msg, trans->conn, trans);
+ rc = gsm48_conn_sendmsg(msg, trans->conn, trans);
+ if (rc)
+ return rc;
+
+ return msc_call_assignment(trans);
}
static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg)
@@ -3073,7 +3096,7 @@ static struct downstate {
} downstatelist[] = {
/* mobile originating call establishment */
{SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */
- MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc},
+ MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc_and_assign},
{SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */
MNCC_ALERT_REQ, gsm48_cc_tx_alerting},
{SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */
@@ -3304,15 +3327,15 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
trans_free(trans);
return 0;
}
- /* store setup informations until paging was successfull */
+ /* store setup information until paging succeeds */
memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc));
/* Request a channel */
- trans->paging_request = subscr_request_channel(
+ trans->paging_request = subscr_request_conn(
vsub,
- RSL_CHANNEED_TCH_F,
setup_trig_pag_evt,
- trans);
+ trans,
+ "MNCC: establish call");
if (!trans->paging_request) {
LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n");
vlr_subscr_put(vsub);
@@ -3323,7 +3346,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
return 0;
}
- /* Assign lchan */
+ /* Assign conn */
trans->conn = msc_subscr_conn_get(conn);
vlr_subscr_put(vsub);
} else {
@@ -3577,6 +3600,16 @@ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg)
return -EACCES;
}
+ if (conn->vsub && conn->vsub->cs.attached_via_ran != conn->via_ran) {
+ LOGP(DMM, LOGL_ERROR,
+ "%s: Illegal situation: RAN type mismatch:"
+ " attached via %s, received message via %s\n",
+ vlr_subscr_name(conn->vsub),
+ ran_type_name(conn->vsub->cs.attached_via_ran),
+ ran_type_name(conn->via_ran));
+ return -EACCES;
+ }
+
#if 0
if (silent_call_reroute(conn, msg))
return silent_call_rx(conn, msg);
@@ -3660,7 +3693,13 @@ static int msc_vlr_tx_lu_rej(void *msc_conn_ref, uint8_t cause)
static int msc_vlr_tx_cm_serv_acc(void *msc_conn_ref)
{
struct gsm_subscriber_connection *conn = msc_conn_ref;
- return gsm48_tx_mm_serv_ack(conn);
+ return msc_gsm48_tx_mm_serv_ack(conn);
+}
+
+static int msc_vlr_tx_common_id(void *msc_conn_ref)
+{
+ struct gsm_subscriber_connection *conn = msc_conn_ref;
+ return msc_tx_common_id(conn);
}
/* VLR asks us to transmit a CM Service Reject */
@@ -3694,7 +3733,7 @@ static int msc_vlr_tx_cm_serv_rej(void *msc_conn_ref, enum vlr_proc_arq_result r
break;
};
- return gsm48_tx_mm_serv_rej(conn, cause);
+ return msc_gsm48_tx_mm_serv_rej(conn, cause);
}
/* VLR asks us to start using ciphering */
@@ -3722,9 +3761,41 @@ static int msc_vlr_set_ciph_mode(void *msc_conn_ref,
return -EINVAL;
}
- /* TODO: MSCSPLIT: don't directly push BSC buttons */
- return gsm0808_cipher_mode(conn, ciph, tuple->vec.kc, 8,
- retrieve_imeisv);
+ switch (conn->via_ran) {
+ 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);
+ case RAN_UTRAN_IU:
+ DEBUGP(DMM, "-> SECURITY MODE CONTROL %s\n",
+ vlr_subscr_name(conn->vsub));
+ return iu_tx_sec_mode_cmd(conn->iu.ue_ctx, tuple, 0, 1);
+
+ default:
+ break;
+ }
+ LOGP(DMM, LOGL_ERROR,
+ "%s: cannot start ciphering, unknown RAN type %d\n",
+ vlr_subscr_name(conn->vsub), conn->via_ran);
+ return -ENOTSUP;
+}
+
+void msc_rx_sec_mode_compl(struct gsm_subscriber_connection *conn)
+{
+ struct vlr_ciph_result vlr_res = {};
+
+ if (!conn || !conn->vsub) {
+ LOGP(DMM, LOGL_ERROR,
+ "Rx Security Mode Complete for invalid conn\n");
+ return;
+ }
+
+ DEBUGP(DMM, "<- SECURITY MODE COMPLETE %s\n",
+ vlr_subscr_name(conn->vsub));
+
+ vlr_res.cause = VLR_CIPH_COMPL;
+ vlr_subscr_rx_ciph_res(conn->vsub, &vlr_res);
}
/* VLR informs us that the subscriber data has somehow been modified */
@@ -3740,6 +3811,7 @@ static void msc_vlr_subscr_assoc(void *msc_conn_ref,
struct gsm_subscriber_connection *conn = msc_conn_ref;
OSMO_ASSERT(!conn->vsub);
conn->vsub = vlr_subscr_get(vsub);
+ conn->vsub->cs.attached_via_ran = conn->via_ran;
}
/* operations that we need to implement for libvlr */
@@ -3752,6 +3824,7 @@ static const struct vlr_ops msc_vlr_ops = {
.tx_cm_serv_acc = msc_vlr_tx_cm_serv_acc,
.tx_cm_serv_rej = msc_vlr_tx_cm_serv_rej,
.set_ciph_mode = msc_vlr_set_ciph_mode,
+ .tx_common_id = msc_vlr_tx_common_id,
.subscr_update = msc_vlr_subscr_update,
.subscr_assoc = msc_vlr_subscr_assoc,
};
diff --git a/openbsc/src/libmsc/gsm_04_11.c b/openbsc/src/libmsc/gsm_04_11.c
index 3255a3b6f..bdf2ad7cc 100644
--- a/openbsc/src/libmsc/gsm_04_11.c
+++ b/openbsc/src/libmsc/gsm_04_11.c
@@ -55,7 +55,7 @@
#include <openbsc/paging.h>
#include <openbsc/bsc_rll.h>
#include <openbsc/chan_alloc.h>
-#include <openbsc/bsc_api.h>
+#include <openbsc/msc_ifaces.h>
#include <openbsc/osmo_msc.h>
#include <openbsc/vlr.h>
@@ -128,7 +128,7 @@ static int gsm411_sendmsg(struct gsm_subscriber_connection *conn, struct msgb *m
{
DEBUGP(DLSMS, "GSM4.11 TX %s\n", osmo_hexdump(msg->data, msg->len));
msg->l3h = msg->data;
- return gsm0808_submit_dtap(conn, msg, UM_SAPI_SMS, 1);
+ return msc_tx_dtap(conn, msg);
}
/* Prefix msg with a 04.08/04.11 CP header */
@@ -1016,8 +1016,7 @@ int gsm411_send_sms_subscr(struct vlr_subscr *vsub,
/* if not, we have to start paging */
LOGP(DLSMS, LOGL_DEBUG, "Sending SMS: no connection open, start paging %s\n",
vlr_subscr_name(vsub));
- res = subscr_request_conn(vsub, RSL_CHANNEED_SDCCH, paging_cb_send_sms,
- sms);
+ res = subscr_request_conn(vsub, paging_cb_send_sms, sms, "send SMS");
if (!res) {
send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, GSM_PAGING_BUSY);
sms_free(sms);
diff --git a/openbsc/src/libmsc/gsm_04_80.c b/openbsc/src/libmsc/gsm_04_80.c
index 479d6fbd2..bec1d26f4 100644
--- a/openbsc/src/libmsc/gsm_04_80.c
+++ b/openbsc/src/libmsc/gsm_04_80.c
@@ -32,7 +32,7 @@
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/gsm_04_80.h>
-#include <openbsc/bsc_api.h>
+#include <openbsc/msc_ifaces.h>
#include <osmocom/gsm/gsm0480.h>
#include <osmocom/gsm/gsm_utils.h>
@@ -106,7 +106,7 @@ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
| (1<<7); /* TI direction = 1 */
gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
- return gsm0808_submit_dtap(conn, msg, 0, 0);
+ return msc_tx_dtap(conn, msg);
}
int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
@@ -135,7 +135,7 @@ int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */
gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
- return gsm0808_submit_dtap(conn, msg, 0, 0);
+ return msc_tx_dtap(conn, msg);
}
int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, const char *text)
@@ -143,7 +143,7 @@ int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, cons
struct msgb *msg = gsm0480_create_ussd_notify(level, text);
if (!msg)
return -1;
- return gsm0808_submit_dtap(conn, msg, 0, 0);
+ return msc_tx_dtap(conn, msg);
}
int msc_send_ussd_release_complete(struct gsm_subscriber_connection *conn)
@@ -151,5 +151,5 @@ int msc_send_ussd_release_complete(struct gsm_subscriber_connection *conn)
struct msgb *msg = gsm0480_create_ussd_release_complete();
if (!msg)
return -1;
- return gsm0808_submit_dtap(conn, msg, 0, 0);
+ return msc_tx_dtap(conn, msg);
}
diff --git a/openbsc/src/libmsc/gsm_subscriber.c b/openbsc/src/libmsc/gsm_subscriber.c
index f425058f0..f92101863 100644
--- a/openbsc/src/libmsc/gsm_subscriber.c
+++ b/openbsc/src/libmsc/gsm_subscriber.c
@@ -40,33 +40,16 @@
#include <openbsc/db.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/vlr.h>
+#include <openbsc/iu.h>
+#include <openbsc/osmo_msc.h>
+#include <openbsc/msc_ifaces.h>
void *tall_sub_req_ctx;
int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq,
gsm_cbfn *cb, void *cb_data);
-static struct bsc_subscr *vlr_subscr_to_bsc_sub(struct llist_head *bsc_subscribers,
- struct vlr_subscr *vsub)
-{
- struct bsc_subscr *sub;
- /* TODO MSC split -- creating a BSC subscriber directly from MSC data
- * structures in RAM. At some point the MSC will send a message to the
- * BSC instead. */
- sub = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, vsub->imsi);
- sub->tmsi = vsub->tmsi;
- sub->lac = vsub->lac;
- return sub;
-}
-
-#if 0
-TODO implement paging response in libmsc!
-Excluding this to be able to link without libbsc:
-
-/*
- * We got the channel assigned and can now hand this channel
- * over to one of our callbacks.
- */
+/* A connection is established and the paging callbacks may run now. */
int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
struct msgb *msg, void *data, void *param)
{
@@ -74,38 +57,42 @@ int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
struct gsm_subscriber_connection *conn = data;
struct vlr_subscr *vsub = param;
struct paging_signal_data sig_data;
- struct bsc_subscr *bsub;
- struct gsm_network *net;
- OSMO_ASSERT(vsub && vsub->cs.is_paging);
- net = vsub->vlr->user_ctx;
+ OSMO_ASSERT(vsub);
+ OSMO_ASSERT(hooknum == GSM_HOOK_RR_PAGING);
+ OSMO_ASSERT(!(conn && (conn->vsub != vsub)));
+ OSMO_ASSERT(!((event == GSM_PAGING_SUCCEEDED) && !conn));
+
+ LOGP(DPAG, LOGL_DEBUG, "Paging %s for %s (event=%d)\n",
+ event == GSM_PAGING_SUCCEEDED ? "success" : "failure",
+ vlr_subscr_name(vsub), event);
- /*
- * Stop paging on all other BTS. E.g. if this is
- * the first timeout on a BTS then the others will
- * timeout soon as well. Let's just stop everything
- * and forget we wanted to page.
- */
+ if (!vsub->cs.is_paging) {
+ LOGP(DPAG, LOGL_ERROR,
+ "Paging Response received for subscriber"
+ " that is not paging.\n");
+ return -EINVAL;
+ }
- bsub = vlr_subscr_to_bsc_sub(conn->network->bsc_subscribers, vsub);
- paging_request_stop(&net->bts_list, NULL, bsub, NULL, NULL);
- bsc_subscr_put(bsub);
+ if (event == GSM_PAGING_SUCCEEDED)
+ msc_stop_paging(vsub);
/* Inform parts of the system we don't know */
- sig_data.vsub = vsub;
- sig_data.bts = conn ? conn->bts : NULL;
- sig_data.conn = conn;
+ sig_data.vsub = vsub;
+ sig_data.conn = conn;
sig_data.paging_result = event;
- osmo_signal_dispatch(
- SS_PAGING,
- event == GSM_PAGING_SUCCEEDED ?
- S_PAGING_SUCCEEDED : S_PAGING_EXPIRED,
- &sig_data
- );
+ osmo_signal_dispatch(SS_PAGING,
+ event == GSM_PAGING_SUCCEEDED ?
+ S_PAGING_SUCCEEDED : S_PAGING_EXPIRED,
+ &sig_data);
llist_for_each_entry_safe(request, tmp, &vsub->cs.requests, entry) {
llist_del(&request->entry);
- request->cbfn(hooknum, event, msg, data, request->param);
+ if (request->cbfn) {
+ LOGP(DPAG, LOGL_DEBUG, "Calling paging cbfn.\n");
+ request->cbfn(hooknum, event, msg, data, request->param);
+ } else
+ LOGP(DPAG, LOGL_DEBUG, "Paging without action.\n");
talloc_free(request);
}
@@ -115,29 +102,48 @@ int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
return 0;
}
-struct subscr_request *subscr_request_channel(struct vlr_subscr *vsub,
- int channel_type,
- gsm_cbfn *cbfn, void *param)
+int msc_paging_request(struct vlr_subscr *vsub)
+{
+ /* The subscriber was last seen in subscr->lac. Find out which
+ * BSCs/RNCs are responsible and send them a paging request via open
+ * SCCP connections (if any). */
+ /* TODO Implementing only RNC paging, since this is code on the iu branch.
+ * 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);
+ case RAN_UTRAN_IU:
+ return iu_page_cs(vsub->imsi,
+ vsub->tmsi == GSM_RESERVED_TMSI?
+ NULL : &vsub->tmsi,
+ vsub->lac);
+ default:
+ break;
+ }
+
+ LOGP(DPAG, LOGL_ERROR, "%s: Cannot page, subscriber not attached\n",
+ vlr_subscr_name(vsub));
+ return -EINVAL;
+}
+
+/*! \brief Start a paging request for vsub, call cbfn(param) when done.
+ * \param vsub subscriber to page.
+ * \param cbfn function to call when the conn is established.
+ * \param param caller defined param to pass to cbfn().
+ * \param label human readable label of the request kind used for logging.
+ */
+struct subscr_request *subscr_request_conn(struct vlr_subscr *vsub,
+ gsm_cbfn *cbfn, void *param,
+ const char *label)
{
int rc;
struct subscr_request *request;
- struct bsc_subscr *bsub;
- struct gsm_network *net = vsub->vlr->user_ctx;
/* Start paging.. we know it is async so we can do it before */
if (!vsub->cs.is_paging) {
- LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet.\n",
+ LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet, start paging.\n",
vlr_subscr_name(vsub));
-#if 0
- TODO implement paging response in libmsc!
- Excluding this to be able to link without libbsc:
-
- bsub = vlr_subscr_to_bsc_sub(net->bsc_subscribers, vsub);
- rc = paging_request(net, bsub, channel_type, NULL, NULL);
- bsc_subscr_put(bsub);
-#else
- rc = -ENOTSUP;
-#endif
+ rc = msc_paging_request(vsub);
if (rc <= 0) {
LOGP(DMM, LOGL_ERROR, "Subscriber %s paging failed: %d\n",
vlr_subscr_name(vsub), rc);
@@ -146,6 +152,9 @@ struct subscr_request *subscr_request_channel(struct vlr_subscr *vsub,
/* reduced on the first paging callback */
vlr_subscr_get(vsub);
vsub->cs.is_paging = true;
+ } else {
+ LOGP(DMM, LOGL_DEBUG, "Subscriber %s already paged.\n",
+ vlr_subscr_name(vsub));
}
/* TODO: Stop paging in case of memory allocation failure */
@@ -177,4 +186,3 @@ struct gsm_subscriber_connection *connection_for_subscr(struct vlr_subscr *vsub)
return NULL;
}
-#endif
diff --git a/openbsc/src/libmsc/iucs.c b/openbsc/src/libmsc/iucs.c
new file mode 100644
index 000000000..aeda1406a
--- /dev/null
+++ b/openbsc/src/libmsc/iucs.c
@@ -0,0 +1,191 @@
+/* Code to manage MSC subscriber connections over IuCS interface */
+
+/*
+ * (C) 2016,2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <inttypes.h>
+
+#include <osmocom/core/logging.h>
+#include <openbsc/debug.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/iu.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/osmo_msc.h>
+#include <openbsc/vlr.h>
+
+/* For A-interface see libbsc/bsc_api.c subscr_con_allocate() */
+static struct gsm_subscriber_connection *subscr_conn_allocate_iu(struct gsm_network *network,
+ struct ue_conn_ctx *ue,
+ uint16_t lac)
+{
+ 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);
+
+ conn = talloc_zero(network, struct gsm_subscriber_connection);
+ if (!conn)
+ return NULL;
+
+ conn->network = network;
+ conn->via_ran = RAN_UTRAN_IU;
+ conn->iu.ue_ctx = ue;
+ conn->iu.ue_ctx->rab_assign_addr_enc = network->iu.rab_assign_addr_enc;
+ conn->lac = lac;
+
+ llist_add_tail(&conn->entry, &network->subscr_conns);
+ return conn;
+}
+
+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);
+}
+
+static inline void log_subscribers(struct gsm_network *network)
+{
+ if (!log_check_level(DIUCS, LOGL_DEBUG))
+ return;
+
+ struct gsm_subscriber_connection *conn;
+ int i = 0;
+ llist_for_each_entry(conn, &network->subscr_conns, entry) {
+ DEBUGP(DIUCS, "%3d: %s", i, vlr_subscr_name(conn->vsub));
+ switch (conn->via_ran) {
+ case RAN_UTRAN_IU:
+ DEBUGPC(DIUCS, " Iu");
+ if (conn->iu.ue_ctx) {
+ DEBUGPC(DIUCS, " link %p, conn_id %d",
+ conn->iu.ue_ctx->link,
+ conn->iu.ue_ctx->conn_id
+ );
+ }
+ break;
+ case RAN_GERAN_A:
+ DEBUGPC(DIUCS, " A");
+ /* TODO log A-interface connection details */
+ break;
+ case RAN_UNKNOWN:
+ DEBUGPC(DIUCS, " ?");
+ break;
+ default:
+ DEBUGPC(DIUCS, " invalid");
+ break;
+ }
+ DEBUGPC(DIUCS, "\n");
+ i++;
+ }
+ DEBUGP(DIUCS, "subscribers registered: %d\n", i);
+}
+
+/* Return an existing IuCS subscriber connection record for the given link and
+ * connection IDs, or return NULL if not found. */
+struct gsm_subscriber_connection *subscr_conn_lookup_iu(
+ struct gsm_network *network,
+ struct ue_conn_ctx *ue)
+{
+ struct gsm_subscriber_connection *conn;
+
+ DEBUGP(DIUCS, "Looking for IuCS subscriber: link_id %p, conn_id %" PRIx32 "\n",
+ ue->link, ue->conn_id);
+ log_subscribers(network);
+
+ llist_for_each_entry(conn, &network->subscr_conns, entry) {
+ if (conn->via_ran != RAN_UTRAN_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);
+ return conn;
+ }
+ DEBUGP(DIUCS, "No IuCS subscriber found for link_id %p, conn_id %" PRIx32 "\n",
+ ue->link, ue->conn_id);
+ return NULL;
+}
+
+/* Receive MM/CC/... message from IuCS (SCCP user SAP).
+ * msg->dst must reference a struct ue_conn_ctx, which identifies the peer that
+ * sent the msg.
+ *
+ * For A-interface see libbsc/bsc_api.c gsm0408_rcvmsg(). */
+int gsm0408_rcvmsg_iucs(struct gsm_network *network, struct msgb *msg,
+ uint16_t *lac)
+{
+ int rc;
+ struct ue_conn_ctx *ue_ctx;
+ struct gsm_subscriber_connection *conn;
+
+ ue_ctx = (struct ue_conn_ctx*)msg->dst;
+
+ /* TODO: are there message types that could allow us to skip this
+ * search? */
+ conn = subscr_conn_lookup_iu(network, ue_ctx);
+
+ if (conn && lac && (conn->lac != *lac)) {
+ LOGP(DIUCS, LOGL_ERROR, "IuCS subscriber has changed LAC"
+ " within the same connection, discarding connection:"
+ " %s from LAC %d to %d\n",
+ vlr_subscr_name(conn->vsub), conn->lac, *lac);
+ /* Deallocate conn with previous LAC */
+ msc_subscr_conn_close(conn, GSM_CAUSE_INV_MAND_INFO);
+ /* At this point we could be tolerant and allocate a new
+ * connection, but changing the LAC within the same connection
+ * is shifty. Rather cancel everything. */
+ return -1;
+ }
+
+ if (conn) {
+ /* Make sure we don't receive RR over IuCS; otherwise all
+ * messages handled by gsm0408_dispatch() are of interest (CC,
+ * MM, SMS, NS_SS, maybe even MM_GPRS and SM_GPRS). */
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ uint8_t pdisc = gh->proto_discr & 0x0f;
+ OSMO_ASSERT(pdisc != GSM48_PDISC_RR);
+
+ msc_dtap(conn, ue_ctx->conn_id, msg);
+ rc = 0;
+ } else {
+ /* allocate a new connection */
+
+ if (!lac) {
+ LOGP(DIUCS, LOGL_ERROR, "New IuCS subscriber"
+ " but no LAC available. Expecting an InitialUE"
+ " message containing a LAI IE."
+ " Dropping connection.\n");
+ return -1;
+ }
+
+ conn = subscr_conn_allocate_iu(network, ue_ctx, *lac);
+ if (!conn)
+ abort();
+
+ /* ownership of conn hereby goes to the MSC: */
+ rc = msc_compl_l3(conn, msg, 0);
+ }
+
+ return rc;
+}
diff --git a/openbsc/src/libmsc/iucs_ranap.c b/openbsc/src/libmsc/iucs_ranap.c
new file mode 100644
index 000000000..b69d52bc7
--- /dev/null
+++ b/openbsc/src/libmsc/iucs_ranap.c
@@ -0,0 +1,106 @@
+/* Implementation of RANAP messages to/from an MSC via an Iu-CS interface.
+ * This keeps direct RANAP dependencies out of libmsc. */
+
+/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include <osmocom/core/logging.h>
+
+#include <osmocom/ranap/ranap_ies_defs.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/iu.h>
+#include <openbsc/iucs.h>
+#include <openbsc/vlr.h>
+#include <openbsc/iucs_ranap.h>
+#include <openbsc/osmo_msc.h>
+
+/* To continue authorization after a Security Mode Complete */
+int gsm0408_authorize(struct gsm_subscriber_connection *conn);
+
+static int iucs_rx_rab_assign(struct gsm_subscriber_connection *conn,
+ RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies)
+{
+ uint8_t rab_id;
+ RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem;
+
+ rab_id = item->rAB_ID.buf[0];
+
+ LOGP(DIUCS, LOGL_NOTICE,
+ "Received RAB assignment event for %s rab_id=%hhd\n",
+ vlr_subscr_name(conn->vsub), rab_id);
+
+ return 0;
+}
+
+int iucs_rx_sec_mode_compl(struct gsm_subscriber_connection *conn,
+ RANAP_SecurityModeCompleteIEs_t *ies)
+{
+ OSMO_ASSERT(conn->via_ran == RAN_UTRAN_IU);
+
+ /* TODO evalute ies */
+
+ if (conn->iu.integrity_protection)
+ LOGP(DIUCS, LOGL_NOTICE, "Integrity Protection"
+ " was already enabled for %s\n",
+ vlr_subscr_name(conn->vsub));
+
+ conn->iu.integrity_protection = INTEGRITY_PROTECTION_IK;
+
+ msc_rx_sec_mode_compl(conn);
+ return 0;
+}
+
+int iucs_rx_ranap_event(struct gsm_network *network,
+ struct ue_conn_ctx *ue_ctx, int type, void *data)
+{
+ struct gsm_subscriber_connection *conn;
+
+ conn = subscr_conn_lookup_iu(network, ue_ctx);
+
+ if (!conn) {
+ LOGP(DRANAP, LOGL_ERROR, "Cannot find subscriber for IU event %u\n", type);
+ return -1;
+ }
+
+ switch (type) {
+ case IU_EVENT_IU_RELEASE:
+ case IU_EVENT_LINK_INVALIDATED:
+ LOGP(DIUCS, LOGL_INFO, "IuCS release for %s\n",
+ vlr_subscr_name(conn->vsub));
+ msc_subscr_conn_close(conn, 0);
+ return 0;
+
+ case IU_EVENT_SECURITY_MODE_COMPLETE:
+ LOGP(DIUCS, LOGL_INFO, "IuCS security mode complete for %s\n",
+ vlr_subscr_name(conn->vsub));
+ return iucs_rx_sec_mode_compl(conn,
+ (RANAP_SecurityModeCompleteIEs_t*)data);
+ case IU_EVENT_RAB_ASSIGN:
+ return iucs_rx_rab_assign(conn,
+ (RANAP_RAB_SetupOrModifiedItemIEs_t*)data);
+ default:
+ LOGP(DIUCS, LOGL_NOTICE, "Unknown message received:"
+ " RANAP event: %i\n", type);
+ return -1;
+ }
+}
diff --git a/openbsc/src/libmsc/msc_ifaces.c b/openbsc/src/libmsc/msc_ifaces.c
index 500c99c2e..1a7d878a2 100644
--- a/openbsc/src/libmsc/msc_ifaces.c
+++ b/openbsc/src/libmsc/msc_ifaces.c
@@ -23,9 +23,25 @@
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/msc_ifaces.h>
+#include <openbsc/iu.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/transaction.h>
+#include <openbsc/mgcp.h>
+#include <openbsc/mgcpgw_client.h>
+#include <openbsc/vlr.h>
+
+#include "../../bscconfig.h"
+
+extern struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id,
+ uint32_t rtp_ip,
+ uint16_t rtp_port,
+ bool use_x213_nsap);
static int msc_tx(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
+ 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;
@@ -60,7 +76,8 @@ int msc_gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn)
gh->proto_discr = GSM48_PDISC_MM;
gh->msg_type = GSM48_MT_MM_CM_SERV_ACC;
- DEBUGP(DMM, "-> CM SERVICE ACCEPT\n");
+ DEBUGP(DMM, "-> CM SERVICE ACCEPT %s\n",
+ vlr_subscr_name(conn->vsub));
return msc_tx_dtap(conn, msg);
}
@@ -70,6 +87,7 @@ int msc_gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
enum gsm48_reject_value value)
{
struct msgb *msg;
+ conn->received_cm_service_request = false;
msg = gsm48_create_mm_serv_rej(value);
if (!msg) {
@@ -81,3 +99,223 @@ int msc_gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
return msc_tx_dtap(conn, msg);
}
+
+int msc_tx_common_id(struct gsm_subscriber_connection *conn)
+{
+ /* Common ID is only sent over IuCS */
+ if (conn->via_ran != RAN_UTRAN_IU) {
+ LOGP(DMM, LOGL_INFO,
+ "%s: Asked to transmit Common ID, but skipping"
+ " because this is not on UTRAN\n",
+ vlr_subscr_name(conn->vsub));
+ return 0;
+ }
+
+#ifdef BUILD_IU
+ DEBUGP(DIUCS, "%s: tx CommonID %s\n",
+ vlr_subscr_name(conn->vsub), conn->vsub->imsi);
+ return iu_tx_common_id(conn->iu.ue_ctx, conn->vsub->imsi);
+#else
+ LOGP(DMM, LOGL_ERROR,
+ "Cannot send CommonID: RAN_UTRAN_IU but IuCS support not built\n");
+ return -ENOTSUP;
+#endif
+}
+
+#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)
+{
+ struct msgb *msg;
+ bool use_x213_nsap;
+ uint32_t conn_id = uectx->conn_id;
+
+ use_x213_nsap = (uectx->rab_assign_addr_enc == NSAP_ADDR_ENC_X213);
+
+ LOGP(DIUCS, LOGL_DEBUG, "Assigning RAB: conn_id=%u, rab_id=%d,"
+ " rtp=%x:%u, use_x213_nsap=%d\n", conn_id, rab_id, rtp_ip,
+ rtp_port, use_x213_nsap);
+
+ msg = ranap_new_msg_rab_assign_voice(rab_id, rtp_ip, rtp_port,
+ use_x213_nsap);
+ msg->l2h = msg->data;
+
+ if (iu_rab_act(uectx, msg))
+ 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);
+}
+
+static void mgcp_response_rab_act_cs_crcx(struct mgcp_response *r, void *priv)
+{
+ struct gsm_trans *trans = priv;
+ struct gsm_subscriber_connection *conn = trans->conn;
+ struct ue_conn_ctx *uectx = conn->iu.ue_ctx;
+ uint32_t rtp_ip;
+ int rc;
+
+ if (r->head.response_code != 200) {
+ LOGP(DMGCP, LOGL_ERROR,
+ "MGCPGW response yields error: %d %s\n",
+ r->head.response_code, r->head.comment);
+ goto rab_act_cs_error;
+ }
+
+ rc = mgcp_response_parse_params(r);
+ if (rc) {
+ LOGP(DMGCP, LOGL_ERROR,
+ "Cannot parse MGCP response, for %s\n",
+ vlr_subscr_name(trans->vsub));
+ goto rab_act_cs_error;
+ }
+
+ conn->iu.mgcp_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 */
+
+rab_act_cs_error:
+ /* FIXME abort call, invalidate conn, ... */
+ return;
+}
+
+static int conn_iu_rab_act_cs(struct gsm_trans *trans)
+{
+ struct gsm_subscriber_connection *conn = trans->conn;
+ struct mgcpgw_client *mgcp = conn->network->mgcpgw.client;
+ struct msgb *msg;
+
+ /* 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->iu.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;
+
+ /* 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);
+ 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(): IuCS RAB Activation not supported"
+ " in this build\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);
+
+static void mgcp_bridge(struct gsm_trans *from, struct gsm_trans *to,
+ enum bridge_state state,
+ enum mgcp_connection_mode mode)
+{
+ struct gsm_subscriber_connection *conn1 = from->conn;
+ struct gsm_subscriber_connection *conn2 = to->conn;
+ struct mgcpgw_client *mgcp = conn1->network->mgcpgw.client;
+ const char *ip;
+ struct msgb *msg;
+
+ OSMO_ASSERT(mgcp);
+
+ from->bridge.peer = to;
+ from->bridge.state = state;
+
+ /* Loop back to the same MGCP GW */
+ ip = mgcpgw_client_remote_addr_str(mgcp);
+
+ msg = mgcp_msg_mdcx(mgcp,
+ conn1->iu.mgcp_rtp_endpoint,
+ ip, conn2->iu.mgcp_rtp_port_cn,
+ mode);
+ if (mgcpgw_client_tx(mgcp, msg, mgcp_response_bridge_mdcx, from))
+ LOGP(DMGCP, LOGL_ERROR,
+ "Failed to send MDCX message for %s\n",
+ vlr_subscr_name(from->vsub));
+}
+
+static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv)
+{
+ struct gsm_trans *trans = priv;
+ struct gsm_trans *peer = trans->bridge.peer;
+
+ switch (trans->bridge.state) {
+ case BRIDGE_STATE_LOOPBACK_PENDING:
+ trans->bridge.state = BRIDGE_STATE_LOOPBACK_ESTABLISHED;
+
+ switch (peer->bridge.state) {
+ case BRIDGE_STATE_LOOPBACK_PENDING:
+ /* Wait until the other is done as well. */
+ return;
+ case BRIDGE_STATE_LOOPBACK_ESTABLISHED:
+ /* Now that both are in loopback, switch both to
+ * forwarding. */
+ mgcp_bridge(trans, peer, BRIDGE_STATE_BRIDGE_PENDING,
+ MGCP_CONN_RECV_SEND);
+ mgcp_bridge(peer, trans, BRIDGE_STATE_BRIDGE_PENDING,
+ MGCP_CONN_RECV_SEND);
+ break;
+ default:
+ LOGP(DMGCP, LOGL_ERROR,
+ "Unexpected bridge state: %d for %s\n",
+ trans->bridge.state, vlr_subscr_name(trans->vsub));
+ break;
+ }
+ break;
+
+ case BRIDGE_STATE_BRIDGE_PENDING:
+ trans->bridge.state = BRIDGE_STATE_BRIDGE_ESTABLISHED;
+ break;
+
+ default:
+ LOGP(DMGCP, LOGL_ERROR,
+ "Unexpected bridge state: %d for %s\n",
+ trans->bridge.state, vlr_subscr_name(trans->vsub));
+ break;
+ }
+}
+
+int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2)
+{
+ /* 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. */
+ mgcp_bridge(trans1, trans2, BRIDGE_STATE_LOOPBACK_PENDING,
+ MGCP_CONN_LOOPBACK);
+ mgcp_bridge(trans2, trans1, BRIDGE_STATE_LOOPBACK_PENDING,
+ MGCP_CONN_LOOPBACK);
+
+ return 0;
+}
diff --git a/openbsc/src/libmsc/msc_vty.c b/openbsc/src/libmsc/msc_vty.c
new file mode 100644
index 000000000..b6fff56af
--- /dev/null
+++ b/openbsc/src/libmsc/msc_vty.c
@@ -0,0 +1,181 @@
+/* MSC interface to quagga VTY */
+/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
+ * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
+ * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009-2011 by Holger Hans Peter Freyther
+ * 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/>.
+ *
+ */
+
+/* NOTE: I would have liked to call this the MSC_NODE instead of the MSC_NODE,
+ * but MSC_NODE already exists to configure a remote MSC for osmo-bsc. */
+
+#include <inttypes.h>
+
+#include <osmocom/vty/command.h>
+
+#include <openbsc/vty.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/vlr.h>
+#include <openbsc/iu.h>
+
+static struct cmd_node msc_node = {
+ MSC_NODE,
+ "%s(config-msc)# ",
+ 1,
+};
+
+DEFUN(cfg_msc, cfg_msc_cmd,
+ "msc", "Configure MSC options")
+{
+ vty->node = MSC_NODE;
+ return CMD_SUCCESS;
+}
+
+/* Note: limit on the parameter length is set by internal vty code limitations */
+DEFUN(cfg_msc_subscr_random, cfg_msc_subscr_random_cmd,
+ "subscriber-create-on-demand random <1-9999999999> <2-9999999999>",
+ "Set random parameters for a new record when a subscriber is first seen.\n"
+ "Set random parameters for a new record when a subscriber is first seen.\n"
+ "Minimum for subscriber extension\n""Maximum for subscriber extension\n")
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ uint64_t mi = atoi(argv[0]), ma = atoi(argv[1]);
+ gsmnet->auto_create_subscr = true;
+ gsmnet->auto_assign_exten = true;
+ if (mi >= ma) {
+ vty_out(vty, "Incorrect range: %s >= %s, expected MIN < MAX%s",
+ argv[0], argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ gsmnet->ext_min = mi;
+ gsmnet->ext_max = ma;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_msc_subscr_create, cfg_msc_subscr_create_cmd,
+ "subscriber-create-on-demand [no-extension]",
+ "Make a new record when a subscriber is first seen.\n"
+ "Do not automatically assign extension to created subscribers\n")
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ gsmnet->auto_create_subscr = true;
+ gsmnet->auto_assign_exten = argc ? false : true;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_msc_no_subscr_create, cfg_msc_no_subscr_create_cmd,
+ "no subscriber-create-on-demand",
+ NO_STR "Make a new record when a subscriber is first seen.\n")
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ gsmnet->auto_create_subscr = false;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_msc_assign_tmsi, cfg_msc_assign_tmsi_cmd,
+ "assign-tmsi",
+ "Assign TMSI during Location Updating.\n")
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ gsmnet->vlr->cfg.assign_tmsi = true;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_msc_no_assign_tmsi, cfg_msc_no_assign_tmsi_cmd,
+ "no assign-tmsi",
+ NO_STR "Assign TMSI during Location Updating.\n")
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ gsmnet->vlr->cfg.assign_tmsi = false;
+ return CMD_SUCCESS;
+}
+
+static int config_write_msc(struct vty *vty)
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+
+ vty_out(vty, "msc%s", VTY_NEWLINE);
+ if (!gsmnet->auto_create_subscr)
+ vty_out(vty, " no subscriber-create-on-demand%s", VTY_NEWLINE);
+ else
+ vty_out(vty, " subscriber-create-on-demand%s%s",
+ gsmnet->auto_assign_exten ? "" : " no-extension",
+ VTY_NEWLINE);
+
+ if (gsmnet->ext_min != GSM_MIN_EXTEN || gsmnet->ext_max != GSM_MAX_EXTEN)
+ vty_out(vty, " subscriber-create-on-demand random %"PRIu64" %"
+ PRIu64"%s", gsmnet->ext_min, gsmnet->ext_max,
+ VTY_NEWLINE);
+ vty_out(vty, " %sassign-tmsi%s",
+ gsmnet->vlr->cfg.assign_tmsi? "" : "no ", VTY_NEWLINE);
+
+ mgcpgw_client_config_write(vty, " ");
+ iu_vty_config_write(vty, " ");
+
+ return CMD_SUCCESS;
+}
+
+static int config_write_net(struct vty *vty)
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+
+ vty_out(vty, "network%s", VTY_NEWLINE);
+ vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE);
+ vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE);
+ vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE);
+ vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE);
+ vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE);
+ vty_out(vty, " location updating reject cause %u%s",
+ gsmnet->reject_cause, VTY_NEWLINE);
+ vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE);
+ vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode),
+ VTY_NEWLINE);
+ vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE);
+ if (gsmnet->tz.override != 0) {
+ if (gsmnet->tz.dst)
+ vty_out(vty, " timezone %d %d %d%s",
+ gsmnet->tz.hr, gsmnet->tz.mn, gsmnet->tz.dst,
+ VTY_NEWLINE);
+ else
+ vty_out(vty, " timezone %d %d%s",
+ gsmnet->tz.hr, gsmnet->tz.mn, VTY_NEWLINE);
+ }
+ if (gsmnet->t3212 == 0)
+ vty_out(vty, " no periodic location update%s", VTY_NEWLINE);
+ else
+ vty_out(vty, " periodic location update %u%s",
+ gsmnet->t3212 * 6, VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+void msc_vty_init(struct gsm_network *msc_network)
+{
+ common_cs_vty_init(msc_network, config_write_net);
+
+ install_element(CONFIG_NODE, &cfg_msc_cmd);
+ install_node(&msc_node, config_write_msc);
+ vty_install_default(MSC_NODE);
+ install_element(MSC_NODE, &cfg_msc_subscr_create_cmd);
+ install_element(MSC_NODE, &cfg_msc_subscr_random_cmd);
+ install_element(MSC_NODE, &cfg_msc_no_subscr_create_cmd);
+ install_element(MSC_NODE, &cfg_msc_assign_tmsi_cmd);
+ install_element(MSC_NODE, &cfg_msc_no_assign_tmsi_cmd);
+ mgcpgw_client_vty_init(MSC_NODE, &msc_network->mgcpgw.conf);
+ iu_vty_init(MSC_NODE, &msc_network->iu.rab_assign_addr_enc);
+}
diff --git a/openbsc/src/libmsc/osmo_msc.c b/openbsc/src/libmsc/osmo_msc.c
index ab53b0be7..bfe5343cf 100644
--- a/openbsc/src/libmsc/osmo_msc.c
+++ b/openbsc/src/libmsc/osmo_msc.c
@@ -28,6 +28,7 @@
#include <openbsc/db.h>
#include <openbsc/vlr.h>
#include <openbsc/osmo_msc.h>
+#include <openbsc/iu.h>
#include <openbsc/gsm_04_11.h>
@@ -40,24 +41,6 @@ static void msc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci)
gsm411_sapi_n_reject(conn);
}
-static bool keep_conn(struct gsm_subscriber_connection *conn)
-{
- /* TODO: what about a silent call? */
-
- if (!conn->conn_fsm) {
- DEBUGP(DMM, "No conn_fsm, release conn\n");
- return false;
- }
-
- switch (conn->conn_fsm->state) {
- case SUBSCR_CONN_S_NEW:
- case SUBSCR_CONN_S_ACCEPTED:
- return true;
- default:
- return false;
- }
-}
-
static void subscr_conn_bump(struct gsm_subscriber_connection *conn)
{
if (!conn)
@@ -65,39 +48,32 @@ static void subscr_conn_bump(struct gsm_subscriber_connection *conn)
if (!conn->conn_fsm)
return;
if (!(conn->conn_fsm->state == SUBSCR_CONN_S_ACCEPTED
- || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING))
+ || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING)) {
+ DEBUGP(DMM, "%s: bump: conn still being established (%s)\n",
+ vlr_subscr_name(conn->vsub),
+ osmo_fsm_inst_state_name(conn->conn_fsm));
return;
+ }
osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_BUMP, NULL);
}
/* receive a Level 3 Complete message and return MSC_CONN_ACCEPT or
* MSC_CONN_REJECT */
-enum msc_compl_l3_rc msc_compl_l3(struct gsm_subscriber_connection *conn,
- struct msgb *msg, uint16_t chosen_channel)
+int msc_compl_l3(struct gsm_subscriber_connection *conn,
+ struct msgb *msg, uint16_t chosen_channel)
{
- /* Ownership of the gsm_subscriber_connection is still a bit mucky
- * between libbsc and libmsc. In libmsc, we use ref counting, but not
- * in libbsc. This will become simpler with the MSCSPLIT. */
-
- /* reserve for the duration of this function */
msc_subscr_conn_get(conn);
-
gsm0408_dispatch(conn, msg);
- if (!keep_conn(conn)) {
- DEBUGP(DMM, "compl_l3: Discarding conn\n");
- /* keep the use_count reserved, libbsc will discard. If we
- * released the ref count and discarded here, libbsc would
- * double-free. And we will not change bsc_api semantics. */
- return MSC_CONN_REJECT;
- }
- DEBUGP(DMM, "compl_l3: Keeping conn\n");
-
/* Bump whether the conn wants to be closed */
subscr_conn_bump(conn);
/* If this should be kept, the conn->conn_fsm has placed a use_count */
msc_subscr_conn_put(conn);
+
+ /* Always return acceptance, because even if the conn was not accepted,
+ * we assumed ownership of it and the caller shall not interfere with
+ * that. We may even already have discarded the conn. */
return MSC_CONN_ACCEPT;
#if 0
@@ -119,7 +95,7 @@ enum msc_compl_l3_rc msc_compl_l3(struct gsm_subscriber_connection *conn,
}
/* Receive a DTAP message from BSC */
-static void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg)
+void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg)
{
msc_subscr_conn_get(conn);
gsm0408_dispatch(conn, msg);
@@ -158,8 +134,8 @@ static void msc_classmark_chg(struct gsm_subscriber_connection *conn,
}
/* Receive a CIPHERING MODE COMPLETE from BSC */
-static void msc_ciph_m_compl(struct gsm_subscriber_connection *conn,
- struct msgb *msg, uint8_t alg_id)
+void msc_cipher_mode_compl(struct gsm_subscriber_connection *conn,
+ struct msgb *msg, uint8_t alg_id)
{
struct gsm48_hdr *gh = msgb_l3(msg);
unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
@@ -277,7 +253,7 @@ static struct bsc_api msc_handler = {
.assign_compl = msc_assign_compl,
.assign_fail = msc_assign_fail,
.classmark_chg = msc_classmark_chg,
- .cipher_mode_compl = msc_ciph_m_compl,
+ .cipher_mode_compl = msc_cipher_mode_compl,
.conn_cleanup = msc_subscr_con_cleanup,
};
@@ -285,6 +261,10 @@ struct bsc_api *msc_bsc_api() {
return &msc_handler;
}
+/* Signal the connection's FSM to gracefully terminate the connection by a
+ * SUBSCR_CONN_E_CN_CLOSE event.
+ * \param cause a GSM_CAUSE_* constant, e.g. GSM_CAUSE_AUTH_FAILED.
+ */
void msc_subscr_conn_close(struct gsm_subscriber_connection *conn,
uint32_t cause)
{
@@ -335,8 +315,12 @@ void _msc_subscr_conn_put(struct gsm_subscriber_connection *conn,
"%s: MSC conn use - 1 == %u\n",
vlr_subscr_name(conn->vsub), conn->use_count);
- if (conn->use_count == 0) {
- gsm0808_clear(conn);
- bsc_subscr_con_free(conn);
- }
+ if (conn->use_count == 0)
+ msc_subscr_con_free(conn);
+}
+
+void msc_stop_paging(struct vlr_subscr *vsub)
+{
+ DEBUGP(DPAG, "Paging can stop for %s\n", vlr_subscr_name(vsub));
+ /* tell BSCs and RNCs to stop paging? How? */
}
diff --git a/openbsc/src/libmsc/silent_call.c b/openbsc/src/libmsc/silent_call.c
index 76816c29d..256b5b453 100644
--- a/openbsc/src/libmsc/silent_call.c
+++ b/openbsc/src/libmsc/silent_call.c
@@ -131,7 +131,8 @@ int gsm_silent_call_start(struct vlr_subscr *vsub, void *data, int type)
/* FIXME the VTY command allows selecting a silent call channel type.
* This doesn't apply to the situation after MSCSPLIT with an
* A-interface. */
- req = subscr_request_conn(vsub, type, paging_cb_silent, data);
+ req = subscr_request_conn(vsub, paging_cb_silent, data,
+ "establish silent call");
return req != NULL;
}
diff --git a/openbsc/src/libmsc/subscr_conn.c b/openbsc/src/libmsc/subscr_conn.c
index 9be53cf4a..9d5dd5d2c 100644
--- a/openbsc/src/libmsc/subscr_conn.c
+++ b/openbsc/src/libmsc/subscr_conn.c
@@ -30,6 +30,7 @@
#include <openbsc/debug.h>
#include <openbsc/transaction.h>
#include <openbsc/signal.h>
+#include <openbsc/iu.h>
#define SUBSCR_CONN_TIMEOUT 5 /* seconds */
@@ -52,8 +53,8 @@ const struct value_string subscr_conn_from_names[] = {
{ 0, NULL }
};
-static void paging_resp(struct gsm_subscriber_connection *conn,
- enum gsm_paging_event pe)
+static void paging_event(struct gsm_subscriber_connection *conn,
+ enum gsm_paging_event pe)
{
subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->vsub);
}
@@ -85,11 +86,17 @@ void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
case SUBSCR_CONN_E_MO_CLOSE:
case SUBSCR_CONN_E_CN_CLOSE:
+ if (data)
+ LOGPFSM(fi, "Close event, cause %u\n",
+ *(uint32_t*)data);
+ /* will release further below, see
+ * 'if (fi->state != SUBSCR_CONN_S_ACCEPTED)' */
break;
default:
- LOGPFSM(fi, "Unexpected event: %d %s\n",
- event, osmo_fsm_event_name(fi->fsm, event));
+ LOGPFSML(fi, LOGL_ERROR,
+ "Unexpected event: %d %s\n", event,
+ osmo_fsm_event_name(fi->fsm, event));
break;
}
@@ -102,21 +109,24 @@ void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
/* signal paging success or failure in case this was a paging */
if (from == SUBSCR_CONN_FROM_PAGING_RESP)
- paging_resp(conn,
- success ? GSM_PAGING_SUCCEEDED
- : GSM_PAGING_EXPIRED);
+ paging_event(conn,
+ success ? GSM_PAGING_SUCCEEDED
+ : GSM_PAGING_EXPIRED);
+
+ /* FIXME rate counters */
+ /*rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED]);*/
/* On failure, discard the conn */
if (!success) {
/* TODO: on MO_CLOSE or CN_CLOSE, first go to RELEASING and
- * await BSC confirmation? */
+ * await BSC/RNC confirmation? */
osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
return;
}
if (from == SUBSCR_CONN_FROM_CM_SERVICE_REQ) {
conn->received_cm_service_request = true;
- LOGPFSM(fi, "received_cm_service_request = true\n");
+ LOGPFSML(fi, LOGL_DEBUG, "received_cm_service_request = true\n");
}
osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_BUMP, data);
@@ -125,19 +135,37 @@ void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = fi->priv;
+ struct gsm_trans *trans;
- if (conn->silent_call)
+ if (conn->silent_call) {
+ LOGPFSML(fi, LOGL_DEBUG, "bump: silent call still active\n");
return;
+ }
- if (conn->received_cm_service_request)
+ if (conn->received_cm_service_request) {
+ LOGPFSML(fi, LOGL_DEBUG, "bump: still awaiting first request after a CM Service Request\n");
return;
+ }
- if (conn->vsub && !llist_empty(&conn->vsub->cs.requests))
+ if (conn->vsub && !llist_empty(&conn->vsub->cs.requests)) {
+ struct subscr_request *sr;
+ if (!log_check_level(fi->fsm->log_subsys, LOGL_DEBUG)) {
+ llist_for_each_entry(sr, &conn->vsub->cs.requests, entry) {
+ LOGPFSML(fi, LOGL_DEBUG, "bump: still active: %s\n",
+ sr->label);
+ }
+ }
return;
+ }
- if (trans_has_conn(conn))
+ if ((trans = trans_has_conn(conn))) {
+ LOGPFSML(fi, LOGL_DEBUG,
+ "bump: connection still has active transaction: %s\n",
+ gsm48_pdisc_name(trans->protocol));
return;
+ }
+ LOGPFSML(fi, LOGL_DEBUG, "bump: releasing conn\n");
osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
}
@@ -204,6 +232,12 @@ static void subscr_conn_fsm_cleanup(struct osmo_fsm_inst *fi,
/* If we're closing in a middle of a trans, we need to clean up */
trans_conn_closed(conn);
+ if (conn->via_ran == RAN_UTRAN_IU)
+ iu_tx_release(conn->iu.ue_ctx, NULL);
+ /* FIXME: keep the conn until the Iu Release Outcome is
+ * received from the UE, or a timeout expires. For now, the log
+ * says "unknown UE" for each release outcome. */
+
msc_subscr_conn_put(conn);
}
@@ -275,7 +309,7 @@ static struct osmo_fsm subscr_conn_fsm = {
.num_states = ARRAY_SIZE(subscr_conn_fsm_states),
.allstate_event_mask = 0,
.allstate_action = NULL,
- .log_subsys = DVLR,
+ .log_subsys = DMM,
.event_names = subscr_conn_fsm_event_names,
.cleanup = subscr_conn_fsm_cleanup,
.timer_cb = subscr_conn_fsm_timeout,
diff --git a/openbsc/src/libmsc/transaction.c b/openbsc/src/libmsc/transaction.c
index 41fb5a914..73c509c2c 100644
--- a/openbsc/src/libmsc/transaction.c
+++ b/openbsc/src/libmsc/transaction.c
@@ -180,23 +180,35 @@ int trans_assign_trans_id(struct gsm_network *net, struct vlr_subscr *vsub,
* \param[in] conn Connection to check
* \returns 1 in case there is a transaction, 0 otherwise
*/
-int trans_has_conn(const struct gsm_subscriber_connection *conn)
+struct gsm_trans *trans_has_conn(const struct gsm_subscriber_connection *conn)
{
struct gsm_trans *trans;
llist_for_each_entry(trans, &conn->network->trans_list, entry)
if (trans->conn == conn)
- return 1;
+ return trans;
- return 0;
+ return NULL;
}
+/*
+ * Free all transactions that are associated with the released
+ * connection. The transaction code will inform the CC or SMS
+ * facilities that will send the release indications.
+ */
void trans_conn_closed(struct gsm_subscriber_connection *conn)
{
- struct gsm_trans *trans, *t2;
+ struct gsm_trans *trans;
- llist_for_each_entry_safe(trans, t2, &conn->network->trans_list, entry) {
- if (trans->conn == conn)
+ /* As part of the CC REL_IND the remote leg might be released and this
+ * will trigger the call to trans_free. This is something the llist
+ * macro can not handle and we will need to re-iterate the list.
+ */
+restart:
+ llist_for_each_entry(trans, &conn->network->trans_list, entry) {
+ if (trans->conn == conn) {
trans_free(trans);
+ goto restart;
+ }
}
}
diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c
index df2d1e4ad..c783cf101 100644
--- a/openbsc/src/libmsc/vty_interface_layer3.c
+++ b/openbsc/src/libmsc/vty_interface_layer3.c
@@ -21,9 +21,8 @@
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
-#include <stdbool.h>
-#include <inttypes.h>
#include <time.h>
+#include <inttypes.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
@@ -902,7 +901,6 @@ DEFUN(logging_fltr_imsi,
"Filter log messages by IMSI\n" "IMSI to be used as filter\n")
{
struct vlr_subscr *vlr_subscr;
- struct bsc_subscr *bsc_subscr;
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
struct log_target *tgt = osmo_log_vty2tgt(vty);
const char *imsi = argv[0];
@@ -911,16 +909,14 @@ DEFUN(logging_fltr_imsi,
return CMD_WARNING;
vlr_subscr = vlr_subscr_find_by_imsi(gsmnet->vlr, imsi);
- bsc_subscr = bsc_subscr_find_by_imsi(gsmnet->bsc_subscribers, imsi);
- if (!vlr_subscr && !bsc_subscr) {
+ if (!vlr_subscr) {
vty_out(vty, "%%no subscriber with IMSI(%s)%s",
argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
log_set_filter_vlr_subscr(tgt, vlr_subscr);
- log_set_filter_bsc_subscr(tgt, bsc_subscr);
return CMD_SUCCESS;
}
@@ -968,92 +964,6 @@ static int config_write_hlr(struct vty *vty)
return CMD_SUCCESS;
}
-static struct cmd_node nitb_node = {
- NITB_NODE,
- "%s(config-nitb)# ",
- 1,
-};
-
-DEFUN(cfg_nitb, cfg_nitb_cmd,
- "nitb", "Configure NITB options")
-{
- vty->node = NITB_NODE;
- return CMD_SUCCESS;
-}
-
-/* Note: limit on the parameter length is set by internal vty code limitations */
-DEFUN(cfg_nitb_subscr_random, cfg_nitb_subscr_random_cmd,
- "subscriber-create-on-demand random <1-9999999999> <2-9999999999>",
- "Set random parameters for a new record when a subscriber is first seen.\n"
- "Set random parameters for a new record when a subscriber is first seen.\n"
- "Minimum for subscriber extension\n""Maximum for subscriber extension\n")
-{
- vty_out(vty, "%% 'subscriber-create-on-demand' is no longer supported.%s"
- "%% This is now up to osmo-hlr.%s",
- VTY_NEWLINE, VTY_NEWLINE);
- return CMD_WARNING;
-}
-
-DEFUN(cfg_nitb_subscr_create, cfg_nitb_subscr_create_cmd,
- "subscriber-create-on-demand [no-extension]",
- "Make a new record when a subscriber is first seen.\n"
- "Do not automatically assign extension to created subscribers\n")
-{
- vty_out(vty, "%% 'subscriber-create-on-demand' is no longer supported.%s"
- "%% This is now up to osmo-hlr.%s",
- VTY_NEWLINE, VTY_NEWLINE);
- return CMD_WARNING;
-}
-
-DEFUN(cfg_nitb_no_subscr_create, cfg_nitb_no_subscr_create_cmd,
- "no subscriber-create-on-demand",
- NO_STR "Make a new record when a subscriber is first seen.\n")
-{
- vty_out(vty, "%% 'subscriber-create-on-demand' is no longer supported.%s"
- "%% This is now up to osmo-hlr.%s",
- VTY_NEWLINE, VTY_NEWLINE);
- return CMD_WARNING;
-}
-
-DEFUN(cfg_nitb_assign_tmsi, cfg_nitb_assign_tmsi_cmd,
- "assign-tmsi",
- "Assign TMSI during Location Updating.\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->vlr->cfg.assign_tmsi = true;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nitb_no_assign_tmsi, cfg_nitb_no_assign_tmsi_cmd,
- "no assign-tmsi",
- NO_STR "Assign TMSI during Location Updating.\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->vlr->cfg.assign_tmsi = false;
- return CMD_SUCCESS;
-}
-
-static int config_write_nitb(struct vty *vty)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- vty_out(vty, "nitb%s", VTY_NEWLINE);
- if (!gsmnet->auto_create_subscr)
- vty_out(vty, " no subscriber-create-on-demand%s", VTY_NEWLINE);
- else
- vty_out(vty, " subscriber-create-on-demand%s%s",
- gsmnet->auto_assign_exten ? "" : " no-extension",
- VTY_NEWLINE);
-
- if (gsmnet->ext_min != GSM_MIN_EXTEN || gsmnet->ext_max != GSM_MAX_EXTEN)
- vty_out(vty, " subscriber-create-on-demand random %"PRIu64" %"
- PRIu64"%s", gsmnet->ext_min, gsmnet->ext_max,
- VTY_NEWLINE);
- vty_out(vty, " %sassign-tmsi%s",
- gsmnet->vlr->cfg.assign_tmsi? "" : "no ", VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
int bsc_vty_init_extra(void)
{
osmo_signal_register_handler(SS_SCALL, scall_cbfn, NULL);
@@ -1105,13 +1015,5 @@ int bsc_vty_init_extra(void)
install_element(HLR_NODE, &cfg_hlr_remote_ip_cmd);
install_element(HLR_NODE, &cfg_hlr_remote_port_cmd);
- install_element(CONFIG_NODE, &cfg_nitb_cmd);
- install_node(&nitb_node, config_write_nitb);
- install_element(NITB_NODE, &cfg_nitb_subscr_create_cmd);
- install_element(NITB_NODE, &cfg_nitb_subscr_random_cmd);
- install_element(NITB_NODE, &cfg_nitb_no_subscr_create_cmd);
- install_element(NITB_NODE, &cfg_nitb_assign_tmsi_cmd);
- install_element(NITB_NODE, &cfg_nitb_no_assign_tmsi_cmd);
-
return 0;
}