aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2015-01-23 11:33:51 +0100
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2015-01-26 08:51:50 +0100
commit81ffb740f78ccdada8ece786cfab852bc10172e8 (patch)
treeaf356b98d257881ecdb0ef37c885d6f83e0a2b8c
parent841d95f86701b9e813402c65a7b1f88d132593fb (diff)
sgsn: Remove inactive LLME/MM after inactivity timeout
Currently old LLMEs and MM contexts that haven't been explicitly detached or cancelled are not removed until another request with the same IMSI is made. These stale entries may accumulate over time and severely compromise the operation of the SGSN. This patch implements age based LLME expiry, when the maximum age has been reached, the corresponding MM context is cancelled. If such an MM context doesn't exist, the LLME is unassigned directly. The implementation works as follows. - llme->age_timestamp is reset on each received PTP LLC message - sgsn_llme_check_cb is invoked periodically (each 30s) - sgsn_llme_check_cb sets the age_timestamp to the current time if it has been reset - sgsn_llme_check_cb computes the age and expires the LLME if it exceeds gprs_max_time_to_idle() Ticket: OW#1364 Sponsored-by: On-Waves ehf [hfreyther: Fix typo in comment LMME -> LLME]
-rw-r--r--openbsc/include/openbsc/gprs_gmm.h2
-rw-r--r--openbsc/include/openbsc/gprs_llc.h5
-rw-r--r--openbsc/include/openbsc/gprs_sgsn.h1
-rw-r--r--openbsc/include/openbsc/sgsn.h2
-rw-r--r--openbsc/src/gprs/Makefile.am2
-rw-r--r--openbsc/src/gprs/gprs_gmm.c6
-rw-r--r--openbsc/src/gprs/gprs_llc.c4
-rw-r--r--openbsc/src/gprs/gprs_llc_vty.c7
-rw-r--r--openbsc/src/gprs/gprs_sgsn.c63
-rw-r--r--openbsc/src/gprs/sgsn_main.c1
-rw-r--r--openbsc/tests/sgsn/Makefile.am2
11 files changed, 92 insertions, 3 deletions
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 <unistd.h>
#include <errno.h>
#include <stdint.h>
+#include <time.h>
#include <arpa/inet.h>
@@ -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 <openbsc/gprs_gmm.h>
#include "openbsc/gprs_llc.h"
+#include <time.h>
+
+#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