gbproxy: Extended test program to simulate SGSN, too

This adds a simulation of the SGSN side of the Gbproxy. The VC is set
up correctly and several combinations of BSSGP messages are sent.

Sponsored-by: On-Waves ehf
diff --git a/openbsc/tests/gbproxy/gbproxy_test.c b/openbsc/tests/gbproxy/gbproxy_test.c
index 0b4521f..93fdbe0 100644
--- a/openbsc/tests/gbproxy/gbproxy_test.c
+++ b/openbsc/tests/gbproxy/gbproxy_test.c
@@ -32,11 +32,13 @@
 #define REMOTE_BSS_ADDR 0x01020304
 #define REMOTE_SGSN_ADDR 0x05060708
 
-#define SGSN_NSEI 0xfffe
+#define SGSN_NSEI 0x0100
 
 struct gbproxy_config gbcfg;
 
-static int gprs_process_message(struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *peer, const unsigned char* data, size_t data_len);
+static int gprs_process_message(struct gprs_ns_inst *nsi, const char *text,
+				struct sockaddr_in *peer, const unsigned char* data,
+				size_t data_len);
 
 static void send_ns_reset(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
 			  enum ns_cause cause, uint16_t nsvci, uint16_t nsei)
@@ -57,6 +59,24 @@
 	gprs_process_message(nsi, "RESET", src_addr, msg, sizeof(msg));
 }
 
+static void send_ns_reset_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
+			      uint16_t nsvci, uint16_t nsei)
+{
+	/* GPRS Network Service, PDU type: NS_RESET_ACK,
+	 */
+	unsigned char msg[9] = {
+		0x03, 0x01, 0x82, 0x11, 0x22,
+		0x04, 0x82, 0x11, 0x22
+	};
+
+	msg[3] = nsvci / 256;
+	msg[4] = nsvci % 256;
+	msg[7] = nsei / 256;
+	msg[8] = nsei % 256;
+
+	gprs_process_message(nsi, "RESET_ACK", src_addr, msg, sizeof(msg));
+}
+
 static void send_ns_alive(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr)
 {
 	/* GPRS Network Service, PDU type: NS_ALIVE */
@@ -87,8 +107,18 @@
 	gprs_process_message(nsi, "UNBLOCK", src_addr, msg, sizeof(msg));
 }
 
-static void send_ns_unitdata(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
-			     uint16_t nsbvci,
+static void send_ns_unblock_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr)
+{
+	/* GPRS Network Service, PDU type: NS_UNBLOCK_ACK */
+	unsigned char msg[1] = {
+		0x07
+	};
+
+	gprs_process_message(nsi, "UNBLOCK_ACK", src_addr, msg, sizeof(msg));
+}
+
+static void send_ns_unitdata(struct gprs_ns_inst *nsi, const char *text,
+			     struct sockaddr_in *src_addr, uint16_t nsbvci,
 			     const unsigned char *bssgp_msg, size_t bssgp_msg_size)
 {
 	/* GPRS Network Service, PDU type: NS_UNITDATA */
@@ -102,7 +132,7 @@
 	msg[3] = nsbvci % 256;
 	memcpy(msg + 4, bssgp_msg, bssgp_msg_size);
 
-	gprs_process_message(nsi, "UNITDATA", src_addr, msg, bssgp_msg_size + 4);
+	gprs_process_message(nsi, text ? text : "UNITDATA", src_addr, msg, bssgp_msg_size + 4);
 }
 
 static void send_bssgp_reset(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
@@ -119,7 +149,23 @@
 	msg[3] = bvci / 256;
 	msg[4] = bvci % 256;
 
-	send_ns_unitdata(nsi, src_addr, 0, msg, sizeof(msg));
+	send_ns_unitdata(nsi, "BVC_RESET", src_addr, 0, msg, sizeof(msg));
+}
+
+static void send_bssgp_reset_ack(struct gprs_ns_inst *nsi,
+				 struct sockaddr_in *src_addr, uint16_t bvci)
+{
+	/* GPRS Network Service, PDU type: NS_UNITDATA, BVCI 0
+	 * BSSGP RESET_ACK */
+	static unsigned char msg[5] = {
+		0x23, 0x04, 0x82, 0x00,
+		0x00
+	};
+
+	msg[3] = bvci / 256;
+	msg[4] = bvci % 256;
+
+	send_ns_unitdata(nsi, "BVC_RESET_ACK", src_addr, 0, msg, sizeof(msg));
 }
 
 static void setup_ns(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
@@ -175,14 +221,19 @@
 			const struct sockaddr *, socklen_t);
 	static sendto_t real_sendto = NULL;
 	uint32_t dest_host = htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr);
