diff options
Diffstat (limited to 'src/libmsc/gsm_04_08.c')
-rw-r--r-- | src/libmsc/gsm_04_08.c | 1301 |
1 files changed, 550 insertions, 751 deletions
diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c index fb02de206..6cea2420c 100644 --- a/src/libmsc/gsm_04_08.c +++ b/src/libmsc/gsm_04_08.c @@ -1,7 +1,7 @@ /* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ -/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> +/* (C) 2008-2016 by Harald Welte <laforge@gnumonks.org> * (C) 2008-2012 by Holger Hans Peter Freyther <zecke@selfish.org> * * All Rights Reserved @@ -58,6 +58,7 @@ #include <openbsc/mncc_int.h> #include <osmocom/abis/e1_input.h> #include <osmocom/core/bitvec.h> +#include <openbsc/vlr.h> #include <osmocom/gsm/gsm48.h> #include <osmocom/gsm/gsm0480.h> @@ -75,11 +76,10 @@ void *tall_authciphop_ctx; static int tch_rtp_signal(struct gsm_lchan *lchan, int signal); -static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn); +static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, + uint32_t send_tmsi); static int gsm48_tx_simple(struct gsm_subscriber_connection *conn, uint8_t pdisc, uint8_t msg_type); -static void schedule_reject(struct gsm_subscriber_connection *conn); -static void release_anchor(struct gsm_subscriber_connection *conn); struct gsm_lai { uint16_t mcc; @@ -169,298 +169,7 @@ int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message) return gsm48_conn_sendmsg(ss_notify, trans->conn, trans); } -void release_security_operation(struct gsm_subscriber_connection *conn) -{ - if (!conn->sec_operation) - return; - - talloc_free(conn->sec_operation); - conn->sec_operation = NULL; - msc_release_connection(conn); -} - -void allocate_security_operation(struct gsm_subscriber_connection *conn) -{ - conn->sec_operation = talloc_zero(tall_authciphop_ctx, - struct gsm_security_operation); -} - -int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, - gsm_cbfn *cb, void *cb_data) -{ - struct gsm_network *net = conn->network; - struct gsm_subscriber *subscr = conn->subscr; - struct gsm_security_operation *op; - struct gsm_auth_tuple atuple; - int status = -1, rc; - - /* Check if we _can_ enable encryption. Cases where we can't: - * - Encryption disabled in config - * - Channel already secured (nothing to do) - * - Subscriber equipment doesn't support configured encryption - */ - if (!net->a5_encryption) { - status = GSM_SECURITY_NOAVAIL; - } else if (conn->lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { - DEBUGP(DMM, "Requesting to secure an already secure channel"); - status = GSM_SECURITY_ALREADY; - } else if (!ms_cm2_a5n_support(subscr->equipment.classmark2, - net->a5_encryption)) { - DEBUGP(DMM, "Subscriber equipment doesn't support requested encryption"); - status = GSM_SECURITY_NOAVAIL; - } - - /* If not done yet, try to get info for this user */ - if (status < 0) { - rc = auth_get_tuple_for_subscr(&atuple, subscr, key_seq); - if (rc <= 0) - status = GSM_SECURITY_NOAVAIL; - } - - /* Are we done yet ? */ - if (status >= 0) - return cb ? - cb(GSM_HOOK_RR_SECURITY, status, NULL, conn, cb_data) : - 0; - - /* Start an operation (can't have more than one pending !!!) */ - if (conn->sec_operation) - return -EBUSY; - - allocate_security_operation(conn); - op = conn->sec_operation; - op->cb = cb; - op->cb_data = cb_data; - memcpy(&op->atuple, &atuple, sizeof(struct gsm_auth_tuple)); - - /* FIXME: Should start a timer for completion ... */ - - /* Then do whatever is needed ... */ - if (rc == AUTH_DO_AUTH_THEN_CIPH) { - /* Start authentication */ - return gsm48_tx_mm_auth_req(conn, op->atuple.vec.rand, NULL, - op->atuple.key_seq); - } else if (rc == AUTH_DO_CIPH) { - /* Start ciphering directly */ - return gsm0808_cipher_mode(conn, net->a5_encryption, - op->atuple.vec.kc, 8, 0); - } - - return -EINVAL; /* not reached */ -} - -static bool subscr_regexp_check(const struct gsm_network *net, const char *imsi) -{ - if (!net->authorized_reg_str) - return false; - - if (regexec(&net->authorized_regexp, imsi, 0, NULL, 0) != REG_NOMATCH) - return true; - - return false; -} - -static int authorize_subscriber(struct gsm_loc_updating_operation *loc, - struct gsm_subscriber *subscriber) -{ - if (!subscriber) - return 0; - - /* - * Do not send accept yet as more information should arrive. Some - * phones will not send us the information and we will have to check - * what we want to do with that. - */ - if (loc && (loc->waiting_for_imsi || loc->waiting_for_imei)) - return 0; - - switch (subscriber->group->net->auth_policy) { - case GSM_AUTH_POLICY_CLOSED: - return subscriber->authorized; - case GSM_AUTH_POLICY_REGEXP: - if (subscriber->authorized) - return 1; - if (subscr_regexp_check(subscriber->group->net, - subscriber->imsi)) - subscriber->authorized = 1; - return subscriber->authorized; - case GSM_AUTH_POLICY_TOKEN: - if (subscriber->authorized) - return subscriber->authorized; - return (subscriber->flags & GSM_SUBSCRIBER_FIRST_CONTACT); - case GSM_AUTH_POLICY_ACCEPT_ALL: - return 1; - default: - return 0; - } -} - -static void _release_loc_updating_req(struct gsm_subscriber_connection *conn, int release) -{ - if (!conn->loc_operation) - return; - - /* No need to keep the connection up */ - release_anchor(conn); - - osmo_timer_del(&conn->loc_operation->updating_timer); - talloc_free(conn->loc_operation); - conn->loc_operation = NULL; - if (release) - msc_release_connection(conn); -} - -static void loc_updating_failure(struct gsm_subscriber_connection *conn, int release) -{ - if (!conn->loc_operation) - return; - LOGP(DMM, LOGL_ERROR, "Location Updating failed for %s\n", - subscr_name(conn->subscr)); - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_FAILED]); - _release_loc_updating_req(conn, release); -} - -static void loc_updating_success(struct gsm_subscriber_connection *conn, int release) -{ - if (!conn->loc_operation) - return; - LOGP(DMM, LOGL_INFO, "Location Updating completed for %s\n", - subscr_name(conn->subscr)); - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED]); - _release_loc_updating_req(conn, release); -} - -static void allocate_loc_updating_req(struct gsm_subscriber_connection *conn) -{ - if (conn->loc_operation) - LOGP(DMM, LOGL_ERROR, "Connection already had operation.\n"); - loc_updating_failure(conn, 0); - - conn->loc_operation = talloc_zero(tall_locop_ctx, - struct gsm_loc_updating_operation); -} - -static int finish_lu(struct gsm_subscriber_connection *conn) -{ - int rc = 0; - int avoid_tmsi = conn->network->avoid_tmsi; - - /* We're all good */ - if (avoid_tmsi) { - conn->subscr->tmsi = GSM_RESERVED_TMSI; - db_sync_subscriber(conn->subscr); - } else { - db_subscriber_alloc_tmsi(conn->subscr); - } - - rc = gsm0408_loc_upd_acc(conn); - if (conn->network->send_mm_info) { - /* send MM INFO with network name */ - rc = gsm48_tx_mm_info(conn); - } - - /* call subscr_update after putting the loc_upd_acc - * in the transmit queue, since S_SUBSCR_ATTACHED might - * trigger further action like SMS delivery */ - subscr_update(conn->subscr, conn->bts, - GSM_SUBSCRIBER_UPDATE_ATTACHED); - - /* - * The gsm0408_loc_upd_acc sends a MI with the TMSI. The - * MS needs to respond with a TMSI REALLOCATION COMPLETE - * (even if the TMSI is the same). - * If avoid_tmsi == true, we don't send a TMSI, we don't - * expect a reply and Location Updating is done. - */ - if (avoid_tmsi) - loc_updating_success(conn, 1); - - return rc; -} - -static int _gsm0408_authorize_sec_cb(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct gsm_subscriber_connection *conn = data; - int rc = 0; - - switch (event) { - case GSM_SECURITY_AUTH_FAILED: - loc_updating_failure(conn, 1); - break; - - case GSM_SECURITY_ALREADY: - LOGP(DMM, LOGL_ERROR, "We don't expect LOCATION " - "UPDATING after CM SERVICE REQUEST\n"); - /* fall through */ - - case GSM_SECURITY_NOAVAIL: - case GSM_SECURITY_SUCCEEDED: - rc = finish_lu(conn); - break; - - default: - rc = -EINVAL; - }; - - return rc; -} - -static int gsm0408_authorize(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - if (!conn->loc_operation) - return 0; - - if (authorize_subscriber(conn->loc_operation, conn->subscr)) - return gsm48_secure_channel(conn, - conn->loc_operation->key_seq, - _gsm0408_authorize_sec_cb, NULL); - return 0; -} - -void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) -{ - struct gsm_trans *trans, *temp; - - /* avoid someone issuing a clear */ - conn->in_release = 1; - - /* - * Cancel any outstanding location updating request - * operation taking place on the subscriber connection. - */ - loc_updating_failure(conn, 0); - - /* We might need to cancel the paging response or such. */ - if (conn->sec_operation && conn->sec_operation->cb) { - conn->sec_operation->cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, - NULL, conn, conn->sec_operation->cb_data); - } - - release_security_operation(conn); - release_anchor(conn); - - /* - * 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. 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. - * - * TODO: Move the trans_list into the subscriber connection and - * create a pending list for MT transactions. These exist before - * we have a subscriber connection. - */ -restart: - llist_for_each_entry_safe(trans, temp, &conn->network->trans_list, entry) { - if (trans->conn == conn) { - trans_free(trans); - goto restart; - } - } -} - +/* clear all transactions globally; used in case of MNCC socket disconnect */ void gsm0408_clear_all_trans(struct gsm_network *net, int protocol) { struct gsm_trans *trans, *temp; @@ -490,14 +199,15 @@ int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause) msg->lchan = conn->lchan; LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT " - "LAC=%u BTS=%u\n", subscr_name(conn->subscr), + "LAC=%u BTS=%u\n", vlr_subscr_name(conn->vsub), bts->location_area_code, bts->nr); return gsm48_conn_sendmsg(msg, conn, NULL); } /* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */ -static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn) +static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, + uint32_t send_tmsi) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 LOC UPD ACC"); struct gsm48_hdr *gh; @@ -515,16 +225,27 @@ static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn) conn->network->network_code, conn->bts->location_area_code); - if (conn->subscr->tmsi == GSM_RESERVED_TMSI) { + if (send_tmsi == GSM_RESERVED_TMSI) { + /* we did not allocate a TMSI to the MS, so we need to + * include the IMSI in order for the MS to delete any + * old TMSI that might still be allocated */ uint8_t mi[10]; int len; - len = gsm48_generate_mid_from_imsi(mi, conn->subscr->imsi); + len = gsm48_generate_mid_from_imsi(mi, conn->vsub->imsi); mid = msgb_put(msg, len); memcpy(mid, mi, len); } 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, conn->subscr->tmsi); + gsm48_generate_mid_from_tmsi(mid, send_tmsi); } + /* TODO: Follow-on proceed */ + /* TODO: CTS permission */ + /* TODO: Equivalent PLMNs */ + /* TODO: Emergency Number List */ + /* TODO: Per-MS T3312 */ DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n"); @@ -547,82 +268,29 @@ static int mm_tx_identity_req(struct gsm_subscriber_connection *conn, uint8_t id return gsm48_conn_sendmsg(msg, conn, NULL); } -static struct gsm_subscriber *subscr_create(const struct gsm_network *net, - const char *imsi) -{ - if (!net->auto_create_subscr) - return NULL; - - if (!subscr_regexp_check(net, imsi)) - return NULL; - - return subscr_create_subscriber(net->subscr_group, imsi); -} - /* Parse Chapter 9.2.11 Identity Response */ static int mm_rx_id_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm_network *net = conn->network; uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; char mi_string[GSM48_MI_SIZE]; + if (!conn->vsub) { + LOGP(DMM, LOGL_ERROR, + "Rx MM Identity Response: invalid: no subscriber\n"); + return -EINVAL; + } + gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); DEBUGP(DMM, "IDENTITY RESPONSE: MI(%s)=%s\n", gsm48_mi_type_name(mi_type), mi_string); osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data); - switch (mi_type) { - case GSM_MI_TYPE_IMSI: - /* look up subscriber based on IMSI, create if not found */ - if (!conn->subscr) { - conn->subscr = subscr_get_by_imsi(net->subscr_group, - mi_string); - if (!conn->subscr) - conn->subscr = subscr_create(net, mi_string); - } - if (!conn->subscr && conn->loc_operation) { - gsm0408_loc_upd_rej(conn, net->reject_cause); - loc_updating_failure(conn, 1); - return 0; - } - if (conn->loc_operation) - conn->loc_operation->waiting_for_imsi = 0; - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* update subscribe <-> IMEI mapping */ - if (conn->subscr) { - db_subscriber_assoc_imei(conn->subscr, mi_string); - db_sync_equipment(&conn->subscr->equipment); - } - if (conn->loc_operation) - conn->loc_operation->waiting_for_imei = 0; - break; - } - - /* Check if we can let the mobile station enter */ - return gsm0408_authorize(conn, msg); -} - - -static void loc_upd_rej_cb(void *data) -{ - struct gsm_subscriber_connection *conn = data; - - LOGP(DMM, LOGL_DEBUG, "Location Updating Request procedure timedout.\n"); - gsm0408_loc_upd_rej(conn, conn->network->reject_cause); - loc_updating_failure(conn, 1); -} - -static void schedule_reject(struct gsm_subscriber_connection *conn) -{ - osmo_timer_setup(&conn->loc_operation->updating_timer, loc_upd_rej_cb, - conn); - osmo_timer_schedule(&conn->loc_operation->updating_timer, 5, 0); + return vlr_subscr_rx_id_resp(conn->vsub, gh->data+1, gh->data[0]); } +/* FIXME: to libosmogsm */ static const struct value_string lupd_names[] = { { GSM48_LUPD_NORMAL, "NORMAL" }, { GSM48_LUPD_PERIODIC, "PERIODIC" }, @@ -630,14 +298,23 @@ static const struct value_string lupd_names[] = { { 0, NULL } }; -/* Chapter 9.2.15: Receive Location Updating Request */ -static int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg) +/* Chapter 9.2.15: Receive Location Updating Request. + * Keep this function non-static for direct invocation by unit tests. */ +int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg) { + static const enum subscr_conn_from conn_from_lu = SUBSCR_CONN_FROM_LU; + struct gsm_network *net = conn->network; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_loc_upd_req *lu; - struct gsm_subscriber *subscr = NULL; 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; + int rc; lu = (struct gsm48_loc_upd_req *) gh->data; @@ -645,97 +322,95 @@ static int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); - DEBUGPC(DMM, "MI(%s)=%s type=%s ", gsm48_mi_type_name(mi_type), - mi_string, get_value_string(lupd_names, lu->type)); + rc = msc_create_conn_fsm(conn, mi_string); + if (rc) + /* logging already happened in msc_create_conn_fsm() */ + return rc; + + conn->classmark.classmark1 = lu->classmark1; + conn->classmark.classmark1_set = true; + + DEBUGP(DMM, "LOCATION UPDATING REQUEST: MI(%s)=%s type=%s\n", + gsm48_mi_type_name(mi_type), mi_string, + get_value_string(lupd_names, lu->type)); osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len); switch (lu->type) { case GSM48_LUPD_NORMAL: rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_NORMAL]); + vlr_lu_type = VLR_LU_TYPE_REGULAR; break; case GSM48_LUPD_IMSI_ATT: rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_ATTACH]); + vlr_lu_type = VLR_LU_TYPE_IMSI_ATTACH; break; case GSM48_LUPD_PERIODIC: rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_PERIODIC]); + vlr_lu_type = VLR_LU_TYPE_PERIODIC; break; } - /* - * Pseudo Spoof detection: Just drop a second/concurrent - * location updating request. - */ - if (conn->loc_operation) { - DEBUGPC(DMM, "ignoring request due an existing one: %p.\n", - conn->loc_operation); - gsm0408_loc_upd_rej(conn, GSM48_REJECT_PROTOCOL_ERROR); - return 0; - } - - allocate_loc_updating_req(conn); - - conn->loc_operation->key_seq = lu->key_seq; + /* TODO: 10.5.1.6 MS Classmark for UMTS / Classmark 2 */ + /* TODO: 10.5.3.14 Aditional update parameters (CS fallback calls) */ + /* TODO: 10.5.7.8 Device properties */ + /* TODO: 10.5.1.15 MS network feature support */ switch (mi_type) { case GSM_MI_TYPE_IMSI: - DEBUGPC(DMM, "\n"); - /* we always want the IMEI, too */ - mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI); - conn->loc_operation->waiting_for_imei = 1; - - /* look up subscriber based on IMSI, create if not found */ - subscr = subscr_get_by_imsi(conn->network->subscr_group, mi_string); - if (!subscr) - subscr = subscr_create(conn->network, mi_string); - if (!subscr) { - gsm0408_loc_upd_rej(conn, conn->network->reject_cause); - loc_updating_failure(conn, 0); /* FIXME: set release == true? */ - return 0; - } + tmsi = GSM_RESERVED_TMSI; + imsi = mi_string; break; case GSM_MI_TYPE_TMSI: - DEBUGPC(DMM, "\n"); - /* look up the subscriber based on TMSI, request IMSI if it fails */ - subscr = subscr_get_by_tmsi(conn->network->subscr_group, - tmsi_from_string(mi_string)); - if (!subscr) { - /* send IDENTITY REQUEST message to get IMSI */ - mm_tx_identity_req(conn, GSM_MI_TYPE_IMSI); - conn->loc_operation->waiting_for_imsi = 1; - } - /* we always want the IMEI, too */ - mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI); - conn->loc_operation->waiting_for_imei = 1; - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* no sim card... FIXME: what to do ? */ - DEBUGPC(DMM, "unimplemented mobile identity type\n"); + tmsi = tmsi_from_string(mi_string); + imsi = NULL; break; default: DEBUGPC(DMM, "unknown mobile identity type\n"); + tmsi = GSM_RESERVED_TMSI; + imsi = NULL; break; } - /* schedule the reject timer */ - schedule_reject(conn); - - if (!subscr) { - DEBUGPC(DRR, "<- Can't find any subscriber for this ID\n"); - /* FIXME: request id? close channel? */ - return -EINVAL; + gsm48_decode_lai(&lu->lai, &old_lai.plmn.mcc, + &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; + DEBUGP(DMM, "LU/new-LAC: %u/%u\n", old_lai.lac, new_lai.lac); + + 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, + classmark_is_r99(&conn->classmark), + conn->via_ran == RAN_UTRAN_IU, + net->vlr->cfg.assign_tmsi); + if (!lu_fsm) { + DEBUGP(DRR, "%s: Can't start LU FSM\n", mi_string); + return 0; } - conn->subscr = subscr; - conn->subscr->equipment.classmark1 = lu->classmark1; + /* From vlr_loc_update() we expect an implicit dispatch of + * VLR_ULA_E_UPDATE_LA, and thus we expect msc_vlr_subscr_assoc() to + * already have been called and completed. Has an error occured? */ + + if (!conn->vsub || conn->vsub->lu_fsm != lu_fsm) { + LOGP(DRR, LOGL_ERROR, + "%s: internal error during Location Updating attempt\n", + mi_string); + return -EIO; + } - /* check if we can let the subscriber into our network immediately - * or if we need to wait for identity responses. */ - return gsm0408_authorize(conn, msg); + return 0; } /* Turn int into semi-octet representation: 98 => 0x89 */ +/* FIXME: libosmocore/libosmogsm */ static uint8_t bcdify(uint8_t value) { uint8_t ret; @@ -939,53 +614,46 @@ int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn) return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_AUTH_REJ); } -/* - * At the 30C3 phones miss their periodic update - * interval a lot and then remain unreachable. In case - * we still know the TMSI we can just attach it again. - */ -static void implit_attach(struct gsm_subscriber_connection *conn) -{ - if (conn->subscr->lac != GSM_LAC_RESERVED_DETACHED) - return; - - subscr_update(conn->subscr, conn->bts, - GSM_SUBSCRIBER_UPDATE_ATTACHED); -} - +static int msc_vlr_tx_cm_serv_acc(void *msc_conn_ref); +static int msc_vlr_tx_cm_serv_rej(void *msc_conn_ref, enum vlr_proc_arq_result result); -static int _gsm48_rx_mm_serv_req_sec_cb( - unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) +static int cm_serv_reuse_conn(struct gsm_subscriber_connection *conn, const uint8_t *mi_lv) { - struct gsm_subscriber_connection *conn = data; - int rc = 0; - - /* auth failed or succeeded, the timer was stopped */ - conn->expire_timer_stopped = 1; - - switch (event) { - case GSM_SECURITY_AUTH_FAILED: - /* Nothing to do */ - break; + uint8_t mi_type; + char mi_string[GSM48_MI_SIZE]; + uint32_t tmsi; - case GSM_SECURITY_NOAVAIL: - case GSM_SECURITY_ALREADY: - rc = gsm48_tx_mm_serv_ack(conn); - implit_attach(conn); - break; + gsm48_mi_to_string(mi_string, sizeof(mi_string), mi_lv+1, mi_lv[0]); + mi_type = mi_lv[1] & GSM_MI_TYPE_MASK; - case GSM_SECURITY_SUCCEEDED: - /* nothing to do. CIPHER MODE COMMAND is - * implicit CM SERV ACK */ - implit_attach(conn); - break; + switch (mi_type) { + case GSM_MI_TYPE_IMSI: + if (vlr_subscr_matches_imsi(conn->vsub, mi_string)) + goto accept_reuse; + break; + case GSM_MI_TYPE_TMSI: + tmsi = osmo_load32be(mi_lv+2); + if (vlr_subscr_matches_tmsi(conn->vsub, tmsi)) + goto accept_reuse; + break; + case GSM_MI_TYPE_IMEI: + if (vlr_subscr_matches_imei(conn->vsub, mi_string)) + goto accept_reuse; + break; + default: + break; + } - default: - rc = -EINVAL; - }; + LOGP(DMM, LOGL_ERROR, "%s: CM Service Request with mismatching mobile identity: %s %s\n", + vlr_subscr_name(conn->vsub), gsm48_mi_type_name(mi_type), mi_string); + msc_vlr_tx_cm_serv_rej(conn, VLR_PR_ARQ_RES_ILLEGAL_SUBSCR); + return -EINVAL; - return rc; +accept_reuse: + DEBUGP(DMM, "%s: re-using already accepted connection\n", + vlr_subscr_name(conn->vsub)); + conn->received_cm_service_request = true; + return conn->network->vlr->ops.tx_cm_serv_acc(conn); } /* @@ -996,14 +664,17 @@ static int _gsm48_rx_mm_serv_req_sec_cb( * c) Check that we know the subscriber with the TMSI otherwise reject * with a HLR cause * d) Set the subscriber on the gsm_lchan and accept + * + * Keep this function non-static for direct invocation by unit tests. */ -static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *msg) +int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *msg) { + static const enum subscr_conn_from conn_from_cm_service_req = + SUBSCR_CONN_FROM_CM_SERVICE_REQ; + struct gsm_network *net = conn->network; uint8_t mi_type; char mi_string[GSM48_MI_SIZE]; - struct gsm_network *network = conn->network; - struct gsm_subscriber *subscr; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_service_request *req = (struct gsm48_service_request *)gh->data; @@ -1012,6 +683,12 @@ static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct m uint8_t *classmark2 = gh->data+2; uint8_t mi_len = *(classmark2 + classmark2_len); uint8_t *mi = (classmark2 + classmark2_len + 1); + struct osmo_location_area_id lai; + int rc; + + lai.plmn.mcc = conn->network->country_code; + lai.plmn.mnc = conn->network->network_code; + lai.lac = conn->bts->location_area_code; DEBUGP(DMM, "<- CM SERVICE REQUEST "); if (msg->data_len < sizeof(struct gsm48_service_request*)) { @@ -1033,14 +710,10 @@ static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct m DEBUGPC(DMM, "serv_type=0x%02x MI(%s)=%s\n", req->cm_service_type, gsm48_mi_type_name(mi_type), mi_string); - subscr = subscr_get_by_imsi(network->subscr_group, - mi_string); } else if (mi_type == GSM_MI_TYPE_TMSI) { DEBUGPC(DMM, "serv_type=0x%02x MI(%s)=%s\n", req->cm_service_type, gsm48_mi_type_name(mi_type), mi_string); - subscr = subscr_get_by_tmsi(network->subscr_group, - tmsi_from_string(mi_string)); } else { DEBUGPC(DMM, "mi_type is not expected: %d\n", mi_type); return gsm48_tx_mm_serv_rej(conn, @@ -1048,34 +721,40 @@ static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct m } osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len)); + memcpy(conn->classmark.classmark2, classmark2, classmark2_len); + conn->classmark.classmark2_len = classmark2_len; + + if (conn->conn_fsm) { + if (msc_subscr_conn_is_accepted(conn)) + return cm_serv_reuse_conn(conn, mi-1); + LOGP(DMM, LOGL_ERROR, "%s: connection already in use\n", + vlr_subscr_name(conn->vsub)); + msc_vlr_tx_cm_serv_rej(conn, VLR_PR_ARQ_RES_UNKNOWN_ERROR); + return -EINVAL; + } - if (is_siemens_bts(conn->bts)) - send_siemens_mrpci(msg->lchan, classmark2-1); - - - /* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */ - if (!subscr) - return gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_IMSI_UNKNOWN_IN_VLR); - - if (!conn->subscr) - conn->subscr = subscr; - else if (conn->subscr == subscr) - subscr_put(subscr); /* lchan already has a ref, don't need another one */ - else { - DEBUGP(DMM, "<- CM Channel already owned by someone else?\n"); - subscr_put(subscr); + rc = msc_create_conn_fsm(conn, mi_string); + if (rc) { + msc_vlr_tx_cm_serv_rej(conn, VLR_PR_ARQ_RES_UNKNOWN_ERROR); + /* logging already happened in msc_create_conn_fsm() */ + return rc; } - subscr->equipment.classmark2_len = classmark2_len; - memcpy(subscr->equipment.classmark2, classmark2, classmark2_len); - db_sync_equipment(&subscr->equipment); + if (is_siemens_bts(conn->bts)) + send_siemens_mrpci(msg->lchan, classmark2-1); - /* we will send a MM message soon */ - conn->expire_timer_stopped = 1; + 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, + classmark_is_r99(&conn->classmark), + conn->via_ran == RAN_UTRAN_IU); - return gsm48_secure_channel(conn, req->cipher_key_seq, - _gsm48_rx_mm_serv_req_sec_cb, NULL); + return 0; } static int gsm48_rx_mm_imsi_detach_ind(struct gsm_subscriber_connection *conn, struct msgb *msg) @@ -1086,51 +765,51 @@ static int gsm48_rx_mm_imsi_detach_ind(struct gsm_subscriber_connection *conn, s (struct gsm48_imsi_detach_ind *) gh->data; uint8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK; char mi_string[GSM48_MI_SIZE]; - struct gsm_subscriber *subscr = NULL; + struct vlr_subscr *vsub = NULL; gsm48_mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len); - DEBUGP(DMM, "IMSI DETACH INDICATION: MI(%s)=%s", - gsm48_mi_type_name(mi_type), mi_string); + DEBUGP(DMM, "IMSI DETACH INDICATION: MI(%s)=%s\n", + gsm48_mi_type_name(mi_type), mi_string); rate_ctr_inc(&network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_DETACH]); switch (mi_type) { case GSM_MI_TYPE_TMSI: - DEBUGPC(DMM, "\n"); - subscr = subscr_get_by_tmsi(network->subscr_group, - tmsi_from_string(mi_string)); + vsub = vlr_subscr_find_by_tmsi(network->vlr, + tmsi_from_string(mi_string)); break; case GSM_MI_TYPE_IMSI: - DEBUGPC(DMM, "\n"); - subscr = subscr_get_by_imsi(network->subscr_group, - mi_string); + vsub = vlr_subscr_find_by_imsi(network->vlr, mi_string); break; case GSM_MI_TYPE_IMEI: case GSM_MI_TYPE_IMEISV: /* no sim card... FIXME: what to do ? */ - DEBUGPC(DMM, ": unimplemented mobile identity type\n"); + LOGP(DMM, LOGL_ERROR, "MI(%s)=%s: unimplemented mobile identity type\n", + gsm48_mi_type_name(mi_type), mi_string); break; default: - DEBUGPC(DMM, ": unknown mobile identity type\n"); + LOGP(DMM, LOGL_ERROR, "MI(%s)=%s: unknown mobile identity type\n", + gsm48_mi_type_name(mi_type), mi_string); break; } - if (subscr) { - subscr_update(subscr, conn->bts, - GSM_SUBSCRIBER_UPDATE_DETACHED); - DEBUGP(DMM, "Subscriber: %s\n", subscr_name(subscr)); - - subscr->equipment.classmark1 = idi->classmark1; - db_sync_equipment(&subscr->equipment); - subscr_put(subscr); - } else - DEBUGP(DMM, "Unknown Subscriber ?!?\n"); + /* TODO? We used to remember the subscriber's classmark1 here and + * stored it in the old sqlite db, but now we store it in a conn that + * will be discarded anyway: */ + conn->classmark.classmark1 = idi->classmark1; - /* FIXME: iterate over all transactions and release them, - * imagine an IMSI DETACH happening during an active call! */ + if (!vsub) { + LOGP(DMM, LOGL_ERROR, "IMSI DETACH for unknown subscriber MI(%s)=%s\n", + gsm48_mi_type_name(mi_type), mi_string); + } else { + LOGP(DMM, LOGL_INFO, "IMSI DETACH for %s\n", vlr_subscr_name(vsub)); + vlr_subscr_rx_imsi_detach(vsub); + osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_DETACHED, vsub); + vlr_subscr_put(vsub); + } - release_anchor(conn); + msc_subscr_conn_close(conn, 0); return 0; } @@ -1154,7 +833,7 @@ static int parse_gsm_auth_resp(uint8_t *res, uint8_t *res_len, LOGP(DMM, LOGL_ERROR, "%s: MM AUTHENTICATION RESPONSE:" " l3 length invalid: %u\n", - subscr_name(conn->subscr), msgb_l3len(msg)); + vlr_subscr_name(conn->vsub), msgb_l3len(msg)); return -EINVAL; } @@ -1187,7 +866,7 @@ static int parse_umts_auth_resp(uint8_t *res, uint8_t *res_len, LOGP(DMM, LOGL_ERROR, "%s: MM AUTHENTICATION RESPONSE:" " l3 length invalid: %u\n", - subscr_name(conn->subscr), msgb_l3len(msg)); + vlr_subscr_name(conn->vsub), msgb_l3len(msg)); return -EINVAL; } @@ -1197,7 +876,7 @@ static int parse_umts_auth_resp(uint8_t *res, uint8_t *res_len, LOGP(DMM, LOGL_ERROR, "%s: MM R99 AUTHENTICATION RESPONSE:" " expected IEI 0x%02x, got 0x%02x\n", - subscr_name(conn->subscr), + vlr_subscr_name(conn->vsub), GSM48_IE_AUTH_RES_EXT, iei); return -EINVAL; } @@ -1206,7 +885,7 @@ static int parse_umts_auth_resp(uint8_t *res, uint8_t *res_len, LOGP(DMM, LOGL_ERROR, "%s: MM R99 AUTHENTICATION RESPONSE:" " extended Auth Resp IE 0x%02x is too large: %u bytes\n", - subscr_name(conn->subscr), GSM48_IE_AUTH_RES_EXT, ie_len); + vlr_subscr_name(conn->vsub), GSM48_IE_AUTH_RES_EXT, ie_len); return -EINVAL; } @@ -1218,17 +897,15 @@ static int parse_umts_auth_resp(uint8_t *res, uint8_t *res_len, /* Chapter 9.2.3: Authentication Response */ static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) { - struct gsm_network *net = conn->network; uint8_t res[16]; uint8_t res_len; int rc; bool is_r99; - if (!conn->subscr) { + if (!conn->vsub) { LOGP(DMM, LOGL_ERROR, "MM AUTHENTICATION RESPONSE: invalid: no subscriber\n"); - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); + msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); return -EINVAL; } @@ -1242,56 +919,18 @@ static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct } if (rc) { - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); + msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); return -EINVAL; } DEBUGP(DMM, "%s: MM %s AUTHENTICATION RESPONSE (%s = %s)\n", - subscr_name(conn->subscr), + vlr_subscr_name(conn->vsub), is_r99 ? "R99" : "GSM", is_r99 ? "res" : "sres", osmo_hexdump_nospc(res, res_len)); - /* Future: vlr_sub_rx_auth_resp(conn->vsub, is_r99, - * conn->via_ran == RAN_UTRAN_IU, - * res, res_len); - */ - - if (res_len != 4) { - LOGP(DMM, LOGL_ERROR, - "%s: MM AUTHENTICATION RESPONSE:" - " UMTS authentication not supported\n", - subscr_name(conn->subscr)); - } - - /* Safety check */ - if (!conn->sec_operation) { - DEBUGP(DMM, "No authentication/cipher operation in progress !!!\n"); - return -EIO; - } - - /* Validate SRES */ - if (memcmp(conn->sec_operation->atuple.vec.sres, res, 4)) { - int rc; - gsm_cbfn *cb = conn->sec_operation->cb; - - DEBUGPC(DMM, "Invalid (expected %s)\n", - osmo_hexdump(conn->sec_operation->atuple.vec.sres, 4)); - - if (cb) - cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, - NULL, conn, conn->sec_operation->cb_data); - - rc = gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return rc; - } - - DEBUGPC(DMM, "OK\n"); - - /* Start ciphering */ - return gsm0808_cipher_mode(conn, net->a5_encryption, - conn->sec_operation->atuple.vec.kc, 8, 0); + return vlr_subscr_rx_auth_resp(conn->vsub, is_r99, + conn->via_ran == RAN_UTRAN_IU, + res, res_len); } static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct msgb *msg) @@ -1301,20 +940,11 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct uint8_t auts_tag; uint8_t auts_len; uint8_t *auts; - int rc; - - if (!conn->sec_operation) { - DEBUGP(DMM, "%s: MM R99 AUTHENTICATION FAILURE:" - " No authentication/cipher operation in progress\n", - subscr_name(conn->subscr)); - return -EINVAL; - } - if (!conn->subscr) { + if (!conn->vsub) { LOGP(DMM, LOGL_ERROR, "MM R99 AUTHENTICATION FAILURE: invalid: no subscriber\n"); - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); + msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); return -EINVAL; } @@ -1322,9 +952,8 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct LOGP(DMM, LOGL_ERROR, "%s: MM R99 AUTHENTICATION FAILURE:" " l3 length invalid: %u\n", - subscr_name(conn->subscr), msgb_l3len(msg)); - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); + vlr_subscr_name(conn->vsub), msgb_l3len(msg)); + msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); return -EINVAL; } @@ -1333,10 +962,9 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct if (cause != GSM48_REJECT_SYNCH_FAILURE) { LOGP(DMM, LOGL_INFO, "%s: MM R99 AUTHENTICATION FAILURE: cause 0x%0x\n", - subscr_name(conn->subscr), cause); - rc = gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return rc; + vlr_subscr_name(conn->vsub), cause); + vlr_subscr_rx_auth_fail(conn->vsub, NULL); + return 0; } /* This is a Synch Failure procedure, which should pass an AUTS to @@ -1347,9 +975,8 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct LOGP(DMM, LOGL_INFO, "%s: MM R99 AUTHENTICATION FAILURE:" " invalid Synch Failure: missing AUTS IE\n", - subscr_name(conn->subscr)); - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); + vlr_subscr_name(conn->vsub)); + msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); return -EINVAL; } @@ -1364,10 +991,9 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct " invalid Synch Failure:" " expected AUTS IE 0x%02x of 14 bytes," " got IE 0x%02x of %u bytes\n", - subscr_name(conn->subscr), + vlr_subscr_name(conn->vsub), GSM48_IE_AUTS, auts_tag, auts_len); - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); + msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); return -EINVAL; } @@ -1375,9 +1001,8 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct LOGP(DMM, LOGL_INFO, "%s: MM R99 AUTHENTICATION FAILURE:" " invalid Synch Failure msg: message truncated (%u)\n", - subscr_name(conn->subscr), msgb_l3len(msg)); - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); + vlr_subscr_name(conn->vsub), msgb_l3len(msg)); + msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); return -EINVAL; } @@ -1385,15 +1010,21 @@ static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct * large enough. */ DEBUGP(DMM, "%s: MM R99 AUTHENTICATION SYNCH (AUTS = %s)\n", - subscr_name(conn->subscr), osmo_hexdump_nospc(auts, 14)); + vlr_subscr_name(conn->vsub), osmo_hexdump_nospc(auts, 14)); - /* Future: vlr_sub_rx_auth_fail(conn->vsub, auts); */ + return vlr_subscr_rx_auth_fail(conn->vsub, auts); +} - LOGP(DMM, LOGL_ERROR, "%s: MM R99 AUTHENTICATION not supported\n", - subscr_name(conn->subscr)); - rc = gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return rc; +static int gsm48_rx_mm_tmsi_reall_compl(struct gsm_subscriber_connection *conn) +{ + DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n", + vlr_subscr_name(conn->vsub)); + if (!conn->vsub) { + LOGP(DMM, LOGL_ERROR, + "Rx MM TMSI Reallocation Complete: invalid: no subscriber\n"); + return -EINVAL; + } + return vlr_subscr_rx_tmsi_reall_compl(conn->vsub); } /* Receive a GSM 04.08 Mobility Management (MM) message */ @@ -1404,7 +1035,6 @@ static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *m switch (gsm48_hdr_msg_type(gh)) { case GSM48_MT_MM_LOC_UPD_REQUEST: - DEBUGP(DMM, "LOCATION UPDATING REQUEST: "); rc = mm_rx_loc_upd_req(conn, msg); break; case GSM48_MT_MM_ID_RESP: @@ -1417,9 +1047,7 @@ static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *m rc = gsm48_rx_mm_status(msg); break; case GSM48_MT_MM_TMSI_REALL_COMPL: - DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n", - subscr_name(conn->subscr)); - loc_updating_success(conn, 1); + rc = gsm48_rx_mm_tmsi_reall_compl(conn); break; case GSM48_MT_MM_IMSI_DETACH_IND: rc = gsm48_rx_mm_imsi_detach_ind(conn, msg); @@ -1442,18 +1070,37 @@ static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *m return rc; } +static uint8_t *gsm48_cm2_get_mi(uint8_t *classmark2_lv, unsigned int tot_len) +{ + /* Check the size for the classmark */ + if (tot_len < 1 + *classmark2_lv) + return NULL; + + uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1; + if (tot_len < 2 + *classmark2_lv + mi_lv[0]) + return NULL; + + return mi_lv; +} + /* Receive a PAGING RESPONSE message from the MS */ static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) { + static const enum subscr_conn_from conn_from_paging_resp = + SUBSCR_CONN_FROM_PAGING_RESP; + struct gsm_network *net = conn->network; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_pag_resp *resp; uint8_t *classmark2_lv = gh->data + 1; + uint8_t *mi_lv; uint8_t mi_type; char mi_string[GSM48_MI_SIZE]; - struct gsm_subscriber *subscr = NULL; - struct bsc_subscr *bsub; - uint32_t tmsi; int rc = 0; + struct osmo_location_area_id lai; + + lai.plmn.mcc = conn->network->country_code; + lai.plmn.mnc = conn->network->network_code; + lai.lac = conn->bts->location_area_code; /* (will be replaced by conn->lac soon) */ resp = (struct gsm48_pag_resp *) &gh->data[0]; gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), @@ -1461,55 +1108,31 @@ static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct m DEBUGP(DRR, "PAGING RESPONSE: MI(%s)=%s\n", gsm48_mi_type_name(mi_type), mi_string); - switch (mi_type) { - case GSM_MI_TYPE_TMSI: - tmsi = tmsi_from_string(mi_string); - subscr = subscr_get_by_tmsi(conn->network->subscr_group, tmsi); - break; - case GSM_MI_TYPE_IMSI: - subscr = subscr_get_by_imsi(conn->network->subscr_group, - mi_string); - break; - } - - if (!subscr) { - DEBUGP(DRR, "<- Can't find any subscriber for this ID\n"); - /* FIXME: request id? close channel? */ - return -EINVAL; - } - - if (!conn->subscr) { - conn->subscr = subscr; - } else if (conn->subscr != subscr) { - LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n"); - subscr_put(subscr); - return -EINVAL; - } else { - DEBUGP(DRR, "<- Channel already owned by us\n"); - subscr_put(subscr); - subscr = conn->subscr; + mi_lv = gsm48_cm2_get_mi(classmark2_lv, msgb_l3len(msg) - sizeof(*gh)); + if (!mi_lv) { + /* FIXME */ + return -1; } - log_set_context(LOG_CTX_VLR_SUBSCR, subscr); - DEBUGP(DRR, "<- Channel was requested by %s\n", - subscr->name && strlen(subscr->name) ? subscr->name : subscr->imsi); - - subscr->equipment.classmark2_len = *classmark2_lv; - memcpy(subscr->equipment.classmark2, classmark2_lv+1, *classmark2_lv); - db_sync_equipment(&subscr->equipment); + rc = msc_create_conn_fsm(conn, mi_string); + if (rc) + /* logging already happened in msc_create_conn_fsm() */ + return rc; - /* 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. */ - bsub = bsc_subscr_find_or_create_by_imsi(conn->network->bsc_subscribers, - subscr->imsi); - bsub->tmsi = subscr->tmsi; - bsub->lac = subscr->lac; + memcpy(conn->classmark.classmark2, classmark2_lv+1, *classmark2_lv); + conn->classmark.classmark2_len = *classmark2_lv; - /* We received a paging */ - conn->expire_timer_stopped = 1; + 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, + classmark_is_r99(&conn->classmark), + conn->via_ran == RAN_UTRAN_IU); - rc = gsm48_handle_paging_resp(conn, msg, bsub); return rc; } @@ -1527,7 +1150,12 @@ static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct m DEBUGP(DRR, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s\n", apdu_id_flags, apdu_len, osmo_hexdump(apdu_data, apdu_len)); + /* we're not using the app info blob anywhere, so ignore. */ +#if 0 return db_apdu_blob_store(conn->subscr, apdu_id_flags, apdu_len, apdu_data); +#else + return 0; +#endif } /* Receive a GSM 04.08 Radio Resource (RR) message */ @@ -1681,12 +1309,12 @@ static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans, trans->conn->lchan->ts->trx->bts->nr, trans->conn->lchan->ts->trx->nr, trans->conn->lchan->ts->nr, trans->transaction_id, - (trans->subscr)?(trans->subscr->extension):"-", + vlr_subscr_msisdn_or_name(trans->vsub), get_mncc_name(msg_type)); else DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " "Sending '%s' to MNCC.\n", - (trans->subscr)?(trans->subscr->extension):"-", + vlr_subscr_msisdn_or_name(trans->vsub), get_mncc_name(msg_type)); else DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) " @@ -1752,7 +1380,8 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, /* check all tranactions (without lchan) for subscriber */ switch (event) { case GSM_PAGING_SUCCEEDED: - DEBUGP(DCC, "Paging subscr %s succeeded!\n", transt->subscr->extension); + DEBUGP(DCC, "Paging subscr %s succeeded!\n", + vlr_subscr_msisdn_or_name(transt->vsub)); OSMO_ASSERT(conn); /* Assign lchan */ transt->conn = conn; @@ -1762,7 +1391,7 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, case GSM_PAGING_EXPIRED: case GSM_PAGING_BUSY: DEBUGP(DCC, "Paging subscr %s expired!\n", - transt->subscr->extension); + vlr_subscr_msisdn_or_name(transt->vsub)); /* Temporarily out of order */ mncc_release_ind(transt->net, transt, transt->callref, @@ -2023,7 +1652,7 @@ static int tch_bridge(struct gsm_network *net, struct gsm_mncc_bridge *bridge) return -EIO; /* Which subscriber do we want to track trans1 or trans2? */ - log_set_context(LOG_CTX_VLR_SUBSCR, trans1->subscr); + log_set_context(LOG_CTX_VLR_SUBSCR, trans1->vsub); /* through-connect channel */ return tch_map(trans1->conn->lchan, trans2->conn->lchan); @@ -2044,7 +1673,7 @@ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable) if (!trans->conn) return 0; - log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr); + log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); lchan = trans->conn->lchan; bts = lchan->ts->trx->bts; @@ -2247,9 +1876,8 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) /* use subscriber as calling party number */ setup.fields |= MNCC_F_CALLING; - osmo_strlcpy(setup.calling.number, trans->subscr->extension, - sizeof(setup.calling.number)); - osmo_strlcpy(setup.imsi, trans->subscr->imsi, sizeof(setup.imsi)); + osmo_strlcpy(setup.calling.number, trans->vsub->msisdn, sizeof(setup.calling.number)); + osmo_strlcpy(setup.imsi, trans->vsub->imsi, sizeof(setup.imsi)); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { @@ -2298,7 +1926,7 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) new_cc_state(trans, GSM_CSTATE_INITIATED); LOGP(DCC, LOGL_INFO, "Subscriber %s (%s) sends SETUP to %s\n", - subscr_name(trans->subscr), trans->subscr->extension, + vlr_subscr_name(trans->vsub), trans->vsub->msisdn, setup.called.number); rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MO_SETUP]); @@ -2335,7 +1963,7 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) } /* Get free transaction_id */ - trans_id = trans_assign_trans_id(trans->net, trans->subscr, + trans_id = trans_assign_trans_id(trans->net, trans->vsub, GSM48_PDISC_CC, 0); if (trans_id < 0) { /* no free transaction ID */ @@ -2426,8 +2054,7 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) } /* IMSI of called subscriber */ - osmo_strlcpy(call_conf.imsi, trans->subscr->imsi, - sizeof(call_conf.imsi)); + osmo_strlcpy(call_conf.imsi, trans->vsub->imsi, sizeof(call_conf.imsi)); new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); @@ -2580,9 +2207,8 @@ static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* use subscriber as connected party number */ connect.fields |= MNCC_F_CONNECTED; - osmo_strlcpy(connect.connected.number, trans->subscr->extension, - sizeof(connect.connected.number)); - osmo_strlcpy(connect.imsi, trans->subscr->imsi, sizeof(connect.imsi)); + osmo_strlcpy(connect.connected.number, trans->vsub->msisdn, sizeof(connect.connected.number)); + osmo_strlcpy(connect.imsi, trans->vsub->imsi, sizeof(connect.imsi)); /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { @@ -3379,7 +3005,7 @@ static int tch_rtp_create(struct gsm_network *net, uint32_t callref) mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE); return -EIO; } - log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr); + log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); if (!trans->conn) { LOGP(DMNCC, LOGL_NOTICE, "RTP create for trans without conn\n"); mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE); @@ -3435,7 +3061,7 @@ static int tch_rtp_connect(struct gsm_network *net, void *arg) mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT); return -EIO; } - log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr); + log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); if (!trans->conn) { LOGP(DMNCC, LOGL_ERROR, "RTP connect for trans without conn\n"); mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT); @@ -3612,7 +3238,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) LOGP(DMNCC, LOGL_ERROR, "TCH frame for non-existing trans\n"); return -EIO; } - log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr); + log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); if (!trans->conn) { LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without conn\n"); return 0; @@ -3656,7 +3282,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) /* Callref unknown */ if (!trans) { - struct gsm_subscriber *subscr; + struct vlr_subscr *vsub; if (msg_type != MNCC_SETUP_REQ) { DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " @@ -3679,17 +3305,16 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) } /* New transaction due to setup, find subscriber */ if (data->called.number[0]) - subscr = subscr_get_by_extension(net->subscr_group, - data->called.number); + vsub = vlr_subscr_find_by_msisdn(net->vlr, + data->called.number); else - subscr = subscr_get_by_imsi(net->subscr_group, - data->imsi); + vsub = vlr_subscr_find_by_imsi(net->vlr, data->imsi); /* update the subscriber we deal with */ - log_set_context(LOG_CTX_VLR_SUBSCR, subscr); + log_set_context(LOG_CTX_VLR_SUBSCR, vsub); /* If subscriber is not found */ - if (!subscr) { + if (!vsub) { DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " "Received '%s' from MNCC with " "unknown subscriber %s\n", data->called.number, @@ -3700,22 +3325,22 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) GSM48_CC_CAUSE_UNASSIGNED_NR); } /* If subscriber is not "attached" */ - if (!subscr->lac) { + if (!vsub->lac) { DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " "Received '%s' from MNCC with " "detached subscriber %s\n", data->called.number, get_mncc_name(msg_type), data->called.number); - subscr_put(subscr); + vlr_subscr_put(vsub); /* Temporarily out of order */ return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_DEST_OOO); } /* Create transaction */ - trans = trans_alloc(net, subscr, GSM48_PDISC_CC, 0xff, data->callref); + trans = trans_alloc(net, vsub, GSM48_PDISC_CC, 0xff, data->callref); if (!trans) { DEBUGP(DCC, "No memory for trans.\n"); - subscr_put(subscr); + vlr_subscr_put(vsub); /* Ressource unavailable */ mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, @@ -3723,7 +3348,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) return -ENOMEM; } /* Find lchan */ - conn = connection_for_subscr(subscr); + conn = connection_for_subscr(vsub); /* If subscriber has no lchan */ if (!conn) { @@ -3731,15 +3356,15 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) llist_for_each_entry(transt, &net->trans_list, entry) { /* Transaction of our lchan? */ if (transt == trans || - transt->subscr != subscr) + transt->vsub != vsub) continue; DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " "Received '%s' from MNCC with " "unallocated channel, paging already " "started for lac %d.\n", data->called.number, - get_mncc_name(msg_type), subscr->lac); - subscr_put(subscr); + get_mncc_name(msg_type), vsub->lac); + vlr_subscr_put(vsub); trans_free(trans); return 0; } @@ -3747,24 +3372,26 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc)); /* Request a channel */ - trans->paging_request = subscr_request_channel(subscr, - RSL_CHANNEED_TCH_F, setup_trig_pag_evt, + trans->paging_request = subscr_request_channel( + vsub, + RSL_CHANNEED_TCH_F, + setup_trig_pag_evt, trans); if (!trans->paging_request) { LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n"); - subscr_put(subscr); + vlr_subscr_put(vsub); trans_free(trans); return 0; } - subscr_put(subscr); + vlr_subscr_put(vsub); return 0; } /* Assign lchan */ - trans->conn = conn; - subscr_put(subscr); + trans->conn = msc_subscr_conn_get(conn); + vlr_subscr_put(vsub); } else { /* update the subscriber we deal with */ - log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr); + log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); } if (trans->conn) @@ -3774,7 +3401,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) if (!conn) { DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " "Received '%s' from MNCC in paging state\n", - (trans->subscr)?(trans->subscr->extension):"-", + vlr_subscr_msisdn_or_name(trans->vsub), get_mncc_name(msg_type)); mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_NORM_CALL_CLEAR); @@ -3791,7 +3418,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) "Received '%s' from MNCC in state %d (%s)\n", conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr, trans->transaction_id, - (trans->conn->subscr)?(trans->conn->subscr->extension):"-", + vlr_subscr_msisdn_or_name(trans->conn->vsub), get_mncc_name(msg_type), trans->cc.state, gsm48_cc_state_name(trans->cc.state)); @@ -3878,8 +3505,8 @@ static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *m return -EINVAL; } - if (!conn->subscr) { - LOGP(DCC, LOGL_ERROR, "Invalid conn, no subscriber\n"); + if (!conn->vsub) { + LOGP(DCC, LOGL_ERROR, "Invalid conn: no subscriber\n"); return -EINVAL; } @@ -3889,7 +3516,7 @@ static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *m DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " "Received '%s' from MS in state %d (%s)\n", conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr, - transaction_id, (conn->subscr)?(conn->subscr->extension):"-", + transaction_id, vlr_subscr_msisdn_or_name(conn->vsub), gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0, gsm48_cc_state_name(trans?(trans->cc.state):0)); @@ -3898,7 +3525,7 @@ static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *m DEBUGP(DCC, "Unknown transaction ID %x, " "creating new trans.\n", transaction_id); /* Create transaction */ - trans = trans_alloc(conn->network, conn->subscr, + trans = trans_alloc(conn->network, conn->vsub, GSM48_PDISC_CC, transaction_id, new_callref++); if (!trans) { @@ -3909,7 +3536,8 @@ static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *m return -ENOMEM; } /* Assign transaction */ - trans->conn = conn; + trans->conn = msc_subscr_conn_get(conn); + cm_service_request_concludes(conn, msg); } /* find function for current state and message */ @@ -3922,70 +3550,74 @@ static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *m return 0; } - assert(trans->subscr); + assert(trans->vsub); rc = datastatelist[i].rout(trans, msg); + msc_subscr_conn_communicating(conn); return rc; } -/* Create a dummy to wait five seconds */ -static void release_anchor(struct gsm_subscriber_connection *conn) +static bool msg_is_initially_permitted(const struct gsm48_hdr *hdr) { - if (!conn->anch_operation) - return; + uint8_t pdisc = gsm48_hdr_pdisc(hdr); + uint8_t msg_type = gsm48_hdr_msg_type(hdr); - osmo_timer_del(&conn->anch_operation->timeout); - talloc_free(conn->anch_operation); - conn->anch_operation = NULL; -} - -static void anchor_timeout(void *_data) -{ - struct gsm_subscriber_connection *con = _data; + switch (pdisc) { + case GSM48_PDISC_MM: + switch (msg_type) { + case GSM48_MT_MM_LOC_UPD_REQUEST: + case GSM48_MT_MM_CM_SERV_REQ: + case GSM48_MT_MM_AUTH_RESP: + case GSM48_MT_MM_AUTH_FAIL: + case GSM48_MT_MM_ID_RESP: + case GSM48_MT_MM_TMSI_REALL_COMPL: + case GSM48_MT_MM_IMSI_DETACH_IND: + return true; + default: + break; + } + break; + case GSM48_PDISC_RR: + switch (msg_type) { + case GSM48_MT_RR_CIPH_M_COMPL: + case GSM48_MT_RR_PAG_RESP: + return true; + default: + break; + } + break; + default: + break; + } - release_anchor(con); - msc_release_connection(con); + return false; } -int gsm0408_new_conn(struct gsm_subscriber_connection *conn) +void cm_service_request_concludes(struct gsm_subscriber_connection *conn, + struct msgb *msg) { - conn->anch_operation = talloc_zero(conn, struct gsm_anchor_operation); - if (!conn->anch_operation) - return -1; - - osmo_timer_setup(&conn->anch_operation->timeout, anchor_timeout, conn); - osmo_timer_schedule(&conn->anch_operation->timeout, 5, 0); - return 0; -} -struct gsm_subscriber_connection *msc_subscr_con_allocate(struct gsm_network *network) -{ - struct gsm_subscriber_connection *conn; + /* If a CM Service Request was received before, this is the request the + * conn was opened for. No need to wait for further messages. */ - conn = talloc_zero(network, struct gsm_subscriber_connection); - if (!conn) - return NULL; - - conn->network = network; - llist_add_tail(&conn->entry, &network->subscr_conns); - return conn; -} - -void msc_subscr_con_free(struct gsm_subscriber_connection *conn) -{ - if (!conn) + if (!conn->received_cm_service_request) return; - if (conn->subscr) { - subscr_put(conn->subscr); - conn->subscr = NULL; - } + if (log_check_level(DMM, LOGL_DEBUG)) { + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t pdisc = gsm48_hdr_pdisc(gh); + uint8_t msg_type = gsm48_hdr_msg_type(gh); - llist_del(&conn->entry); - talloc_free(conn); + DEBUGP(DMM, "%s pdisc=%d msg_type=0x%02x:" + " received_cm_service_request changes to false\n", + vlr_subscr_name(conn->vsub), + pdisc, msg_type); + } + conn->received_cm_service_request = false; } + /* Main entry point for GSM 04.08/44.008 Layer 3 data (e.g. from the BSC). */ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) { @@ -3997,6 +3629,16 @@ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) OSMO_ASSERT(msg); LOGP(DRLL, LOGL_DEBUG, "Dispatching 04.08 message, pdisc=%d\n", pdisc); + + if (!msc_subscr_conn_is_accepted(conn) + && !msg_is_initially_permitted(gh)) { + LOGP(DRLL, LOGL_ERROR, + "subscr %s: Message not permitted for initial conn:" + " pdisc=0x%02x msg_type=0x%02x\n", + vlr_subscr_name(conn->vsub), gh->proto_discr, gh->msg_type); + return -EACCES; + } + #if 0 if (silent_call_reroute(conn, msg)) return silent_call_rx(conn, msg); @@ -4004,7 +3646,6 @@ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) switch (pdisc) { case GSM48_PDISC_CC: - release_anchor(conn); rc = gsm0408_rcv_cc(conn, msg); break; case GSM48_PDISC_MM: @@ -4014,7 +3655,6 @@ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) rc = gsm0408_rcv_rr(conn, msg); break; case GSM48_PDISC_SMS: - release_anchor(conn); rc = gsm0411_rcv_sms(conn, msg); break; case GSM48_PDISC_MM_GPRS: @@ -4024,7 +3664,6 @@ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) rc = -ENOTSUP; break; case GSM48_PDISC_NC_SS: - release_anchor(conn); rc = handle_rcv_ussd(conn, msg); break; default: @@ -4037,6 +3676,166 @@ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) return rc; } +/*********************************************************************** + * VLR integration + ***********************************************************************/ + +/* VLR asks us to send an authentication request */ +static int msc_vlr_tx_auth_req(void *msc_conn_ref, struct gsm_auth_tuple *at, + bool send_autn) +{ + struct gsm_subscriber_connection *conn = msc_conn_ref; + return gsm48_tx_mm_auth_req(conn, at->vec.rand, + send_autn? at->vec.autn : NULL, + at->key_seq); +} + +/* VLR asks us to send an authentication reject */ +static int msc_vlr_tx_auth_rej(void *msc_conn_ref) +{ + struct gsm_subscriber_connection *conn = msc_conn_ref; + return gsm48_tx_mm_auth_rej(conn); +} + +/* VLR asks us to transmit an Identity Request of given type */ +static int msc_vlr_tx_id_req(void *msc_conn_ref, uint8_t mi_type) +{ + struct gsm_subscriber_connection *conn = msc_conn_ref; + return mm_tx_identity_req(conn, mi_type); +} + +/* VLR asks us to transmit a Location Update Accept */ +static int msc_vlr_tx_lu_acc(void *msc_conn_ref, uint32_t send_tmsi) +{ + struct gsm_subscriber_connection *conn = msc_conn_ref; + return gsm0408_loc_upd_acc(conn, send_tmsi); +} + +/* VLR asks us to transmit a Location Update Reject */ +static int msc_vlr_tx_lu_rej(void *msc_conn_ref, uint8_t cause) +{ + struct gsm_subscriber_connection *conn = msc_conn_ref; + return gsm0408_loc_upd_rej(conn, cause); +} + +/* VLR asks us to transmit a CM Service Accept */ +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); +} + +/* VLR asks us to transmit a CM Service Reject */ +static int msc_vlr_tx_cm_serv_rej(void *msc_conn_ref, enum vlr_proc_arq_result result) +{ + uint8_t cause; + struct gsm_subscriber_connection *conn = msc_conn_ref; + conn->received_cm_service_request = false; + + switch (result) { + default: + case VLR_PR_ARQ_RES_NONE: + case VLR_PR_ARQ_RES_SYSTEM_FAILURE: + case VLR_PR_ARQ_RES_UNKNOWN_ERROR: + cause = GSM48_REJECT_NETWORK_FAILURE; + break; + case VLR_PR_ARQ_RES_ILLEGAL_SUBSCR: + cause = GSM48_REJECT_LOC_NOT_ALLOWED; + break; + case VLR_PR_ARQ_RES_UNIDENT_SUBSCR: + cause = GSM48_REJECT_INVALID_MANDANTORY_INF; + break; + case VLR_PR_ARQ_RES_ROAMING_NOTALLOWED: + cause = GSM48_REJECT_ROAMING_NOT_ALLOWED; + break; + case VLR_PR_ARQ_RES_ILLEGAL_EQUIP: + cause = GSM48_REJECT_ILLEGAL_MS; + break; + case VLR_PR_ARQ_RES_TIMEOUT: + cause = GSM48_REJECT_CONGESTION; + break; + }; + + return gsm48_tx_mm_serv_rej(conn, cause); +} + +/* VLR asks us to start using ciphering */ +static int msc_vlr_set_ciph_mode(void *msc_conn_ref, + enum vlr_ciph ciph, + bool retrieve_imeisv) +{ + struct gsm_subscriber_connection *conn = msc_conn_ref; + struct vlr_subscr *vsub; + struct gsm_auth_tuple *tuple; + + if (!conn || !conn->vsub) { + LOGP(DMM, LOGL_ERROR, "Cannot send Ciphering Mode Command to" + " NULL conn/subscriber"); + return -EINVAL; + } + + vsub = conn->vsub; + tuple = vsub->last_tuple; + + if (!tuple) { + LOGP(DMM, LOGL_ERROR, "subscr %s: Cannot send Ciphering Mode" + " Command: no auth tuple available\n", + vlr_subscr_name(vsub)); + return -EINVAL; + } + + /* TODO: MSCSPLIT: don't directly push BSC buttons */ + return gsm0808_cipher_mode(conn, ciph, tuple->vec.kc, 8, + retrieve_imeisv); +} + +/* VLR informs us that the subscriber data has somehow been modified */ +static void msc_vlr_subscr_update(struct vlr_subscr *subscr) +{ + /* FIXME */ +} + +/* VLR informs us that the subscriber has been associated with a conn */ +static void msc_vlr_subscr_assoc(void *msc_conn_ref, + struct vlr_subscr *vsub) +{ + struct gsm_subscriber_connection *conn = msc_conn_ref; + OSMO_ASSERT(!conn->vsub); + conn->vsub = vlr_subscr_get(vsub); +} + +/* operations that we need to implement for libvlr */ +static const struct vlr_ops msc_vlr_ops = { + .tx_auth_req = msc_vlr_tx_auth_req, + .tx_auth_rej = msc_vlr_tx_auth_rej, + .tx_id_req = msc_vlr_tx_id_req, + .tx_lu_acc = msc_vlr_tx_lu_acc, + .tx_lu_rej = msc_vlr_tx_lu_rej, + .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, + .subscr_update = msc_vlr_subscr_update, + .subscr_assoc = msc_vlr_subscr_assoc, +}; + +/* Allocate net->vlr so that the VTY may configure the VLR's data structures */ +int msc_vlr_alloc(struct gsm_network *net) +{ + net->vlr = vlr_alloc(net, &msc_vlr_ops); + if (!net->vlr) + return -ENOMEM; + net->vlr->user_ctx = net; + return 0; +} + +/* Launch the VLR, i.e. its GSUP connection */ +int msc_vlr_start(struct gsm_network *net) +{ + OSMO_ASSERT(net->vlr); + return vlr_start("MSC", net->vlr, net->gsup_server_addr_str, + net->gsup_server_port); +} + /* * This will be run by the linker when loading the DSO. We use it to * do system initialization, e.g. registration of signal handlers. |