Add libvlr implementation

Original libvlr code is by Harald Welte <laforge@gnumonks.org>,
polished and tweaked by Neels Hofmeyr <nhofmeyr@sysmocom.de>.

This is a long series of trial-and-error development collapsed in one patch.
This may be split in smaller commits if reviewers prefer that. If we can keep
it as one, we have saved ourselves the additional separation work.

Related: OS#1592
Change-Id: Ie303c98f8c18e40c87c1b68474b35de332033622
changes/94/3194/10
Harald Welte 7 years ago committed by Neels Hofmeyr
parent 53edff3c70
commit b8b85a1b2e
  1. 1
      configure.ac
  2. 1
      include/openbsc/Makefile.am
  3. 6
      include/openbsc/debug.h
  4. 4
      include/openbsc/iu.h
  5. 413
      include/openbsc/vlr.h
  6. 1
      src/Makefile.am
  7. 5
      src/libcommon/debug.c
  8. 269
      src/libmsc/subscr_conn.c
  9. 1
      src/libmsc/vty_interface_layer3.c
  10. 19
      src/libvlr/Makefile.am
  11. 1108
      src/libvlr/vlr.c
  12. 778
      src/libvlr/vlr_access_req_fsm.c
  13. 17
      src/libvlr/vlr_access_req_fsm.h
  14. 605
      src/libvlr/vlr_auth_fsm.c
  15. 52
      src/libvlr/vlr_auth_fsm.h
  16. 21
      src/libvlr/vlr_core.h
  17. 1424
      src/libvlr/vlr_lu_fsm.c
  18. 18
      src/libvlr/vlr_lu_fsm.h
  19. 1
      src/osmo-nitb/Makefile.am