+	int      dest_port = htons(((struct sockaddr_in *)dest_addr)->sin_port);
 
 	if (!real_sendto)
 		real_sendto = dlsym(RTLD_NEXT, "sendto");
 
 	if (dest_host == REMOTE_BSS_ADDR)
-		printf("MESSAGE to BSS, msg length %d\n%s\n\n", len, osmo_hexdump(buf, len));
+		printf("MESSAGE to BSS at 0x%08x:%d, msg length %d\n%s\n\n",
+		       dest_host, dest_port,
+		       len, osmo_hexdump(buf, len));
 	else if (dest_host == REMOTE_SGSN_ADDR)
-		printf("MESSAGE to SGSN, msg length %d\n%s\n\n", len, osmo_hexdump(buf, len));
+		printf("MESSAGE to SGSN at 0x%08x:%d, msg length %d\n%s\n\n",
+		       dest_host, dest_port,
+		       len, osmo_hexdump(buf, len));
 	else
 		return real_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
 
@@ -192,12 +243,17 @@
 /* override */
 int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg)
 {
+	typedef int (*gprs_ns_sendmsg_t)(struct gprs_ns_inst *nsi, struct msgb *msg);
+	static gprs_ns_sendmsg_t real_gprs_ns_sendmsg = NULL;
 	uint16_t bvci = msgb_bvci(msg);
 	uint16_t nsei = msgb_nsei(msg);
 
 	unsigned char *buf = msg->data;
 	size_t len = msg->len;
 
+	if (!real_gprs_ns_sendmsg)
+		real_gprs_ns_sendmsg = dlsym(RTLD_NEXT, "gprs_ns_sendmsg");
+
 	if (nsei == SGSN_NSEI)
 		printf("NS UNITDATA MESSAGE to SGSN, BVCI 0x%04x, msg length %d\n%s\n\n",
 		       bvci, len, osmo_hexdump(buf, len));
@@ -205,7 +261,7 @@
 		printf("NS UNITDATA MESSAGE to BSS, BVCI 0x%04x, msg length %d\n%s\n\n",
 		       bvci, len, osmo_hexdump(buf, len));
 
-	return 0;
+	return real_gprs_ns_sendmsg(nsi, msg);
 }
 
 static void dump_rate_ctr_group(FILE *stream, const char *prefix,
@@ -312,9 +368,11 @@
 	printf("Current NS-VCIs:\n");
 	llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
 		struct sockaddr_in *peer = &(nsvc->ip.bts_addr);
-		printf("    VCI 0x%04x, NSEI 0x%04x, peer 0x%08x:%d\n",
+		printf("    VCI 0x%04x, NSEI 0x%04x, peer 0x%08x:%d%s%s\n",
 		       nsvc->nsvci, nsvc->nsei,
-		       ntohl(peer->sin_addr.s_addr), ntohs(peer->sin_port)
+		       ntohl(peer->sin_addr.s_addr), ntohs(peer->sin_port),
+		       nsvc->state & NSE_S_BLOCKED ? ", blocked" : "",
+		       nsvc->state & NSE_S_ALIVE   ? "" : ", dead"
 		      );
 		dump_rate_ctr_group(stdout, "        ", nsvc->ctrg);
 	}
@@ -325,11 +383,16 @@
 {
 	struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
 	struct sockaddr_in bss_peer[4] = {{0},};
+	struct sockaddr_in sgsn_peer= {0};
 
 	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);
@@ -343,6 +406,15 @@
 	bss_peer[3].sin_port = htons(4444);
 	bss_peer[3].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR);
 
