nat: Send a IPA PING down the stream and wait for the pong.

We will send a ping every 20 seconds and if we have no pong
within 5 seconds we will close down the BSC connection and
wait for a reconnect. We will start this after having
authenticated the BSC and we stop the timer when destructing
the BSC connection.
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h
index 28ed4e8..d72e3b4 100644
--- a/openbsc/include/openbsc/bsc_nat.h
+++ b/openbsc/include/openbsc/bsc_nat.h
@@ -93,6 +93,10 @@
 	/* a timeout node */
 	struct timer_list id_timeout;
 
+	/* pong timeout */
+	struct timer_list ping_timeout;
+	struct timer_list pong_timeout;
+
 	/* a back pointer */
 	struct bsc_nat *nat;
 };
diff --git a/openbsc/src/nat/bsc_nat.c b/openbsc/src/nat/bsc_nat.c
index 121b2bb..4a8fca7 100644
--- a/openbsc/src/nat/bsc_nat.c
+++ b/openbsc/src/nat/bsc_nat.c
@@ -108,6 +108,46 @@
 	bsc_send_data(bsc, gsm_reset_ack, sizeof(gsm_reset_ack), IPAC_PROTO_SCCP);
 }
 
+static void send_ping(struct bsc_connection *bsc)
+{
+	static const u_int8_t id_ping[] = {
+		IPAC_MSGT_PING,
+	};
+
+	bsc_send_data(bsc, id_ping, sizeof(id_ping), IPAC_PROTO_IPACCESS);
+}
+
+static void bsc_pong_timeout(void *_bsc)
+{
+	struct bsc_connection *bsc = _bsc;
+
+	LOGP(DNAT, LOGL_ERROR, "BSC Nr: %d PONG timeout.\n", bsc->cfg->nr);
+	bsc_close_connection(bsc);
+}
+
+static void bsc_ping_timeout(void *_bsc)
+{
+	struct bsc_connection *bsc = _bsc;
+
+	send_ping(bsc);
+
+	/* send another ping in 20 seconds */
+	bsc_schedule_timer(&bsc->ping_timeout, 20, 0);
+
+	/* also start a pong timer */
+	bsc_schedule_timer(&bsc->pong_timeout, 5, 0);
+}
+
+static void start_ping_pong(struct bsc_connection *bsc)
+{
+	bsc->pong_timeout.data = bsc;
+	bsc->pong_timeout.cb = bsc_pong_timeout;
+	bsc->ping_timeout.data = bsc;
+	bsc->ping_timeout.cb = bsc_ping_timeout;
+
+	bsc_ping_timeout(bsc);
+}
+
 static void send_id_ack(struct bsc_connection *bsc)
 {
 	static const u_int8_t id_ack[] = {
@@ -437,6 +477,8 @@
 
 	/* stop the timeout timer */
 	bsc_del_timer(&connection->id_timeout);
+	bsc_del_timer(&connection->ping_timeout);
+	bsc_del_timer(&connection->pong_timeout);
 
 	/* remove all SCCP connections */
 	llist_for_each_entry_safe(sccp_patch, tmp, &nat->sccp_connections, list_entry) {
@@ -490,6 +532,7 @@
 			bsc->cfg = conf;
 			bsc_del_timer(&bsc->id_timeout);
 			LOGP(DNAT, LOGL_NOTICE, "Authenticated bsc nr: %d lac: %d\n", conf->nr, conf->lac);
+			start_ping_pong(bsc);
 			return;
 		}
 	}
@@ -622,6 +665,18 @@
 	LOGP(DNAT, LOGL_DEBUG, "MSG from BSC: %s proto: %d\n", hexdump(msg->data, msg->len), msg->l2h[0]);
 
 	/* Handle messages from the BSC */
+	if (bsc->authenticated) {
+		struct ipaccess_head *hh;
+		hh = (struct ipaccess_head *) msg->data;
+
+		/* stop the pong timeout */
+		if (hh->proto == IPAC_PROTO_IPACCESS && msg->l2h[0] == IPAC_MSGT_PONG) {
+			bsc_del_timer(&bsc->pong_timeout);
+			msgb_free(msg);
+			return 0;
+		}
+	}
+
 	/* FIXME: Currently no PONG is sent to the BSC */
 	/* FIXME: Currently no ID ACK is sent to the BSC */
 	forward_sccp_to_msc(bsc, msg);