gbproxy major rewrite for SGSN pool support

Rewrite of a large part of osmo-gbproxy in order to prepare
for SGSN pool support.  The amount of changes are of such fundamental
nature that it doesn't make sense to try to split this into hundreds
of individual changesets.

Related: OS#4472
Change-Id: Ie0746f17927a9509c3806cc80dc1a31d25df7937
diff --git a/src/gbproxy/gb_proxy_peer.c b/src/gbproxy/gb_proxy_peer.c
index 052e577..a0586fe 100644
--- a/src/gbproxy/gb_proxy_peer.c
+++ b/src/gbproxy/gb_proxy_peer.c
@@ -55,56 +55,13 @@
 
 
 /* Find the gbproxy_bvc by its BVCI. There can only be one match */
-struct gbproxy_bvc *gbproxy_bvc_by_bvci(struct gbproxy_config *cfg, uint16_t bvci)
+struct gbproxy_bvc *gbproxy_bvc_by_bvci(struct gbproxy_nse *nse, uint16_t bvci)
 {
-	struct gbproxy_nse *nse;
-	int i;
-
-	hash_for_each(cfg->bss_nses, i, nse, list) {
-		struct gbproxy_bvc *bvc;
-		hash_for_each_possible(nse->bvcs, bvc, list, bvci) {
-			if (bvc->bvci == bvci)
-				return bvc;
-		}
-	}
-	return NULL;
-}
-
-/* Find the gbproxy_bvc by its NSEI */
-/* FIXME: Only returns the first bvc, but we could have multiple on this nsei */
-struct gbproxy_bvc *gbproxy_bvc_by_nsei(struct gbproxy_config *cfg,
-					  uint16_t nsei)
-{
-	struct gbproxy_nse *nse = gbproxy_nse_by_nsei(cfg, nsei);
 	struct gbproxy_bvc *bvc;
-	int i;
-
-	if (!nse || hash_empty(nse->bvcs))
-		return NULL;
-
-	/* return the first entry we find */
-	hash_for_each(nse->bvcs, i, bvc, list)
-		return bvc;
-
-	return NULL;
-}
-
-/* look-up a bvc by its Routeing Area Identification (RAI) */
-/* FIXME: this doesn't make sense, as RA can span multiple bvcs! */
-struct gbproxy_bvc *gbproxy_bvc_by_rai(struct gbproxy_config *cfg,
-					 const uint8_t *ra)
-{
-	struct gbproxy_nse *nse;
-	int i, j;
-
-	hash_for_each(cfg->bss_nses, i, nse, list) {
-		struct gbproxy_bvc *bvc;
-		hash_for_each(nse->bvcs, j, bvc, list) {
-			if (!memcmp(bvc->ra, ra, 6))
-				return bvc;
-		}
+	hash_for_each_possible(nse->bvcs, bvc, list, bvci) {
+		if (bvc->bvci == bvci)
+			return bvc;
 	}
-
 	return NULL;
 }
 
@@ -129,11 +86,13 @@
 
 	hash_add(nse->bvcs, &bvc->list, bvc->bvci);
 
-	return bvc;
+        return bvc;
 }
 
 void gbproxy_bvc_free(struct gbproxy_bvc *bvc)
 {
+	struct gbproxy_cell *cell;
+
 	if (!bvc)
 		return;
 
@@ -142,45 +101,141 @@
 	rate_ctr_group_free(bvc->ctrg);
 	bvc->ctrg = NULL;
 
+	osmo_fsm_inst_free(bvc->fi);
+
+	cell = bvc->cell;
+	if (cell) {
+		int i;
+
+		if (cell->bss_bvc == bvc)
+			cell->bss_bvc = NULL;
+
+		/* we could also be a SGSN-side BVC */
+		for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
+			if (cell->sgsn_bvc[i] == bvc)
+				cell->sgsn_bvc[i] = NULL;
+		}
+		bvc->cell = NULL;
+	}
+
 	talloc_free(bvc);
 }
 
