pespin has uploaded this change for review.

View Change

Handle rx UpdatePDPCtxReq with Tunnel Direct Flags EI=1

GGSN informed us that it received an Error Indication when sending DL data to the RNC.
This probably means the RNC lost its state, aka crashed or was rebooted.
Mark the subscriber as PMM-IDLE, release Iu and point GTPU back to SGSN.
Then page the UE so it hopefully re-creates the state at the RNC.

Related: OS#6512
Related: OS#6519
Related: SYS#5435
Depends: osmo-ggsn.git Change-Id Ic80a9a928c55b6ff85be96014920bb42793cb943
Change-Id: I76d4c387730fdbfb9e7e0dd23a5afb9e762228b1
---
M TODO-RELEASE
M include/osmocom/sgsn/gprs_mm_state_iu_fsm.h
M include/osmocom/sgsn/gtp.h
M src/sgsn/gprs_mm_state_iu_fsm.c
M src/sgsn/sgsn_libgtp.c
5 files changed, 114 insertions(+), 31 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/osmo-sgsn refs/changes/67/37667/1
diff --git a/TODO-RELEASE b/TODO-RELEASE
index 79f557b..eb1cdfd 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -8,3 +8,4 @@
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line
libgtp >1.12.0 new field dir_tun_flags in struct pdp_t
+libgtp >1.12.0 gtp_set_cb_update_context_ind(), gtp_update_context_resp()
diff --git a/include/osmocom/sgsn/gprs_mm_state_iu_fsm.h b/include/osmocom/sgsn/gprs_mm_state_iu_fsm.h
index 6dae759..1d0333c 100644
--- a/include/osmocom/sgsn/gprs_mm_state_iu_fsm.h
+++ b/include/osmocom/sgsn/gprs_mm_state_iu_fsm.h
@@ -17,6 +17,7 @@
E_PMM_PS_CONN_RELEASE,
E_PMM_PS_CONN_ESTABLISH,
E_PMM_RA_UPDATE, /* = Serving RNC relocation */
+ E_PMM_RX_GGSN_GTPU_DT_EI, /* param: struct sgsn_pdp_ctx *pctx */
};

extern struct osmo_fsm mm_state_iu_fsm;
diff --git a/include/osmocom/sgsn/gtp.h b/include/osmocom/sgsn/gtp.h
index 2aec553..1d76243 100644
--- a/include/osmocom/sgsn/gtp.h
+++ b/include/osmocom/sgsn/gtp.h
@@ -26,5 +26,4 @@
int sgsn_gtp_data_req(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi,
struct msgb *msg, uint32_t npdu_len, uint8_t *npdu);
int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx);
-void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen);
int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx);
diff --git a/src/sgsn/gprs_mm_state_iu_fsm.c b/src/sgsn/gprs_mm_state_iu_fsm.c
index e770b7c..629f16c 100644
--- a/src/sgsn/gprs_mm_state_iu_fsm.c
+++ b/src/sgsn/gprs_mm_state_iu_fsm.c
@@ -30,6 +30,7 @@
#include <osmocom/sgsn/sgsn.h>
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/gtp.h>
+#include <osmocom/sgsn/gtp_ggsn.h>
#include <osmocom/sgsn/pdpctx.h>
#include <osmocom/sgsn/mmctx.h>

@@ -44,22 +45,30 @@
#define mm_state_iu_fsm_state_chg(fi, NEXT_STATE) \
osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, mm_state_iu_fsm_timeouts, sgsn->cfg.T_defs, -1)

