[GPRS] Properly connect GPRS SM with LIBGTP for PDP context activation

* store LLC SAPI as part of PDP ctx
* store NSEI + BVCI as part of MM ctx
* export gsm48_tx_gsm_act_pdp_acc() and call it from sgsn_libgtp.c
* create and use gsm48_tx_gsm_act_pdp_rej for error cases
* print SAPI as part of VTY show pdp
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
index 8ad6ba1..df1ee47 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -71,6 +71,8 @@
 
 	/* Additional bits not present in the GSM TS */
 	uint32_t		tlli;
+	uint16_t		nsei;
+	uint16_t		bvci;
 	struct timer_list	timer;
 	unsigned int		T;
 };
@@ -88,10 +90,15 @@
 
 enum pdp_ctx_state {
 	PDP_STATE_NONE,
+	PDP_STATE_CR_REQ,
+	PDP_STATE_CR_CONF,
 };
 
 enum pdp_type {
 	PDP_TYPE_NONE,
+	PDP_TYPE_ETSI_PPP,
+	PDP_TYPE_IANA_IPv4,
+	PDP_TYPE_IANA_IPv6,
 };
 
 struct sgsn_pdp_ctx {
@@ -107,7 +114,8 @@
 	uint32_t		address;
 	char 			*apn_subscribed;
 	//char 			*apn_used;
-	uint16_t		nsapi;
+	uint16_t		nsapi;	/* SNDCP */
+	uint16_t		sapi;	/* LLC */
 	uint8_t			ti;	/* transaction identifier */
 	int			vplmn_allowed;
 	uint32_t		qos_profile_subscr;
diff --git a/openbsc/include/openbsc/gsm_04_08_gprs.h b/openbsc/include/openbsc/gsm_04_08_gprs.h
index d9fb285..e314b56 100644
--- a/openbsc/include/openbsc/gsm_04_08_gprs.h
+++ b/openbsc/include/openbsc/gsm_04_08_gprs.h
@@ -2,6 +2,7 @@
 #define _GSM48_GPRS_H
 
 #include <stdint.h>
+#include <osmocore/protocol/gsm_04_08.h>
 
 /* Table 10.4 / 10.4a, GPRS Mobility Management (GMM) */
 #define GSM48_MT_GMM_ATTACH_REQ		0x01
@@ -358,7 +359,4 @@
 
 int gprs_tlli_type(uint32_t tlli);
 
-struct gsm_bts *gsm48_bts_by_ra_id(struct gsm_network *net,
-				   const uint8_t *buf, unsigned int len);
-
 #endif /* _GSM48_GPRS_H */
diff --git a/openbsc/include/openbsc/sgsn.h b/openbsc/include/openbsc/sgsn.h
index b2625db..1633b83 100644
--- a/openbsc/include/openbsc/sgsn.h
+++ b/openbsc/include/openbsc/sgsn.h
@@ -6,6 +6,7 @@
 #include <osmocore/msgb.h>
 
 #include <openbsc/gprs_ns.h>
+#include <openbsc/gprs_sgsn.h>
 
 struct sgsn_config {
 	/* parsed from config file */
@@ -44,4 +45,9 @@
 /* Main input function for Gb proxy */
 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_mm_ctx *mmctx,
+					 uint16_t nsapi,
+					 struct tlv_parsed *tp);
 #endif
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index e1a6dea..391a0b1 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -47,8 +47,11 @@
 #include <openbsc/gprs_bssgp.h>
 #include <openbsc/gprs_llc.h>
 #include <openbsc/gprs_sgsn.h>
+#include <openbsc/gprs_gmm.h>
 #include <openbsc/sgsn.h>
 
+#include <pdp.h>
+
 extern struct sgsn_instance *sgsn;
 
 /* Protocol related stuff, should go into libosmocore */
@@ -148,6 +151,21 @@
 	msgb_nsei(msg) = msgb_nsei(old);
 }
 