@ -225,6 +225,7 @@ AC_OUTPUT(
src/libtrau/Makefile
src/libbsc/Makefile
src/libmsc/Makefile
src/libvlr/Makefile
src/libmgcp/Makefile
src/libcommon/Makefile
src/libfilter/Makefile

@ -84,6 +84,7 @@ noinst_HEADERS = \
trau_mux.h \
trau_upqueue.h \
ussd.h \
vlr.h \
vty.h \
v42bis.h \
v42bis_private.h \

@ -38,12 +38,8 @@ enum {
DSUA,
DV42BIS,
DPCU,
DVLR,
Debug_LastEntry,
};
struct gsm_subscriber;
void log_set_filter_vlr_subscr(struct log_target *target,
struct gsm_subscriber *vlr_subscr);
extern const struct log_info log_info;

@ -22,9 +22,6 @@ enum iu_event_type {
IU_EVENT_SECURITY_MODE_COMPLETE,
IU_EVENT_IU_RELEASE, /* An actual Iu Release message was received */
IU_EVENT_LINK_INVALIDATED, /* A SUA link was lost or closed down */
/* FIXME: maybe IU_EVENT_IU_RELEASE and IU_EVENT_LINK_INVALIDATED
* should be combined to one generic event that simply means the
* ue_conn_ctx should no longer be used, for whatever reason. */
};
extern const struct value_string iu_event_type_names[];
@ -35,7 +32,6 @@ static inline const char *iu_event_type_str(enum iu_event_type e)
/* Implementations of iu_recv_cb_t shall find the ue_conn_ctx in msg->dst. */
typedef int (* iu_recv_cb_t )(struct msgb *msg, struct gprs_ra_id *ra_id,
/* TODO "gprs_" in generic CS+PS domain ^ */
uint16_t *sai);
typedef int (* iu_event_cb_t )(struct ue_conn_ctx *ue_ctx,

@ -0,0 +1,413 @@
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/logging.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/gsm23003.h>
#include <openbsc/gsm_data.h>
// for GSM_NAME_LENGTH
#include <openbsc/gsm_subscriber.h>
/* from 3s to 10s */
#define GSM_29002_TIMER_S 10
/* from 15s to 30s */
#define GSM_29002_TIMER_M 30
/* from 1min to 10min */
#define GSM_29002_TIMER_ML (10*60)
/* from 28h to 38h */
#define GSM_29002_TIMER_L (32*60*60)
/* VLR subscriber authentication state */
enum vlr_subscr_auth_state {
/* subscriber needs to be autenticated */
VLR_SUB_AS_NEEDS_AUTH,
/* waiting for AuthInfo from HLR/AUC */
VLR_SUB_AS_NEEDS_AUTH_WAIT_AI,
/* waiting for response from subscriber */
VLR_SUB_AS_WAIT_RESP,
/* successfully authenticated */
VLR_SUB_AS_AUTHENTICATED,
/* subscriber needs re-sync */
VLR_SUB_AS_NEEDS_RESYNC,
/* waiting for AuthInfo with ReSync */
VLR_SUB_AS_NEEDS_AUTH_WAIT_SAI_RESYNC,
/* waiting for response from subscr, resync case */
VLR_SUB_AS_WAIT_RESP_RESYNC,
/* waiting for IMSI from subscriber */
VLR_SUB_AS_WAIT_ID_IMSI,
/* authentication has failed */
VLR_SUB_AS_AUTH_FAILED,
};
enum vlr_lu_event {
VLR_ULA_E_UPDATE_LA, /* Initial trigger (LU from MS) */
VLR_ULA_E_SEND_ID_ACK, /* Result of Send-ID from PVLR */
VLR_ULA_E_SEND_ID_NACK, /* Result of Send-ID from PVLR */
VLR_ULA_E_AUTH_RES, /* Result of auth procedure */
VLR_ULA_E_CIPH_RES, /* Result of Ciphering Mode Command */
VLR_ULA_E_ID_IMSI, /* IMSI recieved from MS */
VLR_ULA_E_ID_IMEI, /* IMEI received from MS */
VLR_ULA_E_ID_IMEISV, /* IMEISV received from MS */
VLR_ULA_E_HLR_LU_RES, /* HLR UpdateLocation result */
VLR_ULA_E_UPD_HLR_COMPL,/* UpdatE_HLR_VLR result */
VLR_ULA_E_LU_COMPL_SUCCESS,/* Location_Update_Completion_VLR result */
VLR_ULA_E_LU_COMPL_FAILURE,/* Location_Update_Completion_VLR result */
VLR_ULA_E_NEW_TMSI_ACK, /* TMSI Reallocation Complete */
};
enum vlr_ciph_result_cause {
VLR_CIPH_REJECT, /* ? */
VLR_CIPH_COMPL,
};
struct vlr_ciph_result {
enum vlr_ciph_result_cause cause;
const char *imeisv;
};
enum vlr_subscr_security_context {
VLR_SEC_CTX_NONE,
VLR_SEC_CTX_GSM,
VLR_SEC_CTX_UMTS,
};
enum vlr_lu_type {
VLR_LU_TYPE_PERIODIC,
VLR_LU_TYPE_IMSI_ATTACH,
VLR_LU_TYPE_REGULAR,
};
#define OSMO_LBUF_DECL(name, xlen) \
struct { \
uint8_t buf[xlen]; \
size_t len; \
} name
struct sgsn_mm_ctx;
struct vlr_instance;
/* The VLR subscriber is the part of the GSM subscriber state in VLR (CS) or
* SGSN (PS), particularly while interacting with the HLR via GSUP */
struct vlr_subscr {
struct llist_head list;
struct vlr_instance *vlr;
/* TODO either populate from HLR or drop this completely? */
long long unsigned int id;
/* Data from HLR */ /* 3GPP TS 23.008 */
/* Always use vlr_subscr_set_imsi() to write to imsi[] */
char imsi[GSM23003_IMSI_MAX_DIGITS+1]; /* 2.1.1.1 */
char msisdn[GSM_EXTENSION_LENGTH+1]; /* 2.1.2 */
char name[GSM_NAME_LENGTH+1]; /* proprietary */
OSMO_LBUF_DECL(hlr, 16); /* 2.4.7 */
uint32_t periodic_lu_timer; /* 2.4.24 */
uint32_t age_indicator; /* 2.17.1 */
/* Authentication Data */
struct gsm_auth_tuple auth_tuples[5]; /* 2.3.1-2.3.4 */
struct gsm_auth_tuple *last_tuple;
enum vlr_subscr_security_context sec_ctx;
/* Data local to VLR is below */
uint32_t tmsi; /* 2.1.4 */
/* Newly allocated TMSI that was not yet acked by MS */
uint32_t tmsi_new;
/* some redundancy in information below? */
struct osmo_cell_global_id cgi; /* 2.4.16 */
uint16_t lac; /* 2.4.2 */
char imeisv[GSM23003_IMEISV_NUM_DIGITS+1]; /* 2.2.3 */
char imei[GSM23003_IMEISV_NUM_DIGITS+1]; /* 2.1.9 */
bool imsi_detached_flag; /* 2.7.1 */
bool conf_by_radio_contact_ind; /* 2.7.4.1 */
bool sub_dataconf_by_hlr_ind; /* 2.7.4.2 */
bool loc_conf_in_hlr_ind; /* 2.7.4.3 */
bool dormant_ind; /* 2.7.8 */
bool cancel_loc_rx; /* 2.7.8A */
bool ms_not_reachable_flag; /* 2.10.2 (MNRF) */
bool la_allowed;
int use_count;
time_t expire_lu; /* FIXME: overlap with periodic_lu_timer/age_indicator */
struct osmo_fsm_inst *lu_fsm;
struct osmo_fsm_inst *auth_fsm;
struct osmo_fsm_inst *proc_arq_fsm;
bool lu_complete;
void *msc_conn_ref;
/* PS (SGSN) specific parts */
struct {
struct llist_head pdp_list;
uint8_t rac;
uint8_t sac;
struct gprs_mm_ctx *mmctx;
} ps;
/* CS (NITB/CSCN) specific parts */
struct {
/* pending requests */
bool is_paging;
struct llist_head requests;
} cs;
};
enum vlr_proc_arq_result;
enum vlr_ciph {
VLR_CIPH_NONE, /*< A5/0, no encryption */
VLR_CIPH_A5_1, /*< A5/1, encryption */
VLR_CIPH_A5_2, /*< A5/2, deprecated export-grade encryption */
VLR_CIPH_A5_3, /*< A5/3, 'new secure' encryption */
};
struct vlr_ops {
/* encode + transmit an AUTH REQ towards the MS.
* \param[in] at auth tuple providing rand, key_seq and autn.
* \param[in] send_autn True to send AUTN, for r99 UMTS auth.
*/
int (*tx_auth_req)(void *msc_conn_ref, struct gsm_auth_tuple *at,
bool send_autn);
/* encode + transmit an AUTH REJECT towards the MS */
int (*tx_auth_rej)(void *msc_conn_ref);
/* encode + transmit an IDENTITY REQUEST towards the MS */
int (*tx_id_req)(void *msc_conn_ref, uint8_t mi_type);
int (*tx_lu_acc)(void *msc_conn_ref, uint32_t send_tmsi);
int (*tx_lu_rej)(void *msc_conn_ref, uint8_t cause);
int (*tx_cm_serv_acc)(void *msc_conn_ref);
int (*tx_cm_serv_rej)(void *msc_conn_ref, enum vlr_proc_arq_result result);
int (*set_ciph_mode)(void *msc_conn_ref, enum vlr_ciph ciph_mode,
bool retrieve_imeisv);
/* notify MSC/SGSN that the subscriber data in VLR has been updated */
void (*subscr_update)(struct vlr_subscr *vsub);
/* notify MSC/SGSN that the given subscriber has been associated
* with this msc_conn_ref */
void (*subscr_assoc)(void *msc_conn_ref, struct vlr_subscr *vsub);
};
enum vlr_timer {
VLR_T_3250,
VLR_T_3260,
VLR_T_3270,
_NUM_VLR_TIMERS
};
/* An instance of the VLR codebase */
struct vlr_instance {
struct llist_head subscribers;
struct llist_head operations;
struct gsup_client *gsup_client;
struct vlr_ops ops;
struct {
bool retrieve_imeisv;
bool assign_tmsi;
bool check_imei_rqd;
int auth_tuple_max_use_count;
bool auth_reuse_old_sets_on_error;
bool parq_retrieve_imsi;
bool is_ps;
uint32_t timer[_NUM_VLR_TIMERS];
} cfg;
/* A free-form pointer for use by the caller */
void *user_ctx;
};
extern const struct value_string vlr_ciph_names[];
static inline const char *vlr_ciph_name(enum vlr_ciph val)
{
return get_value_string(vlr_ciph_names, val);
}
/* Location Updating request */
struct osmo_fsm_inst *
vlr_loc_update(struct osmo_fsm_inst *parent,
uint32_t parent_event_success,
uint32_t parent_event_failure,
void *parent_event_data,
struct vlr_instance *vlr, void *msc_conn_ref,
enum vlr_lu_type type, uint32_t tmsi, const char *imsi,
const struct osmo_location_area_id *old_lai,
const struct osmo_location_area_id *new_lai,
bool authentication_required,
enum vlr_ciph ciphering_required,
bool is_r99, bool is_utran,
bool assign_tmsi);
void vlr_loc_update_conn_timeout(struct osmo_fsm_inst *fi);
/* tell the VLR that the subscriber connection is gone */
int vlr_subscr_disconnected(struct vlr_subscr *vsub);
int vlr_subscr_rx_id_resp(struct vlr_subscr *vsub, const uint8_t *mi, size_t mi_len);
int vlr_subscr_rx_auth_resp(struct vlr_subscr *vsub, bool is_r99, bool is_utran,
const uint8_t *res, uint8_t res_len);
int vlr_subscr_rx_auth_fail(struct vlr_subscr *vsub, const uint8_t *auts);
int vlr_subscr_tx_auth_fail_rep(struct vlr_subscr *vsub);
void vlr_subscr_rx_ciph_res(struct vlr_subscr *vsub, struct vlr_ciph_result *res);
int vlr_subscr_rx_tmsi_reall_compl(struct vlr_subscr *vsub);
int vlr_subscr_rx_imsi_detach(struct vlr_subscr *vsub);
void vlr_subscr_conn_timeout(struct vlr_subscr *vsub);
struct vlr_instance *vlr_alloc(void *ctx, const struct vlr_ops *ops);
int vlr_start(const char *gsup_unit_name, struct vlr_instance *vlr,
const char *gsup_server_addr_str, uint16_t gsup_server_port);
/* internal use only */
struct osmo_fsm_inst *sub_pres_vlr_fsm_start(struct osmo_fsm_inst *parent,
struct vlr_subscr *vsub,
uint32_t term_event);
struct osmo_fsm_inst *
upd_hlr_vlr_proc_start(struct osmo_fsm_inst *parent,
struct vlr_subscr *vsub,
uint32_t parent_event);
struct osmo_fsm_inst *
lu_compl_vlr_proc_start(struct osmo_fsm_inst *parent,
struct vlr_subscr *vsub,
void *msc_conn_ref,
uint32_t parent_event_success,
uint32_t parent_event_failure);
const char *vlr_subscr_name(struct vlr_subscr *vsub);
const char *vlr_subscr_msisdn_or_name(struct vlr_subscr *vsub);
#define vlr_subscr_find_by_imsi(vlr, imsi) \
_vlr_subscr_find_by_imsi(vlr, imsi, __BASE_FILE__, __LINE__)
#define vlr_subscr_find_or_create_by_imsi(vlr, imsi, created) \
_vlr_subscr_find_or_create_by_imsi(vlr, imsi, created, \
__BASE_FILE__, __LINE__)
#define vlr_subscr_find_by_tmsi(vlr, tmsi) \
_vlr_subscr_find_by_tmsi(vlr, tmsi, __BASE_FILE__, __LINE__)
#define vlr_subscr_find_or_create_by_tmsi(vlr, tmsi, created) \
_vlr_subscr_find_or_create_by_tmsi(vlr, tmsi, created, \
__BASE_FILE__, __LINE__)
#define vlr_subscr_find_by_msisdn(vlr, msisdn) \
_vlr_subscr_find_by_msisdn(vlr, msisdn, __BASE_FILE__, __LINE__)
struct vlr_subscr *_vlr_subscr_find_by_imsi(struct vlr_instance *vlr,
const char *imsi,
const char *file, int line);
struct vlr_subscr *_vlr_subscr_find_or_create_by_imsi(struct vlr_instance *vlr,
const char *imsi,
bool *created,
const char *file,
int line);
struct vlr_subscr *_vlr_subscr_find_by_tmsi(struct vlr_instance *vlr,
uint32_t tmsi,
const char *file, int line);
struct vlr_subscr *_vlr_subscr_find_or_create_by_tmsi(struct vlr_instance *vlr,
uint32_t tmsi,
bool *created,
const char *file,
int line);
struct vlr_subscr *_vlr_subscr_find_by_msisdn(struct vlr_instance *vlr,
const char *msisdn,
const char *file, int line);
#define vlr_subscr_get(sub) _vlr_subscr_get(sub, __BASE_FILE__, __LINE__)
#define vlr_subscr_put(sub) _vlr_subscr_put(sub, __BASE_FILE__, __LINE__)
struct vlr_subscr *_vlr_subscr_get(struct vlr_subscr *sub, const char *file, int line);
struct vlr_subscr *_vlr_subscr_put(struct vlr_subscr *sub, const char *file, int line);
struct vlr_subscr *vlr_subscr_alloc(struct vlr_instance *vlr);
void vlr_subscr_free(struct vlr_subscr *vsub);
int vlr_subscr_alloc_tmsi(struct vlr_subscr *vsub);
void vlr_subscr_set_imsi(struct vlr_subscr *vsub, const char *imsi);
void vlr_subscr_set_imei(struct vlr_subscr *vsub, const char *imei);
void vlr_subscr_set_imeisv(struct vlr_subscr *vsub, const char *imeisv);
void vlr_subscr_set_msisdn(struct vlr_subscr *vsub, const char *msisdn);
bool vlr_subscr_matches_imsi(struct vlr_subscr *vsub, const char *imsi);
bool vlr_subscr_matches_tmsi(struct vlr_subscr *vsub, uint32_t tmsi);
bool vlr_subscr_matches_msisdn(struct vlr_subscr *vsub, const char *msisdn);
bool vlr_subscr_matches_imei(struct vlr_subscr *vsub, const char *imei);
uint32_t vlr_timer(struct vlr_instance *vlr, uint32_t timer);
int vlr_subscr_changed(struct vlr_subscr *vsub);
int vlr_subscr_purge(struct vlr_subscr *vsub);
void vlr_subscr_cancel(struct vlr_subscr *vsub, enum gsm48_gmm_cause cause);
/* Process Acccess Request FSM */
enum vlr_proc_arq_result {
VLR_PR_ARQ_RES_NONE,
VLR_PR_ARQ_RES_SYSTEM_FAILURE,
VLR_PR_ARQ_RES_ILLEGAL_SUBSCR,
VLR_PR_ARQ_RES_UNIDENT_SUBSCR,
VLR_PR_ARQ_RES_ROAMING_NOTALLOWED,
VLR_PR_ARQ_RES_ILLEGAL_EQUIP,
VLR_PR_ARQ_RES_UNKNOWN_ERROR,
VLR_PR_ARQ_RES_TIMEOUT,
VLR_PR_ARQ_RES_PASSED,
};
extern const struct value_string vlr_proc_arq_result_names[];
static inline const char *vlr_proc_arq_result_name(enum vlr_proc_arq_result res)
{
return get_value_string(vlr_proc_arq_result_names, res);
}
enum proc_arq_vlr_event {
PR_ARQ_E_START,
PR_ARQ_E_ID_IMSI,
PR_ARQ_E_AUTH_RES,
PR_ARQ_E_CIPH_RES,
PR_ARQ_E_UPD_LOC_RES,
PR_ARQ_E_TRACE_RES,
PR_ARQ_E_IMEI_RES,
PR_ARQ_E_PRES_RES,
PR_ARQ_E_TMSI_ACK,
};
enum vlr_parq_type {
VLR_PR_ARQ_T_INVALID = 0, /* to guard against unset vars */
VLR_PR_ARQ_T_CM_SERV_REQ,
VLR_PR_ARQ_T_PAGING_RESP,
/* FIXME: differentiate between services of 24.008 10.5.3.3 */
};
/* Process Access Request (CM SERV REQ / PAGING RESP) */
void
vlr_proc_acc_req(struct osmo_fsm_inst *parent,
uint32_t parent_event_success,
uint32_t parent_event_failure,
void *parent_event_data,
struct vlr_instance *vlr, void *msc_conn_ref,
enum vlr_parq_type type, const uint8_t *mi_lv,
const struct osmo_location_area_id *lai,
bool authentication_required,
enum vlr_ciph ciphering_required,
bool is_r99, bool is_utran);
void vlr_parq_conn_timeout(struct osmo_fsm_inst *fi);
void vlr_parq_fsm_init(void);
int vlr_set_ciph_mode(struct vlr_instance *vlr,
struct osmo_fsm_inst *fi,
void *msc_conn_ref,
enum vlr_ciph ciph_mode,
bool retrieve_imeisv);
void log_set_filter_vlr_subscr(struct log_target *target,
struct vlr_subscr *vlr_subscr);

@ -22,6 +22,7 @@ AM_LDFLAGS = \
# Libraries
SUBDIRS = \
libcommon \
libvlr \
libmgcp \
libbsc \
libmsc \

@ -180,6 +180,11 @@ static const struct log_info_cat default_categories[] = {
.description = "PCU Interface",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DVLR] = {
.name = "DVLR",
.description = "Visitor Location Register",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
};
static int filter_fn(const struct log_context *ctx, struct log_target *tar)

@ -0,0 +1,269 @@
/* MSC subscriber connection implementation */
/*
* (C) 2016 by sysmocom s.m.f.c. <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/logging.h>
#include <osmocom/core/fsm.h>
#include <openbsc/osmo_msc.h>
#include <openbsc/vlr.h>
#include <openbsc/debug.h>
#include <openbsc/transaction.h>
static const struct value_string subscr_conn_fsm_event_names[] = {
OSMO_VALUE_STRING(SUBSCR_CONN_E_INVALID),
OSMO_VALUE_STRING(SUBSCR_CONN_E_ACCEPTED),
OSMO_VALUE_STRING(SUBSCR_CONN_E_BUMP),
OSMO_VALUE_STRING(SUBSCR_CONN_E_MO_CLOSE),
OSMO_VALUE_STRING(SUBSCR_CONN_E_CN_CLOSE),
OSMO_VALUE_STRING(SUBSCR_CONN_E_CLOSE_CONF),
{ 0, NULL }
};
const struct value_string subscr_conn_from_names[] = {
OSMO_VALUE_STRING(SUBSCR_CONN_FROM_INVALID),
OSMO_VALUE_STRING(SUBSCR_CONN_FROM_LU),
OSMO_VALUE_STRING(SUBSCR_CONN_FROM_CM_SERVICE_REQ),
OSMO_VALUE_STRING(SUBSCR_CONN_FROM_PAGING_RESP),
{ 0, NULL }
};
static void paging_resp(struct gsm_subscriber_connection *conn,
enum gsm_paging_event pe)
{
subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->subscr);
}
void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = fi->priv;
enum subscr_conn_from from = SUBSCR_CONN_FROM_INVALID;
enum gsm_paging_event pe;
if (data) {
from = *(enum subscr_conn_from*)data;
LOGPFSM(fi, "%s\n", subscr_conn_from_name(from));
}
/* If accepted, transition the state, all other cases mean failure. */
switch (event) {
case SUBSCR_CONN_E_ACCEPTED:
osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED, 0, 0);
break;
case SUBSCR_CONN_E_MO_CLOSE:
case SUBSCR_CONN_E_CN_CLOSE:
case SUBSCR_CONN_E_CLOSE_CONF:
break;
default:
LOGPFSM(fi, "Unexpected event: %d %s\n",
event, osmo_fsm_event_name(fi->fsm, event));
break;
}
/* if appropriate, signal paging success or failure */
if (from == SUBSCR_CONN_FROM_PAGING_RESP) {
pe = (fi->state == SUBSCR_CONN_S_ACCEPTED)?
GSM_PAGING_SUCCEEDED : GSM_PAGING_EXPIRED;
paging_resp(conn, pe);
}
/* On failure, discard the conn */
if (fi->state != SUBSCR_CONN_S_ACCEPTED) {
/* TODO: on MO_CLOSE or CN_CLOSE, first go to RELEASING and
* await BSC confirmation? */
osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
return;
}
/* On success, handle pending requests and/or close conn */
if (from == SUBSCR_CONN_FROM_CM_SERVICE_REQ) {
conn->received_cm_service_request = true;
LOGPFSM(fi, "received_cm_service_request = true\n");
}
osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_BUMP, data);
}
#if 0
case SUBSCR_CONN_E_PARQ_SUCCESS:
osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED, 0, 0);
accept_conn = true;
/* fall through */
case SUBSCR_CONN_E_PARQ_FAILURE:
parq_type = data ? *(enum vlr_parq_type*)data : VLR_PR_ARQ_T_INVALID;
switch (parq_type) {
case VLR_PR_ARQ_T_CM_SERV_REQ:
accept_conn = handle_cm_serv_result(fi, accept_conn);
break;
case VLR_PR_ARQ_T_PAGING_RESP:
accept_conn = handle_paging_result(fi, accept_conn);
break;
default:
LOGPFSML(fi, LOGL_ERROR,
"Invalid VLR Process Access Request type"
" %d\n", parq_type);
accept_conn = false;
break;
}
break;
#endif
static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = fi->priv;
if (conn->silent_call)
return;
if (conn->received_cm_service_request)
return;
/* is this needed? */
if (conn->subscr && !llist_empty(&conn->subscr->requests))
return;
if (trans_has_conn(conn))
return;
osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
}
static void subscr_conn_fsm_accepted(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case SUBSCR_CONN_E_BUMP:
subscr_conn_fsm_bump(fi, event, data);
return;
default:
break;
}
/* Whatever unexpected happens in the accepted state, it means release.
* Even if an unexpected event is passed, the safest thing to do is
* discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */
osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
}
static void subscr_conn_fsm_release(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_subscriber_connection *conn = fi->priv;
if (!conn)
return;
/* temporary hack, see owned_by_msc */
if (!conn->owned_by_msc) {
DEBUGP(DMM, "%s leaving bsc_subscr_con_free() to bsc_api.c, owned_by_msc = false\n",
subscr_name(conn->subscr));
return;
}
DEBUGP(DMM, "%s calling bsc_subscr_con_free(), owned_by_msc = true\n",
subscr_name(conn->subscr));
gsm0808_clear(conn);
bsc_subscr_con_free(conn);
}
#define S(x) (1 << (x))
static const struct osmo_fsm_state subscr_conn_fsm_states[] = {
[SUBSCR_CONN_S_NEW] = {
.name = OSMO_STRINGIFY(SUBSCR_CONN_S_NEW),
.in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) |
S(SUBSCR_CONN_E_MO_CLOSE) |
S(SUBSCR_CONN_E_CN_CLOSE) |
S(SUBSCR_CONN_E_CLOSE_CONF),
.out_state_mask = S(SUBSCR_CONN_S_ACCEPTED) |
S(SUBSCR_CONN_S_RELEASED),
.action = subscr_conn_fsm_new,
},
[SUBSCR_CONN_S_ACCEPTED] = {
.name = OSMO_STRINGIFY(SUBSCR_CONN_S_ACCEPTED),
/* allow everything to release for any odd behavior */
.in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) |
S(SUBSCR_CONN_E_BUMP) |
S(SUBSCR_CONN_E_MO_CLOSE) |
S(SUBSCR_CONN_E_CN_CLOSE) |
S(SUBSCR_CONN_E_CLOSE_CONF),
.out_state_mask = S(SUBSCR_CONN_S_RELEASED),
.action = subscr_conn_fsm_accepted,
},
[SUBSCR_CONN_S_RELEASED] = {
.name = OSMO_STRINGIFY(SUBSCR_CONN_S_RELEASED),
.onenter = subscr_conn_fsm_release,
},
};
static struct osmo_fsm subscr_conn_fsm = {
.name = "Subscr_Conn",
.states = subscr_conn_fsm_states,
.num_states = ARRAY_SIZE(subscr_conn_fsm_states),
.allstate_event_mask = 0,
.allstate_action = NULL,
.log_subsys = DVLR,
.event_names = subscr_conn_fsm_event_names,
};
int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id)
{
struct osmo_fsm_inst *fi;
OSMO_ASSERT(conn);
if (conn->conn_fsm) {
LOGP(DMM, LOGL_ERROR,
"%s: Error: connection already in use\n", id);
return -EINVAL;
}
fi = osmo_fsm_inst_alloc(&subscr_conn_fsm, conn, conn, LOGL_DEBUG, id);
if (!fi) {
LOGP(DMM, LOGL_ERROR,
"%s: Failed to allocate subscr conn master FSM\n", id);
return -ENOMEM;
}
conn->conn_fsm = fi;
return 0;
}
bool msc_subscr_conn_is_accepted(struct gsm_subscriber_connection *conn)
{
if (!conn)
return false;
if (!conn->subscr)
return false;
if (!conn->conn_fsm)
return false;
if (conn->conn_fsm->state != SUBSCR_CONN_S_ACCEPTED)
return false;
return true;
}
void msc_subscr_conn_init(void)
{
osmo_fsm_register(&subscr_conn_fsm);
}

