aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Willmann <dwillmann@sysmocom.de>2020-12-29 21:13:31 +0100
committerDaniel Willmann <dwillmann@sysmocom.de>2021-01-11 15:09:05 +0100
commit02b24c5a44f5b6869833fd4ff6ebd9ded1b9a3aa (patch)
treeae05ddfff2140d73169c33fc73faadd81b7bf66b
parentcd7c7a74b7089925beb167e53ee3150f122ae52e (diff)
gbproxy: Implement TLLI cache and use it for SUSPEND/RESUME
When routing a SUSPEND/RESUME we need to keep track of where it came from so we can send the (N)ACK back to the correct BSS. Use the TLLI which is present in both messages to cache and retrieve the correct BSS. A timer runs every two seconds and expires entries that are older than the timeout (hardcoded to 5 seconds for now). Related: SYS#4865, OS#4472 Change-Id: I42adf70f560d2bb358a9e1c7614281e8d2967568
-rw-r--r--include/osmocom/sgsn/gb_proxy.h27
-rw-r--r--src/gbproxy/gb_proxy.c56
-rw-r--r--src/gbproxy/gb_proxy_peer.c102
3 files changed, 175 insertions, 10 deletions
diff --git a/include/osmocom/sgsn/gb_proxy.h b/include/osmocom/sgsn/gb_proxy.h
index ad5bb27f3..04a3c4b49 100644
--- a/include/osmocom/sgsn/gb_proxy.h
+++ b/include/osmocom/sgsn/gb_proxy.h
@@ -75,6 +75,14 @@ struct gbproxy_config {
/* hash table of all gbproxy_cell */
DECLARE_HASHTABLE(cells, 8);
+ /* tlli<->nse cache used to map SUSPEND/RESUME (N)ACKS */
+ struct {
+ DECLARE_HASHTABLE(entries, 10);
+ struct osmo_timer_list timer;
+ /* Time in seconds that the entries should be valid */
+ uint8_t timeout;
+ } tlli_cache;
+
/* List of all SGSNs */
struct llist_head sgsns;
@@ -163,6 +171,19 @@ struct gbproxy_sgsn {
} pool;
};
+/* TLLI cache */
+struct gbproxy_tlli_cache_entry {
+ /* linked to gbproxy_config.tlli_cache */
+ struct hlist_node list;
+
+ /* TLLI of the entry */
+ uint32_t tlli;
+ /* When was this entry last seen */
+ time_t tstamp;
+ /* The Cell this TLLI was last seen */
+ 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, \
@@ -229,6 +250,12 @@ struct gbproxy_nse *gbproxy_nse_alloc(struct gbproxy_config *cfg, uint16_t nsei,
void gbproxy_nse_free(struct gbproxy_nse *nse);
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);
+
+/* 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);
/* SGSN handling */
struct gbproxy_sgsn *gbproxy_sgsn_alloc(struct gbproxy_config *cfg, uint16_t nsei, const char *name);
diff --git a/src/gbproxy/gb_proxy.c b/src/gbproxy/gb_proxy.c
index 34cff31c1..91324ddf3 100644
--- a/src/gbproxy/gb_proxy.c
+++ b/src/gbproxy/gb_proxy.c
@@ -890,11 +890,20 @@ static int gbprox_rx_sig_from_bss(struct gbproxy_nse *nse, struct msgb *msg, uin
return osmo_fsm_inst_dispatch(from_bvc->fi, BSSGP_BVCFSM_E_RX_UNBLOCK, msg);
case BSSGP_PDUT_SUSPEND:
case BSSGP_PDUT_RESUME:
- /* FIXME: Implement TLLI Cache. Every SUSPEND/RESUME we must
- * take record of the TLLI->BVC mapping so we can map
- * back from TLLI->BVC when the SUSPEND/RESUME-ACK
- * arrives. Cache should have a timeout of 1-3 seconds
- * and the ACK should explicitly delete entries. */
+ {
+ struct gbproxy_sgsn *sgsn;
+
+ tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
+ sgsn = gbproxy_select_sgsn(nse->cfg, &tlli);
+ if (!sgsn) {
+ LOGP(DGPRS, LOGL_ERROR, "Could not find any SGSN for TLLI, dropping message!\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ gbproxy_tlli_cache_update(nse, tlli);
+
+ rc = gbprox_relay2nse(msg, sgsn->nse, 0);
#if 0
/* TODO: Validate the RAI for consistency with the RAI
* we expect for any of the BVC within this BSS side NSE */
@@ -902,6 +911,7 @@ static int gbprox_rx_sig_from_bss(struct gbproxy_nse *nse, struct msgb *msg, uin
gsm48_parse_ra(&raid, from_bvc->ra);
#endif
break;
+ }
case BSSGP_PDUT_STATUS:
/* FIXME: inspect the erroneous PDU IE (if any) and check
* if we can extract a TLLI/RNI to route it to the correct SGSN */
@@ -1149,11 +1159,22 @@ static int gbprox_rx_sig_from_sgsn(struct gbproxy_nse *nse, struct msgb *msg, ui
case BSSGP_PDUT_SUSPEND_NACK:
case BSSGP_PDUT_RESUME_ACK:
case BSSGP_PDUT_RESUME_NACK:
- /* FIXME: handle based on TLLI cache. The RA-ID is not a unique
- * criterion, so we have to rely on the TLLI->BVC state created
- * while processing the SUSPEND/RESUME in uplink */
- /* FIXME: route to SGSN baed on NRI derived from TLLI */
+ {
+ struct gbproxy_nse *nse_peer;
+ uint32_t tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI));
+
+ nse_peer = gbproxy_nse_by_tlli(cfg, tlli);
+ if (!nse_peer) {
+ LOGPNSE(nse, LOGL_ERROR, "Rx %s: Cannot find NSE\n", pdut_name);
+ /* TODO: Counter */
+ return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
+ }
+ /* Delete the entry after we're done */
+ gbproxy_tlli_cache_remove(cfg, tlli);
+ LOGPNSE(nse_peer, LOGL_DEBUG, "Rx %s: forwarding\n", pdut_name);
+ gbprox_relay2nse(msg, nse_peer, ns_bvci);
break;
+ }
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
case BSSGP_PDUT_OVERLOAD:
LOGPNSE(nse, LOGL_DEBUG, "Rx %s: broadcasting\n", pdut_name);
@@ -1375,6 +1396,15 @@ void gbprox_reset(struct gbproxy_config *cfg)
gbproxy_init_config(cfg);
}
+static void tlli_cache_cleanup(void *data)
+{
+ struct gbproxy_config *cfg = data;
+ gbproxy_tlli_cache_cleanup(cfg);
+
+ /* TODO: Disable timer when cache is empty */
+ osmo_timer_schedule(&cfg->tlli_cache.timer, 2, 0);
+}
+
int gbproxy_init_config(struct gbproxy_config *cfg)
{
struct timespec tp;
@@ -1382,12 +1412,18 @@ int gbproxy_init_config(struct gbproxy_config *cfg)
/* by default we advertise 100% of the BSS-side capacity to _each_ SGSN */
cfg->pool.bvc_fc_ratio = 100;
cfg->pool.null_nri_ranges = osmo_nri_ranges_alloc(cfg);
+ /* TODO: Make configurable */
+ cfg->tlli_cache.timeout = 5;
hash_init(cfg->bss_nses);
hash_init(cfg->sgsn_nses);
hash_init(cfg->cells);
+ hash_init(cfg->tlli_cache.entries);
INIT_LLIST_HEAD(&cfg->sgsns);
+ osmo_timer_setup(&cfg->tlli_cache.timer, tlli_cache_cleanup, cfg);
+ osmo_timer_schedule(&cfg->tlli_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");
@@ -1397,4 +1433,4 @@ int gbproxy_init_config(struct gbproxy_config *cfg)
osmo_fsm_log_timeouts(true);
return 0;
-}
+} \ No newline at end of file
diff --git a/src/gbproxy/gb_proxy_peer.c b/src/gbproxy/gb_proxy_peer.c
index d2ddfc17e..104902bba 100644
--- a/src/gbproxy/gb_proxy_peer.c
+++ b/src/gbproxy/gb_proxy_peer.c
@@ -192,6 +192,17 @@ struct gbproxy_cell *gbproxy_cell_by_bvci(struct gbproxy_config *cfg, uint16_t b
return NULL;
}
+static inline struct gbproxy_tlli_cache_entry *_get_tlli_entry(struct gbproxy_config *cfg, uint32_t tlli)
+{
+ struct gbproxy_tlli_cache_entry *cache_entry;
+
+ hash_for_each_possible(cfg->tlli_cache.entries, cache_entry, list, tlli) {
+ if (cache_entry->tlli == tlli)
+ return cache_entry;
+ }
+ return NULL;
+}
+
struct gbproxy_cell *gbproxy_cell_by_bvci_or_new(struct gbproxy_config *cfg, uint16_t bvci)
{
struct gbproxy_cell *cell;
@@ -245,6 +256,83 @@ bool gbproxy_cell_add_sgsn_bvc(struct gbproxy_cell *cell, struct gbproxy_bvc *bv
return false;
}
+
+/***********************************************************************
+ * TLLI cache
+ ***********************************************************************/
+
+void gbproxy_tlli_cache_update(struct gbproxy_nse *nse, uint32_t tlli)
+{
+ struct gbproxy_config *cfg = nse->cfg;
+ struct timespec now;
+ struct gbproxy_tlli_cache_entry *cache_entry = _get_tlli_entry(cfg, tlli);
+
+ 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_tlli_cache_entry);
+ cache_entry->tlli = tlli;
+ cache_entry->nse = nse;
+ cache_entry->tstamp = now.tv_sec;
+ hash_add(cfg->tlli_cache.entries, &cache_entry->list, cache_entry->tlli);
+}
+
+static void _tlli_cache_remove_nse(struct gbproxy_nse *nse) {
+ uint i;
+ struct gbproxy_config *cfg = nse->cfg;
+ struct gbproxy_tlli_cache_entry *tlli_cache;
+ struct hlist_node *tmp;
+
+ hash_for_each_safe(cfg->tlli_cache.entries, i, tmp, tlli_cache, list) {
+ if (tlli_cache->nse == nse) {
+ hash_del(&tlli_cache->list);
+ talloc_free(tlli_cache);
+ }
+ }
+}
+
+void gbproxy_tlli_cache_remove(struct gbproxy_config *cfg, uint32_t tlli)
+{
+ struct gbproxy_tlli_cache_entry *tlli_cache;
+ struct hlist_node *tmp;
+
+ hash_for_each_possible_safe(cfg->tlli_cache.entries, tlli_cache, tmp, list, tlli) {
+ if (tlli_cache->tlli == tlli) {
+ hash_del(&tlli_cache->list);
+ talloc_free(tlli_cache);
+ return;
+ }
+ }
+}
+
+int gbproxy_tlli_cache_cleanup(struct gbproxy_config *cfg)
+{
+ int i, count = 0;
+ struct gbproxy_tlli_cache_entry *tlli_cache;
+ struct hlist_node *tmp;
+ struct timespec now;
+ time_t expiry;
+
+ osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+ expiry = now.tv_sec - cfg->tlli_cache.timeout;
+
+ hash_for_each_safe(cfg->tlli_cache.entries, i, tmp, tlli_cache, list) {
+ if (tlli_cache->tstamp < expiry) {
+ count++;
+ LOGP(DGPRS, LOGL_NOTICE, "Cache entry for TLLI %08x expired, removing\n", tlli_cache->tlli);
+ hash_del(&tlli_cache->list);
+ talloc_free(tlli_cache);
+ }
+ }
+ return count;
+}
+
/***********************************************************************
* NSE - NS Entity
***********************************************************************/
@@ -286,6 +374,8 @@ 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 */
+ _tlli_cache_remove_nse(nse);
hash_for_each_safe(nse->bvcs, i, tmp, bvc, list)
gbproxy_bvc_free(bvc);
@@ -343,6 +433,18 @@ struct gbproxy_nse *gbproxy_nse_by_nsei_or_new(struct gbproxy_config *cfg, uint1
return nse;
}
+struct gbproxy_nse *gbproxy_nse_by_tlli(struct gbproxy_config *cfg, uint32_t tlli)
+{
+ struct gbproxy_tlli_cache_entry *tlli_cache;
+
+ hash_for_each_possible(cfg->tlli_cache.entries, tlli_cache, list, tlli) {
+ if (tlli_cache->tlli == tlli)
+ return tlli_cache->nse;
+ }
+ return NULL;
+}
+
+
/***********************************************************************
* SGSN - Serving GPRS Support Node
***********************************************************************/