gprs: Add expiry timeout for subscriber entries

Set the expiry delay after the subscriber has been deleted (e.g. by
freeing the MM context). If cancelled, the subscriber will be deleted
immediately and no timeout will be set. If the expiry time is set to
SGSN_TIMEOUT_NEVER, no timer will be started and the subscriber entry
will be kept until it is cancelled.

The following VTY command is added to the sgsn node:

  - subscriber-expiry-time <0-999999>    set expiry time in seconds
  - no subscriber-expiry-time            set to SGSN_TIMEOUT_NEVER

The default is an expiry time of 0 seconds, which means that the
subscriber entries are wiped out immediately after an MM context is
destroyed.

Note that unused MM contexts are not expired yet. Therefore the
subscriber will only be expired after a successful MM detach.

Sponsored-by: On-Waves ehf
This commit is contained in:
Jacob Erlbeck 2015-01-06 16:32:41 +01:00 committed by Holger Hans Peter Freyther
parent b8fb1409d1
commit 0f47b8fae7
5 changed files with 119 additions and 3 deletions

View File

@ -276,6 +276,7 @@ struct sgsn_subscriber_data {
struct gsm_auth_tuple auth_triplets[5];
int auth_triplets_updated;
int error_cause;
struct osmo_timer_list timer;
};
#define LOGGSUBSCRP(level, subscr, fmt, args...) \

View File

@ -7,6 +7,8 @@
#include <osmocom/gprs/gprs_ns.h>
#include <openbsc/gprs_sgsn.h>
#define SGSN_TIMEOUT_NEVER (-1)
struct gprs_gsup_client;
enum sgsn_auth_policy {
@ -31,6 +33,8 @@ struct sgsn_config {
struct sockaddr_in gsup_server_addr;
int gsup_server_port;
int subscriber_expiry_timeout;
int require_authentication;
int require_update_location;
};

View File

@ -73,6 +73,43 @@ static int gsup_read_cb(struct gprs_gsup_client *gsupc, struct msgb *msg)
return rc;
}
static void sgsn_subscriber_timeout_cb(void *subscr_);
int gprs_subscr_purge(struct gsm_subscriber *subscr);
void gprs_subscr_stop_timer(struct gsm_subscriber *subscr)
{
if (subscr->sgsn_data->timer.data) {
osmo_timer_del(&subscr->sgsn_data->timer);
subscr->sgsn_data->timer.cb = NULL;
OSMO_ASSERT(subscr->sgsn_data->timer.data == subscr);
subscr->sgsn_data->timer.data = NULL;
subscr_put(subscr);
}
}
void gprs_subscr_start_timer(struct gsm_subscriber *subscr, unsigned seconds)
{
if (!subscr->sgsn_data->timer.data) {
subscr->sgsn_data->timer.cb = sgsn_subscriber_timeout_cb;
subscr->sgsn_data->timer.data = subscr_get(subscr);
}
osmo_timer_schedule(&subscr->sgsn_data->timer, seconds, 0);
}
static void sgsn_subscriber_timeout_cb(void *subscr_)
{
struct gsm_subscriber *subscr = subscr_;
LOGGSUBSCRP(LOGL_INFO, subscr,
"Expired, deleting subscriber entry\n");
/* Make sure, the timer is cleaned up */
subscr->keep_in_ram = 0;
gprs_subscr_stop_timer(subscr);
/* The subscr is freed now, if the timer was the last user */
}
static struct sgsn_subscriber_data *sgsn_subscriber_data_alloc(void *ctx)
{
struct sgsn_subscriber_data *sdata;
@ -97,7 +134,7 @@ struct gsm_subscriber *gprs_subscr_get_or_create(const char *imsi)
if (!subscr->sgsn_data)
subscr->sgsn_data = sgsn_subscriber_data_alloc(subscr);
subscr->keep_in_ram = 1;
gprs_subscr_stop_timer(subscr);
return subscr;
}
@ -116,8 +153,14 @@ void gprs_subscr_delete(struct gsm_subscriber *subscr)
}
if ((subscr->flags & GPRS_SUBSCRIBER_CANCELLED) ||
(subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT))
(subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT)) {
subscr->keep_in_ram = 0;
gprs_subscr_stop_timer(subscr);
} else if (sgsn->cfg.subscriber_expiry_timeout != SGSN_TIMEOUT_NEVER) {
gprs_subscr_start_timer(subscr, sgsn->cfg.subscriber_expiry_timeout);
} else {
subscr->keep_in_ram = 1;
}
subscr_put(subscr);
}

View File

