lynxis lazus has submitted this change. ( https://gerrit.osmocom.org/c/osmo-msc/+/38490?usp=email )
Change subject: vlr: add PS support ......................................................................
vlr: add PS support
Add vlr_ra_update() similar to vlr_lu_update(). Implement resending of GMM PDUs. While CS has a reliable connection between the MS and MSC, the MS and SGSN doesn't have such connection. The PDU is resend N times before the LU fsm is failing.
Change-Id: Ie9ffeb140c9d354b3a0f4822e2619f623235add0 --- M src/libvlr/vlr.c M src/libvlr/vlr_lu_fsm.c 2 files changed, 220 insertions(+), 2 deletions(-)
Approvals: fixeria: Looks good to me, but someone else must approve daniel: Looks good to me, approved Jenkins Builder: Verified
diff --git a/src/libvlr/vlr.c b/src/libvlr/vlr.c index 21f9fc6..919def3 100644 --- a/src/libvlr/vlr.c +++ b/src/libvlr/vlr.c @@ -1487,6 +1487,15 @@ } }
+/* SGSN->VLR: Subscriber has provided ATTACH/RAU Complete */ +int vlr_subscr_rx_rau_complete(struct vlr_subscr *vsub) +{ + if (!vsub->lu_fsm) + return -EINVAL; + + return osmo_fsm_inst_dispatch(vsub->lu_fsm, VLR_ULA_E_NEW_TMSI_ACK, NULL); +} + bool vlr_subscr_expire(struct vlr_subscr *vsub) { if (vsub->lu_complete) { diff --git a/src/libvlr/vlr_lu_fsm.c b/src/libvlr/vlr_lu_fsm.c index ec459a1..4bed335 100644 --- a/src/libvlr/vlr_lu_fsm.c +++ b/src/libvlr/vlr_lu_fsm.c @@ -354,6 +354,7 @@ uint8_t cause; bool assign_tmsi; enum vlr_lu_type lu_type; + int N; /*< counter of timeouts */ };
static inline struct lu_compl_vlr_priv *lu_compl_vlr_fi_priv(struct osmo_fsm_inst *fi); @@ -407,6 +408,7 @@ OSMO_ASSERT(vlr);
OSMO_ASSERT(event == LU_COMPL_VLR_E_START); + lcvp->N = 0;
/* TODO: National Roaming restrictions? */ /* TODO: Roaming restriction due to unsupported feature in subscriber @@ -564,6 +566,47 @@ vlr_lu_compl_fsm_success(fi); }
+static void vlr_lu_compl_fsm_reset_n(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct lu_compl_vlr_priv *lcvp = lu_compl_vlr_fi_priv(fi); + lcvp->N = 0; +} + +static int lu_compl_vlr_timeout(struct osmo_fsm_inst *fi) +{ + struct lu_compl_vlr_priv *lcvp = lu_compl_vlr_fi_priv(fi); + struct vlr_instance *vlr = lcvp->vsub->vlr; + struct vlr_subscr *vsub = lcvp->vsub; + + /* on cs: terminate the FSM */ + if (vlr_is_cs(vlr)) + return 1; + + /* PS: we have to resend the complete message 5x times, before failing */ + lcvp->N++; + if (lcvp->N >= 5) + return 1; + + LOGPFSML(fi, LOGL_ERROR, "LU Compl timeout T%d / N%d\n", fi->T, lcvp->N); + + switch (fi->state) { + case LU_COMPL_VLR_S_WAIT_IMEI: + case LU_COMPL_VLR_S_WAIT_IMEI_TMSI: + vlr->ops.tx_id_req(lcvp->msc_conn_ref, GSM_MI_TYPE_IMEI); + break; + case LU_COMPL_VLR_S_WAIT_TMSI_CNF: + vlr->ops.tx_lu_acc(lcvp->msc_conn_ref, vsub->tmsi_new, lcvp->lu_type); + break; + default: + OSMO_ASSERT(0); + break; + } + + osmo_timer_schedule(&fi->timer, vlr_timer_secs(vlr, fi->T, fi->T), 0); + + return 0; +} + static const struct osmo_fsm_state lu_compl_vlr_states[] = { [LU_COMPL_VLR_S_INIT] = { .in_event_mask = S(LU_COMPL_VLR_E_START), @@ -586,6 +629,7 @@ S(LU_COMPL_VLR_E_IMEI_CHECK_NACK), .out_state_mask = S(LU_COMPL_VLR_S_DONE), .name = OSMO_STRINGIFY(LU_COMPL_VLR_S_WAIT_IMEI), + .onenter = vlr_lu_compl_fsm_reset_n, .action = lu_compl_vlr_wait_imei, }, [LU_COMPL_VLR_S_WAIT_IMEI_TMSI] = { @@ -594,12 +638,14 @@ .out_state_mask = S(LU_COMPL_VLR_S_DONE) | S(LU_COMPL_VLR_S_WAIT_TMSI_CNF), .name = OSMO_STRINGIFY(LU_COMPL_VLR_S_WAIT_IMEI_TMSI), + .onenter = vlr_lu_compl_fsm_reset_n, .action = lu_compl_vlr_wait_imei, }, [LU_COMPL_VLR_S_WAIT_TMSI_CNF] = { .in_event_mask = S(LU_COMPL_VLR_E_NEW_TMSI_ACK), .out_state_mask = S(LU_COMPL_VLR_S_DONE), .name = OSMO_STRINGIFY(LU_COMPL_VLR_S_WAIT_TMSI_CNF), + .onenter = vlr_lu_compl_fsm_reset_n, .action = lu_compl_vlr_wait_tmsi, }, [LU_COMPL_VLR_S_DONE] = { @@ -616,6 +662,7 @@ .allstate_action = NULL, .log_subsys = DLGLOBAL, .event_names = lu_compl_vlr_event_names, + .timer_cb = lu_compl_vlr_timeout, };
static inline struct lu_compl_vlr_priv *lu_compl_vlr_fi_priv(struct osmo_fsm_inst *fi) @@ -711,6 +758,8 @@ uint32_t tmsi; struct osmo_location_area_id old_lai; struct osmo_location_area_id new_lai; + struct osmo_routing_area_id old_rai; + struct osmo_routing_area_id new_rai; bool authentication_required; /* is_ciphering_to_be_attempted: true when any A5/n > 0 are enabled. Ciphering is allowed, always attempt to get Auth Info from * the HLR. */ @@ -722,6 +771,9 @@ bool is_r99; bool is_utran; bool assign_tmsi; + + /*! count times timer T timed out */ + int N; };
@@ -1383,6 +1435,12 @@ } }
+static void lu_fsm_wait_imsi_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct lu_fsm_priv *lfp = lu_fsm_fi_priv(fi); + lfp->N = 0; +} + static const struct osmo_fsm_state vlr_lu_fsm_states[] = { [VLR_ULA_S_IDLE] = { .in_event_mask = S(VLR_ULA_E_UPDATE_LA), @@ -1447,6 +1505,7 @@ S(VLR_ULA_S_WAIT_HLR_UPD) | S(VLR_ULA_S_DONE), .name = OSMO_STRINGIFY(VLR_ULA_S_WAIT_IMSI), + .onenter = lu_fsm_wait_imsi_onenter, .action = lu_fsm_wait_imsi, }, [VLR_ULA_S_WAIT_HLR_CHECK_IMEI_EARLY] = { @@ -1508,6 +1567,59 @@ vsub->lu_fsm = NULL; }
+int fsm_lu_timer_cb(struct osmo_fsm_inst *fi) +{ + struct lu_fsm_priv *lfp = lu_fsm_fi_priv(fi); + struct vlr_instance *vlr = lfp->vlr; + struct vlr_subscr *vsub = lfp->vsub; + + /* only PS requires resending, CS terminate FSM */ + if (vlr_is_cs(vlr)) + return 1; + + /* PS: we have to resend the complete message 5x times, before failing */ + if (++lfp->N >= 5) { + /* LU reject and terminate FSM */ + uint8_t gsm48_cause; + + switch (fi->state) { + case VLR_ULA_S_WAIT_IMSI: + case VLR_ULA_S_WAIT_CIPH: + gsm48_cause = GSM48_REJECT_MS_IDENTITY_NOT_DERVIVABLE; + break; + case VLR_ULA_S_WAIT_AUTH: /* HLR/GSUP timeout */ + default: + gsm48_cause = GSM48_REJECT_NETWORK_FAILURE; + break; + } + + lfp->vlr->ops.tx_lu_rej(lfp->msc_conn_ref, gsm48_cause, lfp->lu_type); + /* 1 will terminate the fsm */ + return 1; + } + + switch (fi->state) { + case VLR_ULA_S_WAIT_CIPH: + if (vlr_set_ciph_mode(vsub->vlr, fi, lfp->msc_conn_ref, + vsub->sec_ctx, + vsub->vlr->cfg.retrieve_imeisv_ciphered)) { + LOGPFSML(fi, LOGL_ERROR, + "Failed to re-send Ciphering Mode Command\n"); + lu_fsm_failure(fi, GSM48_REJECT_NETWORK_FAILURE); + return 0; + } + break; + case VLR_ULA_S_WAIT_IMSI: + vlr->ops.tx_id_req(lfp->msc_conn_ref, GSM_MI_TYPE_IMSI); + break; + } + + osmo_timer_schedule(&fi->timer, vlr_timer_secs(vlr, fi->T, fi->T), 0); + + return 0; +} + + static struct osmo_fsm vlr_lu_fsm = { .name = "vlr_lu_fsm", .states = vlr_lu_fsm_states, @@ -1517,6 +1629,7 @@ .log_subsys = DLGLOBAL, .event_names = fsm_lu_event_names, .cleanup = fsm_lu_cleanup, + .timer_cb = fsm_lu_timer_cb, };
static inline struct lu_fsm_priv *lu_fsm_fi_priv(struct osmo_fsm_inst *fi) @@ -1525,8 +1638,8 @@ return (struct lu_fsm_priv*)fi->priv; }
-struct osmo_fsm_inst * -vlr_loc_update(struct osmo_fsm_inst *parent, +static 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, @@ -1575,6 +1688,102 @@ } fi->priv = lfp;
+ return fi; +} + +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, + bool is_ciphering_to_be_attempted, + bool is_ciphering_required, + uint8_t key_seq, + bool is_r99, bool is_utran, + bool assign_tmsi) +{ + struct osmo_fsm_inst *fi = _vlr_loc_update( + parent, + parent_event_success, + parent_event_failure, + parent_event_data, + vlr, msc_conn_ref, + type, tmsi, imsi, + old_lai, + new_lai, + authentication_required, + is_ciphering_to_be_attempted, + is_ciphering_required, + key_seq, + is_r99, is_utran, + assign_tmsi); + + if (!fi) + return NULL; + + LOGPFSM(fi, "rev=%s net=%s%s%s\n", + is_r99 ? "R99" : "GSM", + is_utran ? "UTRAN" : "GERAN", + (authentication_required || is_ciphering_to_be_attempted) ? + " Auth" : " (no Auth)", + (authentication_required || is_ciphering_to_be_attempted) ? + (is_ciphering_to_be_attempted ? "+Ciph" : " (no Ciph)") + : ""); + + if (is_utran && !authentication_required) + LOGPFSML(fi, LOGL_ERROR, + "Authentication off on UTRAN network. Good luck.\n"); + + osmo_fsm_inst_dispatch(fi, VLR_ULA_E_UPDATE_LA, NULL); + + return fi; +} + +struct osmo_fsm_inst * +vlr_ra_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_routing_area_id *old_rai, + const struct osmo_routing_area_id *new_rai, + bool authentication_required, + bool is_ciphering_to_be_attempted, + bool is_ciphering_required, + uint8_t key_seq, + bool is_r99, bool is_utran, + bool assign_tmsi) +{ + struct lu_fsm_priv *lfp; + struct osmo_fsm_inst *fi = _vlr_loc_update( + parent, + parent_event_success, + parent_event_failure, + parent_event_data, + vlr, msc_conn_ref, + type, tmsi, imsi, + &old_rai->lac, + &new_rai->lac, + authentication_required, + is_ciphering_to_be_attempted, + is_ciphering_required, + key_seq, + is_r99, is_utran, + assign_tmsi); + + if (!fi) + return NULL; + + lfp = fi->priv; + lfp->old_rai = *old_rai; + lfp->new_rai = *new_rai; + LOGPFSM(fi, "rev=%s net=%s%s%s\n", is_r99 ? "R99" : "GSM", is_utran ? "UTRAN" : "GERAN",