diff --git a/openbsc/include/openbsc/gprs_gmm.h b/openbsc/include/openbsc/gprs_gmm.h index 5b3321ffc..702b9b9d3 100644 --- a/openbsc/include/openbsc/gprs_gmm.h +++ b/openbsc/include/openbsc/gprs_gmm.h @@ -22,4 +22,6 @@ int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli); int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli, uint8_t suspend_ref); +time_t gprs_max_time_to_idle(void); + #endif /* _GPRS_GMM_H */ diff --git a/openbsc/include/openbsc/gprs_llc.h b/openbsc/include/openbsc/gprs_llc.h index fc6216ccc..d54b72e4a 100644 --- a/openbsc/include/openbsc/gprs_llc.h +++ b/openbsc/include/openbsc/gprs_llc.h @@ -161,8 +161,13 @@ struct gprs_llc_llme { uint16_t bvci; uint16_t nsei; struct gprs_llc_lle lle[NUM_SAPIS]; + + /* Internal management */ + uint32_t age_timestamp; }; +#define GPRS_LLME_RESET_AGE (0) + extern struct llist_head gprs_llc_llmes; /* LLC low level types */ diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h index 00cf5ccef..7940e1dc1 100644 --- a/openbsc/include/openbsc/gprs_sgsn.h +++ b/openbsc/include/openbsc/gprs_sgsn.h @@ -246,6 +246,7 @@ extern struct llist_head sgsn_apn_ctxts; extern struct llist_head sgsn_pdp_ctxts; uint32_t sgsn_alloc_ptmsi(void); +void sgsn_inst_init(void); /* High-level function to be called in case a GGSN has disappeared or * ottherwise lost state (recovery procedure) */ diff --git a/openbsc/include/openbsc/sgsn.h b/openbsc/include/openbsc/sgsn.h index 8a4514627..4bd412750 100644 --- a/openbsc/include/openbsc/sgsn.h +++ b/openbsc/include/openbsc/sgsn.h @@ -52,6 +52,8 @@ struct sgsn_instance { struct gsn_t *gsn; /* Subscriber */ struct gprs_gsup_client *gsup_client; + /* LLME inactivity timer */ + struct osmo_timer_list llme_timer; }; extern struct sgsn_instance *sgsn; diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index bdbad1921..bc3e21e1b 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -27,4 +27,4 @@ osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c gprs_sndcp_vty.c \ gsm_04_08_gprs.c osmo_sgsn_LDADD = \ $(top_builddir)/src/libcommon/libcommon.a \ - -lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) + -lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) -lrt diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c index 3977c6657..1e1372cd1 100644 --- a/openbsc/src/gprs/gprs_gmm.c +++ b/openbsc/src/gprs/gprs_gmm.c @@ -70,6 +70,7 @@ #define GSM0408_T3313_SECS 30 /* waiting for paging response */ #define GSM0408_T3314_SECS 44 /* force to STBY on expiry, Ready timer */ #define GSM0408_T3316_SECS 44 +#define GSM0408_MOBILE_REACHABLE_SECS (GSM0408_T3312_SECS + 4 * 60) /* Section 11.3 / Table 11.2d Timers of Session Management - network side */ #define GSM0408_T3385_SECS 8 /* wait for ACT PDP CTX REQ */ @@ -140,6 +141,11 @@ static void mmctx_timer_stop(struct sgsn_mm_ctx *mm, unsigned int T) osmo_timer_del(&mm->timer); } +time_t gprs_max_time_to_idle(void) +{ + return GSM0408_T3314_SECS + GSM0408_MOBILE_REACHABLE_SECS; +} + /* Send a message through the underlying layer */ static int gsm48_gmm_sendmsg(struct msgb *msg, int command, struct sgsn_mm_ctx *mm) diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c index 0b4613e0f..9b5bf4b49 100644 --- a/openbsc/src/gprs/gprs_llc.c +++ b/openbsc/src/gprs/gprs_llc.c @@ -256,6 +256,7 @@ static struct gprs_llc_llme *llme_alloc(uint32_t tlli) llme->tlli = tlli; llme->old_tlli = 0xffffffff; llme->state = GPRS_LLMS_UNASSIGNED; + llme->age_timestamp = GPRS_LLME_RESET_AGE; for (i = 0; i < ARRAY_SIZE(llme->lle); i++) lle_init(llme, i); @@ -622,6 +623,9 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) return 0; } + /* reset age computation */ + lle->llme->age_timestamp = GPRS_LLME_RESET_AGE; + /* decrypt information field + FCS, if needed! */ if (llhp.is_encrypted) { uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */ diff --git a/openbsc/src/gprs/gprs_llc_vty.c b/openbsc/src/gprs/gprs_llc_vty.c index ab5269922..f399b2752 100644 --- a/openbsc/src/gprs/gprs_llc_vty.c +++ b/openbsc/src/gprs/gprs_llc_vty.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -69,9 +70,13 @@ static uint8_t valid_sapis[] = { 1, 2, 3, 5, 7, 8, 9, 11 }; static void vty_dump_llme(struct vty *vty, struct gprs_llc_llme *llme) { unsigned int i; + struct timespec now_tp = {0}; + clock_gettime(CLOCK_MONOTONIC, &now_tp); - vty_out(vty, "TLLI %08x (Old TLLI %08x) BVCI=%u NSEI=%u: State %s%s", + vty_out(vty, "TLLI %08x (Old TLLI %08x) BVCI=%u NSEI=%u Age=%d: State %s%s", llme->tlli, llme->old_tlli, llme->bvci, llme->nsei, + llme->age_timestamp == GPRS_LLME_RESET_AGE ? 0 : + (int)(now_tp.tv_sec - (time_t)llme->age_timestamp), get_value_string(gprs_llc_state_strs, llme->state), VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(valid_sapis); i++) { diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c index 6f706642d..2b78d315c 100644 --- a/openbsc/src/gprs/gprs_sgsn.c +++ b/openbsc/src/gprs/gprs_sgsn.c @@ -37,6 +37,10 @@ #include #include "openbsc/gprs_llc.h" +#include + +#define GPRS_LLME_CHECK_TICK 30 + extern struct sgsn_instance *sgsn; LLIST_HEAD(sgsn_mm_ctxts); @@ -508,3 +512,62 @@ void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx, sgsn_auth_update(mmctx); } + +static void sgsn_llme_cleanup_free(struct gprs_llc_llme *llme) +{ + struct sgsn_mm_ctx *mmctx = NULL; + + llist_for_each_entry(mmctx, &sgsn_mm_ctxts, list) { + if (llme == mmctx->llme) { + gsm0408_gprs_access_cancelled(mmctx, SGSN_ERROR_CAUSE_NONE); + return; + } + } + + /* No MM context found */ + LOGP(DGPRS, LOGL_INFO, "Deleting orphaned LLME, TLLI 0x%08x\n", + llme->tlli); + gprs_llgmm_assign(llme, llme->tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL); +} + +static void sgsn_llme_check_cb(void *data_) +{ + struct gprs_llc_llme *llme, *llme_tmp; + struct timespec now_tp; + time_t now, age; + time_t max_age = gprs_max_time_to_idle(); + + int rc; + + rc = clock_gettime(CLOCK_MONOTONIC, &now_tp); + OSMO_ASSERT(rc >= 0); + now = now_tp.tv_sec; + + LOGP(DGPRS, LOGL_DEBUG, + "Checking for inactive LLMEs, time = %u\n", (unsigned)now); + + llist_for_each_entry_safe(llme, llme_tmp, &gprs_llc_llmes, list) { + if (llme->age_timestamp == GPRS_LLME_RESET_AGE) + llme->age_timestamp = now; + + age = now - llme->age_timestamp; + + if (age > max_age || age < 0) { + LOGP(DGPRS, LOGL_INFO, + "Inactivity timeout for TLLI 0x%08x, age %d\n", + llme->tlli, (int)age); + sgsn_llme_cleanup_free(llme); + } + } + + osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0); +} + +void sgsn_inst_init() +{ + sgsn->llme_timer.cb = sgsn_llme_check_cb; + sgsn->llme_timer.data = NULL; + + osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0); +} + diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c index f26b812a8..0db90d5c3 100644 --- a/openbsc/src/gprs/sgsn_main.c +++ b/openbsc/src/gprs/sgsn_main.c @@ -337,6 +337,7 @@ int main(int argc, char **argv) bssgp_nsi = sgsn_inst.cfg.nsi = sgsn_nsi; gprs_llc_init("/usr/local/lib/osmocom/crypt/"); + sgsn_inst_init(); gprs_ns_vty_init(bssgp_nsi); bssgp_vty_init(); diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am index c1b5fbda4..693cf792a 100644 --- a/openbsc/tests/sgsn/Makefile.am +++ b/openbsc/tests/sgsn/Makefile.am @@ -32,5 +32,5 @@ sgsn_test_LDADD = \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOGB_LIBS) \ - -lgtp + -lgtp -lrt