[GPRS] SGSN: properly delete a PDP context after receiving PDP CTX DEACT REQ
diff --git a/openbsc/include/openbsc/gprs_gmm.h b/openbsc/include/openbsc/gprs_gmm.h
index 44a28cf..0d03dd9 100644
--- a/openbsc/include/openbsc/gprs_gmm.h
+++ b/openbsc/include/openbsc/gprs_gmm.h
@@ -3,8 +3,9 @@
 
 int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid,
 			     uint8_t cause, uint8_t pco_len, uint8_t *pco_v);
-
 int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp);
+int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp);
+
 int gsm0408_gprs_rcvmsg(struct msgb *msg);
 
 #endif /* _GPRS_GMM_H */
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
index 59bf5c0..b7e0a1b 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -148,12 +148,14 @@
 
 struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
 					   uint8_t nsapi);
+struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
+					 uint8_t tid);
 struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
 					uint8_t nsapi);
 void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp);
 
 
-struct ggsn_ctx {
+struct sgsn_ggsn_ctx {
 	struct llist_head list;
 	uint32_t id;
 	unsigned int gtp_version;
@@ -166,7 +168,7 @@
 
 struct apn_ctx {
 	struct llist_head list;
-	struct ggsn_ctx *ggsn;
+	struct sgsn_ggsn_ctx *ggsn;
 	char *name;
 	char *description;
 };
diff --git a/openbsc/include/openbsc/sgsn.h b/openbsc/include/openbsc/sgsn.h
index 1633b83..a90a864 100644
--- a/openbsc/include/openbsc/sgsn.h
+++ b/openbsc/include/openbsc/sgsn.h
@@ -46,8 +46,10 @@
 int sgsn_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci);
 
 
-struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct ggsn_ctx *ggsn,
+struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
 					 struct sgsn_mm_ctx *mmctx,
 					 uint16_t nsapi,
 					 struct tlv_parsed *tp);
+int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx);
+
 #endif
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index 474c7f2..3eddaff 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -54,6 +54,7 @@
 #include <pdp.h>
 
 extern struct sgsn_instance *sgsn;
+extern struct ggsn_ctx *dummy_ggsn;
 
 /* Protocol related stuff, should go into libosmocore */
 
@@ -530,8 +531,6 @@
 	struct gprs_ra_id old_ra_id;
 	uint8_t upd_type;
 
-	rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_RA_UPDATE]);
-
 	/* Update Type 10.5.5.18 */
 	upd_type = *cur++ & 0x0f;
 
@@ -572,6 +571,8 @@
 	/* FIXME: Update the MM context with the MS radio acc capabilities */
 	/* FIXME: Update the MM context with the MS network capabilities */
 
+	rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_RA_UPDATE]);
+
 	DEBUGPC(DMM, " ACCEPT\n");
 	return gsm48_tx_gmm_ra_upd_ack(msg);
 }
@@ -596,6 +597,7 @@
 	    gh->msg_type != GSM48_MT_GMM_ATTACH_REQ &&
 	    gh->msg_type != GSM48_MT_GMM_RA_UPD_REQ) {
 		LOGP(DMM, LOGL_NOTICE, "Cannot handle GMM for unknown MM CTX\n");
+		/* FIXME: Send GMM_CAUSE_IMPL_DETACHED */
 		return -EINVAL;
 	}
 
@@ -683,8 +685,8 @@
 	/* PDP address */
 	msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR,
 		     pdp->lib->eua.l, pdp->lib->eua.v);
-	/* Optional: Protocol configuration options */
-	if (pdp->lib->pco_neg.l && pdp->lib->pco_neg.v)
+	/* Optional: Protocol configuration options (FIXME: why 'req') */
+	if (pdp->lib->pco_req.l && pdp->lib->pco_req.v)
 		msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT,
 			     pdp->lib->pco_neg.l, pdp->lib->pco_neg.v);
 	/* Optional: Packet Flow Identifier */
@@ -701,7 +703,7 @@
 	struct gsm48_hdr *gh;
 	uint8_t transaction_id = tid ^ 0x8; /* flip */
 
