Add vty check for max si2quater size

Explicitly check if added (U|E)ARFCN will fit into available si2quater
message.
diff --git a/openbsc/include/openbsc/system_information.h b/openbsc/include/openbsc/system_information.h
index ecc6964..7e3ceaa 100644
--- a/openbsc/include/openbsc/system_information.h
+++ b/openbsc/include/openbsc/system_information.h
@@ -11,6 +11,7 @@
 unsigned earfcn_size(const struct osmo_earfcn_si2q *e);
 unsigned range1024_p(unsigned n);
 unsigned range512_q(unsigned m);
+bool si2q_size_check(const struct gsm_bts *bts);
 int bts_uarfcn_del(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble);
 int bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble,
 		   bool diversity);
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index cc46865..e81308b 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -2789,15 +2789,19 @@
 			VTY_NEWLINE);
 		return CMD_WARNING;
 	}
+	if (si2q_size_check(bts)) {
+		if (e->thresh_hi && thresh != e->thresh_hi)
+			vty_out(vty, "Warning: multiple thresholds are not "
+				"supported, overriding previous threshold %u%s",
+				e->thresh_hi, VTY_NEWLINE);
 
-	if (e->thresh_hi && thresh != e->thresh_hi)
-		vty_out(vty, "Warning: multiple thresholds are not supported, "
-			"overriding previous threshold %u%s",
-			e->thresh_hi, VTY_NEWLINE);
-
-	e->thresh_hi = thresh;
-
-	return CMD_SUCCESS;
+		e->thresh_hi = thresh;
+		return CMD_SUCCESS;
+	}
+	vty_out(vty, "Warning: not enough space in si2quater for a given arfcn%s"
+		, VTY_NEWLINE);
+	osmo_earfcn_del(e, arfcn);
+	return CMD_WARNING;
 }
 
 DEFUN(cfg_bts_si2quater_neigh_del, cfg_bts_si2quater_neigh_del_cmd,
@@ -2835,6 +2839,9 @@
 	case -ENOMEM:
 		vty_out(vty, "Unable to add arfcn: max number of UARFCNs (%u) "
 			"reached%s", MAX_EARFCN_LIST, VTY_NEWLINE);
+	case -ENOSPC:
+		vty_out(vty, "Warning: not enough space in si2quater for a "
+			"given arfcn%s", VTY_NEWLINE);
 	case -EADDRINUSE:
 		vty_out(vty, "Unable to add arfcn: (%u, %u) is already added%s",
 			arfcn, scramble, VTY_NEWLINE);
diff --git a/openbsc/src/libbsc/system_information.c b/openbsc/src/libbsc/system_information.c
index 8952534..3b0f889 100644
--- a/openbsc/src/libbsc/system_information.c
+++ b/openbsc/src/libbsc/system_information.c
@@ -134,6 +134,20 @@
 	return 29 + range1024_p(u_len);
 }
 
+bool si2q_size_check(const struct gsm_bts *bts)
+{
+	const struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
+	const uint16_t *u = bts->si_common.data.uarfcn_list,
+		*sc = bts->si_common.data.scramble_list;
+	size_t len = bts->si_common.uarfcn_length;
+	unsigned e_sz = e ? earfcn_size(e) : 1,
+		u_sz = len ? uarfcn_size(u, sc, len) : 1;
+	/* 2 bits are used in between UARFCN and EARFCN structs */
+	if (SI2Q_MIN_LEN + u_sz + 2 + e_sz > SI2Q_MAX_LEN)
+		return false;
+	return true;
+}
+
 /* 3GPP TS 44.018, Table 9.1.54.1 - prepend diversity bit to scrambling code */
 uint16_t encode_fdd(uint16_t scramble, bool diversity)
 {
@@ -198,7 +212,12 @@
 	ual[k] = arfcn;
 	scl[k] = scr;
 	bts->si_common.uarfcn_length++;
-	return 0;
+
+	if (si2q_size_check(bts))
+		return 0;
+
+	bts_uarfcn_del(bts, arfcn, scramble);
+	return -ENOSPC;
 }
 
 static inline int use_arfcn(const struct gsm_bts *bts, const bool bis, const bool ter,
diff --git a/openbsc/tests/gsm0408/gsm0408_test.c b/openbsc/tests/gsm0408/gsm0408_test.c
index 2d91b68..9262667 100644
--- a/openbsc/tests/gsm0408/gsm0408_test.c
+++ b/openbsc/tests/gsm0408/gsm0408_test.c
@@ -103,6 +103,16 @@
 		printf("failed to generate SI2quater: %s\n", strerror(-r));
 }
 
