UMTS AKA: implement SQN increment according to SEQ and IND

Add ind_bitlen column to auc_3g to record each USIM's IND size according to
3GPP TS 33.102 -- default is 5 bits, as suggested by the spec.

Introduce auc_3g_ind to each connecting GSUP client to use as IND index for
generating auth tuples sent to this client.

With osmo_gsup_server_add_conn(), implement a scheme where clients receive
fixed auc_3g_ind indexes based on the order in which they connect; each new
connection takes the lowest unused auc_3g_ind, so in case one of the clients
restarts, it will most likely receive the same auc_3g_ind, and if one client
disconnects, no other clients' auc_3g_ind are affected.

Add gsup_server_test.c to test the auc_3g_ind index distribution scheme.

Depends: libosmocore I4eac5be0c0b2cede04464c4c3a0873102d952453 for llist_first
Related: OS#1969
Change-Id: If4501ed4ff8e923fa6fe8b80c44c5ad647a8ed60
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 21c0e21..0bd0820 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,5 +1,6 @@
 SUBDIRS = \
 	auc \
+	gsup_server \
 	$(NULL)
 
 # The `:;' works around a Bash 3.2 bug when the output is not writeable.
diff --git a/tests/gsup_server/Makefile.am b/tests/gsup_server/Makefile.am
new file mode 100644
index 0000000..fee60f5
--- /dev/null
+++ b/tests/gsup_server/Makefile.am
@@ -0,0 +1,40 @@
+AM_CPPFLAGS = \
+	$(all_includes) \
+	-I$(top_srcdir)/src \
+	$(NULL)
+
+AM_CFLAGS = \
+	-Wall \
+	-ggdb3 \
+	$(LIBOSMOCORE_CFLAGS) \
+	$(LIBOSMOGSM_CFLAGS) \
+	$(LIBOSMOABIS_CFLAGS) \
+	$(NULL)
+
+AM_LDFLAGS = \
+	$(NULL)
+
+EXTRA_DIST = \
+	gsup_server_test.ok \
+	gsup_server_test.err \
+	$(NULL)
+
+noinst_PROGRAMS = \
+	gsup_server_test \
+	$(NULL)
+
+gsup_server_test_SOURCES = \
+	gsup_server_test.c \
+	$(NULL)
+
+gsup_server_test_LDADD = \
+	$(top_srcdir)/src/gsup_server.c \
+	$(top_srcdir)/src/gsup_router.c \
+	$(LIBOSMOCORE_LIBS) \
+	$(LIBOSMOGSM_LIBS) \
+	$(LIBOSMOABIS_LIBS) \
+	$(NULL)
+
+.PHONY: update_exp
+update_exp:
+	$(builddir)/gsup_server_test >"$(srcdir)/gsup_server_test.ok" 2>"$(srcdir)/gsup_server_test.err"
diff --git a/tests/gsup_server/gsup_server_test.c b/tests/gsup_server/gsup_server_test.c
new file mode 100644
index 0000000..cc475be
--- /dev/null
+++ b/tests/gsup_server/gsup_server_test.c
@@ -0,0 +1,145 @@
+/* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <osmocom/core/utils.h>
+#include "gsup_server.h"
+
+#define comment_start() printf("\n===== %s\n", __func__)
+#define comment_end() printf("===== %s: SUCCESS\n\n", __func__)
+#define btw(fmt, args...) printf("\n" fmt "\n", ## args)
+
+#define VERBOSE_ASSERT(val, expect_op, fmt) \
+	do { \
+		printf(#val " == " fmt "\n", (val)); \
+		OSMO_ASSERT((val) expect_op); \
+	} while (0)
+
+void osmo_gsup_server_add_conn(struct llist_head *clients,
+			       struct osmo_gsup_conn *conn);
+
+static void test_add_conn(void)
+{
+	struct llist_head _list;
+	struct llist_head *clients = &_list;
+	struct osmo_gsup_conn conn_inst[23] = {};
+	struct osmo_gsup_conn *conn;
+	unsigned int i;
+
+	comment_start();
+
+	INIT_LLIST_HEAD(clients);
+
+	btw("Add 10 items");
+	for (i = 0; i < 10; i++) {
+		osmo_gsup_server_add_conn(clients, &conn_inst[i]);
+		printf("conn_inst[%u].auc_3g_ind == %u\n", i, conn_inst[i].auc_3g_ind);
+		OSMO_ASSERT(clients->next == &conn_inst[0].list);
+	}
+
+	btw("Expecting a list of 0..9");
+	i = 0;
+	llist_for_each_entry(conn, clients, list) {
+		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
+		OSMO_ASSERT(conn->auc_3g_ind == i);
+		OSMO_ASSERT(conn == &conn_inst[i]);
+		i++;
+	}
+
+	btw("Punch two holes in the sequence in arbitrary order,"
+	    " a larger one from 2..4 and a single one at 7.");
+	llist_del(&conn_inst[4].list);
+	llist_del(&conn_inst[2].list);
+	llist_del(&conn_inst[3].list);
+	llist_del(&conn_inst[7].list);
+
+	btw("Expecting a list of 0,1, 5,6, 8,9");
+	i = 0;
+	llist_for_each_entry(conn, clients, list) {
+		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
+		i++;
+	}
+
+	btw("Add conns, expecting them to take the open slots");
+	osmo_gsup_server_add_conn(clients, &conn_inst[12]);
+	VERBOSE_ASSERT(conn_inst[12].auc_3g_ind, == 2, "%u");
+
+	osmo_gsup_server_add_conn(clients, &conn_inst[13]);
+	VERBOSE_ASSERT(conn_inst[13].auc_3g_ind, == 3, "%u");
+
+	osmo_gsup_server_add_conn(clients, &conn_inst[14]);
+	VERBOSE_ASSERT(conn_inst[14].auc_3g_ind, == 4, "%u");
+
+	osmo_gsup_server_add_conn(clients, &conn_inst[17]);
+	VERBOSE_ASSERT(conn_inst[17].auc_3g_ind, == 7, "%u");
+
+	osmo_gsup_server_add_conn(clients, &conn_inst[18]);
+	VERBOSE_ASSERT(conn_inst[18].auc_3g_ind, == 10, "%u");
+
+	btw("Expecting a list of 0..10");
+	i = 0;
+	llist_for_each_entry(conn, clients, list) {
+		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
+		OSMO_ASSERT(conn->auc_3g_ind == i);
+		i++;
+	}
+
+	btw("Does it also work for the first item?");
+	llist_del(&conn_inst[0].list);
+
+	btw("Expecting a list of 1..10");
+	i = 0;
+	llist_for_each_entry(conn, clients, list) {
+		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
+		OSMO_ASSERT(conn->auc_3g_ind == i + 1);
+		i++;
+	}
+
+	btw("Add another conn, should take auc_3g_ind == 0");
+	osmo_gsup_server_add_conn(clients, &conn_inst[20]);
+	VERBOSE_ASSERT(conn_inst[20].auc_3g_ind, == 0, "%u");
+
+	btw("Expecting a list of 0..10");
+	i = 0;
+	llist_for_each_entry(conn, clients, list) {
+		printf("conn[%u].auc_3g_ind == %u\n", i, conn->auc_3g_ind);
+		OSMO_ASSERT(conn->auc_3g_ind == i);
+		i++;
+	}
+
+	btw("If a client reconnects, it will (likely) get the same auc_3g_ind");
+	VERBOSE_ASSERT(conn_inst[5].auc_3g_ind, == 5, "%u");
+	llist_del(&conn_inst[5].list);
+	conn_inst[5].auc_3g_ind = 423;
+	osmo_gsup_server_add_conn(clients, &conn_inst[5]);
+	VERBOSE_ASSERT(conn_inst[5].auc_3g_ind, == 5, "%u");
+
+	comment_end();
+}
+
+int main(int argc, char **argv)
+{
+	printf("test_gsup_server.c\n");
+
+	test_add_conn();
+
+	printf("Done\n");
+	return 0;
+}
diff --git a/tests/gsup_server/gsup_server_test.err b/tests/gsup_server/gsup_server_test.err
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/gsup_server/gsup_server_test.err
diff --git a/tests/gsup_server/gsup_server_test.ok b/tests/gsup_server/gsup_server_test.ok
new file mode 100644
index 0000000..80d944c
--- /dev/null
+++ b/tests/gsup_server/gsup_server_test.ok
@@ -0,0 +1,94 @@
+test_gsup_server.c
+
+===== test_add_conn
+
+Add 10 items
+conn_inst[0].auc_3g_ind == 0
+conn_inst[1].auc_3g_ind == 1
+conn_inst[2].auc_3g_ind == 2
+conn_inst[3].auc_3g_ind == 3
+conn_inst[4].auc_3g_ind == 4
+conn_inst[5].auc_3g_ind == 5
+conn_inst[6].auc_3g_ind == 6
+conn_inst[7].auc_3g_ind == 7
+conn_inst[8].auc_3g_ind == 8
+conn_inst[9].auc_3g_ind == 9
+
+Expecting a list of 0..9
+conn[0].auc_3g_ind == 0
+conn[1].auc_3g_ind == 1
+conn[2].auc_3g_ind == 2
+conn[3].auc_3g_ind == 3
+conn[4].auc_3g_ind == 4
+conn[5].auc_3g_ind == 5
+conn[6].auc_3g_ind == 6
+conn[7].auc_3g_ind == 7
+conn[8].auc_3g_ind == 8
+conn[9].auc_3g_ind == 9
+
+Punch two holes in the sequence in arbitrary order, a larger one from 2..4 and a single one at 7.
+
+Expecting a list of 0,1, 5,6, 8,9
+conn[0].auc_3g_ind == 0
+conn[1].auc_3g_ind == 1
+conn[2].auc_3g_ind == 5
+conn[3].auc_3g_ind == 6
+conn[4].auc_3g_ind == 8
+conn[5].auc_3g_ind == 9
+
+Add conns, expecting them to take the open slots
+conn_inst[12].auc_3g_ind == 2
+conn_inst[13].auc_3g_ind == 3
+conn_inst[14].auc_3g_ind == 4
+conn_inst[17].auc_3g_ind == 7
+conn_inst[18].auc_3g_ind == 10
+
+Expecting a list of 0..10
+conn[0].auc_3g_ind == 0
+conn[1].auc_3g_ind == 1
+conn[2].auc_3g_ind == 2
+conn[3].auc_3g_ind == 3
+conn[4].auc_3g_ind == 4
+conn[5].auc_3g_ind == 5
+conn[6].auc_3g_ind == 6
+conn[7].auc_3g_ind == 7
+conn[8].auc_3g_ind == 8
+conn[9].auc_3g_ind == 9
+conn[10].auc_3g_ind == 10
+
+Does it also work for the first item?
+
+Expecting a list of 1..10
+conn[0].auc_3g_ind == 1
+conn[1].auc_3g_ind == 2
+conn[2].auc_3g_ind == 3
+conn[3].auc_3g_ind == 4
+conn[4].auc_3g_ind == 5
+conn[5].auc_3g_ind == 6
+conn[6].auc_3g_ind == 7
+conn[7].auc_3g_ind == 8
+conn[8].auc_3g_ind == 9
+conn[9].auc_3g_ind == 10
+
+Add another conn, should take auc_3g_ind == 0
+conn_inst[20].auc_3g_ind == 0
+
+Expecting a list of 0..10
+conn[0].auc_3g_ind == 0
+conn[1].auc_3g_ind == 1
+conn[2].auc_3g_ind == 2
+conn[3].auc_3g_ind == 3
+conn[4].auc_3g_ind == 4
+conn[5].auc_3g_ind == 5
+conn[6].auc_3g_ind == 6
+conn[7].auc_3g_ind == 7
+conn[8].auc_3g_ind == 8
+conn[9].auc_3g_ind == 9
+conn[10].auc_3g_ind == 10
+
+If a client reconnects, it will (likely) get the same auc_3g_ind
+conn_inst[5].auc_3g_ind == 5
+conn_inst[5].auc_3g_ind == 5
+===== test_add_conn: SUCCESS
+
+Done
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 46d1456..a969082 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -14,3 +14,10 @@
 cat $abs_srcdir/auc/auc_ts_55_205_test_sets.err > experr
 AT_CHECK([$abs_top_builddir/tests/auc/auc_ts_55_205_test_sets], [], [expout], [experr])
 AT_CLEANUP
+
+AT_SETUP([gsup_server])
+AT_KEYWORDS([gsup_server])
+cat $abs_srcdir/gsup_server/gsup_server_test.ok > expout
+cat $abs_srcdir/gsup_server/gsup_server_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/gsup_server/gsup_server_test], [], [expout], [experr])
+AT_CLEANUP