-static void mmctx_change_gtpu_endpoints_to_sgsn(struct sgsn_mm_ctx *mm_ctx)
+
+static void pdpctx_change_gtpu_endpoint_to_sgsn(const struct sgsn_mm_ctx *mm_ctx, struct sgsn_pdp_ctx *pdp)
{
char buf[INET_ADDRSTRLEN];
+ LOGMMCTXP(LOGL_INFO, mm_ctx, "Changing GTP-U endpoints %s/0x%08x -> %s/0x%08x\n",
+ sgsn_gtp_ntoa(&pdp->lib->gsnlu), pdp->lib->teid_own,
+ inet_ntop(AF_INET, &sgsn->cfg.gtp_listenaddr.sin_addr, buf, sizeof(buf)),
+ pdp->sgsn_teid_own);
+ pdp->lib->gsnlu.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr);
+ memcpy(pdp->lib->gsnlu.v, &sgsn->cfg.gtp_listenaddr.sin_addr,
+ sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
+ pdp->lib->teid_own = pdp->sgsn_teid_own;
+ /* Disable Direct Tunnel Flags DTI. Other flags make no sense here, so also set to 0. */
+ pdp->lib->dir_tun_flags.l = 1;
+ pdp->lib->dir_tun_flags.v[0] = 0x00;
+}
+
+static void mmctx_change_gtpu_endpoints_to_sgsn(struct sgsn_mm_ctx *mm_ctx, struct sgsn_pdp_ctx *pdp_skip_gtp_upd_req)
+{
struct sgsn_pdp_ctx *pdp;
llist_for_each_entry(pdp, &mm_ctx->pdp_list, list) {
- LOGMMCTXP(LOGL_INFO, mm_ctx, "Changing GTP-U endpoints %s/0x%08x -> %s/0x%08x\n",
- sgsn_gtp_ntoa(&pdp->lib->gsnlu), pdp->lib->teid_own,
- inet_ntop(AF_INET, &sgsn->cfg.gtp_listenaddr.sin_addr, buf, sizeof(buf)),
- pdp->sgsn_teid_own);
- pdp->lib->teid_own = pdp->sgsn_teid_own;
- /* Disable Direct Tunnel Flags DTI. Other flags make no sense here, so also set to 0. */
- pdp->lib->dir_tun_flags.l = 1;
- pdp->lib->dir_tun_flags.v[0] = 0x00;
- sgsn_pdp_upd_gtp_u(pdp,
- &sgsn->cfg.gtp_listenaddr.sin_addr,
- sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
+ pdpctx_change_gtpu_endpoint_to_sgsn(mm_ctx, pdp);
+ if (pdp != pdp_skip_gtp_upd_req)
+ gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0);
}
}

@@ -71,6 +80,11 @@
break;
case E_PMM_PS_DETACH:
break;
+ case E_PMM_RX_GGSN_GTPU_DT_EI:
+ /* This should in general not happen, since Direct Tunnel is not
+ * enabled during PMM-IDLE, but there may be a race condition of
+ * packets/events, so simply ignore it. */
+ break;
}
}

@@ -82,6 +96,7 @@
case E_PMM_PS_CONN_RELEASE:
sgsn_ranap_iu_free(ctx);
mm_state_iu_fsm_state_chg(fi, ST_PMM_IDLE);
+ mmctx_change_gtpu_endpoints_to_sgsn(ctx, NULL);
break;
case E_PMM_PS_DETACH:
sgsn_ranap_iu_release_free(ctx, NULL);
@@ -89,16 +104,15 @@
break;
case E_PMM_RA_UPDATE:
break;
+ case E_PMM_RX_GGSN_GTPU_DT_EI: /* GTPU Direct Tunnel (RNC<->GGSN): GGSN Received Error Indication when transmitting DL data*/
+ struct sgsn_pdp_ctx *pctx = data;
+ sgsn_ranap_iu_free(ctx);
+ mm_state_iu_fsm_state_chg(fi, ST_PMM_IDLE);
+ mmctx_change_gtpu_endpoints_to_sgsn(ctx, pctx);
+ break;
}
}

-static void st_pmm_idle_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- struct sgsn_mm_ctx *ctx = fi->priv;
-
- mmctx_change_gtpu_endpoints_to_sgsn(ctx);
-}
-
static void st_pmm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch(event) {
@@ -109,12 +123,19 @@
case E_PMM_PS_DETACH:
mm_state_iu_fsm_state_chg(fi, ST_PMM_DETACHED);
break;
+ case E_PMM_RX_GGSN_GTPU_DT_EI:
+ /* This should in general not happen, since Direct Tunnel is not
+ * enabled during PMM-IDLE, but there may be a race condition of
+ * packets/events, so simply ignore it. */
+ break;
}
}

static struct osmo_fsm_state mm_state_iu_fsm_states[] = {
[ST_PMM_DETACHED] = {
- .in_event_mask = X(E_PMM_PS_ATTACH) | X(E_PMM_PS_DETACH),
+ .in_event_mask = X(E_PMM_PS_ATTACH) |
+ X(E_PMM_PS_DETACH) |
+ X(E_PMM_RX_GGSN_GTPU_DT_EI),
.out_state_mask = X(ST_PMM_CONNECTED),
.name = "Detached",
.action = st_pmm_detached,
@@ -123,7 +144,8 @@
.in_event_mask =
X(E_PMM_PS_CONN_RELEASE) |
X(E_PMM_RA_UPDATE) |
- X(E_PMM_PS_DETACH),
+ X(E_PMM_PS_DETACH) |
+ X(E_PMM_RX_GGSN_GTPU_DT_EI),
.out_state_mask = X(ST_PMM_DETACHED) | X(ST_PMM_IDLE),
.name = "Connected",
.action = st_pmm_connected,
@@ -132,10 +154,10 @@
.in_event_mask =
X(E_PMM_PS_DETACH) |
X(E_PMM_PS_CONN_ESTABLISH) |
- X(E_PMM_PS_ATTACH),
+ X(E_PMM_PS_ATTACH) |
+ X(E_PMM_RX_GGSN_GTPU_DT_EI),
.out_state_mask = X(ST_PMM_DETACHED) | X(ST_PMM_CONNECTED),
.name = "Idle",
- .onenter = st_pmm_idle_on_enter,
.action = st_pmm_idle,
},
};
@@ -146,6 +168,7 @@
OSMO_VALUE_STRING(E_PMM_PS_CONN_ESTABLISH),
OSMO_VALUE_STRING(E_PMM_PS_DETACH),
OSMO_VALUE_STRING(E_PMM_RA_UPDATE),
+ OSMO_VALUE_STRING(E_PMM_RX_GGSN_GTPU_DT_EI),
{ 0, NULL }
};

