Merge branch 'zecke/features/gprs-gsup-fixes'

When communicating with a GGSN that is not the OpenGGSN
the PDP context activation does fail. This is because on
the activation of the first PDP context we need to supply
a MSISDN. Extend the protocol, parse the MSISDN and then
send it to the GGSN. The second item is that we have only
forwarded the requested QoS of the subscriber. In most
cases this is 0x0, 0x0, ... which means one requests a
rate 0 byte/sec which the GGSN will not allow. Make it
possible to receive, store and use the subscribed QoS of
the Subscriber.
diff --git a/openbsc/doc/sgsn-remote-protocol.txt b/openbsc/doc/sgsn-remote-protocol.txt
index 3369d19..acb24a5 100644
--- a/openbsc/doc/sgsn-remote-protocol.txt
+++ b/openbsc/doc/sgsn-remote-protocol.txt
@@ -119,6 +119,7 @@
   IEI	Info Element		Type			Pres.	Format	Length
 	Message type		4.2.1			M	V	1
   01	IMSI			4.2.9			M	TLV	2-10
+  08	MSISDN			4.2.10			O	TLV	0-9
   04	PDP info complete	4.2.8			O	TLV	2
   05	PDP info		4.2.3			1-10	TLV
 
@@ -279,6 +280,7 @@
   10	PDP context id		big endian int, 1-N 	C	TLV	3
   11	PDP type		4.2.4			C	TLV	4
   12	Access point name	04.08, 10.5.6.1		C	TLV	3-102
+  13	Quality of Service	4.2.11			O	TLV	1-20
 
 The conditional IE are mandantory unless mentioned otherwise.
 
@@ -354,9 +356,11 @@
   | 0x05   PDP info       4.2.3                             |
   | 0x06   Cancel type    4.2.6                             |
   | 0x07   Freeze P-TMSI  4.2.8                             |
+  | 0x08   MSISDN         ISDN-AddressString/octet, 4.2.10  |
   | 0x10   PDP context id big endian int                    |
   | 0x11   PDP type       4.2.4                             |
   | 0x12   APN            04.08, 10.5.6.1                   |
+  | 0x13   QoS            4.2.11                            |
   | 0x20   RAND           octet string                      |
   | 0x21   SRES           octet string                      |
   | 0x22   Kc             octet string                      |
@@ -397,3 +401,44 @@
 Note 1) Either '1 1 1 1 | Number digit N' (N odd) or
         'Number digit N | Number digit N-1' (N even),
         where N is the number of digits.
+
+4.2.10. ISDN-AddressString / MSISDN / Called Party BCD Number
+
+The MSISDN is encoded as an ISDN-AddressString in GSM 09.02 and Called Party
+BCD Number in GSM 04.08. It will be stored by the SGSN and then passed as is
+to the GGSN during the activation of the primary PDP Context.
+
+     8      7      6      5      4      3      2      1
+  +-----------------------------------------------------+
+  |     |  IEI                                          | octet 1
+  +-----------------------------------------------------+
+  |   Length of IE contents                             | octet 2
+  +-----------------------------------------------------+
+  | ext |  Type of num        |   Numbering plan        | octet 2
+  +-----------------------------------------------------+
+  |  Number digit 2           |   Number digit 1        | octet 3
+  +-----------------------------------------------------+
+  |  Number digit 4           |   Number digit 3        | octet 4
+  +-----------------------------------------------------+
+  :                                                     :   :
+  +-----------------------------------------------------+
+
+
+4.2.11 Quality of Service Subscribed Service
+
+This encodes the subscribed QoS of a subscriber. It will be used by the
+SGSN during the PDP Context activation. If the length of the QoS data
+is 3 (three) octets it is assumed that these are octets 3-5 of the TS
+3GPP TS 24.008 Quality of Service Octets. If it is more than three then
+then it is assumed that the first octet is the Allocation/Retention
+Priority and the reset are encoded as octets 3-N of 24.008.
+
+
+     8      7      6      5      4      3      2      1
+  +-----------------------------------------------------+
+  |     |  IEI                                          | octet 1
+  +-----------------------------------------------------+
+  |   Length of IE contents                             | octet 2
+  +-----------------------------------------------------+
+  :                                                     :   :
+  +-----------------------------------------------------+
diff --git a/openbsc/include/openbsc/gprs_gsup_messages.h b/openbsc/include/openbsc/gprs_gsup_messages.h
index 9857b97..123e1fc 100644
--- a/openbsc/include/openbsc/gprs_gsup_messages.h
+++ b/openbsc/include/openbsc/gprs_gsup_messages.h
@@ -29,6 +29,7 @@
 
 #define GPRS_GSUP_MAX_NUM_PDP_INFO		10 /* GSM 09.02 limits this to 50 */
 #define GPRS_GSUP_MAX_NUM_AUTH_INFO		5
