sgsn: Delete PDP contexts properly

Currently the PDP contexts are hard freed (via sgsn_pdp_ctx_free)
at some places in gprs_gmm.c on the reception of a Detach Req and on
re-use of an IMSI that is already associated with an MM context. This
can lead to segfaults when there is a pending request or a data
indication at libgtp.

This patch add a new function sgsn_pdp_ctx_terminate that de-associates
the PTP context from the MM context, deactivates SNDCP, sets pdp->mm
to NULL and then calls sgsn_delete_pdp_ctx. sgsn_libgtp is updated to
check for pdp->mm being non-NULL before dereferencing it. The
sgsn_pdp_ctx_terminate function will be called for each PDP context of
an MM context before this context is going to be deleted via
sgsn_mm_ctx_free. To ensure, that the ctx->llme (which is accessed
during the deactivation of SNDCP) remains valid, the call to
gprs_llgmm_assign is moved after the call to sgsn_mm_ctx_free. The
handling of re-used IMSIs is changed to mimic the processing of a
Detach Req.

Addresses:
<0002> gprs_gmm.c:654 MM(/f6b31ab0) Deleting old MM Context for same
    IMSI p_tmsi_old=0xc6f19134
<000f> gprs_sgsn.c:259 PDP freeing PDP context that still has a
    libgtp handle attached to it, this shouldn't happen!
[...]
SEGFAULT

Ticket: OW#1311
Sponsored-by: On-Waves ehf
diff --git a/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c
index 44e94b3..b5285dc 100644
--- a/openbsc/src/gprs/sgsn_libgtp.c
+++ b/openbsc/src/gprs/sgsn_libgtp.c
@@ -263,6 +263,12 @@
 	LOGPDPCTXP(LOGL_INFO, pctx, "Received CREATE PDP CTX CONF, cause=%d(%s)\n",
 		cause, get_value_string(gtp_cause_strs, cause));
 
+	if (!pctx->mm) {
+		LOGP(DGPRS, LOGL_INFO,
+		     "No MM context, aborting CREATE PDP CTX CONF\n");
+		return -EIO;
+	}
+
 	/* Check for cause value if it was really successful */
 	if (cause < 0) {
 		LOGP(DGPRS, LOGL_NOTICE, "Create PDP ctx req timed out\n");
@@ -314,16 +320,22 @@
 static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
 {
 	struct sgsn_pdp_ctx *pctx = cbp;
-	int rc;
+	int rc = 0;
 
 	LOGPDPCTXP(LOGL_INFO, pctx, "Received DELETE PDP CTX CONF, cause=%d(%s)\n",
 		cause, get_value_string(gtp_cause_strs, cause));
 
-	/* Deactivate the SNDCP layer */
-	sndcp_sm_deactivate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi);
+	if (pctx->mm) {
+		/* Deactivate the SNDCP layer */
+		sndcp_sm_deactivate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi);
 
-	/* Confirm deactivation of PDP context to MS */
-	rc = gsm48_tx_gsm_deact_pdp_acc(pctx);
+		/* Confirm deactivation of PDP context to MS */
+		rc = gsm48_tx_gsm_deact_pdp_acc(pctx);
+	} else {
+		LOGPDPCTXP(LOGL_NOTICE, pctx,
+			   "Not deactivating SNDCP layer since the MM context "
+			   "is not available\n");
+	}
 
 	/* unlink the now non-existing library handle from the pdp
 	 * context */