+/* Store BVCI/NSEI in MM context */
+static void msgid2mmctx(struct sgsn_mm_ctx *mm, const struct msgb *msg)
+{
+	mm->bvci = msgb_bvci(msg);
+	mm->nsei = msgb_nsei(msg);
+}
+
+/* Store BVCI/NSEI in MM context */
+static void mmctx2msgid(struct msgb *msg, const struct sgsn_mm_ctx *mm)
+{
+	msgb_tlli(msg) = mm->tlli;
+	msgb_bvci(msg) = mm->bvci;
+	msgb_nsei(msg) = mm->nsei;
+}
+
 static struct gsm48_qos default_qos = {
 	.delay_class = 4,	/* best effort */
 	.reliab_class = GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT,
@@ -396,6 +414,7 @@
 		/* FIXME: Start some timer */
 		ctx->mm_state = GMM_COMMON_PROC_INIT;
 		ctx->tlli = msgb_tlli(msg);
+		msgid2mmctx(mmctx, msg);
 		break;
 	case GSM_MI_TYPE_TMSI:
 		tmsi = strtoul(mi_string, NULL, 10);
@@ -406,6 +425,7 @@
 			/* FIXME: Start some timer */
 			ctx->mm_state = GMM_COMMON_PROC_INIT;
 			ctx->tlli = msgb_tlli(msg);
+			msgid2mmctx(mmctx, msg);
 		}
 		break;
 	default:
@@ -631,36 +651,65 @@
 }
 
 /* Section 9.5.2: Ativate PDP Context Accept */
-static int gsm48_tx_gsm_act_pdp_acc(struct msgb *old_msg, struct gsm48_act_pdp_ctx_req *req)
+int gsm48_tx_gsm_act_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_act_pdp_ctx_ack *act_ack;
 	struct gsm48_hdr *gh;
-	uint8_t transaction_id = ((old_gh->proto_discr >> 4) ^ 0x8); /* flip */
+	uint8_t transaction_id = pdp->ti ^ 0x8; /* flip */
 
 	DEBUGP(DMM, "<- ACTIVATE 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_ACT_PDP_ACK;
 
 	/* Negotiated LLC SAPI */
-	msgb_v_put(msg, req->req_llc_sapi);
+	msgb_v_put(msg, pdp->sapi);
 	/* copy QoS parameters from original request */
-	msgb_lv_put(msg, sizeof(default_qos), (uint8_t *)&default_qos);
+	msgb_lv_put(msg, pdp->lib->qos_neg.l, pdp->lib->qos_neg.v);
+	//msgb_lv_put(msg, sizeof(default_qos), (uint8_t *)&default_qos);
 	/* Radio priority 10.5.7.2 */
-	msgb_v_put(msg, 4);
+	msgb_v_put(msg, pdp->lib->radio_pri);
 	/* PDP address */
-	msgb_put_pdp_addr_ipv4(msg, 0x01020304);
+	msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR,
+		     pdp->lib->eua.l, pdp->lib->eua.v);
+	//msgb_put_pdp_addr_ipv4(msg, 0x01020304);
 	/* Optional: Protocol configuration options */
+	if (pdp->lib->pco_neg.l && pdp->lib->pco_neg.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 */
 
 	return gsm48_gmm_sendmsg(msg, 0);
 }
 
+/* Section 9.5.3: Activate PDP Context reject */
+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)
+{
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_act_pdp_ctx_ack *act_ack;
+	struct gsm48_hdr *gh;
+	uint8_t transaction_id = tid ^ 0x8; /* flip */
+
+	DEBUGP(DMM, "<- ACTIVATE PDP CONTEXT ACK\n");
+
+	mmctx2msgid(msg, 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_ACT_PDP_REJ;
+
+	msgb_v_put(msg, cause);
+	if (pco_len && pco_v)
+		msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT, pco_len, pco_v);
+
+	return gsm48_gmm_sendmsg(msg, 0);
+}
+
 /* 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)
@@ -691,8 +740,11 @@
 	uint8_t req_qos_len, req_pdpa_len;
 	uint8_t *req_qos, *req_pdpa;
 	struct tlv_parsed tp;
+	uint8_t transaction_id = (gh->proto_discr >> 4);
+	struct sgsn_pdp_ctx *pdp;
 
-	DEBUGP(DMM, "-> ACTIVATE PDP CONTEXT REQ: ");
+	DEBUGP(DMM, "-> ACTIVATE PDP CONTEXT REQ: SAPI=%u NSAPI=%u ",
+		act_req->req_llc_sapi, act_req->req_nsapi);
 	req_qos_len = act_req->data[0];
 	req_qos = act_req->data + 1;	/* 10.5.6.5 */
 	req_pdpa_len = act_req->data[1 + req_qos_len];
@@ -737,9 +789,24 @@
 	tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].len = req_pdpa_len;
 	tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].val = req_pdpa;
 
-	/* FIXME: parse TLV for AP name and protocol config options */
+	/* FIXME:  determine GGSN based on APN and subscription options */
 	if (TLVP_PRESENT(&tp, GSM48_IE_GSM_APN)) {}