diff --git a/src/sgsn/sgsn_libgtp.c b/src/sgsn/sgsn_libgtp.c
index f497609..39dcc7a 100644
--- a/src/sgsn/sgsn_libgtp.c
+++ b/src/sgsn/sgsn_libgtp.c
@@ -55,6 +55,7 @@
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/gprs_gmm_fsm.h>
#include <osmocom/sgsn/gprs_mm_state_gb_fsm.h>
+#include <osmocom/sgsn/gprs_mm_state_iu_fsm.h>
#include <osmocom/sgsn/gtp_ggsn.h>
#include <osmocom/sgsn/gtp_mme.h>
#include <osmocom/sgsn/sgsn_rim.h>
@@ -468,13 +469,6 @@
return EOF;
}

-void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen)
-{
- pdp->lib->gsnlu.l = alen;
- memcpy(pdp->lib->gsnlu.v, addr, alen);
- gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0);
-}
-
void sgsn_ggsn_echo_req(struct sgsn_ggsn_ctx *ggc)
{
LOGGGSN(ggc, LOGL_INFO, "GTP Tx Echo Request\n");
@@ -655,6 +649,52 @@
return 0;
}

+/* Called whenever a PDP context is updated from the GGSN for any reason */
+static int cb_update_context_ind(struct pdp_t *pdp)
+{
+ struct sgsn_pdp_ctx *pctx;
+ struct sgsn_mm_ctx *mm;
+ int rc;
+
+ LOGPDPX(DGPRS, LOGL_INFO, pdp, "Context %p was updated\n", pdp);
+
+ pctx = pdp->priv;
+ if (!pctx) {
+ LOGP(DGPRS, LOGL_NOTICE,
+ "GTP DATA IND from GGSN for unknown PDP\n");
+ return -EIO;
+ }
+ mm = pctx->mm;
+ if (!mm) {
+ LOGP(DGPRS, LOGL_ERROR,
+ "PDP context (address=%u) without MM context!\n",
+ pctx->address);
+ return -EIO;
+ }
+
+ if (mm->ran_type == MM_CTX_T_UTRAN_Iu) {
+#ifdef BUILD_IU
+ if (pdp->dir_tun_flags.v[0] & 0x04) { /* EI bit set ? */
+ /* GGSN informed us that it received an Error Indication when sending DL data to the RNC.
+ * This probably means the RNC lost its state, aka crashed or was rebooted.
+ * Page the UE so it re-creates the state at the RNC. */
+ LOGMMCTXP(LOGL_INFO, mm,
+ "GGSN received ErrorInd from RNC while tx DL data. Paging UE in state %s\n",
+ osmo_fsm_inst_state_name(mm->gmm_fsm));
+ rc = osmo_fsm_inst_dispatch(mm->iu.mm_state_fsm, E_PMM_RX_GGSN_GTPU_DT_EI, pctx);
+ rc = gtp_update_context_resp(sgsn->gsn, pdp,
+ GTPCAUSE_ACC_REQ);
+ ranap_iu_page_ps(mm->imsi, &mm->p_tmsi, mm->ra.lac, mm->ra.rac);
+ return rc;
+ }
+#endif
+ }
+
+ rc = gtp_update_context_resp(sgsn->gsn, pdp,
+ GTPCAUSE_ACC_REQ);
+ return rc;
+}
+
/* Called whenever a PDP context is deleted for any reason */
static int cb_delete_context(struct pdp_t *pdp)
{
@@ -951,6 +991,7 @@
}

/* Register callbackcs with libgtp */
+ gtp_set_cb_update_context_ind(gsn, cb_update_context_ind);
gtp_set_cb_delete_context(gsn, cb_delete_context);
gtp_set_cb_conf(gsn, cb_conf);
gtp_set_cb_recovery3(gsn, cb_recovery3);

To view, visit change 37667. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: osmo-sgsn
Gerrit-Branch: master
Gerrit-Change-Id: I76d4c387730fdbfb9e7e0dd23a5afb9e762228b1
Gerrit-Change-Number: 37667
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin@sysmocom.de>
Gerrit-MessageType: newchange