-	DEBUGP(DMM, "<- ACTIVATE PDP CONTEXT REJ\n");
+	DEBUGP(DMM, "<- ACTIVATE PDP CONTEXT REJ(cause=%u)\n", cause);
 
 	mmctx2msgid(msg, mm);
 
@@ -717,23 +719,22 @@
 }
 
 /* Section 9.5.9: Deactivate PDP Context Accept */
-static int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_mm_ctx *mmctx,
-				      struct msgb *old_msg)
+int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp)
 {
-	struct gsm48_hdr *old_gh = (struct gsm48_hdr *) msgb_gmmh(old_msg);
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh;
-	uint8_t transaction_id = ((old_gh->proto_discr >> 4) ^ 0x8); /* flip */
+	uint8_t transaction_id = pdp->ti ^ 0x8; /* flip */
+	int rc;
 
 	DEBUGP(DMM, "<- DEACTIVATE PDP CONTEXT ACK\n");
 
-	gmm_copy_id(msg, old_msg);
+	mmctx2msgid(msg, pdp->mm);
 
 	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 	gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4);
 	gh->msg_type = GSM48_MT_GSM_DEACT_PDP_ACK;
 
-	return gsm48_gmm_sendmsg(msg, 0, mmctx);
+	return gsm48_gmm_sendmsg(msg, 0, pdp->mm);
 }
 
 /* Section 9.5.1: Activate PDP Context Request */
@@ -791,6 +792,8 @@
 		break;
 	}
 
+	DEBUGPC(DMM, "\n");
+
 	/* put the non-TLV elements in the TLV parser structure to
 	 * pass them on to the SGSN / GTP code */
 	tp.lv[OSMO_IE_GSM_REQ_QOS].len = req_qos_len;
@@ -819,11 +822,7 @@
 
 #if 1
 	{
-		struct ggsn_ctx ggsn;
-		ggsn.gtp_version = 1;
-		inet_aton("192.168.100.239", &ggsn.remote_addr);
-		ggsn.gsn = sgsn->gsn;
-		pdp = sgsn_create_pdp_ctx(&ggsn, mmctx, act_req->req_nsapi, &tp);
+		pdp = sgsn_create_pdp_ctx(dummy_ggsn, mmctx, act_req->req_nsapi, &tp);
 		if (!pdp)
 			return -1;
 		pdp->sapi = act_req->req_llc_sapi;
@@ -836,14 +835,24 @@
 }
 
 /* Section 9.5.8: Deactivate PDP Context Request */
-static int gsm48_rx_gsm_deact_pdp_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
+static int gsm48_rx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, struct msgb *msg)
 {
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
+	uint8_t transaction_id = (gh->proto_discr >> 4);
+	struct sgsn_pdp_ctx *pdp;
 
 	DEBUGP(DMM, "-> DEACTIVATE PDP CONTEXT REQ (cause: %s)\n",
 		get_value_string(gsm_cause_names, gh->data[0]));
 
-	return gsm48_tx_gsm_deact_pdp_acc(ctx, msg);
+	pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id);
+	if (!pdp) {
+		LOGP(DMM, LOGL_NOTICE, "Deactivate PDP Context Request for "
+			"non-existing PDP Context (IMSI=%s, TI=%u)\n",
+			mm->imsi, transaction_id);
+		return -EINVAL;
+	}
+
+	return sgsn_delete_pdp_ctx(pdp);
 }
 
 static int gsm48_rx_gsm_status(struct sgsn_mm_ctx *ctx, struct msgb *msg)
@@ -873,6 +882,7 @@
 		break;
 	case GSM48_MT_GSM_DEACT_PDP_REQ:
 		rc = gsm48_rx_gsm_deact_pdp_req(mmctx, msg);
+		break;
 	case GSM48_MT_GSM_STATUS:
 		rc = gsm48_rx_gsm_status(mmctx, msg);
 		break;
diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c
index 7b1b138..6d028ab 100644
--- a/openbsc/src/gprs/gprs_ns.c
+++ b/openbsc/src/gprs/gprs_ns.c
@@ -667,9 +667,13 @@
 			nsvc->nsvci = nsvc->nsei = 0xfffe;
 			nsvc->ip.bts_addr = *saddr;
 			nsvc->state = NSE_S_ALIVE;
+#if 0
+			return gprs_ns_tx_reset(nsvc, NS_CAUSE_PDU_INCOMP_PSTATE);
+#else
 			return gprs_ns_tx_status(nsvc,
 						NS_CAUSE_PDU_INCOMP_PSTATE, 0,
 						msg);
+#endif
 		}
 		rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data,
 						msgb_l2len(msg), 0, 0);
diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c
index 38056f0..2671796 100644
--- a/openbsc/src/gprs/gprs_sgsn.c
+++ b/openbsc/src/gprs/gprs_sgsn.c
@@ -123,6 +123,7 @@
 	return ctx;
 }
 
+
 struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
 					   uint8_t nsapi)
 {
@@ -135,6 +136,19 @@
 	return NULL;
 }
 
+struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
+					 uint8_t tid)
+{
+	struct sgsn_pdp_ctx *pdp;
+
+	llist_for_each_entry(pdp, &mm->pdp_list, list) {
+		if (pdp->ti == tid)
+			return pdp;
+	}
+	return NULL;
+}
+
+
 struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
 					uint8_t nsapi)
 {
@@ -165,11 +179,11 @@
 
 /* GGSN contexts */
 
-struct ggsn_ctx *ggsn_ctx_alloc(uint32_t id)
+struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id)
 {
-	struct ggsn_ctx *ggc;
+	struct sgsn_ggsn_ctx *ggc;
 
-	ggc = talloc_zero(tall_bsc_ctx, struct ggsn_ctx);
+	ggc = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_ctx);
 	if (!ggc)
 		return NULL;
 
@@ -179,9 +193,9 @@
 	return ggc;
 }
 
-struct ggsn_ctx *ggsn_ctx_by_id(uint32_t id)
+struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id)
 {
-	struct ggsn_ctx *ggc;
+	struct sgsn_ggsn_ctx *ggc;
 
 	llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
 		if (id == ggc->id)
@@ -190,13 +204,13 @@
 	return NULL;
 }
 
-struct ggsn_ctx *ggsn_ctx_find_alloc(uint32_t id)
+struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id)
 {
-	struct ggsn_ctx *ggc;
+	struct sgsn_ggsn_ctx *ggc;
 
-	ggc = ggsn_ctx_by_id(id);
+	ggc = sgsn_ggsn_ctx_by_id(id);
 	if (!ggc)
-		ggc = ggsn_ctx_alloc(id);
+		ggc = sgsn_ggsn_ctx_alloc(id);
 	return ggc;
 }
 
diff --git a/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c
index a034e7a..64786de 100644
--- a/openbsc/src/gprs/sgsn_libgtp.c
+++ b/openbsc/src/gprs/sgsn_libgtp.c
@@ -89,7 +89,7 @@
 
 /* generate a PDP context based on the IE's from the 04.08 message,
  * and send the GTP create pdp context request to the GGSN */
-struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct ggsn_ctx *ggsn,
+struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
 					 struct sgsn_mm_ctx *mmctx,
 					 uint16_t nsapi,
 					 struct tlv_parsed *tp)
@@ -99,6 +99,7 @@
 	uint64_t imsi_ui64;
 	int rc;
 
+	LOGP(DGPRS, LOGL_ERROR, "Create PDP Context\n");
 	pctx = sgsn_pdp_ctx_alloc(mmctx, nsapi);
 	if (!pctx) {
 		LOGP(DGPRS, LOGL_ERROR, "Couldn't allocate PDP Ctx\n");
@@ -178,6 +179,13 @@
 	return pctx;
 }
 
+int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx)
+{
+	LOGP(DGPRS, LOGL_ERROR, "Delete PDP Context\n");
+
+	/* FIXME: decide if we need teardown or not ! */
+	return gtp_delete_context_req(pctx->ggsn->gsn, pctx->lib, pctx, 1);
+}
 
 struct cause_map {
 	uint8_t cause_in;
@@ -261,20 +269,21 @@
 	return EOF;
 }
 
