<p>pespin <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-ggsn/+/15267">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  osmith: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">ggsn: Implement echo req/resp and recovery<br><br>This patch is quite big because implementing echo req/resp and recovery<br>requires having knowledge and managing differentiated state for each GSN<br>peer attached/connected to osmo-ggsn. This kind of information was not<br>available in osmo-ggsn nor in libgtp.<br><br>So osmo-ggsn is now able to track GSN peers connected to a<br>ggsn_ctx (associated gsn_t from libgtp) by means of "sgsn_peer" data<br>structure, and accessible from the ggsn through a list. The instances of<br>sgsn_peer are currently allocated and destroyed dynamically based on<br>discovered peer who have at least a pdp context attached to us (we are<br>not interested in peers without pdp contexts because we don't need to<br>send echo requests/responses and maintain state in that case).<br><br>A new private pointer (pdp_t->priv) data structure struct pdp_priv_t is<br>added to be able to relate a pdp_t to an sgsn as well as the already<br>existing pointer to an apn.<br><br>An "echo-interval <0-36000>" VTY command is added which allows<br>configuring time wait between echo requests being sent to each<br>sgsn_peer. Transmission of echo requests is disabled by default.<br><br>Finally, a new "show sgsn" VTY command is introduced, and its output is<br>also printed during "show ggsn".<br><br>Related: OS#4165<br>Change-Id: Id2c84165dc59dff495106758146a701ca488834f<br>---<br>M doc/manuals/vty/ggsn_vty_reference.xml<br>M ggsn/Makefile.am<br>M ggsn/ggsn.c<br>M ggsn/ggsn.h<br>M ggsn/ggsn_vty.c<br>A ggsn/sgsn.c<br>A ggsn/sgsn.h<br>7 files changed, 406 insertions(+), 11 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/doc/manuals/vty/ggsn_vty_reference.xml b/doc/manuals/vty/ggsn_vty_reference.xml</span><br><span>index 15128ed..64bd07a 100644</span><br><span>--- a/doc/manuals/vty/ggsn_vty_reference.xml</span><br><span>+++ b/doc/manuals/vty/ggsn_vty_reference.xml</span><br><span>@@ -1380,6 +1380,18 @@</span><br><span>         <param name='default-apn' doc='Remove default-APN to be used if no other APN matches' /></span><br><span>       </params></span><br><span>     </command></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='show sgsn'></span><br><span style="color: hsl(120, 100%, 40%);">+      <params></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='show' doc='Negate a command or set its defaults' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='sgsn' doc='Gateway GPRS Support NODE (GGSN)' /></span><br><span style="color: hsl(120, 100%, 40%);">+      </params></span><br><span style="color: hsl(120, 100%, 40%);">+    </command></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='echo-interval &lt;0-36000&gt;'></span><br><span style="color: hsl(120, 100%, 40%);">+      <params></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='echo-interval' doc='Gateway GPRS Support NODE (GGSN)' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='&lt;0-36000&gt;' doc='GGSN Number' /></span><br><span style="color: hsl(120, 100%, 40%);">+      </params></span><br><span style="color: hsl(120, 100%, 40%);">+    </command></span><br><span>   </node></span><br><span>   <node id='config-ggsn-apn'></span><br><span>     <name>config-ggsn-apn</name></span><br><span>diff --git a/ggsn/Makefile.am b/ggsn/Makefile.am</span><br><span>index a8ddf1e..ca389f0 100644</span><br><span>--- a/ggsn/Makefile.am</span><br><span>+++ b/ggsn/Makefile.am</span><br><span>@@ -12,4 +12,4 @@</span><br><span> endif</span><br><span> </span><br><span> osmo_ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a</span><br><span style="color: hsl(0, 100%, 40%);">-osmo_ggsn_SOURCES = ggsn_main.c ggsn_vty.c ggsn.c ggsn.h icmpv6.c icmpv6.h checksum.c checksum.h pco.c pco.h</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_ggsn_SOURCES = ggsn_main.c ggsn_vty.c ggsn.c ggsn.h sgsn.c sgsn.h icmpv6.c icmpv6.h checksum.c checksum.h pco.c pco.h</span><br><span>diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c</span><br><span>index 94f47e3..89b183f 100644</span><br><span>--- a/ggsn/ggsn.c</span><br><span>+++ b/ggsn/ggsn.c</span><br><span>@@ -61,6 +61,17 @@</span><br><span> static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what);</span><br><span> static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+void ggsn_close_one_pdp(struct pdp_t *pdp)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      LOGPPDP(LOGL_DEBUG, pdp, "Sending DELETE PDP CTX due to shutdown\n");</span><br><span style="color: hsl(120, 100%, 40%);">+       gtp_delete_context_req2(pdp->gsn, pdp, NULL, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+   /* We have nothing more to do with pdp ctx, free it. Upon cb_delete_context</span><br><span style="color: hsl(120, 100%, 40%);">+      called during this call we'll clean up ggsn related stuff attached to this</span><br><span style="color: hsl(120, 100%, 40%);">+        pdp context. After this call, ippool member is cleared so</span><br><span style="color: hsl(120, 100%, 40%);">+     data is no longer valid and should not be accessed anymore. */</span><br><span style="color: hsl(120, 100%, 40%);">+     gtp_freepdp_teardown(pdp->gsn, pdp);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void pool_close_all_pdp(struct ippool_t *pool)</span><br><span> {</span><br><span>   unsigned int i;</span><br><span>@@ -77,13 +88,7 @@</span><br><span>                 pdp = member->peer;</span><br><span>               if (!pdp)</span><br><span>                    continue;</span><br><span style="color: hsl(0, 100%, 40%);">-               LOGPPDP(LOGL_DEBUG, pdp, "Sending DELETE PDP CTX due to shutdown\n");</span><br><span style="color: hsl(0, 100%, 40%);">-         gtp_delete_context_req2(pdp->gsn, pdp, NULL, 1);</span><br><span style="color: hsl(0, 100%, 40%);">-             /* We have nothing more to do with pdp ctx, free it. Upon cb_delete_context</span><br><span style="color: hsl(0, 100%, 40%);">-                called during this call we'll clean up ggsn related stuff attached to this</span><br><span style="color: hsl(0, 100%, 40%);">-                  pdp context. After this call, ippool member is cleared so</span><br><span style="color: hsl(0, 100%, 40%);">-               data is no longer valid and should not be accessed anymore. */</span><br><span style="color: hsl(0, 100%, 40%);">-               gtp_freepdp_teardown(pdp->gsn, pdp);</span><br><span style="color: hsl(120, 100%, 40%);">+               ggsn_close_one_pdp(pdp);</span><br><span>     }</span><br><span> }</span><br><span> </span><br><span>@@ -341,7 +346,8 @@</span><br><span> static int delete_context(struct pdp_t *pdp)</span><br><span> {</span><br><span>        struct gsn_t *gsn = pdp->gsn;</span><br><span style="color: hsl(0, 100%, 40%);">-        struct apn_ctx *apn = pdp->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct pdp_priv_t *pdp_priv = pdp->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct apn_ctx *apn;</span><br><span>         struct ippoolm_t *member;</span><br><span>    int i;</span><br><span> </span><br><span>@@ -356,6 +362,15 @@</span><br><span>                    LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");</span><br><span>     }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (!pdp_priv) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGPPDP(LOGL_NOTICE, pdp, "Deleting PDP context: without private structure!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Remove from SGSN */</span><br><span style="color: hsl(120, 100%, 40%);">+        sgsn_peer_remove_pdp_priv(pdp_priv);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        apn = pdp_priv->apn;</span><br><span>      if (apn && apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP) {</span><br><span>              if (gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name)) {</span><br><span>                  LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n",</span><br><span>@@ -363,6 +378,8 @@</span><br><span>            }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ talloc_free(pdp_priv);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -380,6 +397,36 @@</span><br><span>    return false;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static struct sgsn_peer* ggsn_find_sgsn(struct ggsn_ctx *ggsn, struct in_addr *peer_addr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct sgsn_peer *sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (memcmp(&sgsn->addr, peer_addr, sizeof(*peer_addr)) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                   return sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct sgsn_peer* ggsn_find_or_create_sgsn(struct ggsn_ctx *ggsn, struct pdp_t *pdp)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct sgsn_peer *sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct in_addr ia;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (gsna2in_addr(&ia, &pdp->gsnrc)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGPPDP(LOGL_ERROR, pdp, "Failed parsing gsnrc (len=%u) to discover SGSN\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                        pdp->gsnrc.l);</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if ((sgsn = ggsn_find_sgsn(ggsn, &ia)))</span><br><span style="color: hsl(120, 100%, 40%);">+           return sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        sgsn = sgsn_peer_allocate(ggsn, &ia, pdp->version);</span><br><span style="color: hsl(120, 100%, 40%);">+    llist_add(&sgsn->entry, &ggsn->sgsn_list);</span><br><span style="color: hsl(120, 100%, 40%);">+      return sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int create_context_ind(struct pdp_t *pdp)</span><br><span> {</span><br><span>  static char name_buf[256];</span><br><span>@@ -391,6 +438,8 @@</span><br><span>     struct apn_ctx *apn = NULL;</span><br><span>  int rc, num_addr, i;</span><br><span>         char *apn_name;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct sgsn_peer *sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct pdp_priv_t *pdp_priv;</span><br><span> </span><br><span>     apn_name = osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);</span><br><span>  LOGPPDP(LOGL_DEBUG, pdp, "Processing create PDP context request for APN '%s'\n",</span><br><span>@@ -492,7 +541,14 @@</span><br><span>    }</span><br><span> </span><br><span>        pdp->ipif = apn->tun.tun; /* TODO */</span><br><span style="color: hsl(0, 100%, 40%);">-      pdp->priv = apn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ pdp_priv = talloc_zero(ggsn, struct pdp_priv_t);</span><br><span style="color: hsl(120, 100%, 40%);">+      pdp->priv = pdp_priv;</span><br><span style="color: hsl(120, 100%, 40%);">+      pdp_priv->lib = pdp;</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Create sgsn and assign pdp to it */</span><br><span style="color: hsl(120, 100%, 40%);">+        sgsn = ggsn_find_or_create_sgsn(ggsn, pdp);</span><br><span style="color: hsl(120, 100%, 40%);">+   sgsn_peer_add_pdp_priv(sgsn, pdp_priv);</span><br><span style="color: hsl(120, 100%, 40%);">+       pdp_priv->apn = apn;</span><br><span> </span><br><span>  /* TODO: change trap to send 2 IPs */</span><br><span>        if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */</span><br><span>@@ -707,6 +763,7 @@</span><br><span> /* libgtp callback for confirmations */</span><br><span> static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+     struct sgsn_peer *sgsn;</span><br><span>      int rc = 0;</span><br><span> </span><br><span>      if (cause == EOF)</span><br><span>@@ -725,12 +782,31 @@</span><br><span>               Rx path. This code is nevertheless left here in order to ease</span><br><span>                future developent and avoid possible future memleaks once more</span><br><span>               scenarios where GGSN sends a DeleteCtxRequest are introduced. */</span><br><span style="color: hsl(0, 100%, 40%);">-                if (pdp)</span><br><span style="color: hsl(120, 100%, 40%);">+           if (pdp)</span><br><span>                     rc = pdp_freepdp(pdp);</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case GTP_ECHO_REQ:</span><br><span style="color: hsl(120, 100%, 40%);">+            sgsn = (struct sgsn_peer *)cbp;</span><br><span style="color: hsl(120, 100%, 40%);">+               sgsn_peer_echo_resp(sgsn, cause == EOF);</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span>       }</span><br><span>    return rc;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int cb_recovery3(struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ggsn_ctx *ggsn = (struct ggsn_ctx *)gsn->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct sgsn_peer *sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     sgsn = ggsn_find_sgsn(ggsn, &peer->sin_addr);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!sgsn) {</span><br><span style="color: hsl(120, 100%, 40%);">+          LOGPGGSN(LOGL_NOTICE, ggsn, "Received Recovery IE for unknown SGSN (no PDP contexts active)\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return sgsn_peer_handle_recovery(sgsn, pdp, recovery);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Start a given GGSN */</span><br><span> int ggsn_start(struct ggsn_ctx *ggsn)</span><br><span> {</span><br><span>@@ -778,6 +854,7 @@</span><br><span>  gtp_set_cb_delete_context(ggsn->gsn, delete_context);</span><br><span>     gtp_set_cb_create_context_ind(ggsn->gsn, create_context_ind);</span><br><span>     gtp_set_cb_conf(ggsn->gsn, cb_conf);</span><br><span style="color: hsl(120, 100%, 40%);">+       gtp_set_cb_recovery3(ggsn->gsn, cb_recovery3);</span><br><span> </span><br><span>        LOGPGGSN(LOGL_NOTICE, ggsn, "Successfully started\n");</span><br><span>     ggsn->started = true;</span><br><span>diff --git a/ggsn/ggsn.h b/ggsn/ggsn.h</span><br><span>index 6155b30..f23df54 100644</span><br><span>--- a/ggsn/ggsn.h</span><br><span>+++ b/ggsn/ggsn.h</span><br><span>@@ -14,6 +14,8 @@</span><br><span> #include "../lib/in46_addr.h"</span><br><span> #include "../gtp/gtp.h"</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include "sgsn.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #define APN_TYPE_IPv4       0x01    /* v4-only */</span><br><span> #define APN_TYPE_IPv6  0x02    /* v6-only */</span><br><span> #define APN_TYPE_IPv4v6        0x04    /* v4v6 dual-stack */</span><br><span>@@ -89,6 +91,14 @@</span><br><span>   struct apn_ctx_ip v6;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct pdp_priv_t {</span><br><span style="color: hsl(120, 100%, 40%);">+  struct pdp_t *lib; /* pointer to libgtp associated pdp_t instance */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct sgsn_peer *sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct apn_ctx *apn;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct llist_head entry; /* to be included into sgsn_peer */</span><br><span style="color: hsl(120, 100%, 40%);">+  /* struct ggsn_ctx can be reached through lib->gsn->priv, or through sgsn->ggsn */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct ggsn_ctx {</span><br><span>       /* global list of GGSNs */</span><br><span>   struct llist_head list;</span><br><span>@@ -96,6 +106,9 @@</span><br><span>         /* list of APNs in this GGSN */</span><br><span>      struct llist_head apn_list;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+       /* list of SGSN peers (struct sgsn_peer) in this GGSN. TODO: hash table with key <ip+port>? */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct llist_head sgsn_list;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>       bool started;</span><br><span> </span><br><span>    struct {</span><br><span>@@ -112,6 +125,8 @@</span><br><span>               struct in46_addr gtpu_addr;</span><br><span>          /* directory for state file */</span><br><span>               char *state_dir;</span><br><span style="color: hsl(120, 100%, 40%);">+              /* Time between Echo requests on each SGSN */</span><br><span style="color: hsl(120, 100%, 40%);">+         unsigned int echo_interval;</span><br><span>          /* administratively shut-down (true) or not (false) */</span><br><span>               bool shutdown;</span><br><span>       } cfg;</span><br><span>@@ -145,6 +160,7 @@</span><br><span> extern int ggsn_stop(struct ggsn_ctx *ggsn);</span><br><span> extern int apn_start(struct apn_ctx *apn);</span><br><span> extern int apn_stop(struct apn_ctx *apn);</span><br><span style="color: hsl(120, 100%, 40%);">+void ggsn_close_one_pdp(struct pdp_t *pdp);</span><br><span> </span><br><span> #define LOGPAPN(level, apn, fmt, args...)                   \</span><br><span>    LOGP(DGGSN, level, "APN(%s): " fmt, (apn)->cfg.name, ## args)</span><br><span>diff --git a/ggsn/ggsn_vty.c b/ggsn/ggsn_vty.c</span><br><span>index 5684f5a..cb92a8a 100644</span><br><span>--- a/ggsn/ggsn_vty.c</span><br><span>+++ b/ggsn/ggsn_vty.c</span><br><span>@@ -40,6 +40,7 @@</span><br><span> #include "../lib/util.h"</span><br><span> </span><br><span> #include "ggsn.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "sgsn.h"</span><br><span> </span><br><span> #define PREFIX_STR "Prefix (Network/Netmask)\n"</span><br><span> #define IFCONFIG_STR  "GGSN-based interface configuration\n"</span><br><span>@@ -79,6 +80,7 @@</span><br><span>         ggsn->cfg.state_dir = talloc_strdup(ggsn, "/tmp");</span><br><span>      ggsn->cfg.shutdown = true;</span><br><span>        INIT_LLIST_HEAD(&ggsn->apn_list);</span><br><span style="color: hsl(120, 100%, 40%);">+      INIT_LLIST_HEAD(&ggsn->sgsn_list);</span><br><span> </span><br><span>        llist_add_tail(&ggsn->list, &g_ggsn_list);</span><br><span>        return ggsn;</span><br><span>@@ -328,6 +330,80 @@</span><br><span>  return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void show_one_sgsn(struct vty *vty, const struct sgsn_peer *sgsn, const char* prefix)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char buf[INET_ADDRSTRLEN];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  inet_ntop(AF_INET, &sgsn->addr, buf, sizeof(buf));</span><br><span style="color: hsl(120, 100%, 40%);">+     vty_out(vty, "%s(S)GSN %s%s", prefix, buf, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+    vty_out(vty, "%s Restart Counter: %d%s", prefix, sgsn->remote_restart_ctr, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "%s PDP contexts: %d%s", prefix, llist_count(&sgsn->pdp_list), VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+    vty_out(vty, "%s Echo Requests in-flight: %u%s", prefix, sgsn->tx_msgs_queued, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_ggsn_show_sgsn, cfg_ggsn_show_sgsn_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+     "show sgsn",</span><br><span style="color: hsl(120, 100%, 40%);">+        NO_STR GGSN_STR "Remove the GGSN from administrative shut-down\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct sgsn_peer *sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+          show_one_sgsn(vty, sgsn, "");</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Seee 3GPP TS 29.060 section 7.2.1 */</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_ggsn_echo_interval, cfg_ggsn_echo_interval_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+  "echo-interval <1-36000>",</span><br><span style="color: hsl(120, 100%, 40%);">+    GGSN_STR "GGSN Number\n"</span><br><span style="color: hsl(120, 100%, 40%);">+    "Send an echo request to this static GGSN every interval\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Interval between echo requests in seconds\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;</span><br><span style="color: hsl(120, 100%, 40%);">+    int prev_interval = ggsn->cfg.echo_interval;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct sgsn_peer *sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ggsn->cfg.echo_interval = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ggsn->cfg.echo_interval < 60)</span><br><span style="color: hsl(120, 100%, 40%);">+               vty_out(vty, "%% 3GPP TS 29.060 section states interval should " \</span><br><span style="color: hsl(120, 100%, 40%);">+                       "not be lower than 60 seconds, use this value for " \</span><br><span style="color: hsl(120, 100%, 40%);">+                       "testing purposes only!%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (prev_interval == ggsn->cfg.echo_interval)</span><br><span style="color: hsl(120, 100%, 40%);">+              return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Re-enable echo timer for all sgsn */</span><br><span style="color: hsl(120, 100%, 40%);">+       llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)</span><br><span style="color: hsl(120, 100%, 40%);">+            sgsn_echo_timer_start(sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_ggsn_no_echo_interval, cfg_ggsn_no_echo_interval_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+     "no echo-interval",</span><br><span style="color: hsl(120, 100%, 40%);">+ GGSN_STR "GGSN Number\n"</span><br><span style="color: hsl(120, 100%, 40%);">+    NO_STR "Send an echo request to this static GGSN every interval.\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;</span><br><span style="color: hsl(120, 100%, 40%);">+    int prev_interval = ggsn->cfg.echo_interval;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct sgsn_peer *sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (prev_interval == ggsn->cfg.echo_interval)</span><br><span style="color: hsl(120, 100%, 40%);">+              return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ggsn->cfg.echo_interval = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Disable echo timer for all sgsn */</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)</span><br><span style="color: hsl(120, 100%, 40%);">+            sgsn_echo_timer_stop(sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* APN Node */</span><br><span> </span><br><span> static struct cmd_node apn_node = {</span><br><span>@@ -716,6 +792,8 @@</span><br><span>                  config_write_apn(vty, apn);</span><br><span>          if (ggsn->cfg.default_apn)</span><br><span>                        vty_out(vty, " default-apn %s%s", ggsn->cfg.default_apn->cfg.name, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+              if (ggsn->cfg.echo_interval)</span><br><span style="color: hsl(120, 100%, 40%);">+                       vty_out(vty, " echo-interval %u%s", ggsn->cfg.echo_interval, VTY_NEWLINE);</span><br><span>              /* must be last */</span><br><span>           vty_out(vty, " %sshutdown ggsn%s", ggsn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);</span><br><span>   }</span><br><span>@@ -964,12 +1042,15 @@</span><br><span> static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)</span><br><span> {</span><br><span>         struct apn_ctx *apn;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct sgsn_peer *sgsn;</span><br><span>      vty_out(vty, "GGSN %s: Bound to %s%s", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),</span><br><span>               VTY_NEWLINE);</span><br><span>        /* FIXME */</span><br><span> </span><br><span>      llist_for_each_entry(apn, &ggsn->apn_list, list)</span><br><span>              show_apn(vty, apn);</span><br><span style="color: hsl(120, 100%, 40%);">+   llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)</span><br><span style="color: hsl(120, 100%, 40%);">+            show_one_sgsn(vty, sgsn, " ");</span><br><span> }</span><br><span> </span><br><span> DEFUN(show_ggsn, show_ggsn_cmd,</span><br><span>@@ -1016,6 +1097,9 @@</span><br><span>         install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);</span><br><span>        install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);</span><br><span>   install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+ install_element(GGSN_NODE, &cfg_ggsn_show_sgsn_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+      install_element(GGSN_NODE, &cfg_ggsn_echo_interval_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+  install_element(GGSN_NODE, &cfg_ggsn_no_echo_interval_cmd);</span><br><span> </span><br><span>  install_node(&apn_node, NULL);</span><br><span>   install_element(APN_NODE, &cfg_description_cmd);</span><br><span>diff --git a/ggsn/sgsn.c b/ggsn/sgsn.c</span><br><span>new file mode 100644</span><br><span>index 0000000..8360439</span><br><span>--- /dev/null</span><br><span>+++ b/ggsn/sgsn.c</span><br><span>@@ -0,0 +1,160 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#include "sgsn.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "ggsn.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static bool sgsn_peer_attempt_free(struct sgsn_peer *sgsn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    /* We have to be careful here, since if all pdp ctx for that sgsn were</span><br><span style="color: hsl(120, 100%, 40%);">+           deactivated in-between we sent the Echo Req and receivied the timeout</span><br><span style="color: hsl(120, 100%, 40%);">+         indication, the sgsn (cbp) may be already gone. We need to add some</span><br><span style="color: hsl(120, 100%, 40%);">+           counter reference of echo requets in flight and only free sgsn</span><br><span style="color: hsl(120, 100%, 40%);">+        structures when it goes to zero decreased for all Echo Resp. We do it</span><br><span style="color: hsl(120, 100%, 40%);">+         this way because currently in libgtp there's no understanding of "gsn</span><br><span style="color: hsl(120, 100%, 40%);">+        peer" for which messages are grouped and hence we cannot request</span><br><span style="color: hsl(120, 100%, 40%);">+         libgtp to drop all queued messages for a specific peer. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sgsn->tx_msgs_queued) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGSGSN(LOGL_INFO, sgsn, "Delaying delete, still %u echo messages queued\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                        sgsn->tx_msgs_queued);</span><br><span style="color: hsl(120, 100%, 40%);">+             return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_del(&sgsn->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+       LOGSGSN(LOGL_INFO, sgsn, "Deleting SGSN\n");</span><br><span style="color: hsl(120, 100%, 40%);">+        talloc_free(sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+    return true;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void sgsn_peer_echo_req(struct sgsn_peer *sgsn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ggsn_ctx *ggsn = sgsn->ggsn;</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGSGSN(LOGL_INFO, sgsn, "Tx Echo Request\n");</span><br><span style="color: hsl(120, 100%, 40%);">+      gtp_echo_req(ggsn->gsn, sgsn->gtp_version, sgsn, &sgsn->addr);</span><br><span style="color: hsl(120, 100%, 40%);">+   sgsn->tx_msgs_queued++;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void sgsn_peer_echo_resp(struct sgsn_peer *sgsn, bool timeout)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    if (timeout) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGSGSN(LOGL_NOTICE, sgsn, "Rx Echo Request timed out!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         sgsn_peer_drop_all_pdp(sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGSGSN(LOGL_INFO, sgsn, "Rx Echo Response\n");</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* We decrement it here after dropping all pdps to make sure sgsn was</span><br><span style="color: hsl(120, 100%, 40%);">+    not freed upon last pdp ctx deleted and is still alive now */</span><br><span style="color: hsl(120, 100%, 40%);">+      sgsn->tx_msgs_queued--;</span><br><span style="color: hsl(120, 100%, 40%);">+    if (llist_empty(&sgsn->pdp_list))</span><br><span style="color: hsl(120, 100%, 40%);">+              sgsn_peer_attempt_free(sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void sgsn_echo_timer_start(struct sgsn_peer *sgsn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     if (sgsn->ggsn->cfg.echo_interval == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+       sgsn_peer_echo_req(sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_timer_schedule(&sgsn->echo_timer, sgsn->ggsn->cfg.echo_interval, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void sgsn_echo_timer_stop(struct sgsn_peer *sgsn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_timer_del(&sgsn->echo_timer);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void sgsn_echo_timer_cb(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct sgsn_peer *sgsn = (struct sgsn_peer *) data;</span><br><span style="color: hsl(120, 100%, 40%);">+   sgsn_echo_timer_start(sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct sgsn_peer *sgsn_peer_allocate(struct ggsn_ctx *ggsn, struct in_addr *ia, unsigned int gtp_version)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct sgsn_peer *sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     sgsn = talloc_zero_size(ggsn, sizeof(struct sgsn_peer));</span><br><span style="color: hsl(120, 100%, 40%);">+      sgsn->ggsn = ggsn;</span><br><span style="color: hsl(120, 100%, 40%);">+ sgsn->addr = *ia;</span><br><span style="color: hsl(120, 100%, 40%);">+  sgsn->gtp_version = gtp_version;</span><br><span style="color: hsl(120, 100%, 40%);">+   sgsn->remote_restart_ctr = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+     INIT_LLIST_HEAD(&sgsn->pdp_list);</span><br><span style="color: hsl(120, 100%, 40%);">+      INIT_LLIST_HEAD(&sgsn->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_timer_setup(&sgsn->echo_timer, sgsn_echo_timer_cb, sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       LOGSGSN(LOGL_INFO, sgsn, "Discovered\n");</span><br><span style="color: hsl(120, 100%, 40%);">+   return sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void sgsn_peer_add_pdp_priv(struct sgsn_peer *sgsn, struct pdp_priv_t *pdp_priv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        bool was_empty = llist_empty(&sgsn->pdp_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ pdp_priv->sgsn = sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_add(&pdp_priv->entry, &sgsn->pdp_list);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (was_empty)</span><br><span style="color: hsl(120, 100%, 40%);">+                sgsn_echo_timer_start(sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void sgsn_peer_remove_pdp_priv(struct pdp_priv_t* pdp_priv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct sgsn_peer *sgsn = pdp_priv->sgsn;</span><br><span style="color: hsl(120, 100%, 40%);">+   llist_del(&pdp_priv->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (sgsn && llist_empty(&sgsn->pdp_list)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* No PDP contexts associated to this SGSN, no need to keep it */</span><br><span style="color: hsl(120, 100%, 40%);">+             sgsn_echo_timer_stop(sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+           /* sgsn may not be freed if there are some messages still queued</span><br><span style="color: hsl(120, 100%, 40%);">+                 in libgtp which could return a pointer to it */</span><br><span style="color: hsl(120, 100%, 40%);">+            sgsn_peer_attempt_free(sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   pdp_priv->sgsn = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* High-level function to be called in case a GGSN has disappeared or</span><br><span style="color: hsl(120, 100%, 40%);">+ * otherwise lost state (recovery procedure). It will detach all related pdp ctx</span><br><span style="color: hsl(120, 100%, 40%);">+ * from a ggsn and communicate deact to MS. Optionally (!NULL), one pdp ctx can</span><br><span style="color: hsl(120, 100%, 40%);">+ * be kept alive to allow handling later message which contained the Recovery IE. */</span><br><span style="color: hsl(120, 100%, 40%);">+static unsigned int sgsn_peer_drop_all_pdp_except(struct sgsn_peer *sgsn, struct pdp_priv_t *except)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       unsigned int num = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ char buf[INET_ADDRSTRLEN];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  inet_ntop(AF_INET, &sgsn->addr, buf, sizeof(buf));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   struct pdp_priv_t *pdp, *pdp2;</span><br><span style="color: hsl(120, 100%, 40%);">+        llist_for_each_entry_safe(pdp, pdp2, &sgsn->pdp_list, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (pdp == except)</span><br><span style="color: hsl(120, 100%, 40%);">+                    continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             ggsn_close_one_pdp(pdp->lib);</span><br><span style="color: hsl(120, 100%, 40%);">+              num++;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Note: if except is NULL, all pdp contexts are freed and sgsn is</span><br><span style="color: hsl(120, 100%, 40%);">+       already freed at this point */</span><br><span style="color: hsl(120, 100%, 40%);">+     LOGP(DGGSN, LOGL_INFO, "SGSN(%s) Dropped %u PDP contexts\n", buf, num);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return num;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned int sgsn_peer_drop_all_pdp(struct sgsn_peer *sgsn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      return sgsn_peer_drop_all_pdp_except(sgsn, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int sgsn_peer_handle_recovery(struct sgsn_peer *sgsn, struct pdp_t *pdp, uint8_t recovery)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct pdp_priv_t *pdp_priv = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sgsn->remote_restart_ctr == -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* First received ECHO RESPONSE, note the restart ctr */</span><br><span style="color: hsl(120, 100%, 40%);">+              sgsn->remote_restart_ctr = recovery;</span><br><span style="color: hsl(120, 100%, 40%);">+       } else if (sgsn->remote_restart_ctr != recovery) {</span><br><span style="color: hsl(120, 100%, 40%);">+         /* counter has changed (SGSN restart): release all PDP */</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGSGSN(LOGL_NOTICE, sgsn, "SGSN recovery (%u->%u) pdp=%p, "</span><br><span style="color: hsl(120, 100%, 40%);">+                  "releasing all%s PDP contexts\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                   sgsn->remote_restart_ctr, recovery, pdp, pdp ? " other" : "");</span><br><span style="color: hsl(120, 100%, 40%);">+            sgsn->remote_restart_ctr = recovery;</span><br><span style="color: hsl(120, 100%, 40%);">+               if (pdp)</span><br><span style="color: hsl(120, 100%, 40%);">+                      pdp_priv = pdp->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+              sgsn_peer_drop_all_pdp_except(sgsn, pdp_priv);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/ggsn/sgsn.h b/ggsn/sgsn.h</span><br><span>new file mode 100644</span><br><span>index 0000000..d2c3c0c</span><br><span>--- /dev/null</span><br><span>+++ b/ggsn/sgsn.h</span><br><span>@@ -0,0 +1,46 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdbool.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <netinet/in.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <arpa/inet.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/timer.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "../gtp/pdp.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ggsn_ctx;</span><br><span style="color: hsl(120, 100%, 40%);">+struct pdp_priv_t;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct sgsn_peer {</span><br><span style="color: hsl(120, 100%, 40%);">+       struct llist_head entry; /* to be included into ggsn_ctx */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ggsn_ctx *ggsn; /* backpointer to ggsn_ctx */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct in_addr addr;    /* Addr of the sgsn peer */</span><br><span style="color: hsl(120, 100%, 40%);">+   unsigned int gtp_version; /* GTP version */</span><br><span style="color: hsl(120, 100%, 40%);">+   int remote_restart_ctr; /* Last received Restart Ctr from sgsn peer, -1 == unknown */</span><br><span style="color: hsl(120, 100%, 40%);">+ /* list of pdp contexts associated with this sgsn */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct llist_head pdp_list;</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Sends echo request towards SGSN on expiration. Echo Resp is received</span><br><span style="color: hsl(120, 100%, 40%);">+          through cb_recovery2(), and echo Req timeout through</span><br><span style="color: hsl(120, 100%, 40%);">+          cb_conf(GTP_ECHO_REQ, EOF, NULL, cbp); */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_timer_list echo_timer;</span><br><span style="color: hsl(120, 100%, 40%);">+    /* Number of GTP messages in libgtp transmit queue */</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int tx_msgs_queued;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct sgsn_peer *sgsn_peer_allocate(struct ggsn_ctx *ggsn, struct in_addr *ia, unsigned int gtp_version);</span><br><span style="color: hsl(120, 100%, 40%);">+void sgsn_peer_add_pdp_priv(struct sgsn_peer *sgsn, struct pdp_priv_t *pdp_priv);</span><br><span style="color: hsl(120, 100%, 40%);">+void sgsn_peer_remove_pdp_priv(struct pdp_priv_t *pdp_priv);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void sgsn_echo_timer_start(struct sgsn_peer *sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+void sgsn_echo_timer_stop(struct sgsn_peer *sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void sgsn_peer_echo_resp(struct sgsn_peer *sgsn, bool timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned int sgsn_peer_drop_all_pdp(struct sgsn_peer *sgsn);</span><br><span style="color: hsl(120, 100%, 40%);">+int sgsn_peer_handle_recovery(struct sgsn_peer *sgsn, struct pdp_t *pdp, uint8_t recovery);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOGSGSN(level, sgsn, fmt, args...) { \</span><br><span style="color: hsl(120, 100%, 40%);">+    char _buf[INET_ADDRSTRLEN]; \</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGP(DGGSN, level, "SGSN(%s): " fmt, inet_ntop(AF_INET, &sgsn->addr, _buf, sizeof(_buf)), ## args); \</span><br><span style="color: hsl(120, 100%, 40%);">+        } while (0)</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-ggsn/+/15267">change 15267</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-ggsn/+/15267"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-ggsn </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Id2c84165dc59dff495106758146a701ca488834f </div>
<div style="display:none"> Gerrit-Change-Number: 15267 </div>
<div style="display:none"> Gerrit-PatchSet: 5 </div>
<div style="display:none"> Gerrit-Owner: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: osmith <osmith@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>