gprs/test: Add additional gbproxy tests

This adds a test case with several messages to test BSSGP patching.
New messages:

- BSSGP/DTAP Attach Request
- BSSGP/DTAP Attach Accept
- BSSGP/DTAP Routing Area Update Request
- BSSGP/DTAP Routing Area Update Accept
- BSSGP/DTAP Activate PDP Context Request
- BSSGP SUSPEND
- BSSGP SUSPEND ACK

Sponsored-by: On-Waves ehf
diff --git a/openbsc/tests/gbproxy/gbproxy_test.c b/openbsc/tests/gbproxy/gbproxy_test.c
index ba1c51b..97dbfec 100644
--- a/openbsc/tests/gbproxy/gbproxy_test.c
+++ b/openbsc/tests/gbproxy/gbproxy_test.c
@@ -28,13 +28,88 @@
 #include <osmocom/gprs/gprs_bssgp.h>
 
 #include <openbsc/gb_proxy.h>
+#include <openbsc/debug.h>
 
 #define REMOTE_BSS_ADDR 0x01020304
 #define REMOTE_SGSN_ADDR 0x05060708
 
 #define SGSN_NSEI 0x0100
 
-struct gbproxy_config gbcfg;
+struct gbproxy_config gbcfg = {0};
+
+/* Base Station Subsystem GPRS Protocol: GSM A-I/F DTAP - Attach Request */
+static const unsigned char bssgp_attach_req[75] = {
+	0x01, 0xbb, 0xc5, 0x46, 0x79, 0x00, 0x00, 0x04,
+	0x08, 0x88, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60,
+	0x75, 0x30, 0x00, 0x80, 0x0e, 0x00, 0x34, 0x01,
+	0xc0, 0x01, 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21,
+	0x08, 0x02, 0x05, 0xf4, 0xfb, 0xc5, 0x46, 0x79,
+	0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18,
+	0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60,
+	0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80,
+	0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00,
+	0x16, 0x6d, 0x01
+};
+
+/* Base Station Subsystem GPRS Protocol: GSM A-I/F DTAP - Attach Accept */
+static const unsigned char bssgp_attach_acc[88] = {
+	0x00, 0xbb, 0xc5, 0x46, 0x79, 0x00, 0x50, 0x20,
+	0x16, 0x82, 0x02, 0x58, 0x13, 0x99, 0x18, 0xb3,
+	0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80,
+	0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba,
+	0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, 0x0a,
+	0x82, 0x08, 0x02, 0x0d, 0x88, 0x11, 0x12, 0x13,
+	0x14, 0x15, 0x16, 0x17, 0x18, 0x00, 0x81, 0x00,
+	0x0e, 0x9e, 0x41, 0xc0, 0x05, 0x08, 0x02, 0x01,
+	0x49, 0x04, 0x21, 0x63, 0x54, 0x40, 0x50, 0x60,
+	0x19, 0xcd, 0xd7, 0x08, 0x17, 0x16, 0x18, 0x05,
+	0xf4, 0xfb, 0xc5, 0x47, 0x22, 0x42, 0x67, 0x9a
+};
+
+/* Base Station Subsystem GPRS Protocol: GSM A-I/F DTAP - Routing Area Update Request */
+static const unsigned char bssgp_ra_upd_req[85] = {
+	0x01, 0xaf, 0xe2, 0x80, 0x6e, 0x00, 0x00, 0x04,
+	0x08, 0x88, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60,
+	0x70, 0x80, 0x00, 0x80, 0x0e, 0x00, 0x3e, 0x01,
+	0xc0, 0x15, 0x08, 0x08, 0x10, 0x11, 0x22, 0x33,
+	0x40, 0x50, 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33,
+	0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48,
+	0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8,
+	0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02,
+	0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27,
+	0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02,
+	0x20, 0x00, 0x96, 0x3e, 0x97
+};
+
+/* Base Station Subsystem GPRS Protocol: GSM A-I/F DTAP - Routing Area Update Accept */
+static const unsigned char bssgp_ra_upd_acc[91] = {
+	0x00, 0xaf, 0xe2, 0x80, 0x6e, 0x00, 0x50, 0x20,
+	0x16, 0x82, 0x02, 0x58, 0x13, 0x9d, 0x19, 0x13,
+	0x42, 0x33, 0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02,
+	0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48,
+	0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8,
+	0x48, 0x02, 0x00, 0x0a, 0x82, 0x07, 0x04, 0x0d,
+	0x88, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x00, 0x81, 0x00, 0x0e, 0x9d, 0x41, 0xc0,
+	0x19, 0x08, 0x09, 0x00, 0x49, 0x21, 0x63, 0x54,
+	0x40, 0x50, 0x60, 0x19, 0x54, 0xab, 0xb3, 0x18,
+	0x05, 0xf4, 0xef, 0xe2, 0x81, 0x17, 0x17, 0x16,
+	0xc3, 0xbf, 0xcc
+};
+
+/* Base Station Subsystem GPRS Protocol: GSM A-I/F DTAP - Activate PDP Context Request */
+static const unsigned char bssgp_act_pdp_ctx_req[76] = {
+	0x01, 0xef, 0xe2, 0xb7, 0x00, 0x00, 0x00, 0x04,
+	0x08, 0x88, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60,
+	0x75, 0x30, 0x00, 0x80, 0x0e, 0x00, 0x35, 0x01,
+	0xc0, 0x0d, 0x0a, 0x41, 0x05, 0x03, 0x0c, 0x00,
+	0x00, 0x1f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x01, 0x21, 0x28, 0x03,
+	0x02, 0x61, 0x62, 0x27, 0x14, 0x80, 0x80, 0x21,
+	0x10, 0x01, 0x00, 0x00, 0x10, 0x81, 0x06, 0x00,
+	0x00, 0x00, 0x00, 0x83, 0x06, 0x00, 0x00, 0x00,
+	0x00, 0x5a, 0xff, 0x02
+};
 
 static int gprs_process_message(struct gprs_ns_inst *nsi, const char *text,
 				struct sockaddr_in *peer, const unsigned char* data,
@@ -168,6 +243,37 @@
 	send_ns_unitdata(nsi, "BVC_RESET_ACK", src_addr, 0, msg, sizeof(msg));
 }
 
