client: Add IPA Keepalive handling

Change-Id: I4acf862bc575741e57ef76c1709d3a00e1850566
diff --git a/src/client.h b/src/client.h
index 1917cb5..b2b885c 100644
--- a/src/client.h
+++ b/src/client.h
@@ -22,6 +22,7 @@
 enum server_conn_fsm_event {
 	SRVC_E_TCP_UP,
 	SRVC_E_TCP_DOWN,
+	SRVC_E_KA_TIMEOUT,
 	SRVC_E_CLIENT_CONN_RES,
 };
 
@@ -32,7 +33,10 @@
 	/* state */
 	struct ipa_client_conn *conn;
 	struct osmo_fsm_inst *fi;
+	struct osmo_fsm_inst *keepalive_fi;
 	int (*handle_rx)(struct rspro_server_conn *conn, const RsproPDU_t *pdu);
+	/* IPA protocol identity */
+	struct ipaccess_unit ipa_dev;
 
 	/* our own component ID */
 	struct app_comp_id own_comp_id;
diff --git a/src/remsim_client.c b/src/remsim_client.c
index fca93f8..49d3f17 100644
--- a/src/remsim_client.c
+++ b/src/remsim_client.c
@@ -50,6 +50,19 @@
 		goto invalid;
 	msg->l2h = &hh->data[0];
 	switch (hh->proto) {
+	case IPAC_PROTO_IPACCESS:
+		rc = ipaccess_bts_handle_ccm(conn, &bc->srv_conn.ipa_dev, msg);
+		if (rc < 0)
+			break;
+		switch (hh->data[0]) {
+		case IPAC_MSGT_PONG:
+			ipa_keepalive_fsm_pong_received(bc->srv_conn.keepalive_fi);
+			rc = 0;
+			break;
+		default:
+			break;
+		}
+		break;
 	case IPAC_PROTO_OSMO:
 		if (!he || msgb_l2len(msg) < sizeof(*he))
 			goto invalid;
diff --git a/src/remsim_client_fsm.c b/src/remsim_client_fsm.c
index dfbc335..a76a24d 100644
--- a/src/remsim_client_fsm.c
+++ b/src/remsim_client_fsm.c
@@ -257,6 +257,7 @@
 static const struct value_string server_conn_fsm_event_names[] = {
 	OSMO_VALUE_STRING(SRVC_E_TCP_UP),
 	OSMO_VALUE_STRING(SRVC_E_TCP_DOWN),
+	OSMO_VALUE_STRING(SRVC_E_KA_TIMEOUT),
 	OSMO_VALUE_STRING(SRVC_E_CLIENT_CONN_RES),
 	{ 0, NULL }
 };
@@ -282,6 +283,19 @@
 		goto invalid;
 	msg->l2h = &hh->data[0];
 	switch (hh->proto) {
+	case IPAC_PROTO_IPACCESS:
+		rc = ipaccess_bts_handle_ccm(srvc->conn, &srvc->ipa_dev, msg);
+		if (rc < 0)
+			break;
+		switch (hh->data[0]) {
+		case IPAC_MSGT_PONG:
+			ipa_keepalive_fsm_pong_received(srvc->keepalive_fi);
+			rc = 0;
+			break;
+		default:
+			break;
+		}
+		break;
 	case IPAC_PROTO_OSMO:
 		if (!he || msgb_l2len(msg) < sizeof(*he))
 			goto invalid;
@@ -310,6 +324,10 @@
 	return -1;
 }
 
+static const struct ipa_keepalive_params ka_params = {
+	.interval = 30,
+	.wait_for_resp = 10,
+};
 
 static void srvc_st_init_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 {
@@ -319,15 +337,33 @@
 	srvc->conn = ipa_client_conn_create(fi, NULL, 0, srvc->server_host, srvc->server_port,
 						srvc_updown_cb, srvc_read_cb, NULL, srvc);
 	if (!srvc->conn) {
-		fprintf(stderr, "Unable to create socket: %s\n", strerror(errno));
-		/* FIXME */
+		LOGPFSM(fi, "Unable to create socket: %s\n", strerror(errno));
+		goto out_fi;
 	}
+
+	srvc->keepalive_fi = ipa_client_conn_alloc_keepalive_fsm(srvc->conn, &ka_params, fi->id);
+	if (!srvc->keepalive_fi) {
+		LOGPFSM(fi, "Unable to create keepalive FSM\n");
+		goto out_conn;
+	}
+	/* ensure parent is notified once keepalive FSM instance is dying */
+	osmo_fsm_inst_change_parent(srvc->keepalive_fi, srvc->fi, SRVC_E_KA_TIMEOUT);
+
 	/* Attempt to connect TCP socket */
 	rc = ipa_client_conn_open(srvc->conn);
 	if (rc < 0) {
 		fprintf(stderr, "Unable to connect: %s\n", strerror(errno));
-		/* FIXME */
+		goto out_ka;
 	}
+
+	return;
+
+out_ka:
+	osmo_fsm_inst_term(srvc->keepalive_fi, OSMO_FSM_TERM_ERROR, NULL);
+out_conn:
+	ipa_client_conn_destroy(srvc->conn);
+out_fi:
+	osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 }
 
 static void srvc_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -349,6 +385,8 @@
 	struct rspro_server_conn *srvc = (struct rspro_server_conn *) fi->priv;
 	RsproPDU_t *pdu;
 
+	ipa_keepalive_fsm_start(srvc->keepalive_fi);
+
 	/* FIXME: Bankd in case of Bankd connection! */
 	pdu = rspro_gen_ConnectClientReq(&srvc->own_comp_id, NULL);
 	ipa_client_conn_send_rspro(srvc->conn, pdu);
@@ -358,6 +396,7 @@
 {
 	switch (event) {
 	case SRVC_E_TCP_DOWN:
+	case SRVC_E_KA_TIMEOUT:
 		osmo_fsm_inst_state_chg(fi, SRVC_ST_REESTABLISH, T2_RECONNECT, 2);
 		break;
 	case SRVC_E_CLIENT_CONN_RES:
@@ -373,6 +412,7 @@
 {
 	switch (event) {
 	case SRVC_E_TCP_DOWN:
+	case SRVC_E_KA_TIMEOUT:
 		osmo_fsm_inst_state_chg(fi, SRVC_ST_REESTABLISH, T2_RECONNECT, 2);
 		break;
 	default:
@@ -385,6 +425,8 @@
 	struct rspro_server_conn *srvc = (struct rspro_server_conn *) fi->priv;
 	int rc;
 
+	ipa_keepalive_fsm_stop(srvc->keepalive_fi);
+
 	/* Attempt to connect TCP socket */
 	rc = ipa_client_conn_open(srvc->conn);
 	if (rc < 0) {
@@ -435,14 +477,14 @@
 	},
 	[SRVC_ST_ESTABLISHED] = {
 		.name = "ESTABLISHED",
-		.in_event_mask = S(SRVC_E_TCP_DOWN) | S(SRVC_E_CLIENT_CONN_RES),
+		.in_event_mask = S(SRVC_E_TCP_DOWN) | S(SRVC_E_KA_TIMEOUT) | S(SRVC_E_CLIENT_CONN_RES),
 		.out_state_mask = S(SRVC_ST_CONNECTED) | S(SRVC_ST_REESTABLISH),
 		.action = srvc_st_established,
 		.onenter = srvc_st_established_onenter,
 	},
 	[SRVC_ST_CONNECTED] = {
 		.name = "CONNECTED",
-		.in_event_mask = S(SRVC_E_TCP_DOWN),
+		.in_event_mask = S(SRVC_E_TCP_DOWN) | S(SRVC_E_KA_TIMEOUT),
 		.out_state_mask = S(SRVC_ST_REESTABLISH),
 		.action = srvc_st_connected,
 	},