<p>daniel <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-sgsn/+/22251">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, but someone else must approve
  lynxis lazus: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">gbproxy: Implement IMSI cache<br><br>When SGSN pooling is enabled we need to route some responses based on<br>IMSI back to the correct SGSN, e.g. PAGING_PS_REJECT.<br><br>The IMSI cache keeps track of this IMSI <-> NSE(SGSN) mapping.<br><br>Change-Id: If0a8d6cc1d63f2fb2c395cc5d4373a915bc2cb87<br>Related: OS#4951, OS#4472<br>---<br>M include/osmocom/sgsn/gb_proxy.h<br>M src/gbproxy/gb_proxy.c<br>M src/gbproxy/gb_proxy_peer.c<br>3 files changed, 153 insertions(+), 2 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/sgsn/gb_proxy.h b/include/osmocom/sgsn/gb_proxy.h</span><br><span>index 04a3c4b..d988cae 100644</span><br><span>--- a/include/osmocom/sgsn/gb_proxy.h</span><br><span>+++ b/include/osmocom/sgsn/gb_proxy.h</span><br><span>@@ -8,6 +8,7 @@</span><br><span> #include <osmocom/core/hashtable.h></span><br><span> #include <osmocom/gsm/gsm23003.h></span><br><span> #include <osmocom/gsm/gsm23236.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/protocol/gsm_23_003.h></span><br><span> </span><br><span> #include <osmocom/gprs/gprs_ns2.h></span><br><span> #include <osmocom/vty/command.h></span><br><span>@@ -83,6 +84,14 @@</span><br><span>              uint8_t timeout;</span><br><span>     } tlli_cache;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+     /* imsi<->nse cache used for PAGING REJECT */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct {</span><br><span style="color: hsl(120, 100%, 40%);">+              DECLARE_HASHTABLE(entries, 10);</span><br><span style="color: hsl(120, 100%, 40%);">+               struct osmo_timer_list timer;</span><br><span style="color: hsl(120, 100%, 40%);">+         /* Time in seconds that the entries should be valid */</span><br><span style="color: hsl(120, 100%, 40%);">+                uint8_t timeout;</span><br><span style="color: hsl(120, 100%, 40%);">+      } imsi_cache;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      /* List of all SGSNs */</span><br><span>      struct llist_head sgsns;</span><br><span> </span><br><span>@@ -173,7 +182,7 @@</span><br><span> </span><br><span> /* TLLI cache */</span><br><span> struct gbproxy_tlli_cache_entry {</span><br><span style="color: hsl(0, 100%, 40%);">- /* linked to gbproxy_config.tlli_cache */</span><br><span style="color: hsl(120, 100%, 40%);">+     /* linked to gbproxy_config.tlli_cache.entries */</span><br><span>    struct hlist_node list;</span><br><span> </span><br><span>  /* TLLI of the entry */</span><br><span>@@ -184,6 +193,19 @@</span><br><span>       struct gbproxy_nse *nse;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* IMSI cache */</span><br><span style="color: hsl(120, 100%, 40%);">+struct gbproxy_imsi_cache_entry {</span><br><span style="color: hsl(120, 100%, 40%);">+       /* linked to gbproxy_config.imsi_cache.entries */</span><br><span style="color: hsl(120, 100%, 40%);">+     struct hlist_node list;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* IMSI of the entry */</span><br><span style="color: hsl(120, 100%, 40%);">+       char imsi[OSMO_IMSI_BUF_SIZE];</span><br><span style="color: hsl(120, 100%, 40%);">+        /* When was this entry last seen */</span><br><span style="color: hsl(120, 100%, 40%);">+   time_t tstamp;</span><br><span style="color: hsl(120, 100%, 40%);">+        /* The SGSN where the request came from */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct gbproxy_nse *nse;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Convenience logging macros for NSE/BVC */</span><br><span> #define LOGPNSE_CAT(NSE, SUBSYS, LEVEL, FMT, ARGS...) \</span><br><span>    LOGP(SUBSYS, LEVEL, "NSE(%05u/%s) " FMT, (NSE)->nsei, \</span><br><span>@@ -251,12 +273,18 @@</span><br><span> struct gbproxy_nse *gbproxy_nse_by_nsei(struct gbproxy_config *cfg, uint16_t nsei, uint32_t flags);</span><br><span> struct gbproxy_nse *gbproxy_nse_by_nsei_or_new(struct gbproxy_config *cfg, uint16_t nsei, bool sgsn_facing);</span><br><span> struct gbproxy_nse *gbproxy_nse_by_tlli(struct gbproxy_config *cfg, uint32_t tlli);</span><br><span style="color: hsl(120, 100%, 40%);">+struct gbproxy_nse *gbproxy_nse_by_imsi(struct gbproxy_config *cfg, const char *imsi);</span><br><span> </span><br><span> /* TLLI cache */</span><br><span> void gbproxy_tlli_cache_update(struct gbproxy_nse *nse, uint32_t tlli);</span><br><span> void gbproxy_tlli_cache_remove(struct gbproxy_config *cfg, uint32_t tlli);</span><br><span> int gbproxy_tlli_cache_cleanup(struct gbproxy_config *cfg);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* IMSI cache */</span><br><span style="color: hsl(120, 100%, 40%);">+void gbproxy_imsi_cache_update(struct gbproxy_nse *nse, const char *imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+void gbproxy_imsi_cache_remove(struct gbproxy_config *cfg, const char *imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+int gbproxy_imsi_cache_cleanup(struct gbproxy_config *cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* SGSN handling */</span><br><span> struct gbproxy_sgsn *gbproxy_sgsn_alloc(struct gbproxy_config *cfg, uint16_t nsei, const char *name);</span><br><span> void gbproxy_sgsn_free(struct gbproxy_sgsn *sgsn);</span><br><span>diff --git a/src/gbproxy/gb_proxy.c b/src/gbproxy/gb_proxy.c</span><br><span>index 7f8260c..d8bca58 100644</span><br><span>--- a/src/gbproxy/gb_proxy.c</span><br><span>+++ b/src/gbproxy/gb_proxy.c</span><br><span>@@ -1405,6 +1405,15 @@</span><br><span>       osmo_timer_schedule(&cfg->tlli_cache.timer, 2, 0);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void imsi_cache_cleanup(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gbproxy_config *cfg = data;</span><br><span style="color: hsl(120, 100%, 40%);">+    gbproxy_imsi_cache_cleanup(cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* TODO: Disable timer when cache is empty */</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_timer_schedule(&cfg->imsi_cache.timer, 2, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int gbproxy_init_config(struct gbproxy_config *cfg)</span><br><span> {</span><br><span>   struct timespec tp;</span><br><span>@@ -1414,6 +1423,7 @@</span><br><span>  cfg->pool.null_nri_ranges = osmo_nri_ranges_alloc(cfg);</span><br><span>   /* TODO: Make configurable */</span><br><span>        cfg->tlli_cache.timeout = 10;</span><br><span style="color: hsl(120, 100%, 40%);">+      cfg->imsi_cache.timeout = 10;</span><br><span> </span><br><span>         hash_init(cfg->bss_nses);</span><br><span>         hash_init(cfg->sgsn_nses);</span><br><span>@@ -1424,6 +1434,10 @@</span><br><span>       osmo_timer_setup(&cfg->tlli_cache.timer, tlli_cache_cleanup, cfg);</span><br><span>    osmo_timer_schedule(&cfg->tlli_cache.timer, 2, 0);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* We could also combine both timers */</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_timer_setup(&cfg->imsi_cache.timer, imsi_cache_cleanup, cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_timer_schedule(&cfg->imsi_cache.timer, 2, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  cfg->ctrg = rate_ctr_group_alloc(tall_sgsn_ctx, &global_ctrg_desc, 0);</span><br><span>        if (!cfg->ctrg) {</span><br><span>                 LOGP(DGPRS, LOGL_ERROR, "Cannot allocate global counter group!\n");</span><br><span>diff --git a/src/gbproxy/gb_proxy_peer.c b/src/gbproxy/gb_proxy_peer.c</span><br><span>index 104902b..1d9352d 100644</span><br><span>--- a/src/gbproxy/gb_proxy_peer.c</span><br><span>+++ b/src/gbproxy/gb_proxy_peer.c</span><br><span>@@ -25,11 +25,13 @@</span><br><span> #include <osmocom/sgsn/debug.h></span><br><span> </span><br><span> #include <osmocom/gprs/protocol/gsm_08_18.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/crc16.h></span><br><span> #include <osmocom/core/logging.h></span><br><span> #include <osmocom/core/linuxlist.h></span><br><span> #include <osmocom/core/rate_ctr.h></span><br><span> #include <osmocom/core/stats.h></span><br><span> #include <osmocom/core/talloc.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span> #include <osmocom/gsm/tlv.h></span><br><span> </span><br><span> #include <string.h></span><br><span>@@ -331,6 +333,101 @@</span><br><span>          }</span><br><span>    }</span><br><span>    return count;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+/***********************************************************************</span><br><span style="color: hsl(120, 100%, 40%);">+ * IMSI cache</span><br><span style="color: hsl(120, 100%, 40%);">+ ***********************************************************************/</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint16_t _checksum_imsi(const char *imsi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    size_t len = strlen(imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+    return osmo_crc16(0, (const uint8_t *)imsi, len);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static inline struct gbproxy_imsi_cache_entry *_get_imsi_entry(struct gbproxy_config *cfg, const char *imsi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gbproxy_imsi_cache_entry *cache_entry;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t imsi_hash = _checksum_imsi(imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  hash_for_each_possible(cfg->imsi_cache.entries, cache_entry, list, imsi_hash) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!strncmp(cache_entry->imsi, imsi, sizeof(cache_entry->imsi)))</span><br><span style="color: hsl(120, 100%, 40%);">+                       return cache_entry;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void gbproxy_imsi_cache_update(struct gbproxy_nse *nse, const char *imsi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gbproxy_config *cfg = nse->cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct timespec now;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gbproxy_imsi_cache_entry *cache_entry = _get_imsi_entry(cfg, imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+    uint16_t imsi_hash = _checksum_imsi(imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_clock_gettime(CLOCK_MONOTONIC, &now);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (cache_entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* Update the entry if it already exists */</span><br><span style="color: hsl(120, 100%, 40%);">+           cache_entry->nse = nse;</span><br><span style="color: hsl(120, 100%, 40%);">+            cache_entry->tstamp = now.tv_sec;</span><br><span style="color: hsl(120, 100%, 40%);">+          return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   cache_entry = talloc_zero(cfg, struct gbproxy_imsi_cache_entry);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_STRLCPY_ARRAY(cache_entry->imsi, imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+       cache_entry->nse = nse;</span><br><span style="color: hsl(120, 100%, 40%);">+    cache_entry->tstamp = now.tv_sec;</span><br><span style="color: hsl(120, 100%, 40%);">+  hash_add(cfg->imsi_cache.entries, &cache_entry->list, imsi_hash);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void _imsi_cache_remove_nse(struct gbproxy_nse *nse) {</span><br><span style="color: hsl(120, 100%, 40%);">+       uint i;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gbproxy_config *cfg = nse->cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gbproxy_imsi_cache_entry *imsi_cache;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct hlist_node *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     hash_for_each_safe(cfg->imsi_cache.entries, i, tmp, imsi_cache, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (imsi_cache->nse == nse) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      hash_del(&imsi_cache->list);</span><br><span style="color: hsl(120, 100%, 40%);">+                   talloc_free(imsi_cache);</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void gbproxy_imsi_cache_remove(struct gbproxy_config *cfg, const char *imsi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gbproxy_imsi_cache_entry *imsi_cache;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct hlist_node *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint16_t imsi_hash = _checksum_imsi(imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  hash_for_each_possible_safe(cfg->imsi_cache.entries, imsi_cache, tmp, list, imsi_hash) {</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!(strncmp(imsi_cache->imsi, imsi, sizeof(imsi_cache->imsi)))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     hash_del(&imsi_cache->list);</span><br><span style="color: hsl(120, 100%, 40%);">+                   talloc_free(imsi_cache);</span><br><span style="color: hsl(120, 100%, 40%);">+                      return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int gbproxy_imsi_cache_cleanup(struct gbproxy_config *cfg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int i, count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gbproxy_imsi_cache_entry *imsi_cache;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct hlist_node *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct timespec now;</span><br><span style="color: hsl(120, 100%, 40%);">+  time_t expiry;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_clock_gettime(CLOCK_MONOTONIC, &now);</span><br><span style="color: hsl(120, 100%, 40%);">+        expiry = now.tv_sec - cfg->imsi_cache.timeout;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   hash_for_each_safe(cfg->imsi_cache.entries, i, tmp, imsi_cache, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (imsi_cache->tstamp < expiry) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      count++;</span><br><span style="color: hsl(120, 100%, 40%);">+                      LOGP(DGPRS, LOGL_NOTICE, "Cache entry for IMSI %s expired, removing\n", imsi_cache->imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+                       hash_del(&imsi_cache->list);</span><br><span style="color: hsl(120, 100%, 40%);">+                   talloc_free(imsi_cache);</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return count;</span><br><span> }</span><br><span> </span><br><span> /***********************************************************************</span><br><span>@@ -374,8 +471,9 @@</span><br><span>     LOGPNSE_CAT(nse, DOBJ, LOGL_INFO, "NSE Destroying\n");</span><br><span> </span><br><span>         hash_del(&nse->list);</span><br><span style="color: hsl(0, 100%, 40%);">-    /* Clear the tlli_cache from this NSE */</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Clear the cache entries of this NSE */</span><br><span>    _tlli_cache_remove_nse(nse);</span><br><span style="color: hsl(120, 100%, 40%);">+  _imsi_cache_remove_nse(nse);</span><br><span> </span><br><span>     hash_for_each_safe(nse->bvcs, i, tmp, bvc, list)</span><br><span>          gbproxy_bvc_free(bvc);</span><br><span>@@ -444,6 +542,17 @@</span><br><span>        return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct gbproxy_nse *gbproxy_nse_by_imsi(struct gbproxy_config *cfg, const char *imsi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gbproxy_imsi_cache_entry *imsi_cache;</span><br><span style="color: hsl(120, 100%, 40%);">+  uint16_t imsi_hash = _checksum_imsi(imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  hash_for_each_possible(cfg->imsi_cache.entries, imsi_cache, list, imsi_hash) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!strncmp(imsi_cache->imsi, imsi, sizeof(imsi_cache->imsi)))</span><br><span style="color: hsl(120, 100%, 40%);">+                 return imsi_cache->nse;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span> </span><br><span> /***********************************************************************</span><br><span>  * SGSN - Serving GPRS Support Node</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-sgsn/+/22251">change 22251</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-sgsn/+/22251"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-sgsn </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: If0a8d6cc1d63f2fb2c395cc5d4373a915bc2cb87 </div>
<div style="display:none"> Gerrit-Change-Number: 22251 </div>
<div style="display:none"> Gerrit-PatchSet: 2 </div>
<div style="display:none"> Gerrit-Owner: daniel <dwillmann@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: daniel <dwillmann@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: lynxis lazus <lynxis@fe80.eu> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>