aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmocom/sgsn/gprs_sgsn.h4
-rw-r--r--src/gprs/gprs_gmm.c87
-rw-r--r--src/gprs/gprs_sgsn.c5
-rw-r--r--src/gprs/sgsn_vty.c2
-rw-r--r--tests/test_nodes.vty2
5 files changed, 98 insertions, 2 deletions
diff --git a/include/osmocom/sgsn/gprs_sgsn.h b/include/osmocom/sgsn/gprs_sgsn.h
index 9753ea211..336155c9b 100644
--- a/include/osmocom/sgsn/gprs_sgsn.h
+++ b/include/osmocom/sgsn/gprs_sgsn.h
@@ -156,6 +156,10 @@ struct sgsn_mm_ctx {
struct gprs_llc_llme *llme;
uint32_t tlli;
uint32_t tlli_new;
+
+ /* timer for mm state. state=READY: T3314 (aka TS 23.060 "READY timer") */
+ struct osmo_timer_list state_timer;
+ unsigned int state_T; /* Txxxx number but only used for pmm_states */
} gb;
struct {
int new_key;
diff --git a/src/gprs/gprs_gmm.c b/src/gprs/gprs_gmm.c
index db8defcc6..f7aff7311 100644
--- a/src/gprs/gprs_gmm.c
+++ b/src/gprs/gprs_gmm.c
@@ -137,6 +137,57 @@ static void mmctx_change_gtpu_endpoints_to_sgsn(struct sgsn_mm_ctx *mm_ctx)
}
}
+static void mmctx_set_mm_state(struct sgsn_mm_ctx *ctx, enum gprs_pmm_state state);
+static void mmctx_state_timer_cb(void *_mm)
+{
+ struct sgsn_mm_ctx *mm = _mm;
+
+ switch (mm->gb.state_T) {
+ case 3314:
+ switch (mm->pmm_state) {
+ case MM_READY:
+ LOGMMCTXP(LOGL_INFO, mm, "T3314 expired\n");
+ mmctx_set_mm_state(mm, MM_STANDBY);
+ break;
+ default:
+ LOGMMCTXP(LOGL_ERROR, mm, "T3314 expired in state %s != MM_READY\n",
+ get_value_string(gprs_pmm_state_names, mm->pmm_state));
+ break;
+ }
+ break;
+ default:
+ LOGMMCTXP(LOGL_ERROR, mm, "state timer expired in unknown mode %u\n",
+ mm->gb.state_T);
+ break;
+ }
+}
+
+static void mmctx_state_timer_start(struct sgsn_mm_ctx *mm, unsigned int T)
+{
+ unsigned long seconds;
+
+ if (mm->gb.state_T && mm->gb.state_T != T)
+ LOGMMCTXP(LOGL_ERROR, mm, "Attempting to start timer %u but %u is active!\n",
+ T, mm->gb.state_T);
+
+ mm->gb.state_T = T;
+ mm->gb.state_timer.data = mm;
+ mm->gb.state_timer.cb = &mmctx_state_timer_cb;
+
+ seconds = osmo_tdef_get(sgsn->cfg.T_defs, T, OSMO_TDEF_S, -1);
+ osmo_timer_schedule(&mm->gb.state_timer, seconds, 0);
+}
+
+static void mmctx_state_timer_stop(struct sgsn_mm_ctx *mm, unsigned int T)
+{
+ if (mm->gb.state_T == T)
+ osmo_timer_del(&mm->gb.state_timer);
+ else
+ LOGMMCTXP(LOGL_ERROR, mm, "Attempting to stop timer %u but %u is active!\n",
+ T, mm->gb.state_T);
+ mm->gb.state_T = 0;
+}
+
static void mmctx_set_pmm_state(struct sgsn_mm_ctx *ctx, enum gprs_pmm_state state)
{
OSMO_ASSERT(ctx->ran_type == MM_CTX_T_UTRAN_Iu);
@@ -173,6 +224,24 @@ static void mmctx_set_mm_state(struct sgsn_mm_ctx *ctx, enum gprs_pmm_state stat
get_value_string(gprs_pmm_state_names, ctx->pmm_state),
get_value_string(gprs_pmm_state_names, state));
+ switch (state) {
+ case MM_READY:
+ /* on expiration, T3314 moves mm state back to MM_STANDBY */
+ mmctx_state_timer_start(ctx, 3314);
+ break;
+ case MM_IDLE:
+ if (ctx->pmm_state == MM_READY)
+ mmctx_state_timer_stop(ctx, 3314);
+ break;
+ case MM_STANDBY:
+ if (ctx->pmm_state == MM_READY)
+ mmctx_state_timer_stop(ctx, 3314);
+ break;
+ default:
+ /* when changing to state != MM_READY */
+ break;
+ }
+
ctx->pmm_state = state;
}
@@ -2968,6 +3037,21 @@ int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id,
return rc;
}
+/* Update the MM context state */
+static void gsm0408_gprs_notify_pdu_gb(struct sgsn_mm_ctx *mmctx)
+{
+ switch (mmctx->pmm_state) {
+ case MM_STANDBY:
+ mmctx_set_mm_state(mmctx, MM_READY);
+ break;
+ case MM_READY: /* RE-arm the timer upon receival of Gb PDUs */
+ mmctx_state_timer_start(mmctx, 3314);
+ break;
+ default:
+ break;
+ }
+}
+
/* Main entry point for incoming 04.08 GPRS messages from Gb */
int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
bool drop_cipherable)
@@ -2988,6 +3072,9 @@ int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
/* MMCTX can be NULL */
+ if (mmctx)
+ gsm0408_gprs_notify_pdu_gb(mmctx);
+
switch (pdisc) {
case GSM48_PDISC_MM_GPRS:
rc = gsm0408_rcv_gmm(mmctx, msg, llme, drop_cipherable);
diff --git a/src/gprs/gprs_sgsn.c b/src/gprs/gprs_sgsn.c
index eb04846eb..9f02d5420 100644
--- a/src/gprs/gprs_sgsn.c
+++ b/src/gprs/gprs_sgsn.c
@@ -341,6 +341,11 @@ void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *mm)
osmo_timer_del(&mm->timer);
}
+ if (osmo_timer_pending(&mm->gb.state_timer)) {
+ LOGMMCTXP(LOGL_INFO, mm, "Cancelling MM state timer %u\n", mm->gb.state_T);
+ osmo_timer_del(&mm->gb.state_timer);
+ }
+
memset(&sig_data, 0, sizeof(sig_data));
sig_data.mm = mm;
osmo_signal_dispatch(SS_SGSN, S_SGSN_MM_FREE, &sig_data);
diff --git a/src/gprs/sgsn_vty.c b/src/gprs/sgsn_vty.c
index ae26cbeae..66986911c 100644
--- a/src/gprs/sgsn_vty.c
+++ b/src/gprs/sgsn_vty.c
@@ -94,7 +94,7 @@ const struct value_string sgsn_auth_pol_strs[] = {
static struct osmo_tdef sgsn_T_defs[] = {
{ .T=3312, .default_val=GSM0408_T3312_SECS, .desc="Periodic RA Update timer (s)" },
{ .T=3313, .default_val=GSM0408_T3313_SECS, .desc="Waiting for paging response timer (s)" },
- { .T=3314, .default_val=GSM0408_T3314_SECS, .desc="Force to STANDBY on expiry timer (s)" },
+ { .T=3314, .default_val=GSM0408_T3314_SECS, .desc="READY timer. Force to STANDBY on expiry timer (s)" },
{ .T=3316, .default_val=GSM0408_T3316_SECS, .desc="AA-Ready timer (s)" },
{ .T=3322, .default_val=GSM0408_T3322_SECS, .desc="Detach request -> accept timer (s)" },
{ .T=3350, .default_val=GSM0408_T3350_SECS, .desc="Waiting for ATT/RAU/TMSI_COMPL timer (s)" },
diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty
index 630094fda..2ef926af1 100644
--- a/tests/test_nodes.vty
+++ b/tests/test_nodes.vty
@@ -2,7 +2,7 @@ OsmoSGSN> enable
OsmoSGSN# show timer
T3312 = 600 s Periodic RA Update timer (s) (default: 600 s)
T3313 = 30 s Waiting for paging response timer (s) (default: 30 s)
-T3314 = 44 s Force to STANDBY on expiry timer (s) (default: 44 s)
+T3314 = 44 s READY timer. Force to STANDBY on expiry timer (s) (default: 44 s)
T3316 = 44 s AA-Ready timer (s) (default: 44 s)
T3322 = 6 s Detach request -> accept timer (s) (default: 6 s)
T3350 = 6 s Waiting for ATT/RAU/TMSI_COMPL timer (s) (default: 6 s)