@ -1053,7 +1053,6 @@ DEFUN(logging_fltr_imsi,
return CMD_WARNING;
}
log_set_filter_vlr_subscr(tgt, vlr_subscr);
log_set_filter_bsc_subscr(tgt, bsc_subscr);
return CMD_SUCCESS;
}

@ -0,0 +1,19 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) \
$(COVERAGE_CFLAGS) $(LIBCRYPTO_CFLAGS)
noinst_HEADERS = \
vlr_access_req_fsm.h \
vlr_auth_fsm.h \
vlr_core.h \
vlr_lu_fsm.h \
$(NULL)
noinst_LIBRARIES = libvlr.a
libvlr_a_SOURCES = \
vlr.c \
vlr_access_req_fsm.c \
vlr_auth_fsm.c \
vlr_lu_fsm.c \
$(NULL)

File diff suppressed because it is too large Load Diff

@ -0,0 +1,778 @@
/* Osmocom Visitor Location Register (VLR): Access Request FSMs */
/* (C) 2016 by Harald Welte <laforge@gnumonks.org>
*
* 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/fsm.h>
#include <osmocom/gsm/gsup.h>
#include <openbsc/vlr.h>
#include <openbsc/debug.h>
#include "vlr_core.h"
#include "vlr_auth_fsm.h"
#include "vlr_lu_fsm.h"
#include "vlr_access_req_fsm.h"
#define S(x) (1 << (x))
/***********************************************************************
* Process_Access_Request_VLR, TS 29.002 Chapter 25.4.2
***********************************************************************/
const struct value_string vlr_proc_arq_result_names[] = {
OSMO_VALUE_STRING(VLR_PR_ARQ_RES_NONE),
OSMO_VALUE_STRING(VLR_PR_ARQ_RES_SYSTEM_FAILURE),
OSMO_VALUE_STRING(VLR_PR_ARQ_RES_ILLEGAL_SUBSCR),
OSMO_VALUE_STRING(VLR_PR_ARQ_RES_UNIDENT_SUBSCR),
OSMO_VALUE_STRING(VLR_PR_ARQ_RES_ROAMING_NOTALLOWED),
OSMO_VALUE_STRING(VLR_PR_ARQ_RES_ILLEGAL_EQUIP),
OSMO_VALUE_STRING(VLR_PR_ARQ_RES_UNKNOWN_ERROR),
OSMO_VALUE_STRING(VLR_PR_ARQ_RES_TIMEOUT),
OSMO_VALUE_STRING(VLR_PR_ARQ_RES_PASSED),
{ 0, NULL }
};
static const struct value_string proc_arq_vlr_event_names[] = {
OSMO_VALUE_STRING(PR_ARQ_E_START),
OSMO_VALUE_STRING(PR_ARQ_E_ID_IMSI),
OSMO_VALUE_STRING(PR_ARQ_E_AUTH_RES),
OSMO_VALUE_STRING(PR_ARQ_E_CIPH_RES),
OSMO_VALUE_STRING(PR_ARQ_E_UPD_LOC_RES),
OSMO_VALUE_STRING(PR_ARQ_E_TRACE_RES),
OSMO_VALUE_STRING(PR_ARQ_E_IMEI_RES),
OSMO_VALUE_STRING(PR_ARQ_E_PRES_RES),
OSMO_VALUE_STRING(PR_ARQ_E_TMSI_ACK),
{ 0, NULL }
};
struct proc_arq_priv {
struct vlr_instance *vlr;
struct vlr_subscr *vsub;
void *msc_conn_ref;
struct osmo_fsm_inst *ul_child_fsm;
struct osmo_fsm_inst *sub_pres_vlr_fsm;
uint32_t parent_event_success;
uint32_t parent_event_failure;
void *parent_event_data;
enum vlr_parq_type type;
enum vlr_proc_arq_result result;
bool by_tmsi;
char imsi[16];
uint32_t tmsi;
struct osmo_location_area_id lai;
bool authentication_required;
enum vlr_ciph ciphering_required;
bool is_r99;
bool is_utran;
bool implicitly_accepted_parq_by_ciphering_cmd;
};
static void assoc_par_with_subscr(struct osmo_fsm_inst *fi, struct vlr_subscr *vsub)
{
struct proc_arq_priv *par = fi->priv;
struct vlr_instance *vlr = par->vlr;
vsub->msc_conn_ref = par->msc_conn_ref;
par->vsub = vsub;
/* Tell MSC to associate this subscriber with the given
* connection */
vlr->ops.subscr_assoc(par->msc_conn_ref, par->vsub);
}
#define proc_arq_fsm_done(fi, res) _proc_arq_fsm_done(fi, res, __FILE__, __LINE__)
static void _proc_arq_fsm_done(struct osmo_fsm_inst *fi,
enum vlr_proc_arq_result res,
const char *file, int line)
{
struct proc_arq_priv *par = fi->priv;
LOGPFSMSRC(fi, file, line, "proc_arq_fsm_done(%s)\n",
vlr_proc_arq_result_name(res));
par->result = res;
osmo_fsm_inst_state_chg(fi, PR_ARQ_S_DONE, 0, 0);
}
static void proc_arq_vlr_dispatch_result(struct osmo_fsm_inst *fi,
uint32_t prev_state)
{
struct proc_arq_priv *par = fi->priv;
bool success;
int rc;
LOGPFSM(fi, "Process Access Request result: %s\n",
vlr_proc_arq_result_name(par->result));
success = (par->result == VLR_PR_ARQ_RES_PASSED);
/* It would be logical to first dispatch the success event to the
* parent FSM, but that could start actions that send messages to the
* MS. Rather send the CM Service Accept message first and then signal
* success. Since messages are handled synchronously, the success event
* will be processed before we handle new incoming data from the MS. */
if (par->type == VLR_PR_ARQ_T_CM_SERV_REQ) {
if (success
&& !par->implicitly_accepted_parq_by_ciphering_cmd) {
rc = par->vlr->ops.tx_cm_serv_acc(par->msc_conn_ref);
if (rc) {
LOGPFSML(fi, LOGL_ERROR,
"Failed to send CM Service Accept\n");
success = false;
}
}
if (!success) {
rc = par->vlr->ops.tx_cm_serv_rej(par->msc_conn_ref,
par->result);
if (rc)
LOGPFSML(fi, LOGL_ERROR,
"Failed to send CM Service Reject\n");
}
}
/* For VLR_PR_ARQ_T_PAGING_RESP, there is nothing to send. The conn_fsm
* will start handling pending paging transactions. */
if (!fi->proc.parent) {
LOGPFSML(fi, LOGL_ERROR, "No parent FSM");
return;
}
osmo_fsm_inst_dispatch(fi->proc.parent,
success ? par->parent_event_success
: par->parent_event_failure,
par->parent_event_data);
}
void proc_arq_vlr_cleanup(struct osmo_fsm_inst *fi,
enum osmo_fsm_term_cause cause)
{
struct proc_arq_priv *par = fi->priv;
if (par->vsub && par->vsub->proc_arq_fsm == fi)
par->vsub->proc_arq_fsm = NULL;
}
static void _proc_arq_vlr_post_imei(struct osmo_fsm_inst *fi)
{
struct proc_arq_priv *par = fi->priv;
struct vlr_subscr *vsub = par->vsub;
LOGPFSM(fi, "%s()\n", __func__);
/* TODO: Identity := IMSI */
if (0 /* TODO: TMSI reallocation at access: vlr->cfg.alloc_tmsi_arq */) {
vlr_subscr_alloc_tmsi(vsub);
/* TODO: forward TMSI to MS, wait for TMSI
* REALLOC COMPLETE */
/* TODO: Freeze old TMSI */
osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_TMSI_ACK, 0, 0);
return;
}
proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_PASSED);
}
static void _proc_arq_vlr_post_trace(struct osmo_fsm_inst *fi)
{
struct proc_arq_priv *par = fi->priv;
struct vlr_subscr *vsub = par->vsub;
struct vlr_instance *vlr = vsub->vlr;
LOGPFSM(fi, "%s()\n", __func__);
/* Node 3 */
if (0 /* IMEI check required */) {
/* Chck_IMEI_VLR */
vlr->ops.tx_id_req(par->msc_conn_ref, GSM_MI_TYPE_IMEI);
osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_CHECK_IMEI,
vlr_timer(vlr, 3270), 3270);
} else
_proc_arq_vlr_post_imei(fi);
}
/* After Subscriber_Present_VLR */
static void _proc_arq_vlr_post_pres(struct osmo_fsm_inst *fi)
{
LOGPFSM(fi, "%s()\n", __func__);
if (0 /* TODO: tracing required */) {
/* TODO: Trace_Subscriber_Activity_VLR */
osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_TRACE_SUB, 0, 0);
}
_proc_arq_vlr_post_trace(fi);
}
/* After Update_Location_Child_VLR */
static void _proc_arq_vlr_node2_post_vlr(struct osmo_fsm_inst *fi)
{
struct proc_arq_priv *par = fi->priv;
struct vlr_subscr *vsub = par->vsub;
LOGPFSM(fi, "%s()\n", __func__);
if (!vsub->sub_dataconf_by_hlr_ind) {
/* Set User Error: Unidentified Subscriber */
proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_UNIDENT_SUBSCR);
return;
}
if (0 /* roaming not allowed in LA */) {
/* Set User Error: Roaming not allowed in this LA */
proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_ROAMING_NOTALLOWED);
return;
}
vsub->imsi_detached_flag = false;
if (vsub->ms_not_reachable_flag) {
/* Start Subscriber_Present_VLR */
osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_SUB_PRES, 0, 0);
par->sub_pres_vlr_fsm = sub_pres_vlr_fsm_start(fi, vsub,
PR_ARQ_E_PRES_RES);
return;
}
_proc_arq_vlr_post_pres(fi);
}
static void _proc_arq_vlr_node2_post_ciph(struct osmo_fsm_inst *fi)
{
struct proc_arq_priv *par = fi->priv;
struct vlr_subscr *vsub = par->vsub;
LOGPFSM(fi, "%s()\n", __func__);
vsub->conf_by_radio_contact_ind = true;
if (vsub->loc_conf_in_hlr_ind == false) {
/* start Update_Location_Child_VLR. WE use
* Update_HLR_VLR instead, the differences appear
* insignificant for now. */
par->ul_child_fsm = upd_hlr_vlr_proc_start(fi, vsub,
PR_ARQ_E_UPD_LOC_RES);
osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_UPD_LOC_CHILD, 0, 0);
return;
}
_proc_arq_vlr_node2_post_vlr(fi);
}
static bool is_ciph_required(struct proc_arq_priv *par)
{
return par->ciphering_required != VLR_CIPH_NONE;
}
static void _proc_arq_vlr_node2(struct osmo_fsm_inst *fi)
{
struct proc_arq_priv *par = fi->priv;
struct vlr_subscr *vsub = par->vsub;
LOGPFSM(fi, "%s()\n", __func__);
if (!is_ciph_required(par)) {
_proc_arq_vlr_node2_post_ciph(fi);
return;
}
if (vlr_set_ciph_mode(vsub->vlr, fi, par->msc_conn_ref,
par->ciphering_required,
vsub->vlr->cfg.retrieve_imeisv)) {
LOGPFSML(fi, LOGL_ERROR,
"Failed to send Ciphering Mode Command\n");
proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_SYSTEM_FAILURE);
return;
}
par->implicitly_accepted_parq_by_ciphering_cmd = true;
osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_CIPH, 0, 0);
}
static bool is_auth_required(struct proc_arq_priv *par)
{
/* The cases where the authentication procedure should be used
* are defined in 3GPP TS 33.102 */
/* For now we use a default value passed in to vlr_lu_fsm(). */
return par->authentication_required
|| (par->ciphering_required != VLR_CIPH_NONE);
}
/* after the IMSI is known */
static void proc_arq_vlr_fn_post_imsi(struct osmo_fsm_inst *fi)
{
struct proc_arq_priv *par = fi->priv;
struct vlr_subscr *vsub = par->vsub;
LOGPFSM(fi, "%s()\n", __func__);
OSMO_ASSERT(vsub);
/* TODO: Identity IMEI -> System Failure */
if (is_auth_required(par)) {
osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_AUTH,
0, 0);
vsub->auth_fsm = auth_fsm_start(vsub, fi->log_level, fi,
PR_ARQ_E_AUTH_RES,
par->is_r99,
par->is_utran);
} else {
_proc_arq_vlr_node2(fi);
}
}
static void proc_arq_vlr_fn_init(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
struct proc_arq_priv *par = fi->priv;
struct vlr_instance *vlr = par->vlr;
struct vlr_subscr *vsub = NULL;
OSMO_ASSERT(event == PR_ARQ_E_START);
/* Obtain_Identity_VLR */
if (!par->by_tmsi) {
/* IMSI was included */
vsub = vlr_subscr_find_by_imsi(par->vlr, par->imsi);
} else {
/* TMSI was included */
vsub = vlr_subscr_find_by_tmsi(par->vlr, par->tmsi);
}
if (vsub) {
if (vsub->proc_arq_fsm && fi != vsub->proc_arq_fsm) {
LOGPFSML(fi, LOGL_ERROR,
"Another proc_arq_fsm is already"
" associated with subscr %s,"
" terminating the other FSM.\n",
vlr_subscr_name(vsub));
proc_arq_fsm_done(vsub->proc_arq_fsm,
VLR_PR_ARQ_RES_SYSTEM_FAILURE);
}
vsub->proc_arq_fsm = fi;
assoc_par_with_subscr(fi, vsub);
proc_arq_vlr_fn_post_imsi(fi);
vlr_subscr_put(vsub);
return;
}
/* No VSUB could be resolved. What now? */
if (!par->by_tmsi) {
/* We couldn't find a subscriber even by IMSI,
* Set User Error: Unidentified Subscriber */
proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_UNIDENT_SUBSCR);
return;
} else {
/* TMSI was included, are we permitted to use it? */
if (vlr->cfg.parq_retrieve_imsi) {
/* Obtain_IMSI_VLR */
osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_OBTAIN_IMSI,
vlr_timer(vlr, 3270), 3270);
return;
} else {
/* Set User Error: Unidentified Subscriber */
proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_UNIDENT_SUBSCR);
return;
}
}
}
/* ID REQ(IMSI) has returned */
static void proc_arq_vlr_fn_w_obt_imsi(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
struct proc_arq_priv *par = fi->priv;
struct vlr_instance *vlr = par->vlr;
struct vlr_subscr *vsub;
OSMO_ASSERT(event == PR_ARQ_E_ID_IMSI);
vsub = vlr_subscr_find_by_imsi(vlr, par->imsi);
if (!vsub) {
/* Set User Error: Unidentified Subscriber */
proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_UNIDENT_SUBSCR);
return;
}
assoc_par_with_subscr(fi, vsub);
proc_arq_vlr_fn_post_imsi(fi);
vlr_subscr_put(vsub);
}
/* Authenticate_VLR has completed */
static void proc_arq_vlr_fn_w_auth(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
enum vlr_auth_fsm_result res;
enum vlr_proc_arq_result ret;
OSMO_ASSERT(event == PR_ARQ_E_AUTH_RES);
res = data ? *(enum vlr_auth_fsm_result*)data : -1;
LOGPFSM(fi, "got %s\n", vlr_auth_fsm_result_name(res));
switch (res) {
case VLR_AUTH_RES_PASSED:
/* Node 2 */
_proc_arq_vlr_node2(fi);
return;
case VLR_AUTH_RES_ABORTED:
/* Error */
ret = VLR_PR_ARQ_RES_UNKNOWN_ERROR;
break;
case VLR_AUTH_RES_UNKNOWN_SUBSCR:
/* Set User Error: Unidentified Subscriber */
ret = VLR_PR_ARQ_RES_UNIDENT_SUBSCR;
break;
case VLR_AUTH_RES_AUTH_FAILED:
/* Set User Error: Illegal Subscriber */
ret = VLR_PR_ARQ_RES_ILLEGAL_SUBSCR;
break;
case VLR_AUTH_RES_PROC_ERR:
/* Set User Error: System failure */
ret = VLR_PR_ARQ_RES_SYSTEM_FAILURE;
break;
default:
LOGPFSML(fi, LOGL_ERROR, "Unexpected vlr_auth_fsm_result value: %d (data=%p)\n", res, data);
ret = VLR_PR_ARQ_RES_UNKNOWN_ERROR;
break;
}
/* send process_access_req response to caller, enter error state */
proc_arq_fsm_done(fi, ret);
}
static void proc_arq_vlr_fn_w_ciph(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
struct proc_arq_priv *par = fi->priv;
struct vlr_subscr *vsub = par->vsub;
struct vlr_ciph_result res = { .cause = VLR_CIPH_REJECT };
OSMO_ASSERT(event == PR_ARQ_E_CIPH_RES);
if (!data)
LOGPFSML(fi, LOGL_ERROR, "invalid ciphering result: NULL\n");
else
res = *(struct vlr_ciph_result*)data;
switch (res.cause) {
case VLR_CIPH_COMPL:
break;
case VLR_CIPH_REJECT:
LOGPFSM(fi, "ciphering rejected\n");
proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_ILLEGAL_SUBSCR);
return;
default:
LOGPFSML(fi, LOGL_ERROR, "invalid ciphering result: %d\n",
res.cause);
proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_ILLEGAL_SUBSCR);
return;
}
if (res.imeisv) {
LOGPFSM(fi, "got IMEISV: %s\n", res.imeisv);
vlr_subscr_set_imeisv(vsub, res.imeisv);
}
_proc_arq_vlr_node2_post_ciph(fi);
}
/* Update_Location_Child_VLR has completed */
static void proc_arq_vlr_fn_w_upd_loc(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
OSMO_ASSERT(event == PR_ARQ_E_UPD_LOC_RES);
_proc_arq_vlr_node2_post_vlr(fi);
}
/* Subscriber_Present_VLR has completed */
static void proc_arq_vlr_fn_w_pres(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
OSMO_ASSERT(event == PR_ARQ_E_PRES_RES);
_proc_arq_vlr_post_pres(fi);
}
static void proc_arq_vlr_fn_w_trace(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
OSMO_ASSERT(event == PR_ARQ_E_TRACE_RES);
_proc_arq_vlr_post_trace(fi);
}
/* we have received the ID RESPONSE (IMEI) */
static void proc_arq_vlr_fn_w_imei(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
OSMO_ASSERT(event == PR_ARQ_E_IMEI_RES);
_proc_arq_vlr_post_imei(fi);
}
/* MSC tells us that MS has acknowleded TMSI re-allocation */
static void proc_arq_vlr_fn_w_tmsi(struct osmo_fsm_inst *fi,
uint32_t event, void *data)
{
OSMO_ASSERT(event == PR_ARQ_E_TMSI_ACK);
/* FIXME: check confirmation? unfreeze? */
proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_PASSED);
}
static const struct osmo_fsm_state proc_arq_vlr_states[] = {
[PR_ARQ_S_INIT] = {
.name = OSMO_STRINGIFY(PR_ARQ_S_INIT),
.in_event_mask = S(PR_ARQ_E_START),
.out_state_mask = S(PR_ARQ_S_DONE) |
S(PR_ARQ_S_WAIT_OBTAIN_IMSI) |
S(PR_ARQ_S_WAIT_AUTH) |
S(PR_ARQ_S_WAIT_UPD_LOC_CHILD) |
S(PR_ARQ_S_WAIT_SUB_PRES) |
S(PR_ARQ_S_WAIT_TRACE_SUB) |
S(PR_ARQ_S_WAIT_CHECK_IMEI) |
S(PR_ARQ_S_WAIT_TMSI_ACK),
.action = proc_arq_vlr_fn_init,
},
[PR_ARQ_S_WAIT_OBTAIN_IMSI] = {
.name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_OBTAIN_IMSI),
.in_event_mask = S(PR_ARQ_E_ID_IMSI),
.out_state_mask = S(PR_ARQ_S_DONE) |
S(PR_ARQ_S_WAIT_AUTH) |
S(PR_ARQ_S_WAIT_UPD_LOC_CHILD) |
S(PR_ARQ_S_WAIT_SUB_PRES) |
S(PR_ARQ_S_WAIT_TRACE_SUB) |
S(PR_ARQ_S_WAIT_CHECK_IMEI) |
S(PR_ARQ_S_WAIT_TMSI_ACK),
.action = proc_arq_vlr_fn_w_obt_imsi,
},
[PR_ARQ_S_WAIT_AUTH] = {
.name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_AUTH),
.in_event_mask = S(PR_ARQ_E_AUTH_RES),
.out_state_mask = S(PR_ARQ_S_DONE) |
S(PR_ARQ_S_WAIT_CIPH) |
S(PR_ARQ_S_WAIT_UPD_LOC_CHILD) |
S(PR_ARQ_S_WAIT_SUB_PRES) |
S(PR_ARQ_S_WAIT_TRACE_SUB) |
S(PR_ARQ_S_WAIT_CHECK_IMEI) |
S(PR_ARQ_S_WAIT_TMSI_ACK),
.action = proc_arq_vlr_fn_w_auth,
},
[PR_ARQ_S_WAIT_CIPH] = {
.name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_CIPH),
.in_event_mask = S(PR_ARQ_E_CIPH_RES),
.out_state_mask = S(PR_ARQ_S_DONE) |
S(PR_ARQ_S_WAIT_UPD_LOC_CHILD) |
S(PR_ARQ_S_WAIT_SUB_PRES) |
S(PR_ARQ_S_WAIT_TRACE_SUB) |
S(PR_ARQ_S_WAIT_CHECK_IMEI) |
S(PR_ARQ_S_WAIT_TMSI_ACK),
.action = proc_arq_vlr_fn_w_ciph,
},
[PR_ARQ_S_WAIT_UPD_LOC_CHILD] = {
.name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_UPD_LOC_CHILD),
.in_event_mask = S(PR_ARQ_E_UPD_LOC_RES),
.out_state_mask = S(PR_ARQ_S_DONE) |
S(PR_ARQ_S_WAIT_SUB_PRES) |
S(PR_ARQ_S_WAIT_TRACE_SUB) |
S(PR_ARQ_S_WAIT_CHECK_IMEI) |
S(PR_ARQ_S_WAIT_TMSI_ACK),
.action = proc_arq_vlr_fn_w_upd_loc,
},
[PR_ARQ_S_WAIT_SUB_PRES] = {
.name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_SUB_PRES),
.in_event_mask = S(PR_ARQ_E_PRES_RES),
.out_state_mask = S(PR_ARQ_S_DONE) |
S(PR_ARQ_S_WAIT_TRACE_SUB) |
S(PR_ARQ_S_WAIT_CHECK_IMEI) |
S(PR_ARQ_S_WAIT_TMSI_ACK),
.action = proc_arq_vlr_fn_w_pres,
},
[PR_ARQ_S_WAIT_TRACE_SUB] = {
.name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_TRACE_SUB),
.in_event_mask = S(PR_ARQ_E_TRACE_RES),
.out_state_mask = S(PR_ARQ_S_DONE) |
S(PR_ARQ_S_WAIT_CHECK_IMEI) |
S(PR_ARQ_S_WAIT_TMSI_ACK),
.action = proc_arq_vlr_fn_w_trace,
},
[PR_ARQ_S_WAIT_CHECK_IMEI] = {
.name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_CHECK_IMEI),
.in_event_mask = S(PR_ARQ_E_IMEI_RES),
.out_state_mask = S(PR_ARQ_S_DONE) |
S(PR_ARQ_S_WAIT_TMSI_ACK),
.action = proc_arq_vlr_fn_w_imei,
},
[PR_ARQ_S_WAIT_TMSI_ACK] = {
.name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_TMSI_ACK),
.in_event_mask = S(PR_ARQ_E_TMSI_ACK),
.out_state_mask = S(PR_ARQ_S_DONE),
.action = proc_arq_vlr_fn_w_tmsi,
},
[PR_ARQ_S_DONE] = {
.name = OSMO_STRINGIFY(PR_ARQ_S_DONE),
.onenter = proc_arq_vlr_dispatch_result,
},
};
static struct osmo_fsm proc_arq_vlr_fsm = {