bsc: Add a "IPA PING" to the SCCP CR messages

We want to reduce the background traffic and might set the ping
interval to be in the range of minutes. But this means that if
the TCP connection is frozen several "SCCP CR CM Service Requests"
will be stuck in the send queue without ever being answered. I
could have used the logic of not receiving the "SCCP CC" to close
the connection but instead I am introducing an overload to schedule
the ping as part of the normal SCCP connection establishment.

The VTY write case has been manually verified, I have also looked
at a single trace to see that the SCCP CR and the IPA PING is
transfered in the same ethernet frame.
diff --git a/openbsc/include/openbsc/osmo_bsc.h b/openbsc/include/openbsc/osmo_bsc.h
index cd4e4de..19b879f 100644
--- a/openbsc/include/openbsc/osmo_bsc.h
+++ b/openbsc/include/openbsc/osmo_bsc.h
@@ -27,6 +27,9 @@
 	uint16_t cic;
 	int rtp_port;
 
+	/* for advanced ping/pong */
+	int send_ping;
+
 	/* SCCP connection realted */
 	struct sccp_connection *sccp;
 	struct osmo_msc_data *msc;
@@ -45,7 +48,7 @@
 int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg);
 int bsc_open_connection(struct osmo_bsc_sccp_con *sccp, struct msgb *msg);
 enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn,
-				       struct osmo_msc_data *msc);
+				       struct osmo_msc_data *msc, int send_ping);
 int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp);
 
 struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, struct msgb *);
diff --git a/openbsc/include/openbsc/osmo_msc_data.h b/openbsc/include/openbsc/osmo_msc_data.h
index 9c312ca..add561c 100644
--- a/openbsc/include/openbsc/osmo_msc_data.h
+++ b/openbsc/include/openbsc/osmo_msc_data.h
@@ -63,6 +63,7 @@
 	int pong_timeout;
 	struct osmo_timer_list ping_timer;
 	struct osmo_timer_list pong_timer;
+	int advanced_ping;
 	struct bsc_msc_connection *msc_con;
 	int core_ncc;
 	int core_mcc;
@@ -115,6 +116,7 @@
 int osmo_bsc_msc_init(struct osmo_msc_data *msc);
 int osmo_bsc_sccp_init(struct gsm_network *gsmnet);
 int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto);
+int msc_queue_write_with_ping(struct bsc_msc_connection *, struct msgb *msg, int proto);
 
 int osmo_bsc_audio_init(struct gsm_network *network);
 
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_api.c b/openbsc/src/osmo-bsc/osmo_bsc_api.c
index 1751df7..6bda3d4 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_api.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_api.c
@@ -157,13 +157,23 @@
 static int complete_layer3(struct gsm_subscriber_connection *conn,
 			   struct msgb *msg, struct osmo_msc_data *msc)
 {
+	struct timeval tv;
 	struct msgb *resp;
 	uint16_t network_code;
 	uint16_t country_code;
 	enum bsc_con ret;
+	int send_ping = msc->advanced_ping;
+
+	/* Advanced ping/pong handling */
+	if (osmo_timer_pending(&msc->pong_timer))
+		send_ping = 0;
+	if (msc->ping_timeout == 0)
+		send_ping = 0;
+	if (send_ping && osmo_timer_remaining(&msc->ping_timer, NULL, &tv) == -1)
+		send_ping = 0;
 
 	/* allocate resource for a new connection */
-	ret = bsc_create_new_connection(conn, msc);
+	ret = bsc_create_new_connection(conn, msc, send_ping);
 
 	if (ret != BSC_CON_SUCCESS) {
 		/* allocation has failed */
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_msc.c b/openbsc/src/osmo-bsc/osmo_bsc_msc.c
index aebe847..5f2c1c5 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_msc.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_msc.c
@@ -1,8 +1,8 @@
 /*
  * Handle the connection to the MSC. This include ping/timeout/reconnect
  * (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2011 by On-Waves
+ * (C) 2009-2014 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-2014 by On-Waves
  * All Rights Reserved
  *
  * This program is free software; you can redistribute it and/or modify
@@ -46,6 +46,7 @@
 static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn);
 static void send_id_get_response(struct osmo_msc_data *data, int fd);
 static void send_ping(struct osmo_msc_data *data);
+static void schedule_ping_pong(struct osmo_msc_data *data);
 
 /*
  * MGCP forwarding code
@@ -181,6 +182,29 @@
 	return 0;
 }
 
+int msc_queue_write_with_ping(struct bsc_msc_connection *conn,
+			struct msgb *msg, int proto)
+{
+	struct osmo_msc_data *data;
+	uint8_t val;
+
+	/* prepend the header */
+	ipa_prepend_header(msg, proto);
+	if (osmo_wqueue_enqueue(&conn->write_queue, msg) != 0) {
+		LOGP(DMSC, LOGL_FATAL, "Failed to queue IPA/%d\n", proto);
+		msgb_free(msg);
+		return -1;
+	}
+
+	/* add the ping as the other message */
+	val = IPAC_MSGT_PING;
+	msgb_l16tv_put(msg, 1, IPAC_PROTO_IPACCESS, &val);
+
+	data = (struct osmo_msc_data *) conn->write_queue.bfd.data;
+	schedule_ping_pong(data);
+	return 0;
+}
+
 static int msc_alink_do_write(struct osmo_fd *fd, struct msgb *msg)
 {
 	int ret;
@@ -310,6 +334,15 @@
 	msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS);
 }
 
