aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
***********************************************************************/