-	if (TLVP_PRESENT(&tp, GSM48_IE_GSM_PROTO_CONF_OPT)) {}
+
+	/* Check if NSAPI is out of range (TS 04.65 / 7.2) */
+	if (act_req->req_nsapi < 5 || act_req->req_nsapi > 15) {
+		/* Send reject with GSM_CAUSE_INV_MAND_INFO */
+		return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id,
+						GSM_CAUSE_INV_MAND_INFO,
+						0, NULL);
+	}
+
+	/* Check if NSAPI is already in use */
+	if (sgsn_pdp_ctx_by_nsapi(mmctx, act_req->req_nsapi)) {
+		/* FIXME: send reject with GSM_CAUSE_NSAPI_IN_USE */
+		return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id,
+						GSM_CAUSE_NSAPI_IN_USE,
+						0, NULL);
+	}
 
 #if 1
 	{
@@ -747,10 +814,15 @@
 		ggsn.gtp_version = 1;
 		inet_aton("192.168.100.239", &ggsn.remote_addr);
 		ggsn.gsn = sgsn->gsn;
-		return sgsn_create_pdp_ctx(ggsn, mmctx, 5, &tp);
+		pdp = sgsn_create_pdp_ctx(&ggsn, mmctx, act_req->req_nsapi, &tp);
+		if (!pdp)
+			return -1;
+		pdp->sapi = act_req->req_llc_sapi;
+		pdp->ti = transaction_id;
+		
 	}
 #else
-	return gsm48_tx_gsm_act_pdp_acc(msg, act_req);
+	return gsm48_tx_gsm_act_pdp_acc(mmctx, transaction_id, act_req);
 #endif
 }
 
@@ -822,6 +894,8 @@
 
 	bssgp_parse_cell_id(&ra_id, msgb_bcid(msg));
 	mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id);
+	if (mmctx)
+		msgid2mmctx(mmctx, msg);
 
 	/* MMCTX can be NULL */
 
@@ -835,6 +909,7 @@
 	default:
 		DEBUGP(DMM, "Unknown GSM 04.08 discriminator 0x%02x\n",
 			pdisc);
+		/* FIXME: return status message */
 		break;
 	}
 
diff --git a/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c
index f6331b9..78bb57a 100644
--- a/openbsc/src/gprs/sgsn_libgtp.c
+++ b/openbsc/src/gprs/sgsn_libgtp.c
@@ -37,14 +37,13 @@
 #include <osmocore/talloc.h>
 #include <osmocore/select.h>
 #include <osmocore/rate_ctr.h>
+#include <openbsc/gsm_04_08_gprs.h>
 
 #include <openbsc/signal.h>
 #include <openbsc/debug.h>
 #include <openbsc/sgsn.h>
-//#include <openbsc/gprs_ns.h>
-//#include <openbsc/gprs_bssgp.h>
 #include <openbsc/gprs_sgsn.h>
-#include <openbsc/gsm_04_08_gprs.h>
+#include <openbsc/gprs_gmm.h>
 
 #include <gtp.h>
 #include <pdp.h>
@@ -170,7 +169,8 @@
 	memcpy(pdp->gsnlu.v, &sgsn->cfg.gtp_listenaddr,
 		sizeof(sgsn->cfg.gtp_listenaddr));
 
-	/* FIXME: change pdp state to 'requested' */
+	/* change pdp state to 'requested' */
+	pctx->state = PDP_STATE_CR_REQ;
 
 	rc = gtp_create_context_req(ggsn->gsn, pdp, pctx);
 	/* FIXME */
@@ -178,10 +178,51 @@
 	return pctx;
 }
 