+#define GPRS_GSUP_MAX_MSISDN_LEN		9
 
 #define GPRS_GSUP_PDP_TYPE_SIZE			2
 
@@ -40,9 +41,11 @@
 	GPRS_GSUP_PDP_INFO_IE			= 0x05,
 	GPRS_GSUP_CANCEL_TYPE_IE		= 0x06,
 	GPRS_GSUP_FREEZE_PTMSI_IE		= 0x07,
+	GPRS_GSUP_MSISDN_IE			= 0x08,
 	GPRS_GSUP_PDP_CONTEXT_ID_IE		= 0x10,
 	GPRS_GSUP_PDP_TYPE_IE			= 0x11,
 	GPRS_GSUP_ACCESS_POINT_NAME_IE		= 0x12,
+	GPRS_GSUP_PDP_QOS_IE			= 0x13,
 	GPRS_GSUP_RAND_IE			= 0x20,
 	GPRS_GSUP_SRES_IE			= 0x21,
 	GPRS_GSUP_KC_IE				= 0x22
@@ -89,6 +92,8 @@
 	uint16_t			pdp_type;
 	const uint8_t			*apn_enc;
 	size_t				apn_enc_len;
+	const uint8_t			*qos_enc;
+	size_t				qos_enc_len;
 };
 
 struct gprs_gsup_message {
@@ -102,6 +107,8 @@
 	size_t				num_auth_tuples;
 	struct gprs_gsup_pdp_info	pdp_infos[GPRS_GSUP_MAX_NUM_PDP_INFO];
 	size_t				num_pdp_infos;
+	const uint8_t			*msisdn_enc;
+	size_t				msisdn_enc_len;
 };
 
 int gprs_gsup_decode(const uint8_t *data, size_t data_len,
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
index 2572ead..7a429cd 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -294,6 +294,8 @@
 	unsigned int		context_id;
 	uint16_t		pdp_type;
 	char			apn_str[GSM_APN_LENGTH];
+	uint8_t			qos_subscribed[20];
+	size_t			qos_subscribed_len;
 };
 
 struct sgsn_subscriber_data {
@@ -302,6 +304,9 @@
 	int			auth_triplets_updated;
 	struct llist_head	pdp_list;
 	int			error_cause;
+
+	uint8_t			msisdn[9];
+	size_t			msisdn_len;
 };
 
 #define SGSN_ERROR_CAUSE_NONE (-1)
diff --git a/openbsc/include/openbsc/gsm_04_08_gprs.h b/openbsc/include/openbsc/gsm_04_08_gprs.h
index 3eec983..f35d11b 100644
--- a/openbsc/include/openbsc/gsm_04_08_gprs.h
+++ b/openbsc/include/openbsc/gsm_04_08_gprs.h
@@ -100,6 +100,7 @@
 	 * but which we use to simplify internal APIs */
 	OSMO_IE_GSM_REQ_QOS		= 0xfd,
 	OSMO_IE_GSM_REQ_PDP_ADDR	= 0xfe,
