diff options
Diffstat (limited to 'openbsc/tests/vlr/vlr_test.c')
-rw-r--r-- | openbsc/tests/vlr/vlr_test.c | 693 |
1 files changed, 693 insertions, 0 deletions
diff --git a/openbsc/tests/vlr/vlr_test.c b/openbsc/tests/vlr/vlr_test.c new file mode 100644 index 000000000..5433e3e41 --- /dev/null +++ b/openbsc/tests/vlr/vlr_test.c @@ -0,0 +1,693 @@ +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <signal.h> + +#include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> +#include <osmocom/core/application.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/fsm.h> + +#include <openbsc/vlr.h> +#include <openbsc/debug.h> + +#define S(x) (1 << (x)) + +/* + * TODO: + * * test FSM for all testvlr_mode (and more) + * * test also the time-outs in the vlr code + * * test for memory leaks + * * how to get the HLR running? Or test against stub? + * * test disappearing MS connection + * * test absence of HLR + */ + +void *tall_bsc_ctx; +static struct vlr_instance *g_vlr; + +/*********************************************************************** + * Finite State Machine simulating MS and MSC towards VLR + ***********************************************************************/ + +static int timer_error_cb(struct osmo_fsm_inst *fi) +{ + LOGPFSM(fi, "timer expired waiting for completion\n"); + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + return 1; +} + +enum testvlr_mode { + MODE_SUCCESS, + MODE_SUCCESS_TMSI, + MODE_AUTH_FAIL, + MODE_AUTH_RESYNC, +}; + +struct testvlr_priv { + enum testvlr_mode mode; + uint32_t tmsi; + char imsi[16]; + char imei[16]; + struct osmo_location_area_id old_lai; + struct osmo_location_area_id new_lai; + + struct vlr_subscr *subscr; + + struct osmo_fsm_inst *lu_fsm; +}; + +#define fsi_priv(x) (struct testvlr_priv *)(x)->priv + +enum f_state { + /*! initial state */ + ST_NULL, + /*! LU was sent by MS */ + ST_LU_SENT, + /*! waiting for auth re-sync */ + ST_RESYNC_SENT, + /* Waiting for LU ACK */ + ST_WAIT_LU_ACK, + ST_DONE, + ST_FAILED, +}; + +enum f_event { + /* events from MS */ + EVT_MS_TX_LU, /* transmit LU REQ to network */ + EVT_MS_TX_ID_RESP, /* tranmit ID RSP to network */ + EVT_MS_TX_AUTH_RESP, /* transmit AUTH RESP to network */ + EVT_MS_TX_AUTH_FAIL, /* transmit AUTH FAIL to network */ + EVT_MS_CONN_LOST, /* connection to MS was lost */ + + /* events from VLR */ + EVT_VLR_AUTH_REQ, /* transmit AUTH REQ to MS */ + EVT_VLR_ID_REQ_IMSI, /* transmit ID REQ(IMSI) to MS */ + EVT_VLR_ID_REQ_IMEI, /* tramsmit ID REQ(IMEI) to MS */ + EVT_VLR_ID_REQ_IMEISV, /* trasnmit ID REQ(IMEISV) to MS */ + EVT_VLR_AUTH_REJ, /* transmit AUTH REJ to MS */ + EVT_VLR_SET_CIPH, /* transmit SET CIPH to MS */ + EVT_VLR_LU_ACK, /* transmit LU ACK to MS */ + EVT_VLR_LU_REJ, /* transmit LU REJ to MS */ +}; + +static struct value_string f_event_names[] = { + { EVT_MS_TX_LU, "MS-TX-LU" }, + { EVT_MS_TX_ID_RESP, "MS-TX-ID-RESP" }, + { EVT_MS_TX_AUTH_RESP, "MS-TX-AUTH-RESP" }, + { EVT_MS_TX_AUTH_FAIL, "MS-TX-AUTH-FAIL" }, + { EVT_MS_CONN_LOST, "MS-CONN-LOST" }, + + { EVT_VLR_AUTH_REQ, "VLR-AUTH-REQ" }, + { EVT_VLR_ID_REQ_IMSI, "VLR-ID-REQ-IMSI" }, + { EVT_VLR_ID_REQ_IMEI, "VLR-ID-REQ-IMEI" }, + { EVT_VLR_ID_REQ_IMEISV,"VLR-ID-REQ-IMEISV" }, + { EVT_VLR_AUTH_REJ, "VLR-AUTH-REJ" }, + { EVT_VLR_SET_CIPH, "VLR-SET-CIPH" }, + { EVT_VLR_LU_ACK, "VLR-LU-ACK" }, + { EVT_VLR_LU_REJ, "VLR-LU-REJ" }, + { 0, NULL } +}; + +static void fsm_f_allstate(struct osmo_fsm_inst *fi, uint32_t event, + void *data) +{ + struct testvlr_priv *priv = fsi_priv(fi); + uint8_t mi[16]; + unsigned int mi_len; + + switch (event) { + case EVT_VLR_ID_REQ_IMSI: + if (priv->mode != MODE_SUCCESS_TMSI) { + LOGP(DGPRS, LOGL_NOTICE, "Unexpected ID REQ " + "(IMSI)\n"); + } + mi_len = gsm48_generate_mid_from_imsi(mi, priv->imsi); + vlr_subscr_rx_id_resp(priv->subscr, mi+2, mi_len-2); + break; + case EVT_VLR_ID_REQ_IMEI: + mi_len = gsm48_generate_mid_from_imsi(mi, priv->imei); + mi[0] = (mi[0] & 0xf8) | GSM_MI_TYPE_IMEI; + vlr_subscr_rx_id_resp(priv->subscr+2, mi, mi_len-2); + break; + case EVT_VLR_ID_REQ_IMEISV: + mi_len = gsm48_generate_mid_from_imsi(mi, priv->imei); + mi[0] = (mi[0] & 0xf8) | GSM_MI_TYPE_IMEISV; + vlr_subscr_rx_id_resp(priv->subscr, mi+2, mi_len-2); + break; + case EVT_MS_CONN_LOST: + vlr_subscr_disconnected(priv->subscr); + /* IDEA: not release but keep around in extra state to + * see if VLR still sends us anything? */ + osmo_fsm_inst_free(fi); + break; + } +} + +static void fsm_f_null(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct testvlr_priv *priv = fsi_priv(fi); + uint32_t tmsi = 0; + const char *imsi = NULL; + + switch (event) { + case EVT_MS_TX_LU: + /* send LU to VLR */ + if (priv->mode == MODE_SUCCESS) + imsi = priv->imsi; + else + tmsi = priv->tmsi; + priv->lu_fsm = vlr_loc_update(fi, + EVT_VLR_LU_ACK, + EVT_VLR_LU_REJ, + NULL, + g_vlr, NULL, + VLR_LU_TYPE_IMSI_ATTACH, tmsi, + imsi, &priv->old_lai, + &priv->new_lai, + true, + true, + false, + false); + OSMO_ASSERT(priv->subscr); + osmo_fsm_inst_state_chg(fi, ST_LU_SENT, 0, 0); + break; + default: + break; + } +} + +static void fsm_f_lu_sent(struct osmo_fsm_inst *fi, uint32_t event, + void *data) +{ + struct gsm_auth_tuple *at = NULL; + struct testvlr_priv *priv = fsi_priv(fi); + uint8_t res_fail[4]; + uint8_t auts[14]; + + switch (event) { + case EVT_VLR_AUTH_REQ: + at = data; + OSMO_ASSERT(at); + DEBUGP(DGPRS, "%s: at->res=%s\n", __func__, osmo_hexdump(at->vec.res, at->vec.res_len)); + switch (priv->mode) { + case MODE_SUCCESS: + case MODE_SUCCESS_TMSI: + /* return matching SRES/AUTS */ + vlr_subscr_rx_auth_resp(priv->subscr, true, false, + at->vec.res, at->vec.res_len); + break; + case MODE_AUTH_FAIL: + /* return not matching SRES/AUTS */ + vlr_subscr_rx_auth_resp(priv->subscr, true, false, + res_fail, sizeof(res_fail)); + /* FIXME: state transition? */ + break; + case MODE_AUTH_RESYNC: + /* return SRES/AUTS requesting re-sync */ + /* FIXME: generate a proper authenticating + * re-sync request */ + vlr_subscr_rx_auth_fail(priv->subscr, auts); + /* FIXME: state transition? */ + osmo_fsm_inst_state_chg(fi, ST_RESYNC_SENT, 0, 0); + break; + } + osmo_fsm_inst_state_chg(fi, ST_WAIT_LU_ACK, 0, 0); + break; + case EVT_VLR_LU_REJ: + { + uint8_t cause = *(uint8_t *)data; + LOGP(DGPRS, LOGL_NOTICE, "LU(%s): Rejected; cause=0x%02x\n", + priv->imsi, cause); + + } + break; + default: + break; + } +} + +#if 0 +static void fsm_f_resync_sent(struct osmo_fsm_inst *fi, uint32_t event, + void *data) +{ + struct testvlr_priv *priv = fsi_priv(fi); + struct gsm_auth_tuple *at = NULL; + + /* second auth request is supposed to succed after the + * re-sync procedure before */ + switch (event) { + case EVT_VLR_AUTH_REQ: + at = data; + /* return matching SRES/AUTS now */ + vlr_subscr_rx_auth_resp(priv->subscr, true, false, + at->vec.res, at->vec.res_len); + osmo_fsm_inst_state_chg(fi, ST_WAIT_LU_ACK, 0, 0); + break; + } +} +#endif + +static void fsm_f_wait_lu_ack(struct osmo_fsm_inst *fi, uint32_t event, + void *data) +{ + struct testvlr_priv *priv = fsi_priv(fi); + + switch (event) { + case EVT_VLR_LU_ACK: + if (priv->subscr->tmsi != GSM_RESERVED_TMSI) { + /* we need to send an TMSI REALLOC COMPL */ + vlr_subscr_rx_tmsi_reall_compl(priv->subscr); + } + osmo_fsm_inst_state_chg(fi, ST_DONE, 0, 0); + break; + case EVT_VLR_LU_REJ: + osmo_fsm_inst_state_chg(fi, ST_FAILED, 0, 0); + break; + } +} + +static void fsm_f_imsi_sent(struct osmo_fsm_inst *fi, uint32_t event, + void *data) +{ + switch (event) { + case EVT_MS_TX_ID_RESP: + break; + } +} + +#if 0 +static void fsm_f_areq_sent(struct osmo_fsm_inst *fi, uint32_t event, + void *data) +{ + switch (event) { + case EVT_MS_TX_AUTH_RESP: + break; + case EVT_MS_TX_AUTH_FAIL: + break; + } +} +#endif + +static struct osmo_fsm_state fsm_success_states[] = { + [ST_NULL] = { + .in_event_mask = S(EVT_MS_TX_LU), + .out_state_mask = S(ST_LU_SENT), + .name = "NULL", + .action = fsm_f_null, + }, + [ST_LU_SENT] = { + .in_event_mask = S(EVT_VLR_AUTH_REQ) | + S(EVT_VLR_LU_REJ), + //.out_state_mask = S(ST_IDREQ_IMSI_SENT) | S(ST_AUTH_REQ_SENT), + .out_state_mask = S(ST_WAIT_LU_ACK), + .name = "LU Sent", + .action = fsm_f_lu_sent, + }, + [ST_RESYNC_SENT] = { + .in_event_mask = S(EVT_VLR_AUTH_REQ), + .out_state_mask = S(ST_WAIT_LU_ACK), + .name = "AUTH-RESYNC sent", + .action = fsm_f_imsi_sent, + }, + [ST_WAIT_LU_ACK] = { + .in_event_mask = S(EVT_VLR_LU_ACK) | + S(EVT_VLR_SET_CIPH) | + S(EVT_VLR_LU_REJ), + .out_state_mask = S(ST_DONE), + .name = "WAIT-LU-ACK", + .action = fsm_f_wait_lu_ack, + }, + [ST_DONE] = { + .name = "DONE" + }, +}; + +static struct osmo_fsm vlr_test_fsm = { + .name = "VLR Test FSM", + .states = fsm_success_states, + .num_states = ARRAY_SIZE(fsm_success_states), + .log_subsys = DGPRS, + .event_names = f_event_names, + .allstate_event_mask = S(EVT_MS_CONN_LOST) | + S(EVT_VLR_ID_REQ_IMSI) | + S(EVT_VLR_ID_REQ_IMEI) | + S(EVT_VLR_ID_REQ_IMEISV), + .allstate_action = fsm_f_allstate, +}; + +/* Testing of Subscriber_Present_VLR */ + +enum test_sub_pres_state { + TSPV_S_INIT, + TSPV_S_RUNNING, +}; + +enum test_sub_pres_evt { + TSPV_E_START, + TSPV_E_COMPL, +}; + +static void tspv_f_running(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct vlr_subscr *vsub = fi->priv; + + switch (event) { + case TSPV_E_COMPL: + OSMO_ASSERT(vsub); + OSMO_ASSERT(vsub->ms_not_reachable_flag == false); + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); + break; + } +} + +static void tspv_f_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct osmo_fsm_inst *spv; + struct vlr_subscr *vsub = fi->priv; + + switch (event) { + case TSPV_E_START: + OSMO_ASSERT(vsub); + vsub->ms_not_reachable_flag = true; + spv = sub_pres_vlr_fsm_start(fi, vsub, TSPV_E_COMPL); + OSMO_ASSERT(spv); + osmo_fsm_inst_state_chg(fi, TSPV_S_RUNNING, 4, 0); + break; + } +} + +static const struct osmo_fsm_state test_sub_pres_vlr_states[] = { + [TSPV_S_INIT] = { + .in_event_mask = S(TSPV_E_START), + .out_state_mask = S(TSPV_S_RUNNING), + .name = "INIT", + .action = tspv_f_init, + }, + [TSPV_S_RUNNING] = { + .in_event_mask = S(TSPV_E_COMPL), + .out_state_mask = 0, + .name = "RUNNING", + .action = tspv_f_running, + }, +}; + +static struct osmo_fsm test_sub_pres_vlr_fsm = { + .name = "Test Subscriber_Present_VLR", + .states = test_sub_pres_vlr_states, + .num_states = ARRAY_SIZE(test_sub_pres_vlr_states), + .log_subsys = DGPRS, + .event_names = f_event_names, + .timer_cb = timer_error_cb, +}; + +#if 0 +static void start_sub_pres_vlr(void *ctx, uint32_t tmsi, const char *imsi) +{ + struct osmo_fsm_inst *fi; + struct vlr_subscr *vsub = vlr_subscr_alloc(g_vlr); + + vsub->tmsi = tmsi; + strncpy(vsub->imsi, imsi, sizeof(vsub->imsi)); + fi = osmo_fsm_inst_alloc(&test_sub_pres_vlr_fsm, ctx, vsub, LOGL_DEBUG, vsub->imsi); + osmo_fsm_inst_dispatch(fi, TSPV_E_START, NULL); +} +#endif + +/* Testing of Update_HLR_VLR */ + +enum test_update_hlr_vlr_state { + TUHV_S_INIT, + TUHV_S_RUNNING, +}; + +enum test_update_hlr_vlr_event { + TUHV_E_START, + TUHV_E_COMPL, +}; + +static void tuhv_f_running(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + enum gsm48_gmm_cause *res = data; + + switch (event) { + case TUHV_E_COMPL: + if (!res) { + /* Success */ + LOGPFSM(fi, "success\n"); + } else { + /* error */ + LOGPFSM(fi, "errror cause=0x%u\n", *res); + } + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); + break; + } +} + +static void tuhv_f_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct osmo_fsm_inst *child; + struct vlr_subscr *vsub = fi->priv; + + switch (event) { + case TUHV_E_START: + child = upd_hlr_vlr_proc_start(fi, vsub, TUHV_E_COMPL); + OSMO_ASSERT(child); + osmo_fsm_inst_state_chg(fi, TUHV_S_RUNNING, 4, 0); + break; + } +} + +static const struct osmo_fsm_state test_upd_hlr_vlr_states[] = { + [TUHV_S_INIT] = { + .in_event_mask = S(TUHV_E_START), + .out_state_mask = S(TUHV_S_RUNNING), + .name = "INIT", + .action = tuhv_f_init, + }, + [TUHV_S_RUNNING] = { + .in_event_mask = S(TUHV_E_COMPL), + .out_state_mask = 0, + .name = "RUNNING", + .action = tuhv_f_running, + }, +}; + +static struct osmo_fsm test_upd_hlr_vlr_fsm = { + .name = "Test Update_HLR_VLR", + .states = test_upd_hlr_vlr_states, + .num_states = ARRAY_SIZE(test_upd_hlr_vlr_states), + .log_subsys = DGPRS, + .event_names = f_event_names, + .timer_cb = timer_error_cb, +}; + +#if 0 +static void start_upd_hlr_vlr(void *ctx, uint32_t tmsi, const char *imsi) +{ + struct osmo_fsm_inst *fi; + struct vlr_subscr *vsub = vlr_subscr_alloc(g_vlr); + + vsub->tmsi = tmsi; + strncpy(vsub->imsi, imsi, sizeof(vsub->imsi)); + + + fi = osmo_fsm_inst_alloc(&test_upd_hlr_vlr_fsm, ctx, vsub, LOGL_DEBUG, + vsub->imsi); + /* we need to set this to fool vlr.c in an ongoing LU */ + vsub->lu_fsm = fi; + osmo_fsm_inst_dispatch(fi, TUHV_E_START, NULL); +} +#endif + +/*********************************************************************** + * Integration with VLR code + ***********************************************************************/ + +static struct vlr_instance *g_vlr; + +/* 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 osmo_fsm_inst *fi = msc_conn_ref; + OSMO_ASSERT(at); + DEBUGP(DGPRS, "%s: RES=%s\n", __func__, + osmo_hexdump_nospc(at->vec.res, at->vec.res_len)); + osmo_fsm_inst_dispatch(fi, EVT_VLR_AUTH_REQ, at); + return 0; +} + +/* VLR asks us to send an authentication reject */ +static int msc_vlr_tx_auth_rej(void *msc_conn_ref) +{ + struct osmo_fsm_inst *fi = msc_conn_ref; + DEBUGP(DGPRS, "%s\n", __func__); + osmo_fsm_inst_dispatch(fi, EVT_VLR_AUTH_REJ, NULL); + return 0; +} + +/* 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 osmo_fsm_inst *fi = msc_conn_ref; + uint32_t event; + + DEBUGP(DGPRS, "%s (%u)\n", __func__, mi_type); + + switch (mi_type) { + case GSM_MI_TYPE_IMSI: + event = EVT_VLR_ID_REQ_IMSI; + break; + case GSM_MI_TYPE_IMEI: + event = EVT_VLR_ID_REQ_IMEI; + break; + case GSM_MI_TYPE_IMEISV: + event = EVT_VLR_ID_REQ_IMEISV; + break; + default: + LOGP(DGPRS, LOGL_ERROR, "Unknown identity 0x%02x\n", + mi_type); + return -1; + } + osmo_fsm_inst_dispatch(fi, event, NULL); + return 0; +} + +/* VLR asks us to transmit a Location Update Accept */ +static int msc_vlr_tx_lu_ack(void *msc_conn_ref, uint32_t send_tmsi) +{ + struct osmo_fsm_inst *fi = msc_conn_ref; + DEBUGP(DGPRS, "%s\n", __func__); + osmo_fsm_inst_dispatch(fi, EVT_VLR_LU_ACK, NULL); + return 0; +} + +/* VLR asks us to transmit a Location Update Reject */ +static int msc_vlr_tx_lu_rej(void *msc_conn_ref, uint8_t cause) +{ + struct osmo_fsm_inst *fi = msc_conn_ref; + DEBUGP(DGPRS, "%s\n", __func__); + osmo_fsm_inst_dispatch(fi, EVT_VLR_LU_REJ, (void *) &cause); + return 0; +} + +static int msc_vlr_set_ciph_mode(void *msc_conn_ref, enum vlr_ciph mode, + bool retrieve_imeisv) +{ + struct osmo_fsm_inst *fi = msc_conn_ref; + DEBUGP(DGPRS, "%s\n", __func__); + osmo_fsm_inst_dispatch(fi, EVT_VLR_SET_CIPH, NULL); + return 0; +} + +/* VLR informs us that the subscriber data has somehow been modified */ +static void msc_vlr_subscr_update(struct vlr_subscr *subscr) +{ + DEBUGP(DGPRS, "%s\n", __func__); + /* FIXME */ +} + +static void msc_vlr_subscr_assoc(void *msc_conn_ref, struct vlr_subscr *vsub) +{ + struct osmo_fsm_inst *fi = msc_conn_ref; + struct testvlr_priv *priv = fsi_priv(fi); + DEBUGP(DGPRS, "%s(%p, %s)\n", __func__, msc_conn_ref, vlr_subscr_name(vsub)); + priv->subscr = vsub; +} + +/* operations that we need to implement for libvlr */ +static const struct vlr_ops test_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_ack, + .tx_lu_rej = msc_vlr_tx_lu_rej, + .set_ciph_mode = msc_vlr_set_ciph_mode, + .subscr_update = msc_vlr_subscr_update, + .subscr_assoc = msc_vlr_subscr_assoc, +}; + +/*********************************************************************** + * Actual test cases + ***********************************************************************/ + + +static struct osmo_fsm_inst * +start_lu(enum testvlr_mode mode, uint32_t tmsi, + const char *imsi, const char *imei) +{ + struct testvlr_priv *vp; + struct osmo_fsm_inst *fi; + + vp = talloc_zero(tall_bsc_ctx, struct testvlr_priv); + vp->mode = mode; + vp->tmsi = tmsi; + strncpy(vp->imsi, imsi, sizeof(vp->imsi)); + strncpy(vp->imei, imei, sizeof(vp->imei)); + + fi = osmo_fsm_inst_alloc(&vlr_test_fsm, vp, vp, LOGL_DEBUG, vp->imsi); + osmo_fsm_inst_dispatch(fi, EVT_MS_TX_LU, NULL); + return fi; +} + +/*********************************************************************** + * Main / Misc + ***********************************************************************/ + +static struct osmo_timer_list tmr; + +static void timer_cb(void *data) +{ + uint32_t tmsi = rand() % 1000000; + uint64_t imsi = 901790000000000 + tmsi; + char imsi_str[32]; + + snprintf(imsi_str, sizeof(imsi_str), "%lu", imsi); + //start_lu(MODE_AUTH_FAIL, tmsi, imsi_str, "23422342"); + start_lu(MODE_SUCCESS_TMSI, tmsi, imsi_str, "23422342"); + //start_lu(MODE_SUCCESS, tmsi, imsi_str, "23422342"); + //start_upd_hlr_vlr(tall_bsc_ctx, tmsi, imsi_str); + //start_sub_pres_vlr(tall_bsc_ctx); + osmo_timer_schedule(&tmr, 8, 0); +} + +static void sighdlr(int sig) +{ + switch (sig) { + case SIGUSR1: + talloc_report_full(tall_bsc_ctx, stderr); + break; + } +} + +int main(int argc, char **argv) +{ + tall_bsc_ctx = talloc_named_const(NULL, 1, "tall_bsc_ctx"); + + signal(SIGUSR1, sighdlr); + + osmo_init_logging(&log_info); + + g_vlr = vlr_alloc(NULL, &test_vlr_ops); + vlr_start(g_vlr, "localhost", 2222); + OSMO_ASSERT(g_vlr); + osmo_fsm_register(&vlr_test_fsm); + osmo_fsm_register(&test_sub_pres_vlr_fsm); + osmo_fsm_register(&test_upd_hlr_vlr_fsm); + + g_vlr->cfg.assign_tmsi = true; + + tmr.cb = timer_cb; + timer_cb(NULL); + + while (1) { + osmo_select_main(0); + } + + exit(0); +} + +struct gsm_subscriber_connection; +int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn) { return 0; } |