+
+struct cause_map {
+	uint8_t cause_in;
+	uint8_t cause_out;
+};
+
+static uint8_t cause_map(const struct cause_map *map, uint8_t in, uint8_t deflt)
+{
+	const struct cause_map *m;
+
+	for (m = map; m->cause_in && m->cause_out; m++) {
+		if (m->cause_in == in)
+			return m->cause_out;
+	}
+	return deflt;
+}
+
+/* how do we map from gtp cause to SM cause */
+static const struct cause_map gtp2sm_cause_map[] = {
+	{ GTPCAUSE_NO_RESOURCES, 	GSM_CAUSE_INSUFF_RSRC },
+	{ GTPCAUSE_NOT_SUPPORTED,	GSM_CAUSE_SERV_OPT_NOTSUPP },
+	{ GTPCAUSE_MAN_IE_INCORRECT,	GSM_CAUSE_INV_MAND_INFO },
+	{ GTPCAUSE_MAN_IE_MISSING,	GSM_CAUSE_INV_MAND_INFO },
+	{ GTPCAUSE_OPT_IE_INCORRECT,	GSM_CAUSE_PROTO_ERR_UNSPEC },
+	{ GTPCAUSE_SYS_FAIL,		GSM_CAUSE_NET_FAIL },
+	{ GTPCAUSE_ROAMING_REST,	GSM_CAUSE_REQ_SERV_OPT_NOTSUB },
+	{ GTPCAUSE_PTIMSI_MISMATCH,	GSM_CAUSE_PROTO_ERR_UNSPEC },
+	{ GTPCAUSE_CONN_SUSP,		GSM_CAUSE_PROTO_ERR_UNSPEC },
+	{ GTPCAUSE_AUTH_FAIL,		GSM_CAUSE_AUTH_FAILED },
+	{ GTPCAUSE_USER_AUTH_FAIL,	GSM_CAUSE_ACT_REJ_GGSN },
+	{ GTPCAUSE_CONTEXT_NOT_FOUND,	GSM_CAUSE_PROTO_ERR_UNSPEC },
+	{ GTPCAUSE_ADDR_OCCUPIED,	GSM_CAUSE_INSUFF_RSRC },
+	{ GTPCAUSE_NO_MEMORY,		GSM_CAUSE_INSUFF_RSRC },
+	{ GTPCAUSE_RELOC_FAIL,		GSM_CAUSE_PROTO_ERR_UNSPEC },
+	{ GTPCAUSE_UNKNOWN_MAN_EXTHEADER, GSM_CAUSE_PROTO_ERR_UNSPEC },
+	{ GTPCAUSE_MISSING_APN,		GSM_CAUSE_MISSING_APN },
+	{ GTPCAUSE_UNKNOWN_PDP,		GSM_CAUSE_UNKNOWN_PDP },
+	{ 0, 0 }
+};
+
 /* The GGSN has confirmed the creation of a PDP Context */
 static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
 {
 	struct sgsn_pdp_ctx *pctx = cbp;
+	uint8_t reject_cause;
 
 	DEBUGP(DGPRS, "Received CREATE PDP CTX CONF, cause=%d(%s)\n",
 		cause, get_value_string(gtp_cause_strs, cause));
@@ -194,19 +235,30 @@
 			gtp_create_context_req(sgsn->gsn, pdp, cbp);
 			return 0;
 		} else {
-			pdp_freepdp(pdp);
-			return EOF;
+			reject_cause = GSM_CAUSE_NET_FAIL;
+			goto reject;
 		}
 	}
 
 	/* Check for cause value if it was really successful */
 	if (cause != GTPCAUSE_ACC_REQ) {
-		pdp_freepdp(pdp);
-		return EOF;
+		reject_cause = cause_map(gtp2sm_cause_map, cause,
+					 GSM_CAUSE_ACT_REJ_GGSN);
+		goto reject;
 	}
 
-	/* FIXME: Send PDP CTX ACT ACK/REJ to MS */
-	return 0;
+	/* Send PDP CTX ACT to MS */
+	return gsm48_tx_gsm_act_pdp_acc(pctx);
+
+reject:
+	pctx->state = PDP_STATE_NONE;
+	pdp_freepdp(pdp);
+	sgsn_pdp_ctx_free(pctx);
+	/* Send PDP CTX ACT REJ to MS */
+	return gsm48_tx_gsm_act_pdp_rej(pctx->mm, pdp->ti, reject_cause,
+					0, NULL);
+
+	return EOF;
 }
 
 /* If we receive a 04.08 DEACT PDP CTX REQ or GPRS DETACH, we need to
@@ -289,6 +341,7 @@
 static int cb_data_ind(struct pdp_t *pdp, void *packet, unsigned int len)
 {
 	DEBUGP(DGPRS, "GTP DATA IND from GGSN, length=%u\n", len);
+	/* FIXME: resolve PDP/MM context, forward to SNDCP layer */
 
 	return 0;
 }
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
index b59529c..5584824 100644
--- a/openbsc/src/gprs/sgsn_vty.c
+++ b/openbsc/src/gprs/sgsn_vty.c
@@ -168,7 +168,7 @@
 			 struct sgsn_pdp_ctx *pdp)
 {
 	vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u%s",
-		pfx, pdp->mm->imsi, 2342 /* FIXME */, pdp->nsapi, VTY_NEWLINE);
+		pfx, pdp->mm->imsi, pdp->sapi, pdp->nsapi, VTY_NEWLINE);
 	vty_out(vty, "%s  APN: %s\n", pfx, pdp->lib->apn_use.v);
 	/* FIXME: statistics */
 }