Change in osmo-sgsn[master]: gbproxy: Add SGSN pooling support

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.org
Mon Dec 28 18:27:59 UTC 2020


daniel 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>


More information about the gerrit-log mailing list