gprs_ns2: set transfer cap in NS Status primitive

Related: SYS#5153 OS#4835
Change-Id: Ia1046db9e0d50855bff9de670b612ffc57af9995
diff --git a/src/gb/Makefile.am b/src/gb/Makefile.am
index c829c29..cbee333 100644
--- a/src/gb/Makefile.am
+++ b/src/gb/Makefile.am
@@ -28,6 +28,12 @@
 		  gprs_ns2_message.c gprs_ns2_vty.c gprs_ns2_vty2.c \
 		  gprs_bssgp2.c bssgp_bvc_fsm.c \
 		  common_vty.c frame_relay.c
+
+# convenience library for testing with access to all non-static symbols
+noinst_LTLIBRARIES = libosmogb-test.la
+libosmogb_test_la_LIBADD = $(libosmogb_la_LIBADD)
+libosmogb_test_la_SOURCES= $(libosmogb_la_SOURCES)
+
 endif
 
 EXTRA_DIST = libosmogb.map
diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c
index 1098f22..e43b636 100644
--- a/src/gb/gprs_ns2.c
+++ b/src/gb/gprs_ns2.c
@@ -471,7 +471,7 @@
 	nsp.nsei = nse->nsei;
 	nsp.bvci = bvci;
 	nsp.u.status.cause = cause;
-	nsp.u.status.transfer = -1;
+	nsp.u.status.transfer = ns2_count_transfer_cap(nse, bvci);
 	nsp.u.status.first = nse->first;
 	nsp.u.status.persistent = nse->persistent;
 	if (nsvc)
@@ -1263,4 +1263,68 @@
 	}
 }
 
+static void add_bind_array(struct gprs_ns2_vc_bind **array,
+			   struct gprs_ns2_vc_bind *bind, int size)
+{
+	int i;
+	for (i=0; i < size; i++) {
+		if (array[i] == bind)
+			return;
+		if (!array[i])
+			break;
+	}
+
+	if (i == size)
+		return;
+
+	array[i] = bind;
+}
+
+/*! calculate the transfer capabilities for a nse
+ *  \param nse the nse to count the transfer capability
+ *  \param bvci a bvci - unused
+ *  \return the transfer capability in mbit. On error < 0.
+ */
+int ns2_count_transfer_cap(struct gprs_ns2_nse *nse,
+			   uint16_t bvci)
+{
+	struct gprs_ns2_vc *nsvc;
+	struct gprs_ns2_vc_bind **active_binds;
+	int i, active_nsvcs = 0, transfer_cap = 0;
+
+	/* calculate the transfer capabilities based on the binds.
+	 * A bind has a transfer capability which is shared across all NSVCs.
+	 * Take care the bind cap is not counted twice within a NSE.
+	 * This should be accurate for FR and UDP but not for FR/GRE. */
+
+	if (!nse->alive)
+		return 0;
+
+	llist_for_each_entry(nsvc, &nse->nsvc, list) {
+		if (gprs_ns2_vc_is_unblocked(nsvc))
+			active_nsvcs++;
+	}
+	/* an alive nse should always have active_nsvcs */
+	OSMO_ASSERT(active_nsvcs);
+
+	active_binds = talloc_zero_array(nse, struct gprs_ns2_vc_bind*, active_nsvcs);
+	if (!active_binds)
+		return -ENOMEM;
+
+	llist_for_each_entry(nsvc, &nse->nsvc, list) {
+		if (!gprs_ns2_vc_is_unblocked(nsvc))
+			continue;
+		add_bind_array(active_binds, nsvc->bind, active_nsvcs);
+	}
+
+	/* TODO: change calcuation for FR/GRE */
+	for (i = 0; i < active_nsvcs; i++) {
+		if (active_binds[i])
+			transfer_cap += active_binds[i]->transfer_capability;
+	}
+
+	talloc_free(active_binds);
+	return transfer_cap;
+}
+
 /*! @} */
diff --git a/src/gb/gprs_ns2_fr.c b/src/gb/gprs_ns2_fr.c
index e972a34..ca2d38c 100644
--- a/src/gb/gprs_ns2_fr.c
+++ b/src/gb/gprs_ns2_fr.c
@@ -579,6 +579,8 @@
 
 	bind->driver = &vc_driver_fr;
 	bind->ll = GPRS_NS2_LL_FR;
+	/* 2 mbit */
+	bind->transfer_capability = 2;
 	bind->send_vc = fr_vc_sendmsg;
 	bind->free_vc = free_vc;
 	bind->dump_vty = dump_vty;
diff --git a/src/gb/gprs_ns2_frgre.c b/src/gb/gprs_ns2_frgre.c
index 014517a..625d05c 100644
--- a/src/gb/gprs_ns2_frgre.c
+++ b/src/gb/gprs_ns2_frgre.c
@@ -569,6 +569,8 @@
 
 	bind->driver = &vc_driver_frgre;
 	bind->ll = GPRS_NS2_LL_FR_GRE;
+	/* 2 mbit transfer capability. Counting should be done different for this. */
+	bind->transfer_capability = 2;
 	bind->send_vc = frgre_vc_sendmsg;
 	bind->free_vc = free_vc;
 	bind->nsi = nsi;
diff --git a/src/gb/gprs_ns2_internal.h b/src/gb/gprs_ns2_internal.h
index 4c0cdd0..c33f7f8 100644
--- a/src/gb/gprs_ns2_internal.h
+++ b/src/gb/gprs_ns2_internal.h
@@ -198,6 +198,9 @@
 	bool accept_ipaccess;
 	bool accept_sns;
 
+	/*! transfer capability in mbit */
+	int transfer_capability;
+
 	/*! which link-layer are we based on? */
 	enum gprs_ns2_ll ll;
 
@@ -312,3 +315,5 @@
 /* nse */
 void ns2_nse_notify_unblocked(struct gprs_ns2_vc *nsvc, bool unblocked);
 enum gprs_ns2_vc_mode gprs_ns2_dialect_to_vc_mode(enum gprs_ns2_dialect dialect);
+int ns2_count_transfer_cap(struct gprs_ns2_nse *nse,
+			   uint16_t bvci);
diff --git a/src/gb/gprs_ns2_udp.c b/src/gb/gprs_ns2_udp.c
index 9058279..370937f 100644
--- a/src/gb/gprs_ns2_udp.c
+++ b/src/gb/gprs_ns2_udp.c
@@ -336,6 +336,10 @@
 
 	bind->driver = &vc_driver_ip;
 	bind->ll = GPRS_NS2_LL_UDP;
+	/* expect 100 mbit at least.
+	 * TODO: ask the network layer about the speed. But would require
+	 * notification on change. */
+	bind->transfer_capability = 100;
 	bind->send_vc = nsip_vc_sendmsg;
 	bind->free_vc = free_vc;
 	bind->dump_vty = dump_vty;