+	OSMO_IE_GSM_SUB_QOS		= 0xff,
 };
 
 /* Chapter 9.4.15 / Table 9.4.15 */
diff --git a/openbsc/src/gprs/gprs_gsup_messages.c b/openbsc/src/gprs/gprs_gsup_messages.c
index 9d9b6be..cb14fa1 100644
--- a/openbsc/src/gprs/gprs_gsup_messages.c
+++ b/openbsc/src/gprs/gprs_gsup_messages.c
@@ -2,6 +2,7 @@
 
 /*
  * (C) 2014 by Sysmocom s.f.m.c. GmbH
+ * (C) 2015 by Holger Hans Peter Freyther
  * All Rights Reserved
  *
  * Author: Jacob Erlbeck
@@ -93,6 +94,11 @@
 			pdp_info->apn_enc_len = value_len;
 			break;
 
+		case GPRS_GSUP_PDP_QOS_IE:
+			pdp_info->qos_enc = value;
+			pdp_info->qos_enc_len = value_len;
+			break;
+
 		default:
 			LOGP(DGPRS, LOGL_ERROR,
 			     "GSUP IE type %d not expected in PDP info\n", iei);
@@ -291,6 +297,12 @@
 			gsup_msg->auth_tuples[gsup_msg->num_auth_tuples++] =
 				auth_info;
 			break;
+
+		case GPRS_GSUP_MSISDN_IE:
+			gsup_msg->msisdn_enc = value;
+			gsup_msg->msisdn_enc_len = value_len;
+			break;
+
 		default:
 			LOGP(DGPRS, LOGL_NOTICE,
 			     "GSUP IE type %d unknown\n", iei);
@@ -326,6 +338,11 @@
 			     pdp_info->apn_enc_len, pdp_info->apn_enc);
 	}
 
+	if (pdp_info->qos_enc) {
+		msgb_tlv_put(msg, GPRS_GSUP_PDP_QOS_IE,
+				pdp_info->qos_enc_len, pdp_info->qos_enc);
+	}
+
 	/* Update length field */
 	*len_field = msgb_length(msg) - old_len;
 }
@@ -374,6 +391,10 @@
 	msgb_tlv_put(msg, GPRS_GSUP_IMSI_IE, bcd_len - 1, &bcd_buf[1]);
 
 	/* specific parts */
+	if (gsup_msg->msisdn_enc)
+		msgb_tlv_put(msg, GPRS_GSUP_MSISDN_IE,
+				gsup_msg->msisdn_enc_len, gsup_msg->msisdn_enc);
+
 	if ((u8 = gsup_msg->cause))
 		msgb_tlv_put(msg, GPRS_GSUP_CAUSE_IE, sizeof(u8), &u8);
 
diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c
index 94c2b6f..711540e 100644
--- a/openbsc/src/gprs/gprs_sgsn.c
+++ b/openbsc/src/gprs/gprs_sgsn.c
@@ -582,6 +582,17 @@
 	sgsn_auth_update(mmctx);
 }
 
+static void insert_qos(struct tlv_parsed *tp, struct sgsn_subscriber_pdp_data *pdp)
+{
+	tp->lv[OSMO_IE_GSM_SUB_QOS].len = pdp->qos_subscribed_len;
+	tp->lv[OSMO_IE_GSM_SUB_QOS].val = pdp->qos_subscribed;
+}
+
+/**
+ * The tlv_parsed tp parameter will be modified to insert a
+ * OSMO_IE_GSM_SUB_QOS in case the data is available in the
+ * PDP context handling.
+ */
 struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx,
 						struct tlv_parsed *tp,
 						enum gsm48_gsm_cause *gsm_cause)
@@ -621,6 +632,7 @@
 			{
 				allow_any_apn = 1;
 				selected_apn_str = "";
+				insert_qos(tp, pdp);
 				continue;
 			}
 			if (!llist_empty(&sgsn_apn_ctxts)) {
@@ -629,6 +641,7 @@
 				if (apn_ctx == NULL)
 					continue;
 			}
