aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Willmann <dwillmann@sysmocom.de>2021-01-17 13:11:41 +0100
committerDaniel Willmann <dwillmann@sysmocom.de>2021-01-17 13:51:57 +0100
commit914484d561c66e9ecdaf292f6af4d8879cac7b66 (patch)
treeed3a8450eb098786cb1616fb49947a7e37f21d05 /src
parentb6343a72d8804cba0bf8571f9861534bbf584fad (diff)
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
Diffstat (limited to 'src')
-rw-r--r--src/gbproxy/gb_proxy.c14
-rw-r--r--src/gbproxy/gb_proxy_peer.c111
2 files changed, 124 insertions, 1 deletions
diff --git a/src/gbproxy/gb_proxy.c b/src/gbproxy/gb_proxy.c
index 7f8260c8..d8bca582 100644
--- a/src/gbproxy/gb_proxy.c
+++ b/src/gbproxy/gb_proxy.c
@@ -1405,6 +1405,15 @@ static void tlli_cache_cleanup(void *data)
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 @@ int gbproxy_init_config(struct gbproxy_config *cfg)
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 @@ int gbproxy_init_config(struct gbproxy_config *cfg)
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 104902bb..1d9352de 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 @@ int gbproxy_tlli_cache_cleanup(struct gbproxy_config *cfg)
}
}
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 @@ static void _nse_free(struct gbproxy_nse *nse)
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 @@ struct gbproxy_nse *gbproxy_nse_by_tlli(struct gbproxy_config *cfg, uint32_t tll
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