Change in osmo-sgsn[master]: gbproxy: Implement IMSI cache

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

daniel gerrit-no-reply at lists.osmocom.org
Mon Jan 18 13:07:13 UTC 2021


daniel has submitted this change. ( https://gerrit.osmocom.org/c/osmo-sgsn/+/22251 )

Change subject: gbproxy: Implement IMSI cache
......................................................................

gbproxy: Implement IMSI cache

When SGSN pooling is enabled we need to route some responses based on
IMSI back to the correct SGSN, e.g. PAGING_PS_REJECT.

The IMSI cache keeps track of this IMSI <-> NSE(SGSN) mapping.

Change-Id: If0a8d6cc1d63f2fb2c395cc5d4373a915bc2cb87
Related: OS#4951, OS#4472
---
M include/osmocom/sgsn/gb_proxy.h
M src/gbproxy/gb_proxy.c
M src/gbproxy/gb_proxy_peer.c
3 files changed, 153 insertions(+), 2 deletions(-)

Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, but someone else must approve
  lynxis lazus: Looks good to me, approved



diff --git a/include/osmocom/sgsn/gb_proxy.h b/include/osmocom/sgsn/gb_proxy.h
index 04a3c4b..d988cae 100644
--- a/include/osmocom/sgsn/gb_proxy.h
+++ b/include/osmocom/sgsn/gb_proxy.h
@@ -8,6 +8,7 @@
 #include <osmocom/core/hashtable.h>
 #include <osmocom/gsm/gsm23003.h>
 #include <osmocom/gsm/gsm23236.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
 
 #include <osmocom/gprs/gprs_ns2.h>
 #include <osmocom/vty/command.h>
@@ -83,6 +84,14 @@
 		uint8_t timeout;
 	} tlli_cache;
 
+	/* imsi<->nse cache used for PAGING REJECT */
+	struct {
+		DECLARE_HASHTABLE(entries, 10);
+		struct osmo_timer_list timer;
+		/* Time in seconds that the entries should be valid */
+		uint8_t timeout;
+	} imsi_cache;
+
 	/* List of all SGSNs */
 	struct llist_head sgsns;
 
@@ -173,7 +182,7 @@
 
 /* TLLI cache */
 struct gbproxy_tlli_cache_entry {
-	/* linked to gbproxy_config.tlli_cache */
+	/* linked to gbproxy_config.tlli_cache.entries */
 	struct hlist_node list;
 
 	/* TLLI of the entry */
@@ -184,6 +193,19 @@
 	struct gbproxy_nse *nse;
 };
 
+/* IMSI cache */
+struct gbproxy_imsi_cache_entry {
+	/* linked to gbproxy_config.imsi_cache.entries */
+	struct hlist_node list;
+
+	/* IMSI of the entry */
+	char imsi[OSMO_IMSI_BUF_SIZE];
+	/* When was this entry last seen */
+	time_t tstamp;
+	/* The SGSN where the request came from */
+	struct gbproxy_nse *nse;
+};
+
 /* Convenience logging macros for NSE/BVC */
 #define LOGPNSE_CAT(NSE, SUBSYS, LEVEL, FMT, ARGS...) \
 	LOGP(SUBSYS, LEVEL, "NSE(%05u/%s) " FMT, (NSE)->nsei, \
@@ -251,12 +273,18 @@
 struct gbproxy_nse *gbproxy_nse_by_nsei(struct gbproxy_config *cfg, uint16_t nsei, uint32_t flags);
 struct gbproxy_nse *gbproxy_nse_by_nsei_or_new(struct gbproxy_config *cfg, uint16_t nsei, bool sgsn_facing);
 struct gbproxy_nse *gbproxy_nse_by_tlli(struct gbproxy_config *cfg, uint32_t tlli);
+struct gbproxy_nse *gbproxy_nse_by_imsi(struct gbproxy_config *cfg, const char *imsi);
 
 /* TLLI cache */
 void gbproxy_tlli_cache_update(struct gbproxy_nse *nse, uint32_t tlli);
 void gbproxy_tlli_cache_remove(struct gbproxy_config *cfg, uint32_t tlli);
 int gbproxy_tlli_cache_cleanup(struct gbproxy_config *cfg);
 
+/* IMSI cache */
+void gbproxy_imsi_cache_update(struct gbproxy_nse *nse, const char *imsi);
+void gbproxy_imsi_cache_remove(struct gbproxy_config *cfg, const char *imsi);
+int gbproxy_imsi_cache_cleanup(struct gbproxy_config *cfg);
+
 /* SGSN handling */
 struct gbproxy_sgsn *gbproxy_sgsn_alloc(struct gbproxy_config *cfg, uint16_t nsei, const char *name);
 void gbproxy_sgsn_free(struct gbproxy_sgsn *sgsn);
diff --git a/src/gbproxy/gb_proxy.c b/src/gbproxy/gb_proxy.c
index 7f8260c..d8bca58 100644
--- a/src/gbproxy/gb_proxy.c
+++ b/src/gbproxy/gb_proxy.c
@@ -1405,6 +1405,15 @@
 	osmo_timer_schedule(&cfg->tlli_cache.timer, 2, 0);
 }
 
