osmo-sgsn: ping GGSN periodically and check for restart counter

Before this commit, echo req/rsp logic was implemented in libgtp but
never used in osmo-sgsn.

This commit adds a timer which periodically sends a GTP ECHO Request to
every GGSN if there's at least one pdpd context associated with it. This
way by checking the restart counter in the ECHO Reply it can be known if
the GGSN was restarted. In this case, logic already present in osmo-sgsn
will terminate all pdp contexts associated with that GGSN.

Change-Id: I9d714726785407859f26bbef052cd0efc28e8dae
diff --git a/src/gprs/gprs_sgsn.c b/src/gprs/gprs_sgsn.c
index a58e504..55ce096 100644
--- a/src/gprs/gprs_sgsn.c
+++ b/src/gprs/gprs_sgsn.c
@@ -411,7 +411,7 @@
 		return NULL;
 	}
 	llist_add(&pdp->list, &mm->pdp_list);
-	llist_add(&pdp->ggsn_list, &ggsn->pdp_list);
+	sgsn_ggsn_ctx_add_pdp(pdp->ggsn, pdp);
 	llist_add(&pdp->g_list, &sgsn_pdp_ctxts);
 
 	return pdp;
@@ -466,7 +466,7 @@
 	if (pdp->mm)
 		llist_del(&pdp->list);
 	if (pdp->ggsn)
-		llist_del(&pdp->ggsn_list);
+		sgsn_ggsn_ctx_remove_pdp(pdp->ggsn, pdp);
 	llist_del(&pdp->g_list);
 
 	/* _if_ we still have a library handle, at least set it to NULL
@@ -487,6 +487,12 @@
 }
 
 /* GGSN contexts */
+static void echo_timer_cb(void *data)
+{
+	struct sgsn_ggsn_ctx *ggc = (struct sgsn_ggsn_ctx *) data;
+	sgsn_ggsn_echo_req(ggc);
+	osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0);
+}
 
 struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id)
 {
@@ -499,9 +505,11 @@
 	ggc->id = id;
 	ggc->gtp_version = 1;
 	ggc->remote_restart_ctr = -1;
+	ggc->echo_interval = -1;
 	/* if we are called from config file parse, this gsn doesn't exist yet */
 	ggc->gsn = sgsn->gsn;
 	INIT_LLIST_HEAD(&ggc->pdp_list);
+	osmo_timer_setup(&ggc->echo_timer, echo_timer_cb, ggc);
 	llist_add(&ggc->list, &sgsn_ggsn_ctxts);
 
 	return ggc;
@@ -722,6 +730,19 @@
 	return num;
 }
 
+void sgsn_ggsn_ctx_add_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp)
+{
+	if (llist_empty(&ggc->pdp_list) && ggc->echo_interval > 0)
+		osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0);
+	llist_add(&pdp->ggsn_list, &ggc->pdp_list);
+}
+void sgsn_ggsn_ctx_remove_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp)
+{
+	llist_del(&pdp->ggsn_list);
+	if (llist_empty(&ggc->pdp_list) && osmo_timer_pending(&ggc->echo_timer))
+		osmo_timer_del(&ggc->echo_timer);
+}
+
 void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx)
 {
 	OSMO_ASSERT(mmctx != NULL);