+static void schedule_ping_pong(struct osmo_msc_data *data)
+{
+	/* send another ping in 20 seconds */
+	osmo_timer_schedule(&data->ping_timer, data->ping_timeout, 0);
+
+	/* also start a pong timer */
+	osmo_timer_schedule(&data->pong_timer, data->pong_timeout, 0);
+}
+
 static void msc_ping_timeout_cb(void *_data)
 {
 	struct osmo_msc_data *data = (struct osmo_msc_data *) _data;
@@ -317,12 +350,7 @@
 		return;
 
 	send_ping(data);
-
-	/* send another ping in 20 seconds */
-	osmo_timer_schedule(&data->ping_timer, data->ping_timeout, 0);
-
-	/* also start a pong timer */
-	osmo_timer_schedule(&data->pong_timer, data->pong_timeout, 0);
+	schedule_ping_pong(data);
 }
 
 static void msc_pong_timeout_cb(void *_data)
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_sccp.c b/openbsc/src/osmo-bsc/osmo_bsc_sccp.c
index 3dd0a52..33c737f 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_sccp.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_sccp.c
@@ -1,7 +1,7 @@
 /* Interaction with the SCCP subsystem */
 /*
- * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2011 by On-Waves
+ * (C) 2009-2014 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-2014 by On-Waves
  * All Rights Reserved
  *
  * This program is free software; you can redistribute it and/or modify
@@ -146,6 +146,11 @@
 	if (conn) {
 		struct osmo_bsc_sccp_con *bsc_con = conn->data_ctx;
 		msc_con = bsc_con->msc->msc_con;
+		if (bsc_con->send_ping) {
+			bsc_con->send_ping = 0;
+			msc_queue_write_with_ping(msc_con, msg, IPAC_PROTO_SCCP);
+			return;
+		}
 	} else {
 		msc_con = ctx;
 	}
@@ -189,7 +194,7 @@
 }
 
 enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn,
-			      struct osmo_msc_data *msc)
+			      struct osmo_msc_data *msc, int send_ping)
 {
 	struct osmo_bsc_sccp_con *bsc_con;
 	struct sccp_connection *sccp;
@@ -224,6 +229,8 @@
 	sccp->data_cb = msc_outgoing_sccp_data;
 	sccp->data_ctx = bsc_con;
 
+	bsc_con->send_ping = send_ping;
+
 	/* prepare the timers */
 	bsc_con->sccp_it_timeout.cb = sccp_it_timeout;
 	bsc_con->sccp_it_timeout.data = bsc_con;
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_vty.c b/openbsc/src/osmo-bsc/osmo_bsc_vty.c
index eb12287..b1d09f3 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_vty.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_vty.c
@@ -112,6 +112,10 @@
 	vty_out(vty, " ip.access rtp-base %d%s", msc->rtp_base, VTY_NEWLINE);
 	vty_out(vty, " timeout-ping %d%s", msc->ping_timeout, VTY_NEWLINE);
 	vty_out(vty, " timeout-pong %d%s", msc->pong_timeout, VTY_NEWLINE);
+	if (msc->advanced_ping)
+		vty_out(vty, " timeout-ping advanced%s", VTY_NEWLINE);
+	else
+		vty_out(vty, " no timeout-ping advanced%s", VTY_NEWLINE);
 
 	if (msc->ussd_welcome_txt)
 		vty_out(vty, " bsc-welcome-text %s%s", msc->ussd_welcome_txt, VTY_NEWLINE);
@@ -372,6 +376,26 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_net_msc_advanced_ping,
+      cfg_net_msc_advanced_ping_cmd,
+      "timeout-ping advanced",
+      "Ping timeout handling\nEnable advanced mode during SCCP\n")
+{
+	struct osmo_msc_data *data = osmo_msc_data(vty);
+	data->advanced_ping = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_net_msc_advanced_ping,
+      cfg_no_net_msc_advanced_ping_cmd,
+      "no timeout-ping advanced",
+      NO_STR "Ping timeout handling\nEnable advanced mode during SCCP\n")
+{
+	struct osmo_msc_data *data = osmo_msc_data(vty);
+	data->advanced_ping = 0;
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_net_msc_welcome_ussd,
       cfg_net_msc_welcome_ussd_cmd,
       "bsc-welcome-text .TEXT",
@@ -719,6 +743,8 @@
 	install_element(MSC_NODE, &cfg_net_msc_no_dest_cmd);
 	install_element(MSC_NODE, &cfg_net_msc_ping_time_cmd);
 	install_element(MSC_NODE, &cfg_net_msc_pong_time_cmd);
+	install_element(MSC_NODE, &cfg_net_msc_advanced_ping_cmd);
+	install_element(MSC_NODE, &cfg_no_net_msc_advanced_ping_cmd);
 	install_element(MSC_NODE, &cfg_net_msc_welcome_ussd_cmd);
 	install_element(MSC_NODE, &cfg_net_msc_no_welcome_ussd_cmd);
 	install_element(MSC_NODE, &cfg_net_msc_lost_ussd_cmd);