+static void imsi_cache_cleanup(void *data)
+{
+	struct gbproxy_config *cfg = data;
+	gbproxy_imsi_cache_cleanup(cfg);
+
+	/* TODO: Disable timer when cache is empty */
+	osmo_timer_schedule(&cfg->imsi_cache.timer, 2, 0);
+}
+
 int gbproxy_init_config(struct gbproxy_config *cfg)
 {
 	struct timespec tp;
@@ -1414,6 +1423,7 @@
 	cfg->pool.null_nri_ranges = osmo_nri_ranges_alloc(cfg);
 	/* TODO: Make configurable */
 	cfg->tlli_cache.timeout = 10;
+	cfg->imsi_cache.timeout = 10;
 
 	hash_init(cfg->bss_nses);
 	hash_init(cfg->sgsn_nses);
@@ -1424,6 +1434,10 @@
 	osmo_timer_setup(&cfg->tlli_cache.timer, tlli_cache_cleanup, cfg);
 	osmo_timer_schedule(&cfg->tlli_cache.timer, 2, 0);
 
+	/* We could also combine both timers */
+	osmo_timer_setup(&cfg->imsi_cache.timer, imsi_cache_cleanup, cfg);
+	osmo_timer_schedule(&cfg->imsi_cache.timer, 2, 0);
+
 	cfg->ctrg = rate_ctr_group_alloc(tall_sgsn_ctx, &global_ctrg_desc, 0);
 	if (!cfg->ctrg) {
 		LOGP(DGPRS, LOGL_ERROR, "Cannot allocate global counter group!\n");
diff --git a/src/gbproxy/gb_proxy_peer.c b/src/gbproxy/gb_proxy_peer.c
index 104902b..1d9352d 100644
--- a/src/gbproxy/gb_proxy_peer.c
+++ b/src/gbproxy/gb_proxy_peer.c
@@ -25,11 +25,13 @@
 #include <osmocom/sgsn/debug.h>
 
 #include <osmocom/gprs/protocol/gsm_08_18.h>
+#include <osmocom/core/crc16.h>
 #include <osmocom/core/logging.h>
 #include <osmocom/core/linuxlist.h>
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/core/stats.h>
 #include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
 #include <osmocom/gsm/tlv.h>
 
 #include <string.h>
@@ -331,6 +333,101 @@
 		}
 	}
 	return count;