+	printf("--- Initialise SGSN ---\n\n");
+
+	gprs_ns_nsip_connect(nsi, &sgsn_peer, SGSN_NSEI, SGSN_NSEI+1);
+	send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, SGSN_NSEI);
+	send_ns_alive_ack(nsi, &sgsn_peer);
+	send_ns_unblock_ack(nsi, &sgsn_peer);
+	send_ns_alive(nsi, &sgsn_peer);
+	gprs_dump_nsi(nsi);
+
 	printf("--- Initialise BSS 1 ---\n\n");
 
 	setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000);
@@ -350,6 +422,8 @@
 	gprs_dump_nsi(nsi);
 	gbprox_dump_peers(stdout, 0);
 
+	send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002);
+
 	printf("--- Initialise BSS 2 ---\n\n");
 
 	setup_ns(nsi, &bss_peer[1], 0x2001, 0x2000);
@@ -357,6 +431,8 @@
 	gprs_dump_nsi(nsi);
 	gbprox_dump_peers(stdout, 0);
 
+	send_bssgp_reset_ack(nsi, &sgsn_peer, 0x2002);
+
 	printf("--- Move BSS 1 to new port ---\n\n");
 
 	setup_ns(nsi, &bss_peer[2], 0x1001, 0x1000);
@@ -387,6 +463,76 @@
 	gprs_dump_nsi(nsi);
 	gbprox_dump_peers(stdout, 0);
 
+	printf("--- Move BSS 1 to original BSS 1 port ---\n\n");
+
+	setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000);
+	gprs_dump_nsi(nsi);
+	gbprox_dump_peers(stdout, 0);
+
+	printf("--- Reset BSS 1 with a new BVCI ---\n\n");
+
+	setup_bssgp(nsi, &bss_peer[0], 0x1012);
+	gprs_dump_nsi(nsi);
+	gbprox_dump_peers(stdout, 0);
+
+	send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1012);
+
+	printf("--- Reset BSS 1 with the old BVCI ---\n\n");
+
+	setup_bssgp(nsi, &bss_peer[0], 0x1002);
+	gprs_dump_nsi(nsi);
+	gbprox_dump_peers(stdout, 0);
+
+	send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002);
+
+	printf("--- Reset BSS 1 with the old BVCI again ---\n\n");
+
+	setup_bssgp(nsi, &bss_peer[0], 0x1002);
+	gprs_dump_nsi(nsi);
+	gbprox_dump_peers(stdout, 0);
+
+	send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002);
+
+	printf("--- Send message from BSS 1 to SGSN, BVCI 0x1012 ---\n\n");
+
+	send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1012, (uint8_t *)"", 0);
+
+	printf("--- Send message from SGSN to BSS 1, BVCI 0x1012 ---\n\n");
+
+	send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x1012, (uint8_t *)"", 0);
+
+	printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n");
+
+	send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1012, (uint8_t *)"", 0);
+
+	printf("--- Send message from SGSN to BSS 1, BVCI 0x1002 ---\n\n");
+
+	send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x1012, (uint8_t *)"", 0);
+
+	printf("--- Send message from BSS 2 to SGSN, BVCI 0x2002 ---\n\n");
+
+	send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x2002, (uint8_t *)"", 0);
+
+	printf("--- Send message from SGSN to BSS 2, BVCI 0x2002 ---\n\n");
+
+	send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x2002, (uint8_t *)"", 0);
+
+	printf("--- Reset BSS 1 with the old BVCI on BSS2's link ---\n\n");
+
+	setup_bssgp(nsi, &bss_peer[2], 0x1002);
+	gprs_dump_nsi(nsi);
+	gbprox_dump_peers(stdout, 0);
+
+	send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002);
+
+	printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n");
+
+	send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1012, (uint8_t *)"", 0);
+
+	printf("--- Send message from SGSN to BSS 1, BVCI 0x1002 ---\n\n");
+
+	send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x1012, (uint8_t *)"", 0);
+
 	gprs_ns_destroy(nsi);
 	nsi = NULL;
 }