+static void send_bssgp_suspend(struct gprs_ns_inst *nsi,
+			       struct sockaddr_in *src_addr,
+			       struct gprs_ra_id *raid)
+{
+	/* Base Station Subsystem GPRS Protocol, BSSGP SUSPEND */
+	unsigned char msg[15] = {
+		0x0b, 0x1f, 0x84, 0xcc, 0xd1, 0x75, 0x8b, 0x1b,
+		0x86, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60
+	};
+
+	gsm48_construct_ra(msg + 9, raid);
+
+	send_ns_unitdata(nsi, "BVC_SUSPEND", src_addr, 0, msg, sizeof(msg));
+}
+
+static void send_bssgp_suspend_ack(struct gprs_ns_inst *nsi,
+				   struct sockaddr_in *src_addr,
+				   struct gprs_ra_id *raid)
+{
+	/* Base Station Subsystem GPRS Protocol, BSSGP SUSPEND ACK */
+	unsigned char msg[18] = {
+		0x0c, 0x1f, 0x84, 0xcc, 0xd1, 0x75, 0x8b, 0x1b,
+		0x86, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x1d,
+		0x81, 0x01
+	};
+
+	gsm48_construct_ra(msg + 9, raid);
+
+	send_ns_unitdata(nsi, "BVC_SUSPEND_ACK", src_addr, 0, msg, sizeof(msg));
+}
+
 static void setup_ns(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
 		     uint16_t nsvci, uint16_t nsei)
 {
@@ -687,6 +793,90 @@
 	gbprox_reset();
 }
 
+static void test_gbproxy_ra_patching()
+{
+	struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
+	struct sockaddr_in bss_peer[1] = {{0},};
+	struct sockaddr_in sgsn_peer= {0};
+	struct  gprs_ra_id rai_bss =
+		{.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96};
+	struct  gprs_ra_id rai_sgsn =
+		{.mcc = 123, .mnc = 456, .lac = 16464, .rac = 96};
+	struct  gprs_ra_id rai_unknown =
+		{.mcc = 1, .mnc = 99, .lac = 99, .rac = 96};
+
+	bssgp_nsi = nsi;
+	gbcfg.nsi = bssgp_nsi;
+	gbcfg.nsip_sgsn_nsei = SGSN_NSEI;
+
+	sgsn_peer.sin_family = AF_INET;
+	sgsn_peer.sin_port = htons(32000);
+	sgsn_peer.sin_addr.s_addr = htonl(REMOTE_SGSN_ADDR);
+
+	bss_peer[0].sin_family = AF_INET;
+	bss_peer[0].sin_port = htons(1111);
+	bss_peer[0].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR);
+
+	printf("--- Initialise SGSN ---\n\n");
+
+	connect_sgsn(nsi, &sgsn_peer);
+	gprs_dump_nsi(nsi);
+
+	printf("--- Initialise BSS 1 ---\n\n");
+
+	setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000);
+	setup_bssgp(nsi, &bss_peer[0], 0x1002);
+	gprs_dump_nsi(nsi);
+	gbprox_dump_peers(stdout, 0);
+
+	send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002);
+
+	send_bssgp_suspend(nsi, &bss_peer[0], &rai_bss);
+	send_bssgp_suspend_ack(nsi, &sgsn_peer, &rai_sgsn);
+
+	gbprox_dump_global(stdout, 0);
+	gbprox_dump_peers(stdout, 0);
+
+	printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n");
+
+	send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1002,
+			 bssgp_attach_req, sizeof(bssgp_attach_req));
+
+	send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x1002,
+			 bssgp_attach_acc, sizeof(bssgp_attach_acc));
+
+	send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1002,
+			 bssgp_ra_upd_req, sizeof(bssgp_ra_upd_req));
+
+	send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x1002,
+			 bssgp_ra_upd_acc, sizeof(bssgp_ra_upd_acc));
+
+	/* Replace APN */
+	send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1002,
+			 bssgp_act_pdp_ctx_req, sizeof(bssgp_act_pdp_ctx_req));
+
+	/* TODO: Re-configure to test APN IE removal */
+
+	/* Remove APN */
+	send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1002,
+			 bssgp_act_pdp_ctx_req, sizeof(bssgp_act_pdp_ctx_req));
+
+	gbprox_dump_global(stdout, 0);
+	gbprox_dump_peers(stdout, 0);
+
+	printf("--- Bad cases ---\n\n");
+
+	send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1eee);
+	send_bssgp_suspend_ack(nsi, &sgsn_peer, &rai_unknown);
+
+	gbprox_dump_global(stdout, 0);
+	gbprox_dump_peers(stdout, 0);
+
+	gprs_ns_destroy(nsi);
+	nsi = NULL;
+	gbprox_reset();
+}
+
 
 static struct log_info info = {};
 
@@ -707,6 +897,7 @@
 	printf("===== GbProxy test START\n");
 	test_gbproxy();
 	test_gbproxy_ident_changes();
+	test_gbproxy_ra_patching();
 	printf("===== GbProxy test END\n\n");
 
 	exit(EXIT_SUCCESS);