+
+}
+/***********************************************************************
+ * IMSI cache
+ ***********************************************************************/
+static inline uint16_t _checksum_imsi(const char *imsi)
+{
+	size_t len = strlen(imsi);
+	return osmo_crc16(0, (const uint8_t *)imsi, len);
+}
+
+static inline struct gbproxy_imsi_cache_entry *_get_imsi_entry(struct gbproxy_config *cfg, const char *imsi)
+{
+	struct gbproxy_imsi_cache_entry *cache_entry;
+	uint16_t imsi_hash = _checksum_imsi(imsi);
+
+	hash_for_each_possible(cfg->imsi_cache.entries, cache_entry, list, imsi_hash) {
+		if (!strncmp(cache_entry->imsi, imsi, sizeof(cache_entry->imsi)))
+			return cache_entry;
+	}
+	return NULL;
+}
+
+void gbproxy_imsi_cache_update(struct gbproxy_nse *nse, const char *imsi)
+{
+	struct gbproxy_config *cfg = nse->cfg;
+	struct timespec now;
+	struct gbproxy_imsi_cache_entry *cache_entry = _get_imsi_entry(cfg, imsi);
+	uint16_t imsi_hash = _checksum_imsi(imsi);
+
+	osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+
+	if (cache_entry) {
+		/* Update the entry if it already exists */
+		cache_entry->nse = nse;
+		cache_entry->tstamp = now.tv_sec;
+		return;
+	}
+
+	cache_entry = talloc_zero(cfg, struct gbproxy_imsi_cache_entry);
+	OSMO_STRLCPY_ARRAY(cache_entry->imsi, imsi);
+	cache_entry->nse = nse;
+	cache_entry->tstamp = now.tv_sec;
+	hash_add(cfg->imsi_cache.entries, &cache_entry->list, imsi_hash);
+}
+
+static void _imsi_cache_remove_nse(struct gbproxy_nse *nse) {
+	uint i;
+	struct gbproxy_config *cfg = nse->cfg;
+	struct gbproxy_imsi_cache_entry *imsi_cache;
+	struct hlist_node *tmp;
+
+	hash_for_each_safe(cfg->imsi_cache.entries, i, tmp, imsi_cache, list) {
+		if (imsi_cache->nse == nse) {
+			hash_del(&imsi_cache->list);
+			talloc_free(imsi_cache);
+		}
+	}
+}
+
+void gbproxy_imsi_cache_remove(struct gbproxy_config *cfg, const char *imsi)
+{
+	struct gbproxy_imsi_cache_entry *imsi_cache;
+	struct hlist_node *tmp;
+	uint16_t imsi_hash = _checksum_imsi(imsi);
+
+	hash_for_each_possible_safe(cfg->imsi_cache.entries, imsi_cache, tmp, list, imsi_hash) {
+		if (!(strncmp(imsi_cache->imsi, imsi, sizeof(imsi_cache->imsi)))) {
+			hash_del(&imsi_cache->list);
+			talloc_free(imsi_cache);
+			return;
+		}
+	}
+}
+
+int gbproxy_imsi_cache_cleanup(struct gbproxy_config *cfg)
+{
+	int i, count = 0;
+	struct gbproxy_imsi_cache_entry *imsi_cache;
+	struct hlist_node *tmp;
+	struct timespec now;
+	time_t expiry;
+
+	osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+	expiry = now.tv_sec - cfg->imsi_cache.timeout;
+
+	hash_for_each_safe(cfg->imsi_cache.entries, i, tmp, imsi_cache, list) {
+		if (imsi_cache->tstamp < expiry) {
+			count++;
+			LOGP(DGPRS, LOGL_NOTICE, "Cache entry for IMSI %s expired, removing\n", imsi_cache->imsi);
+			hash_del(&imsi_cache->list);
+			talloc_free(imsi_cache);
+		}
+	}
+	return count;
 }
 
 /***********************************************************************
@@ -374,8 +471,9 @@
 	LOGPNSE_CAT(nse, DOBJ, LOGL_INFO, "NSE Destroying\n");
 
 	hash_del(&nse->list);
-	/* Clear the tlli_cache from this NSE */
+	/* Clear the cache entries of this NSE */
 	_tlli_cache_remove_nse(nse);
+	_imsi_cache_remove_nse(nse);
 
 	hash_for_each_safe(nse->bvcs, i, tmp, bvc, list)
 		gbproxy_bvc_free(bvc);
@@ -444,6 +542,17 @@
 	return NULL;
 }
 
+struct gbproxy_nse *gbproxy_nse_by_imsi(struct gbproxy_config *cfg, const char *imsi)
+{
+	struct gbproxy_imsi_cache_entry *imsi_cache;
+	uint16_t imsi_hash = _checksum_imsi(imsi);
+
+	hash_for_each_possible(cfg->imsi_cache.entries, imsi_cache, list, imsi_hash) {
+		if (!strncmp(imsi_cache->imsi, imsi, sizeof(imsi_cache->imsi)))
+			return imsi_cache->nse;
+	}
+	return NULL;
+}
 
 /***********************************************************************
  * SGSN - Serving GPRS Support Node

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-sgsn/+/22251
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-sgsn
Gerrit-Branch: master
Gerrit-Change-Id: If0a8d6cc1d63f2fb2c395cc5d4373a915bc2cb87
Gerrit-Change-Number: 22251
Gerrit-PatchSet: 2
Gerrit-Owner: daniel <dwillmann at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <dwillmann at sysmocom.de>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: lynxis lazus <lynxis at fe80.eu>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210118/c25d24b8/attachment.htm>


More information about the gerrit-log mailing list