This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
daniel gerrit-no-reply at lists.osmocom.orgdaniel has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-sgsn/+/21881 ) Change subject: gbproxy: Add SGSN pooling support ...................................................................... gbproxy: Add SGSN pooling support Change-Id: I58b9f55065f6bd43450e4b07cffe7ba132b1fd9b Related: OS#4472 --- M include/osmocom/sgsn/gb_proxy.h M src/gbproxy/gb_proxy.c M src/gbproxy/gb_proxy_main.c M src/gbproxy/gb_proxy_peer.c 4 files changed, 202 insertions(+), 36 deletions(-) git pull ssh://gerrit.osmocom.org:29418/osmo-sgsn refs/changes/81/21881/1 diff --git a/include/osmocom/sgsn/gb_proxy.h b/include/osmocom/sgsn/gb_proxy.h index 46decc0..dd529a3 100644 --- a/include/osmocom/sgsn/gb_proxy.h +++ b/include/osmocom/sgsn/gb_proxy.h @@ -60,6 +60,10 @@ /* NRI bitlen and usable NULL-NRI ranges */ uint8_t nri_bitlen; struct osmo_nri_ranges *null_nri_ranges; + + /* Used for testing: If not NULL then this SGSN is returned by + * gbproxy_sgsn_by_tlli() */ + struct gbproxy_sgsn *nsf_override; } pool; /* hash table of all BSS side Gb peers */ @@ -229,5 +233,7 @@ struct gbproxy_sgsn *gbproxy_sgsn_by_nsei(struct gbproxy_config *cfg, uint16_t nsei); struct gbproxy_sgsn *gbproxy_sgsn_by_nsei_or_new(struct gbproxy_config *cfg, uint16_t nsei); struct gbproxy_sgsn *gbproxy_sgsn_by_nri(struct gbproxy_config *cfg, uint16_t nri, bool *null_nri); +struct gbproxy_sgsn *gbproxy_sgsn_by_tlli(struct gbproxy_config *cfg, struct gbproxy_sgsn *sgsn_avoid, + uint32_t tlli); #endif diff --git a/src/gbproxy/gb_proxy.c b/src/gbproxy/gb_proxy.c index 4b6dc09..bce4055 100644 --- a/src/gbproxy/gb_proxy.c +++ b/src/gbproxy/gb_proxy.c @@ -20,6 +20,7 @@ * */ +#include "osmocom/vty/command.h" #include <unistd.h> #include <stdio.h> #include <stdlib.h> @@ -38,6 +39,7 @@ #include <osmocom/core/select.h> #include <osmocom/core/rate_ctr.h> #include <osmocom/core/stats.h> +#include <osmocom/core/utils.h> #include <osmocom/gprs/gprs_ns2.h> #include <osmocom/gprs/gprs_bssgp.h> @@ -45,6 +47,7 @@ #include <osmocom/gprs/gprs_bssgp_bss.h> #include <osmocom/gprs/bssgp_bvc_fsm.h> +#include <osmocom/gsm/gsm23236.h> #include <osmocom/gsm/gsm_utils.h> #include <osmocom/sgsn/signal.h> @@ -201,42 +204,131 @@ * PTP BVC handling ***********************************************************************/ -/* route an uplink message on a PTP-BVC to a SGSN using the TLLI */ -static int gbprox_bss2sgsn_tlli(struct gbproxy_cell *cell, struct msgb *msg, uint32_t tlli, +/* FIXME: Handle the tlli NULL case correctly, + * This function should take a generic selector + * and choose an sgsn based on that + */ +static struct gbproxy_sgsn *gbproxy_select_sgsn(struct gbproxy_config *cfg, const uint32_t *tlli) +{ + struct gbproxy_sgsn *sgsn = NULL; + struct gbproxy_sgsn *sgsn_avoid = NULL; + + int tlli_type; + int16_t nri; + bool null_nri = false; + + if (!tlli) { + sgsn = llist_first_entry(&cfg->sgsns, struct gbproxy_sgsn, list); + if (!sgsn) { + return NULL; + } + LOGPSGSN(sgsn, LOGL_INFO, "Could not get TLLI, using first SGSN\n"); + return sgsn; + } + + if (cfg->pool.nri_bitlen == 0) { + /* Pooling is disabled */ + sgsn = llist_first_entry(&cfg->sgsns, struct gbproxy_sgsn, list); + if (!sgsn) { + return NULL; + } + + LOGPSGSN(sgsn, LOGL_INFO, "Pooling disabled, using first configured SGSN\n"); + } else { + /* Pooling is enabled, try to use the NRI for routing to an SGSN + * See 3GPP TS 23.236 Ch. 5.3.2 */ + tlli_type = gprs_tlli_type(*tlli); + if (tlli_type == TLLI_LOCAL || tlli_type == TLLI_FOREIGN) { + /* Only get/use the NRI if tlli type is local */ + osmo_tmsi_nri_v_get(&nri, *tlli, cfg->pool.nri_bitlen); + if (nri >= 0) { + /* Get the SGSN for the NRI */ + sgsn = gbproxy_sgsn_by_nri(cfg, nri, &null_nri); + if (sgsn && !null_nri) + return sgsn; + /* If the NRI is the null NRI, we need to avoid the chosen SGSN */ + if (null_nri && sgsn) { + sgsn_avoid = sgsn; + } + } else { + /* We couldn't get the NRI from the TLLI */ + LOGP(DGPRS, LOGL_ERROR, "Could not extract NRI from local TLLI %u\n", *tlli); + } + } + } + + /* If we haven't found an SGSN yet we need to choose one, but avoid the one in sgsn_avoid + * NOTE: This function is not stable if the number of SGSNs or allow_attach changes + * We could implement TLLI tracking here, but 3GPP TS 23.236 Ch. 5.3.2 (see NOTE) argues that + * we can just wait for the MS to reattempt the procedure. + */ + if (!sgsn) + sgsn = gbproxy_sgsn_by_tlli(cfg, sgsn_avoid, *tlli); + + if (!sgsn) { + LOGP(DGPRS, LOGL_ERROR, "No suitable SGSN found for TLLI %u\n", *tlli); + return NULL; + } + + return sgsn; +} + +/*! Find the correct gbproxy_bvc given a cell and an SGSN + * \param[in] cfg The gbproxy configuration + * \param[in] cell The cell the message belongs to + * \param[in] tlli An optional TLLI used for tracking + * \return Returns 0 on success, otherwise a negative value + */ +static struct gbproxy_bvc *gbproxy_select_sgsn_bvc(struct gbproxy_config *cfg, struct gbproxy_cell *cell, const uint32_t *tlli) +{ + struct gbproxy_sgsn *sgsn; + struct gbproxy_bvc *sgsn_bvc = NULL; + + sgsn = gbproxy_select_sgsn(cfg, tlli); + if (!sgsn) { + LOGPCELL(cell, LOGL_ERROR, "Could not find any SGSN, dropping message!\n"); + return NULL; + } + + /* Get the BVC for this SGSN/NSE */ + for (int i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) { + sgsn_bvc = cell->sgsn_bvc[i]; + if (!sgsn_bvc) + continue; + if (sgsn->nse != sgsn_bvc->nse) + continue; + + return sgsn_bvc; + } + + /* This shouldn't happen */ + LOGPCELL(cell, LOGL_ERROR, "Could not find matching BVC for SGSN, dropping message!\n"); + return NULL; +} + +/*! Send a message to the next SGSN, possibly ignoring the null SGSN + * route an uplink message on a PTP-BVC to a SGSN using the TLLI + * \param[in] cell The cell the message belongs to + * \param[in] msg The BSSGP message + * \param[in] null_sgsn If not NULL then avoid this SGSN (because this message contains its null NRI) + * \param[in] tlli An optional TLLI used for tracking + * \return Returns 0 on success, otherwise a negative value + */ +static int gbprox_bss2sgsn_tlli(struct gbproxy_cell *cell, struct msgb *msg, const uint32_t *tlli, bool sig_bvci) { + struct gbproxy_config *cfg = cell->cfg; struct gbproxy_bvc *sgsn_bvc; - unsigned int i; - /* FIXME: derive NRI from TLLI */ - /* FIXME: find the SGSN for that NRI */ - - /* HACK: we currently simply pick the first SGSN we find */ - for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) { - sgsn_bvc = cell->sgsn_bvc[i]; - if (sgsn_bvc) - return gbprox_relay2peer(msg, sgsn_bvc, sig_bvci ? 0 : sgsn_bvc->bvci); + sgsn_bvc = gbproxy_select_sgsn_bvc(cfg, cell, tlli); + if (!sgsn_bvc) { + LOGPCELL(cell, LOGL_NOTICE, "Could not find any SGSN for TLLI %u, dropping message!\n", *tlli); + return -EINVAL; } - return 0; + + return gbprox_relay2peer(msg, sgsn_bvc, sig_bvci ? 0 : sgsn_bvc->bvci); } -static int gbprox_bss2sgsn_null_nri(struct gbproxy_cell *cell, struct msgb *msg) -{ - struct gbproxy_bvc *sgsn_bvc; - unsigned int i; - - /* FIXME: find the SGSN for that NRI */ - - /* HACK: we currently simply pick the first SGSN we find */ - for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) { - sgsn_bvc = cell->sgsn_bvc[i]; - if (sgsn_bvc) - return gbprox_relay2peer(msg, sgsn_bvc, sgsn_bvc->bvci); - } - return 0; -} - - /* Receive an incoming PTP message from a BSS-side NS-VC */ static int gbprox_rx_ptp_from_bss(struct gbproxy_nse *nse, struct msgb *msg, uint16_t ns_bvci) { @@ -314,18 +406,20 @@ case BSSGP_PDUT_PS_HO_CANCEL: /* We can route based on TLLI-NRI */ tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI)); - rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, tlli, false); + rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, &tlli, false); break; case BSSGP_PDUT_RADIO_STATUS: if (TLVP_PRESENT(&tp, BSSGP_IE_TLLI)) { tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TLLI)); - rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, tlli, false); + rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, &tlli, false); } else if (TLVP_PRESENT(&tp, BSSGP_IE_TMSI)) { /* we treat the TMSI like a TLLI and extract the NRI from it */ tlli = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TMSI)); - rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, tlli, false); + rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, &tlli, false); } else if (TLVP_PRESENT(&tp, BSSGP_IE_IMSI)) { - rc = gbprox_bss2sgsn_null_nri(bss_bvc->cell, msg); + // FIXME: Use the IMSI as selector? + rc = gbprox_bss2sgsn_tlli(bss_bvc->cell, msg, NULL, false); + //rc = gbprox_bss2sgsn_hashed(bss_bvc->cell, msg, NULL); } else LOGPBVC(bss_bvc, LOGL_ERROR, "Rx RADIO-STATUS without any of the conditional IEs\n"); break; @@ -828,7 +922,7 @@ from_bvc = gbproxy_bvc_by_bvci(nse, ptp_bvci); if (!from_bvc) goto err_no_bvc; - gbprox_bss2sgsn_tlli(from_bvc->cell, msg, tlli, true); + gbprox_bss2sgsn_tlli(from_bvc->cell, msg, &tlli, true); break; default: LOGPNSE(nse, LOGL_ERROR, "Rx %s: Implementation missing\n", pdut_name); @@ -1288,7 +1382,6 @@ /* 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); - hash_init(cfg->bss_nses); hash_init(cfg->sgsn_nses); hash_init(cfg->cells); diff --git a/src/gbproxy/gb_proxy_main.c b/src/gbproxy/gb_proxy_main.c index e85e951..c660ede 100644 --- a/src/gbproxy/gb_proxy_main.c +++ b/src/gbproxy/gb_proxy_main.c @@ -303,6 +303,8 @@ gprs_ns2_vty_create(); + /* TODO: Warn if we create a gbproxy_nse for an NSEI which we don't have a bind */ + /* start telnet after reading config for vty_get_bind_addr() */ rc = telnet_init_dynif(tall_sgsn_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY); diff --git a/src/gbproxy/gb_proxy_peer.c b/src/gbproxy/gb_proxy_peer.c index 863ec50..3b12046 100644 --- a/src/gbproxy/gb_proxy_peer.c +++ b/src/gbproxy/gb_proxy_peer.c @@ -343,7 +343,15 @@ return nse; } -/* SGSN */ +/*********************************************************************** + * SGSN - Serving GPRS Support Node + ***********************************************************************/ + +/*! Allocate a new SGSN. This ensures the corresponding gbproxy_nse is allocated as well + * \param[in] cfg The gbproxy configuration + * \param[in] nsei The nsei where the SGSN can be reached + * \return The SGSN, NULL if it couldn't be allocated + */ struct gbproxy_sgsn *gbproxy_sgsn_alloc(struct gbproxy_config *cfg, uint16_t nsei) { struct gbproxy_sgsn *sgsn; @@ -381,6 +389,9 @@ talloc_free(sgsn); } +/*! Free the SGSN. This ensures the corresponding gbproxy_nse is freed as well + * \param[in] sgsn The SGSN + */ void gbproxy_sgsn_free(struct gbproxy_sgsn *sgsn) { if (!sgsn) @@ -392,6 +403,11 @@ _sgsn_free(sgsn); } +/*! Return the SGSN for a given NSEI + * \param[in] cfg The gbproxy configuration + * \param[in] nsei The nsei where the SGSN can be reached + * \return Returns the matching SGSN or NULL if it couldn't be found + */ struct gbproxy_sgsn *gbproxy_sgsn_by_nsei(struct gbproxy_config *cfg, uint16_t nsei) { struct gbproxy_sgsn *sgsn; @@ -405,6 +421,11 @@ return NULL; } +/*! Return the SGSN for a given NSEI, creating a new one if none exists + * \param[in] cfg The gbproxy configuration + * \param[in] nsei The nsei where the SGSN can be reached + * \return Returns the SGSN + */ struct gbproxy_sgsn *gbproxy_sgsn_by_nsei_or_new(struct gbproxy_config *cfg, uint16_t nsei) { struct gbproxy_sgsn *sgsn; @@ -443,3 +464,47 @@ return NULL; } + +/*! Seleect a pseudo-random SGSN for a given TLLI, ignoring any SGSN that is not accepting connections + * \param[in] cfg The gbproxy configuration + * \param[in] sgsn_avoid If not NULL then avoid this SGSN when selecting a new one. Use for load redistribution + * \param[in] tlli The tlli to choose an SGSN for. The same tlli will map to the same SGSN as long as no SGSN is + added/removed or allow_attach changes. + * \return Returns the sgsn on success, NULL if no SGSN that allows new connections could be found + */ +struct gbproxy_sgsn *gbproxy_sgsn_by_tlli(struct gbproxy_config *cfg, struct gbproxy_sgsn *sgsn_avoid, + uint32_t tlli) +{ + uint32_t i = 0; + uint32_t index, num_sgsns; + struct gbproxy_sgsn *sgsn; + OSMO_ASSERT(cfg); + + // TODO: We should keep track of count in cfg + num_sgsns = llist_count(&cfg->sgsns); + + if (num_sgsns == 0) + return NULL; + + // FIXME: 256 SGSNs ought to be enough for everyone + index = hash_32(tlli, 8) % num_sgsns; + + // Get the first enabled SGSN after index + llist_for_each_entry(sgsn, &cfg->sgsns, list) { + if (i >= index && sgsn->pool.allow_attach) { + return sgsn; + } + i++; + } + // Start again from the beginning + llist_for_each_entry(sgsn, &cfg->sgsns, list) { + if (i > index) { + break; + } else if (sgsn->pool.allow_attach) { + return sgsn; + } + i++; + } + + return NULL; +} -- To view, visit https://gerrit.osmocom.org/c/osmo-sgsn/+/21881 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-sgsn Gerrit-Branch: master Gerrit-Change-Id: I58b9f55065f6bd43450e4b07cffe7ba132b1fd9b Gerrit-Change-Number: 21881 Gerrit-PatchSet: 1 Gerrit-Owner: daniel <dwillmann at sysmocom.de> Gerrit-MessageType: newchange -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20201228/58ab54a3/attachment.htm>