aboutsummaryrefslogtreecommitdiffstats
path: root/src
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 /src
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
Diffstat (limited to 'src')
-rw-r--r--src/gbproxy/gb_proxy.c56
-rw-r--r--src/gbproxy/gb_proxy_peer.c102
2 files changed, 148 insertions, 10 deletions
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
***********************************************************************/