+			insert_qos(tp, pdp);
 			selected_apn_str = pdp->apn_str;
 			break;
 		}
@@ -636,11 +649,13 @@
 		/* Check whether the given APN is granted */
 		llist_for_each_entry(pdp, &mmctx->subscr->sgsn_data->pdp_list, list) {
 			if (strcmp(pdp->apn_str, "*") == 0) {
+				insert_qos(tp, pdp);
 				selected_apn_str = req_apn_str;
 				allow_any_apn = 1;
 				continue;
 			}
 			if (strcasecmp(pdp->apn_str, req_apn_str) == 0) {
+				insert_qos(tp, pdp);
 				selected_apn_str = req_apn_str;
 				break;
 			}
diff --git a/openbsc/src/gprs/gprs_subscriber.c b/openbsc/src/gprs/gprs_subscriber.c
index 60f223a..c2a3ae1 100644
--- a/openbsc/src/gprs/gprs_subscriber.c
+++ b/openbsc/src/gprs/gprs_subscriber.c
@@ -1,6 +1,7 @@
 /* MS subscriber data handling */
 
 /* (C) 2014 by sysmocom s.f.m.c. GmbH
+ * (C) 2015 by Holger Hans Peter Freyther
  *
  * All Rights Reserved
  *
@@ -259,9 +260,22 @@
 static void gprs_subscr_gsup_insert_data(struct gsm_subscriber *subscr,
 					 struct gprs_gsup_message *gsup_msg)
 {
+	struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
 	unsigned idx;
 	int rc;
 
+	if (gsup_msg->msisdn_enc) {
+		if (gsup_msg->msisdn_enc_len > sizeof(sdata->msisdn)) {
+			LOGP(DGPRS, LOGL_ERROR, "MSISDN too long (%zu)\n",
+				gsup_msg->msisdn_enc_len);
+			sdata->msisdn_len = 0;
+		} else {
+			memcpy(sdata->msisdn, gsup_msg->msisdn_enc,
+				gsup_msg->msisdn_enc_len);
+			sdata->msisdn_len = gsup_msg->msisdn_enc_len;
+		}
+	}
+
 	if (gsup_msg->pdp_info_compl) {
 		rc = gprs_subscr_pdp_data_clear(subscr);
 		if (rc > 0)
@@ -281,6 +295,13 @@
 			continue;
 		}
 
+		if (pdp_info->qos_enc_len > sizeof(pdp_data->qos_subscribed)) {
+			LOGGSUBSCRP(LOGL_ERROR, subscr,
+				"QoS info too long (%zu)\n",
+				pdp_info->qos_enc_len);
+			continue;
+		}
+
 		LOGGSUBSCRP(LOGL_INFO, subscr,
 		     "Will set PDP info, context id = %zu, APN = %s\n",
 		     ctx_id, osmo_hexdump(pdp_info->apn_enc, pdp_info->apn_enc_len));
@@ -296,6 +317,8 @@
 		pdp_data->pdp_type = pdp_info->pdp_type;
 		gprs_apn_to_str(pdp_data->apn_str,
 				pdp_info->apn_enc, pdp_info->apn_enc_len);
+		memcpy(pdp_data->qos_subscribed, pdp_info->qos_enc, pdp_info->qos_enc_len);
+		pdp_data->qos_subscribed_len = pdp_info->qos_enc_len;
 	}
 }
 
diff --git a/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c
index 455e8af..eee3ef2 100644
--- a/openbsc/src/gprs/sgsn_libgtp.c
+++ b/openbsc/src/gprs/sgsn_libgtp.c
@@ -3,6 +3,7 @@
 
 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
  * (C) 2010 by On-Waves
+ * (C) 2015 by Holger Hans Peter Freyther
  * All Rights Reserved
  *
  * This program is free software; you can redistribute it and/or modify
@@ -45,6 +46,7 @@
 #include <openbsc/gprs_llc.h>
 #include <openbsc/gprs_sgsn.h>
 #include <openbsc/gprs_gmm.h>
+#include <openbsc/gsm_subscriber.h>
 
 #include <gtp.h>
 #include <pdp.h>
@@ -121,6 +123,8 @@
 	struct sgsn_pdp_ctx *pctx;
 	struct pdp_t *pdp;
 	uint64_t imsi_ui64;
+	size_t qos_len;
+	const uint8_t *qos;
 	int rc;
 
 	LOGP(DGPRS, LOGL_ERROR, "Create PDP Context\n");
@@ -153,8 +157,14 @@
 
 	/* IMSI, TEID/TEIC, FLLU/FLLC, TID, NSAPI set in pdp_newpdp */
 
-	/* FIXME: MSISDN in BCD format from mmctx */
-	//pdp->msisdn.l/.v
+	/* Put the MSISDN in case we have it */
+	if (mmctx->subscr) {
+		pdp->msisdn.l = mmctx->subscr->sgsn_data->msisdn_len;
+		if (pdp->msisdn.l > sizeof(pdp->msisdn.v))
+			pdp->msisdn.l = sizeof(pdp->msisdn.l);
+		memcpy(pdp->msisdn.v, mmctx->subscr->sgsn_data->msisdn,
+			pdp->msisdn.l);
+	}
 
 	/* End User Address from GMM requested PDP address */
 	pdp->eua.l = TLVP_LEN(tp, OSMO_IE_GSM_REQ_PDP_ADDR);
@@ -180,12 +190,27 @@
 	memcpy(pdp->pco_req.v, TLVP_VAL(tp, GSM48_IE_GSM_PROTO_CONF_OPT),
 		pdp->pco_req.l);
 
-	/* QoS options from GMM */
-	pdp->qos_req.l = TLVP_LEN(tp, OSMO_IE_GSM_REQ_QOS);
-	if (pdp->qos_req.l > sizeof(pdp->qos_req.v))
-		pdp->qos_req.l = sizeof(pdp->qos_req.v);
-	memcpy(pdp->qos_req.v, TLVP_VAL(tp, OSMO_IE_GSM_REQ_QOS),
-		pdp->qos_req.l);
+	/* QoS options from GMM or remote */
+	if (TLVP_LEN(tp, OSMO_IE_GSM_SUB_QOS) > 0) {
+		qos_len = TLVP_LEN(tp, OSMO_IE_GSM_SUB_QOS);
+		qos = TLVP_VAL(tp, OSMO_IE_GSM_SUB_QOS);
+	} else {
+		qos_len = TLVP_LEN(tp, OSMO_IE_GSM_REQ_QOS);
+		qos = TLVP_VAL(tp, OSMO_IE_GSM_REQ_QOS);
+	}
+
+	if (qos_len <= 3) {
+		pdp->qos_req.l = qos_len + 1;
+		if (pdp->qos_req.l > sizeof(pdp->qos_req.v))
+			pdp->qos_req.l = sizeof(pdp->qos_req.v);
+		pdp->qos_req.v[0] = 0; /* Allocation/Retention policy */
+		memcpy(&pdp->qos_req.v[1], qos, pdp->qos_req.l - 1);
+	} else {
+		pdp->qos_req.l = qos_len;
+		if (pdp->qos_req.l > sizeof(pdp->qos_req.v))
+			pdp->qos_req.l = sizeof(pdp->qos_req.v);
+		memcpy(pdp->qos_req.v, qos, pdp->qos_req.l);
+	}
 
 	/* SGSN address for control plane */
 	pdp->gsnlc.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr);
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
index 684204a..be575d3 100644
--- a/openbsc/src/gprs/sgsn_vty.c
+++ b/openbsc/src/gprs/sgsn_vty.c
@@ -471,6 +471,11 @@
 	if (subscr->tmsi != GSM_RESERVED_TMSI)
 		vty_out(vty, "    TMSI: %08X%s", subscr->tmsi,
 			VTY_NEWLINE);
