aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/libmsc
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2016-05-20 21:59:55 +0200
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2017-06-18 17:49:00 +0200
commit61692adb2b0cd090c8fb8c81376a28bca099d079 (patch)
treebee3b3fde06ec8dc2a4cb9208ceae3465cea25a5 /openbsc/src/libmsc
parent58774cba12ec4675e32fa524cfa631bdc690a9da (diff)
Implement IuCS (large refactoring and addition)
osmo-nitb becomes osmo-msc add DIUCS debug log constant add iucs.[hc] add msc vty, remove nitb vty add libiudummy, to avoid linking Iu deps in tests Use new msc_tx_dtap() instead of gsm0808_submit_dtap() libmgcp: add mgcpgw client API bridge calls via mgcpgw mgcp: hack RAB success from nano3G: patch first RTP payload The ip.access nano3G needs the first RTP payload's first two bytes to read hex 'e400', or it will reject the RAB assignment. Add flag patched_first_rtp_payload to mgcp_rtp_state to detect the first RTP payload on a stream, and overwrite its first bytes with e400. This should probably be configurable, but seems to not harm other femto cells (as long as we patch only the first RTP payload in each stream). Only do this when sending to the BTS side. Change-Id: Ie13ff348117e892d41b8355ab6c24915301eaeaf
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;
}