-/* If we receive a 04.08 DEACT PDP CTX REQ or GPRS DETACH, we need to
- * look-up the PDP context and request its deletion from the SGSN */
-int sgsn_delete_pdp_ctx(struct ggsn_ctx *ggsn, struct sgsn_mm_ctx *mmctx,
-			struct tlv_parsed *tp)
-{
-	//return gtp_delete_context_req(gsn, pdp, cbp, teardown);
-}
-
 /* Confirmation of a PDP Context Delete */
-static int delete_pdp_conf(struct pdp_t *pdp, int cause)
+static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
 {
+	struct sgsn_pdp_ctx *pctx = cbp;
+	int rc;
+
 	DEBUGP(DGPRS, "Received DELETE PDP CTX CONF, cause=%d(%s)\n",
 		cause, get_value_string(gtp_cause_strs, cause));
-	return 0;
+
+	/* Confirm deactivation of PDP context to MS */
+	rc = gsm48_tx_gsm_deact_pdp_acc(pctx);
+
+	sgsn_pdp_ctx_free(pctx);
+
+	return rc;
 }
 
 /* Confirmation of an GTP ECHO request */
@@ -305,7 +314,7 @@
 	case GTP_CREATE_PDP_REQ:
 		return create_pdp_conf(pdp, cbp, cause);
 	case GTP_DELETE_PDP_REQ:
-		return delete_pdp_conf(pdp, cause);
+		return delete_pdp_conf(pdp, cbp, cause);
 	default:
 		break;
 	}
diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c
index 2a119e3..e72d398 100644
--- a/openbsc/src/gprs/sgsn_main.c
+++ b/openbsc/src/gprs/sgsn_main.c
@@ -123,6 +123,8 @@
 /* NSI that BSSGP uses when transmitting on NS */
 extern struct gprs_ns_inst *bssgp_nsi;
 extern void *tall_msgb_ctx;
+static struct sgsn_ggsn_ctx _ggsn;
+struct sgsn_ggsn_ctx *dummy_ggsn = &_ggsn;
 
 int main(int argc, char **argv)
 {
@@ -174,6 +176,10 @@
 	rc = sgsn_gtp_init(&sgsn_inst);
 	nsip_listen(sgsn_nsi, sgsn_inst.cfg.nsip_listen_port);
 
+	_ggsn.gtp_version = 1;
+	inet_aton("192.168.100.239", &_ggsn.remote_addr);
+	_ggsn.gsn = sgsn_inst.gsn;
+
 	while (1) {
 		rc = bsc_select_main(0);
 		if (rc < 0)
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
index 212f0e6..4fe3e97 100644
--- a/openbsc/src/gprs/sgsn_vty.c
+++ b/openbsc/src/gprs/sgsn_vty.c
@@ -49,7 +49,7 @@
 static int config_write_sgsn(struct vty *vty)
 {
 	struct in_addr ia;
-	struct ggsn_ctx *gctx;
+	struct sgsn_ggsn_ctx *gctx;
 
 	vty_out(vty, "sgsn%s", VTY_NEWLINE);
 
@@ -111,7 +111,7 @@
 	"")
 {
 	uint32_t id = atoi(argv[0]);
-	struct ggsn_ctx *ggc = ggsn_ctx_find_alloc(id);
+	struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
 
 	inet_aton(argv[1], &ggc->remote_addr);
 
@@ -124,7 +124,7 @@
 	"")
 {
 	uint32_t id = atoi(argv[0]);
-	struct ggsn_ctx *ggc = ggsn_ctx_find_alloc(id);
+	struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
 	uint16_t port = atoi(argv[1]);
 
 }
@@ -135,7 +135,7 @@
 	"")
 {
 	uint32_t id = atoi(argv[0]);
-	struct ggsn_ctx *ggc = ggsn_ctx_find_alloc(id);
+	struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id);
 	uint16_t port = atoi(argv[1]);
 
 	if (atoi(argv[1]))