gbproxy: Add SGSN NRI configuration

In order to support SGSN pooling we need to configure the various NRI
parameters such as the bitlen, NULL NRI, and which NRIs are assigned to
which SGSN.

Related: OS#4890, OS#4472
Change-Id: Id67592aa7712e5e04e7264b2fb8f26d57eb7e69e
diff --git a/src/gbproxy/gb_proxy_peer.c b/src/gbproxy/gb_proxy_peer.c
index c38b2f7..863ec50 100644
--- a/src/gbproxy/gb_proxy_peer.c
+++ b/src/gbproxy/gb_proxy_peer.c
@@ -26,6 +26,7 @@
 
 #include <osmocom/gprs/protocol/gsm_08_18.h>
 #include <osmocom/core/logging.h>
+#include <osmocom/core/linuxlist.h>
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/core/stats.h>
 #include <osmocom/core/talloc.h>
@@ -273,7 +274,7 @@
 	return nse;
 }
 
-void gbproxy_nse_free(struct gbproxy_nse *nse)
+static void _nse_free(struct gbproxy_nse *nse)
 {
 	struct gbproxy_bvc *bvc;
 	struct hlist_node *tmp;
@@ -291,6 +292,22 @@
 
 	talloc_free(nse);
 }
+static void _sgsn_free(struct gbproxy_sgsn *sgsn);
+
+void gbproxy_nse_free(struct gbproxy_nse *nse)
+{
+	if (!nse)
+		return;
+	OSMO_ASSERT(nse->cfg);
+
+	if (nse->sgsn_facing) {
+		struct gbproxy_sgsn *sgsn = gbproxy_sgsn_by_nsei(nse->cfg, nse->nsei);
+		OSMO_ASSERT(sgsn);
+		_sgsn_free(sgsn);
+	}
+
+	_nse_free(nse);
+}
 
 struct gbproxy_nse *gbproxy_nse_by_nsei(struct gbproxy_config *cfg, uint16_t nsei, uint32_t flags)
 {
@@ -325,3 +342,104 @@
 
 	return nse;
 }
+
+/* SGSN */
+struct gbproxy_sgsn *gbproxy_sgsn_alloc(struct gbproxy_config *cfg, uint16_t nsei)
+{
+	struct gbproxy_sgsn *sgsn;
+	OSMO_ASSERT(cfg);
+
+	sgsn = talloc_zero(tall_sgsn_ctx, struct gbproxy_sgsn);
+	if (!sgsn)
+		return NULL;
+
+	sgsn->nse = gbproxy_nse_alloc(cfg, nsei, true);
+	if (!sgsn->nse) {
+		LOGPSGSN_CAT(sgsn, DOBJ, LOGL_INFO, "Could not allocate NSE(%05u) for SGSN\n", nsei);
+		talloc_free(sgsn);
+		return NULL;
+	}
+
+	sgsn->pool.allow_attach = true;
+	sgsn->pool.nri_ranges = osmo_nri_ranges_alloc(sgsn);
+
+	llist_add_tail(&sgsn->list, &cfg->sgsns);
+	LOGPSGSN_CAT(sgsn, DOBJ, LOGL_INFO, "SGSN Created\n");
+	return sgsn;
+}
+
+/* Only free gbproxy_sgsn, sgsn can't be NULL */
+static void _sgsn_free(struct gbproxy_sgsn *sgsn) {
+	struct gbproxy_config *cfg;
+
+	OSMO_ASSERT(sgsn->nse);
+	cfg = sgsn->nse->cfg;
+	OSMO_ASSERT(cfg);
+
+	LOGPSGSN_CAT(sgsn, DOBJ, LOGL_INFO, "SGSN Destroying\n");
+	llist_del(&sgsn->list);
+	talloc_free(sgsn);
+}
+
+void gbproxy_sgsn_free(struct gbproxy_sgsn *sgsn)
+{
+	if (!sgsn)
+		return;
+
+	OSMO_ASSERT(sgsn->nse)
+
+	_nse_free(sgsn->nse);
+	_sgsn_free(sgsn);
+}
+
+struct gbproxy_sgsn *gbproxy_sgsn_by_nsei(struct gbproxy_config *cfg, uint16_t nsei)
+{
+	struct gbproxy_sgsn *sgsn;
+	OSMO_ASSERT(cfg);
+
+	llist_for_each_entry(sgsn, &cfg->sgsns, list) {
+		if (sgsn->nse->nsei == nsei)
+			return sgsn;
+	}
+
+	return NULL;
+}
+
+struct gbproxy_sgsn *gbproxy_sgsn_by_nsei_or_new(struct gbproxy_config *cfg, uint16_t nsei)
+{
+	struct gbproxy_sgsn *sgsn;
+	OSMO_ASSERT(cfg);
+
+	sgsn = gbproxy_sgsn_by_nsei(cfg, nsei);
+	if (!sgsn)
+		sgsn = gbproxy_sgsn_alloc(cfg, nsei);
+
+	return sgsn;
+}
+
+/*! Return the gbproxy_sgsn matching that NRI
+ *  \param[in] cfg proxy in which we operate
+ *  \param[in] nri NRI to look for
+ *  \param[out] null_nri If not NULL this indicates whether the NRI is a null NRI
+ *  \return The SGSN this NRI has been added to, NULL if no matching SGSN could be found
+ */
+struct gbproxy_sgsn *gbproxy_sgsn_by_nri(struct gbproxy_config *cfg, uint16_t nri, bool *null_nri)
+{
+	struct gbproxy_sgsn *sgsn;
+	OSMO_ASSERT(cfg);
+
+	llist_for_each_entry(sgsn, &cfg->sgsns, list) {
+		if (osmo_nri_v_matches_ranges(nri, sgsn->pool.nri_ranges)) {
+			/* Also check if the NRI we're looking for is a NULL NRI */
+			if (sgsn && null_nri) {
+				if (osmo_nri_v_matches_ranges(nri, cfg->pool.null_nri_ranges))
+					*null_nri = true;
+				else
+					*null_nri = false;
+			}
+			return sgsn;
+		}
+	}
+
+	return NULL;
+}