test GGSN support for optional GTP-U sequence numbers

Add VTY functionality to GGSN tests, and use the VTY to enable/disable
GTP-U Tx sequence numbers in the running osmo-ggsn.

The GTPU packet template now makes sequence numbers optional.
A template created with its sequence number set to 'omit' will result in
a packet without a sequence number, i.e. the 'sequence number present' bit
in the packet header is cleared, and the sequence number field is omitted
from the encoded GTPU T-PDU packet.

Re-use the existing TC_pdp4_clients_interact() test for testing the
behaviour of osmo-ggsn. This test is now run twice, once with and
once without GTP-U Tx sequence numbers. Verify that packets relayed by
osmo-ggsn match its "g-pdu tx-sequence-numbers" configuration setting.

Change-Id: I1dc299407c61b1c865035add44067b8ab89001b3
Related: OS#2519
diff --git a/ggsn_tests/GGSN_Tests.ttcn b/ggsn_tests/GGSN_Tests.ttcn
index 1469062..ba8fae5 100644
--- a/ggsn_tests/GGSN_Tests.ttcn
+++ b/ggsn_tests/GGSN_Tests.ttcn
@@ -14,6 +14,8 @@
 	import from ICMP_Types all;
 	import from ICMPv6_Types all;
 	import from Native_Functions all;
+	import from Osmocom_VTY_Functions all;
+	import from TELNETasp_PortType all;
 
 	const integer GTP0_PORT := 3386;
 	const integer GTP1C_PORT := 2123;
@@ -89,6 +91,42 @@
 		var uint16_t g_c_seq_nr;
 		/* next to-be-sent GTP-U sequence number */
 		var uint16_t g_d_seq_nr;
+
+		port TELNETasp_PT GGSNVTY;
+		var boolean use_gptu_txseq := true;
+	}
+
+	private function f_init_vty() runs on GT_CT {
+		map(self:GGSNVTY, system:GGSNVTY);
+		f_vty_set_prompts(GGSNVTY);
+		f_vty_transceive(GGSNVTY, "enable");
+	}
+
+	private function f_vty_set_gpdu_txseq(boolean enable) runs on GT_CT {
+		f_vty_enter_config(GGSNVTY);
+		f_vty_transceive(GGSNVTY, "ggsn ggsn0");
+		f_vty_transceive(GGSNVTY, "apn internet");
+		if (enable) {
+			f_vty_transceive(GGSNVTY, "g-pdu tx-sequence-numbers");
+		} else {
+			f_vty_transceive(GGSNVTY, "no g-pdu tx-sequence-numbers");
+		}
+		f_vty_transceive(GGSNVTY, "end");
+	}
+
+	private function f_verify_gtpu_txseq(in PDU_GTPU gtpu, in boolean expect_gptu_txseq) return boolean {
+			if (expect_gptu_txseq) {
+				if (gtpu.s_bit != '1'B) {
+					log("GTPU sequence number expected but not present")
+					return false;
+				}
+			} else {
+				if (gtpu.s_bit != '0'B) {
+					log("GTPU sequence number not expected but present")
+					return false;
+				}
+			}
+			return true;
 	}
 
 	function f_init() runs on GT_CT {
@@ -113,6 +151,9 @@
 		g_restart_ctr := f_rnd_octstring(1);
 		g_c_seq_nr := f_rnd_int(65535);
 		g_d_seq_nr := f_rnd_int(65535);
+
+		f_init_vty();
+		f_vty_set_gpdu_txseq(use_gptu_txseq);
 	}
 
 	/* Altstep implementing responses to any incoming echo requests */