+static inline void _bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn,
+			      uint16_t scramble, bool diversity)
+{
+	int r = bts_uarfcn_add(bts, arfcn, scramble, diversity);
+	if (r < 0)
+		printf("failed to add UARFCN to SI2quater: %s\n", strerror(-r));
+	else
+		gen(bts);
+}
+
 static inline void test_si2q_u(void)
 {
 	struct gsm_bts *bts;
@@ -113,37 +123,17 @@
 		exit(1);
 	bts = gsm_bts_alloc(network);
 
-	bts_uarfcn_add(bts, 1982, 13, 1);
-	gen(bts);
-
-	bts_uarfcn_add(bts, 1982, 44, 0);
-	gen(bts);
-
-	bts_uarfcn_add(bts, 1982, 61, 1);
-	gen(bts);
-
-	bts_uarfcn_add(bts, 1982, 89, 1);
-	gen(bts);
-
-	bts_uarfcn_add(bts, 1982, 113, 0);
-	gen(bts);
-
-	bts_uarfcn_add(bts, 1982, 123, 0);
-	gen(bts);
-
-	bts_uarfcn_add(bts, 1982, 56, 1);
-	gen(bts);
-
-	bts_uarfcn_add(bts, 1982, 72, 1);
-	gen(bts);
-
-	bts_uarfcn_add(bts, 1982, 223, 1);
-	gen(bts);
-
-	bts_uarfcn_add(bts, 1982, 14, 0);
-	gen(bts);
-
-	bts_uarfcn_add(bts, 1982, 88, 0);
+	_bts_uarfcn_add(bts, 1982, 13, 1);
+	_bts_uarfcn_add(bts, 1982, 44, 0);
+	_bts_uarfcn_add(bts, 1982, 61, 1);
+	_bts_uarfcn_add(bts, 1982, 89, 1);
+	_bts_uarfcn_add(bts, 1982, 113, 0);
+	_bts_uarfcn_add(bts, 1982, 123, 0);
+	_bts_uarfcn_add(bts, 1982, 56, 1);
+	_bts_uarfcn_add(bts, 1982, 72, 1);
+	_bts_uarfcn_add(bts, 1982, 223, 1);
+	_bts_uarfcn_add(bts, 1982, 14, 0);
+	_bts_uarfcn_add(bts, 1982, 88, 0);
 	gen(bts);
 }
 
diff --git a/openbsc/tests/gsm0408/gsm0408_test.ok b/openbsc/tests/gsm0408/gsm0408_test.ok
index 565eac6..7b7a2cc 100644
--- a/openbsc/tests/gsm0408/gsm0408_test.ok
+++ b/openbsc/tests/gsm0408/gsm0408_test.ok
@@ -80,6 +80,7 @@
 generated SI2quater: [23] 59 06 07 c0 00 25 0f 7c 38 58 12 22 fd ce 8e 05 04 86 59 00 03 2b 2b 
 generated SI2quater: [23] 59 06 07 c0 00 25 0f 7c 40 58 1d 22 fa ce 88 85 7b 00 44 b2 00 03 2b 
 generated SI2quater: [23] 59 06 07 c0 00 25 0f 7c 4c 7a 34 0e 64 77 85 43 55 c8 10 99 64 00 0b 
-failed to generate SI2quater: Cannot allocate memory
-failed to generate SI2quater: Cannot allocate memory
+failed to add UARFCN to SI2quater: No space left on device
+failed to add UARFCN to SI2quater: No space left on device
+generated SI2quater: [23] 59 06 07 c0 00 25 0f 7c 4c 7a 34 0e 64 77 85 43 55 c8 10 99 64 00 0b 
 Done.