@ -151,6 +151,10 @@ static int config_write_sgsn(struct vty *vty)
llist_for_each_entry(acl, &g_cfg->imsi_acl, list)
vty_out(vty, " imsi-acl add %s%s", acl->imsi, VTY_NEWLINE);
if (g_cfg->subscriber_expiry_timeout != SGSN_TIMEOUT_NEVER)
vty_out(vty, " subscriber-expiry-timeout %d%s",
g_cfg->subscriber_expiry_timeout, VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -401,6 +405,7 @@ static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr,
char expire_time[200];
struct gsm_auth_tuple *at;
int at_idx;
struct timeval tv;
vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
subscr->authorized, VTY_NEWLINE);
@ -446,6 +451,17 @@ static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr,
vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE);
}
/* print the expiration time if the timer is active */
if (osmo_timer_pending(&subscr->sgsn_data->timer)) {
osmo_timer_remaining(&subscr->sgsn_data->timer, NULL, &tv);
strftime(expire_time, sizeof(expire_time),
"%a, %d %b %Y %T %z",
localtime(&subscr->sgsn_data->timer.timeout.tv_sec));
expire_time[sizeof(expire_time) - 1] = '\0';
vty_out(vty, " Expires in: %ds (%s)%s",
(int)tv.tv_sec, expire_time, VTY_NEWLINE);
}
if (subscr->flags)
vty_out(vty, " Flags: %s%s%s%s%s",
subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT ?
@ -687,7 +703,24 @@ DEFUN(cfg_gsup_remote_port, cfg_gsup_remote_port_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_subscriber_expiry_timeout, cfg_subscriber_expiry_timeout_cmd,
"subscriber-expiry-timeout <0-999999>",
"Set the expiry time for unused subscriber entries\n"
"Expiry time in seconds\n")
{
g_cfg->subscriber_expiry_timeout = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_no_subscriber_expiry_timeout, cfg_no_subscriber_expiry_timeout_cmd,
"no subscriber-expiry-timeout",
NO_STR "Set the expiry time for unused subscriber entries\n")
{
g_cfg->subscriber_expiry_timeout = atoi(argv[0]);
return CMD_SUCCESS;
}
int sgsn_vty_init(void)
{
@ -716,6 +749,8 @@ int sgsn_vty_init(void)
install_element(SGSN_NODE, &cfg_auth_policy_cmd);
install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd);
install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd);
install_element(SGSN_NODE, &cfg_subscriber_expiry_timeout_cmd);
install_element(SGSN_NODE, &cfg_no_subscriber_expiry_timeout_cmd);
return 0;
}

View File

@ -44,6 +44,7 @@ static struct sgsn_instance sgsn_inst = {
.cfg = {
.gtp_statedir = "./",
.auth_policy = SGSN_AUTH_POLICY_CLOSED,
.subscriber_expiry_timeout = SGSN_TIMEOUT_NEVER,
},
};
struct sgsn_instance *sgsn = &sgsn_inst;
@ -200,9 +201,11 @@ static void assert_subscr(const struct gsm_subscriber *subscr, const char *imsi)
static void test_subscriber(void)
{
struct gsm_subscriber *s1, *s2, *sfound;
struct gsm_subscriber *s1, *s2, *s3, *sfound;
const char *imsi1 = "1234567890";
const char *imsi2 = "9876543210";
const char *imsi3 = "5656565656";
int saved_expiry_timeout = sgsn->cfg.subscriber_expiry_timeout;
update_subscriber_data_cb = my_dummy_sgsn_update_subscriber_data;
@ -211,6 +214,7 @@ static void test_subscriber(void)
/* Check for emptiness */
OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL);
OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi2) == NULL);
OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi3) == NULL);
/* Allocate entry 1 */
s1 = gprs_subscr_get_or_create(imsi1);
@ -222,9 +226,13 @@ static void test_subscriber(void)
s2 = gprs_subscr_get_or_create(imsi2);
s2->flags |= GSM_SUBSCRIBER_FIRST_CONTACT;
/* Allocate entry 3 */
s3 = gprs_subscr_get_or_create(imsi3);
/* Check entries */
assert_subscr(s1, imsi1);
assert_subscr(s2, imsi2);
assert_subscr(s3, imsi3);
/* Update entry 1 */
last_updated_subscr = NULL;
@ -251,12 +259,34 @@ static void test_subscriber(void)
s1 = NULL;
OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL);
assert_subscr(s2, imsi2);
assert_subscr(s3, imsi3);
/* Free entry 2 (GSM_SUBSCRIBER_FIRST_CONTACT is set) */
gprs_subscr_delete(s2);
s2 = NULL;
OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL);
OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi2) == NULL);
assert_subscr(s3, imsi3);
/* Try to delete entry 3 */
OSMO_ASSERT(sgsn->cfg.subscriber_expiry_timeout == SGSN_TIMEOUT_NEVER);
gprs_subscr_delete(s3);
assert_subscr(s3, imsi3);
/* Process timeouts, this shouldn't delete s3 (SGSN_TIMEOUT_NEVER) */
osmo_timers_update();
assert_subscr(s3, imsi3);
s3 = subscr_get(s3);
/* Free entry 3 (TIMEOUT == 0) */
sgsn->cfg.subscriber_expiry_timeout = 0;
gprs_subscr_delete(s3);
assert_subscr(s3, imsi3);
/* Process timeouts, this should delete s3 */
osmo_timers_update();
OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL);
OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi2) == NULL);
OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi3) == NULL);
sgsn->cfg.subscriber_expiry_timeout = saved_expiry_timeout;
OSMO_ASSERT(llist_empty(&active_subscribers));
@ -862,6 +892,7 @@ static void test_gmm_attach_subscr(void)
subscr = gprs_subscr_get_or_create("123456789012345");
subscr->authorized = 1;
subscr->keep_in_ram = 1;
subscr_put(subscr);
printf("Auth policy 'remote': ");
@ -895,6 +926,7 @@ static void test_gmm_attach_subscr_fake_auth(void)
subscr = gprs_subscr_get_or_create("123456789012345");
subscr->authorized = 1;
subscr->keep_in_ram = 1;
sgsn->cfg.require_authentication = 1;
sgsn->cfg.require_update_location = 1;
subscr_put(subscr);
@ -936,6 +968,7 @@ static void test_gmm_attach_subscr_real_auth(void)
subscr = gprs_subscr_get_or_create("123456789012345");
subscr->authorized = 1;
subscr->keep_in_ram = 1;
sgsn->cfg.require_authentication = 1;
sgsn->cfg.require_update_location = 1;
subscr_put(subscr);