ggsn: Implement echo req/resp and recovery

This patch is quite big because implementing echo req/resp and recovery
requires having knowledge and managing differentiated state for each GSN
peer attached/connected to osmo-ggsn. This kind of information was not
available in osmo-ggsn nor in libgtp.

So osmo-ggsn is now able to track GSN peers connected to a
ggsn_ctx (associated gsn_t from libgtp) by means of "sgsn_peer" data
structure, and accessible from the ggsn through a list. The instances of
sgsn_peer are currently allocated and destroyed dynamically based on
discovered peer who have at least a pdp context attached to us (we are
not interested in peers without pdp contexts because we don't need to
send echo requests/responses and maintain state in that case).

A new private pointer (pdp_t->priv) data structure struct pdp_priv_t is
added to be able to relate a pdp_t to an sgsn as well as the already
existing pointer to an apn.

An "echo-interval <0-36000>" VTY command is added which allows
configuring time wait between echo requests being sent to each
sgsn_peer. Transmission of echo requests is disabled by default.

Finally, a new "show sgsn" VTY command is introduced, and its output is
also printed during "show ggsn".

Related: OS#4165
Change-Id: Id2c84165dc59dff495106758146a701ca488834f
diff --git a/ggsn/sgsn.h b/ggsn/sgsn.h
new file mode 100644
index 0000000..d2c3c0c
--- /dev/null
+++ b/ggsn/sgsn.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+
+#include "../gtp/pdp.h"
+
+struct ggsn_ctx;
+struct pdp_priv_t;
+
+struct sgsn_peer {
+	struct llist_head entry; /* to be included into ggsn_ctx */
+	struct ggsn_ctx *ggsn; /* backpointer to ggsn_ctx */
+	struct in_addr addr;	/* Addr of the sgsn peer */
+	unsigned int gtp_version; /* GTP version */
+	int remote_restart_ctr; /* Last received Restart Ctr from sgsn peer, -1 == unknown */
+	/* list of pdp contexts associated with this sgsn */
+	struct llist_head pdp_list;
+	/* Sends echo request towards SGSN on expiration. Echo Resp is received
+	   through cb_recovery2(), and echo Req timeout through
+	   cb_conf(GTP_ECHO_REQ, EOF, NULL, cbp); */
+	struct osmo_timer_list echo_timer;
+	/* Number of GTP messages in libgtp transmit queue */
+	unsigned int tx_msgs_queued;
+};
+
+struct sgsn_peer *sgsn_peer_allocate(struct ggsn_ctx *ggsn, struct in_addr *ia, unsigned int gtp_version);
+void sgsn_peer_add_pdp_priv(struct sgsn_peer *sgsn, struct pdp_priv_t *pdp_priv);
+void sgsn_peer_remove_pdp_priv(struct pdp_priv_t *pdp_priv);
+
+void sgsn_echo_timer_start(struct sgsn_peer *sgsn);
+void sgsn_echo_timer_stop(struct sgsn_peer *sgsn);
+
+void sgsn_peer_echo_resp(struct sgsn_peer *sgsn, bool timeout);
+unsigned int sgsn_peer_drop_all_pdp(struct sgsn_peer *sgsn);
+int sgsn_peer_handle_recovery(struct sgsn_peer *sgsn, struct pdp_t *pdp, uint8_t recovery);
+
+#define LOGSGSN(level, sgsn, fmt, args...) { \
+	char _buf[INET_ADDRSTRLEN]; \
+	LOGP(DGGSN, level, "SGSN(%s): " fmt, inet_ntop(AF_INET, &sgsn->addr, _buf, sizeof(_buf)), ## args); \
+	} while (0)