+	if (subscr->sgsn_data->msisdn_len > 0)
+		vty_out(vty, "    MSISDN (BCD): %s%s",
+			osmo_hexdump(subscr->sgsn_data->msisdn,
+					subscr->sgsn_data->msisdn_len),
+			VTY_NEWLINE);
 
 	if (strlen(subscr->equipment.imei) > 0)
 		vty_out(vty, "    IMEI: %s%s", subscr->equipment.imei, VTY_NEWLINE);
@@ -495,8 +500,9 @@
 	}
 
 	llist_for_each_entry(pdp, &subscr->sgsn_data->pdp_list, list) {
-		vty_out(vty, "    PDP info: Id: %d, Type: 0x%04x, APN: '%s'%s",
+		vty_out(vty, "    PDP info: Id: %d, Type: 0x%04x, APN: '%s' QoS: %s%s",
 			pdp->context_id, pdp->pdp_type, pdp->apn_str,
+			osmo_hexdump(pdp->qos_subscribed, pdp->qos_subscribed_len),
 			VTY_NEWLINE);
 	}
 
diff --git a/openbsc/tests/gprs/gprs_test.c b/openbsc/tests/gprs/gprs_test.c
index 0ae06e7..bbd1d8a 100644
--- a/openbsc/tests/gprs/gprs_test.c
+++ b/openbsc/tests/gprs/gprs_test.c
@@ -443,11 +443,14 @@
 	static const uint8_t update_location_res[] = {
 		0x06,
 		TEST_IMSI_IE,
+		0x08, 0x07, /* MSISDN of the subscriber */
+			0x91, 0x94, 0x61, 0x46, 0x32, 0x24, 0x43,
 		0x04, 0x00, /* PDP info complete */
-		0x05, 0x12,
+		0x05, 0x15,
 			0x10, 0x01, 0x01,
 			0x11, 0x02, 0xf1, 0x21, /* IPv4 */
 			0x12, 0x09, 0x04, 't', 'e', 's', 't', 0x03, 'a', 'p', 'n',
+			0x13, 0x01, 0x02,
 		0x05, 0x11,
 			0x10, 0x01, 0x02,
 			0x11, 0x02, 0xf1, 0x21, /* IPv4 */
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index 197be9d..d9b162d 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -414,9 +414,14 @@
 		0x02, 0x01, 0x07 /* GPRS not allowed */
 	};
 
+#define MSISDN	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09
+
+	static const uint8_t s1_msisdn[] = { MSISDN };
+
 	static const uint8_t update_location_res[] = {
 		0x06,
 		TEST_GSUP_IMSI1_IE,
+		0x08, 0x09, MSISDN,
 		0x04, 0x00, /* PDP info complete */
 		0x05, 0x12,
 			0x10, 0x01, 0x01,
@@ -428,6 +433,8 @@
 			0x12, 0x08, 0x03, 'f', 'o', 'o', 0x03, 'a', 'p', 'n',
 	};
 
+#undef MSISDN
+
 	static const uint8_t update_location_err[] = {
 		0x05,
 		TEST_GSUP_IMSI1_IE,
@@ -534,6 +541,8 @@
 	OSMO_ASSERT(last_updated_subscr == s1);
 	OSMO_ASSERT(s1->flags & GPRS_SUBSCRIBER_ENABLE_PURGE);
 	OSMO_ASSERT(s1->sgsn_data->error_cause == SGSN_ERROR_CAUSE_NONE);
+	OSMO_ASSERT(s1->sgsn_data->msisdn_len == sizeof(s1_msisdn));
+	OSMO_ASSERT(memcmp(s1->sgsn_data->msisdn, s1_msisdn, sizeof(s1_msisdn)) == 0);
 	OSMO_ASSERT(!llist_empty(&s1->sgsn_data->pdp_list));
 	pdpd = llist_entry(s1->sgsn_data->pdp_list.next,
 		struct sgsn_subscriber_pdp_data, list);