-void gbproxy_bvc_move(struct gbproxy_bvc *bvc, struct gbproxy_nse *nse)
-{
-	hash_del(&bvc->list);
-	hash_add(nse->bvcs, &bvc->list, bvc->bvci);
-	bvc->nse = nse;
-}
-
-/*! remove bvcs (BVCs) on NSE specified by NSEI.
+/*! remove BVCs on NSE specified by NSEI.
  *  \param[in] cfg proxy in which we operate
  *  \param[in] nsei NS entity in which we should clean up
- *  \param[in] bvci if 0: remove all BVCs; if != 0: BVCI of the single BVC to clean up */
-int gbproxy_cleanup_bvcs(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci)
+ *  \param[in] bvci if 0: remove all PTP BVCs; if != 0: BVCI of the single BVC to clean up */
+int gbproxy_cleanup_bvcs(struct gbproxy_nse *nse, uint16_t bvci)
 {
-	int i, j, counter = 0;
-	struct gbproxy_nse *nse;
-	struct hlist_node *ntmp;
-	OSMO_ASSERT(cfg);
+	struct hlist_node *btmp;
+	struct gbproxy_bvc *bvc;
+	int j, counter = 0;
 
-	hash_for_each_safe(cfg->bss_nses, i, ntmp, nse, list) {
-		struct gbproxy_bvc *bvc;
-		struct hlist_node *btmp;
-		if (nse->nsei != nsei)
+	if (!nse)
+		return 0;
+
+	hash_for_each_safe(nse->bvcs, j, btmp, bvc, list) {
+		if (bvci && bvc->bvci != bvci)
 			continue;
-		hash_for_each_safe(nse->bvcs, j, btmp, bvc, list) {
-			if (bvci && bvc->bvci != bvci)
-				continue;
+		if (bvci == 0 && bvc->bvci == 0)
+			continue;
 
-			gbproxy_bvc_free(bvc);
-			counter += 1;
-		}
+		gbproxy_bvc_free(bvc);
+		counter += 1;
 	}
 
 	return counter;
 }
 
-struct gbproxy_nse *gbproxy_nse_alloc(struct gbproxy_config *cfg, uint16_t nsei)
+
+/***********************************************************************
+ * CELL
+ ***********************************************************************/
+
+/* Allocate a new 'cell' object */
+struct gbproxy_cell *gbproxy_cell_alloc(struct gbproxy_config *cfg, uint16_t bvci)
+{
+	struct gbproxy_cell *cell;
+	OSMO_ASSERT(cfg);
+
+	cell = talloc_zero(cfg, struct gbproxy_cell);
+	if (!cell)
+		return NULL;
+
+	cell->cfg = cfg;
+	cell->bvci = bvci;
+
+	hash_add(cfg->cells, &cell->list, cell->bvci);
+
+	return cell;
+}
+
+/* Find cell by BVCI */
+struct gbproxy_cell *gbproxy_cell_by_bvci(struct gbproxy_config *cfg, uint16_t bvci)
+{
+	struct gbproxy_cell *cell;
+
+	hash_for_each_possible(cfg->cells, cell, list, bvci) {
+		if (cell->bvci == bvci)
+			return cell;
+	}
+	return NULL;
+}
+
+struct gbproxy_cell *gbproxy_cell_by_bvci_or_new(struct gbproxy_config *cfg, uint16_t bvci)
+{
+	struct gbproxy_cell *cell;
+	OSMO_ASSERT(cfg);
+
+	cell = gbproxy_cell_by_bvci(cfg, bvci);
+	if (!cell)
+		cell = gbproxy_cell_alloc(cfg, bvci);
+
+	return cell;
+}
+
+void gbproxy_cell_free(struct gbproxy_cell *cell)
+{
+	unsigned int i;
+
+	if (!cell)
+		return;
+
+	/* remove from cfg.cells */
+	hash_del(&cell->list);
+
+	/* remove back-pointers from the BSS side */
+	if (cell->bss_bvc && cell->bss_bvc->cell)
+		cell->bss_bvc->cell = NULL;
+
+	/* remove back-pointers from the SGSN side */
+	for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
+		if (!cell->sgsn_bvc[i])
+			continue;
+		if (cell->sgsn_bvc[i]->cell)
+			cell->sgsn_bvc[i]->cell = NULL;
+	}
+
+	talloc_free(cell);
+}
+
+bool gbproxy_cell_add_sgsn_bvc(struct gbproxy_cell *cell, struct gbproxy_bvc *bvc)
+{
+	unsigned int i;
+	for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
+		if (!cell->sgsn_bvc[i]) {
+			cell->sgsn_bvc[i] = bvc;
+			return true;
+		}
+	}
+	return false;
+}
+
+/***********************************************************************
+ * NSE - NS Entity
+ ***********************************************************************/
+
+struct gbproxy_nse *gbproxy_nse_alloc(struct gbproxy_config *cfg, uint16_t nsei, bool sgsn_facing)
 {
 	struct gbproxy_nse *nse;
 	OSMO_ASSERT(cfg);
@@ -191,8 +246,12 @@
 
 	nse->nsei = nsei;
 	nse->cfg = cfg;
+	nse->sgsn_facing = sgsn_facing;
 
-	hash_add(cfg->bss_nses, &nse->list, nsei);
+	if (sgsn_facing)
+		hash_add(cfg->sgsn_nses, &nse->list, nsei);
+	else
+		hash_add(cfg->bss_nses, &nse->list, nsei);
 
 	hash_init(nse->bvcs);
 
@@ -216,27 +275,36 @@
 	talloc_free(nse);
 }
 
-struct gbproxy_nse *gbproxy_nse_by_nsei(struct gbproxy_config *cfg, uint16_t nsei)
+struct gbproxy_nse *gbproxy_nse_by_nsei(struct gbproxy_config *cfg, uint16_t nsei, uint32_t flags)
 {
 	struct gbproxy_nse *nse;
 	OSMO_ASSERT(cfg);
 
-	hash_for_each_possible(cfg->bss_nses, nse, list, nsei) {
-		if (nse->nsei == nsei)
-			return nse;
+	if (flags & NSE_F_SGSN) {
+		hash_for_each_possible(cfg->sgsn_nses, nse, list, nsei) {
+			if (nse->nsei == nsei)
+				return nse;
+		}
+	}
+
+	if (flags & NSE_F_BSS) {
+		hash_for_each_possible(cfg->bss_nses, nse, list, nsei) {
+			if (nse->nsei == nsei)
+				return nse;
+		}
 	}
 
 	return NULL;
 }
 
-struct gbproxy_nse *gbproxy_nse_by_nsei_or_new(struct gbproxy_config *cfg, uint16_t nsei)
+struct gbproxy_nse *gbproxy_nse_by_nsei_or_new(struct gbproxy_config *cfg, uint16_t nsei, bool sgsn_facing)
 {
 	struct gbproxy_nse *nse;
 	OSMO_ASSERT(cfg);
 
-	nse = gbproxy_nse_by_nsei(cfg, nsei);
+	nse = gbproxy_nse_by_nsei(cfg, nsei, sgsn_facing ? NSE_F_SGSN : NSE_F_BSS);
 	if (!nse)
-		nse = gbproxy_nse_alloc(cfg, nsei);
+		nse = gbproxy_nse_alloc(cfg, nsei, sgsn_facing);
 
 	return nse;
 }