@@ -167,8 +208,12 @@
 
 	/* send GTP-U for a given context and increment sequence number */
 	function f_send_gtpu(inout PdpContext ctx, in octetstring data) runs on GT_CT {
-		GTPU.send(ts_GTP1U_GPDU(g_peer_u, g_d_seq_nr, ctx.teid_remote, data));
-		g_d_seq_nr := g_d_seq_nr + 1;
+		if (use_gptu_txseq) {
+			GTPU.send(ts_GTP1U_GPDU(g_peer_u, g_d_seq_nr, ctx.teid_remote, data));
+			g_d_seq_nr := g_d_seq_nr + 1;
+		} else {
+			GTPU.send(ts_GTP1U_GPDU(g_peer_u, omit, ctx.teid_remote, data));
+		}
 	}
 
 	/* send a PDP context activation */
@@ -558,6 +603,10 @@
 		T_default.start;
 		alt {
 			[] GTPU.receive(tr_GTPU_GPDU(g_peer_u, ?)) -> value ud {
+				if (f_verify_gtpu_txseq(ud.gtpu, use_gptu_txseq) == false) {
+					setverdict(fail);
+					stop;
+				}
 				var octetstring gpdu := ud.gtpu.gtpu_IEs.g_PDU_IEs.data;
 				var IPv4_packet ip4 := f_IPv4_dec(gpdu);
 				if (ip4.header.ver != 4) {
@@ -590,6 +639,10 @@
 		T_default.start;
 		alt {
 			[] GTPU.receive(tr_GTPU_GPDU(g_peer_u, ?)) -> value ud {
+				if (f_verify_gtpu_txseq(ud.gtpu, use_gptu_txseq) == false) {
+					setverdict(fail);
+					stop;
+				}
 				var octetstring gpdu := ud.gtpu.gtpu_IEs.g_PDU_IEs.data;
 				var IPv6_packet ip6 := f_IPv6_dec(gpdu);
 				if (ip6.header.ver != 6 or ip6.header.nexthead != 58) {
@@ -840,8 +893,8 @@
 		f_pdp_ctx_del(ctx, '1'B);
 	}
 
-	/* Validate if different clients (pdp ctx) can reach one another through GGSN. */
-	testcase TC_pdp4_clients_interact() runs on GT_CT {
+	/* Helper function for tests below. */
+	function f_pdp4_clients_interact() runs on GT_CT {
 		f_init();
 		var PdpContext ctxA := valueof(t_DefinePDP(f_rnd_imsi('26242'H), '1234'O, c_ApnInternet, valueof(t_EuaIPv4Dyn)));
 		var PdpContext ctxB := valueof(t_DefinePDP(f_rnd_imsi('26242'H), '1234'O, c_ApnInternet, valueof(t_EuaIPv4Dyn)));
@@ -855,6 +908,18 @@
 		f_pdp_ctx_del(ctxA, '1'B);
 	}
 
+	/* Validate if different clients (pdp ctx) can reach one another through GGSN. */
+	testcase TC_pdp4_clients_interact_with_txseq() runs on GT_CT {
+		use_gptu_txseq := true;
+		f_pdp4_clients_interact();
+	}
+
+	/* Validate if different clients (pdp ctx) can reach one another through GGSN (without Tx sequence number). */
+	testcase TC_pdp4_clients_interact_without_txseq() runs on GT_CT {
+		use_gptu_txseq := false;
+		f_pdp4_clients_interact();
+	}
+
 	testcase TC_echo_req_resp() runs on GT_CT {
 		f_init();
 		f_send_gtpc(ts_GTPC_PING(g_peer_c, g_c_seq_nr));
@@ -872,7 +937,8 @@
 		execute(TC_pdp4_act_deact_ipcp());
 		execute(TC_pdp4_act_deact_pcodns());
 		execute(TC_pdp4_act_deact_gtpu_access());
-		execute(TC_pdp4_clients_interact());
+		execute(TC_pdp4_clients_interact_with_txseq());
+		execute(TC_pdp4_clients_interact_without_txseq());
 
 		execute(TC_pdp6_act_deact());
 		execute(TC_pdp6_act_deact_pcodns());