diff options
-rw-r--r-- | include/osmocom/sgsn/gb_proxy.h | 27 | ||||
-rw-r--r-- | src/gbproxy/gb_proxy.c | 56 | ||||
-rw-r--r-- | src/gbproxy/gb_proxy_peer.c | 102 |
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 ***********************************************************************/ |