Merge branch 'zecke/features/sgsn-hlr-number'

Store the hlr-Number for purgeMS and CDR handling
diff --git a/openbsc/doc/sgsn-remote-protocol.txt b/openbsc/doc/sgsn-remote-protocol.txt
index acb24a5..6591d63 100644
--- a/openbsc/doc/sgsn-remote-protocol.txt
+++ b/openbsc/doc/sgsn-remote-protocol.txt
@@ -120,6 +120,7 @@
 	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
+  09	HLR Number		4.2.12			O	TLV	0-9
   04	PDP info complete	4.2.8			O	TLV	2
   05	PDP info		4.2.3			1-10	TLV
 
@@ -149,6 +150,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
+  09	HLR Number		4.2.12			M	TLV	0-9
 
 3.2.10. Purge MS Error
 
@@ -357,6 +359,7 @@
   | 0x06   Cancel type    4.2.6                             |
   | 0x07   Freeze P-TMSI  4.2.8                             |
   | 0x08   MSISDN         ISDN-AddressString/octet, 4.2.10  |
+  | 0x09   HLR Number     4.2.12                            |
   | 0x10   PDP context id big endian int                    |
   | 0x11   PDP type       4.2.4                             |
   | 0x12   APN            04.08, 10.5.6.1                   |
@@ -442,3 +445,24 @@
   +-----------------------------------------------------+
   :                                                     :   :
   +-----------------------------------------------------+
+
+4.2.12. HLR Number encoded as GSM 09.02 ISDN-AddressString
+
+The HLR Number is encoded as an ISDN-AddressString in GSM 09.02. It
+will be stored by the SGSN can be used by the CDR module to keep a
+record.
+
+     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
+  +-----------------------------------------------------+
+  :                                                     :   :
+  +-----------------------------------------------------+
diff --git a/openbsc/include/openbsc/gprs_gsup_messages.h b/openbsc/include/openbsc/gprs_gsup_messages.h
index 123e1fc..8cbc809 100644
--- a/openbsc/include/openbsc/gprs_gsup_messages.h
+++ b/openbsc/include/openbsc/gprs_gsup_messages.h
@@ -42,6 +42,7 @@
 	GPRS_GSUP_CANCEL_TYPE_IE		= 0x06,
 	GPRS_GSUP_FREEZE_PTMSI_IE		= 0x07,
 	GPRS_GSUP_MSISDN_IE			= 0x08,
+	GPRS_GSUP_HLR_NUMBER_IE			= 0x09,
 	GPRS_GSUP_PDP_CONTEXT_ID_IE		= 0x10,
 	GPRS_GSUP_PDP_TYPE_IE			= 0x11,
 	GPRS_GSUP_ACCESS_POINT_NAME_IE		= 0x12,
@@ -109,6 +110,8 @@
 	size_t				num_pdp_infos;
 	const uint8_t			*msisdn_enc;
 	size_t				msisdn_enc_len;
+	const uint8_t			*hlr_enc;
+	size_t				hlr_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 4bbde9c..8abe97c 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -136,6 +136,9 @@
 	enum sgsn_auth_state	auth_state;
 	int			is_authenticated;
 
+	/* the string representation of the current hlr */
+	char 			hlr[GSM_EXTENSION_LENGTH];
+
 	struct gsm_subscriber   *subscr;
 };
 
@@ -312,6 +315,9 @@
 
 	uint8_t			msisdn[9];
 	size_t			msisdn_len;
+
+	uint8_t			hlr[9];
+	size_t			hlr_len;
 };
 
 #define SGSN_ERROR_CAUSE_NONE (-1)
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index 8ada3d4..b17873e 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -578,6 +578,43 @@
 	}
 }
 
+static void extract_subscr_hlr(struct sgsn_mm_ctx *ctx)
+{
+	struct gsm_mncc_number called;
+	uint8_t hlr_number[sizeof(ctx->subscr->sgsn_data->hlr) + 1];
+
+	if (!ctx->subscr)
+		return;
+
+	if (ctx->subscr->sgsn_data->hlr_len < 1)
+		return;
+
+	/* prepare the data for the decoder */
+	memset(&called, 0, sizeof(called));
+	hlr_number[0] = ctx->subscr->sgsn_data->hlr_len;
+	memcpy(&hlr_number[1], ctx->subscr->sgsn_data->hlr,
+		ctx->subscr->sgsn_data->hlr_len);
+
+	/* decode the string now */
+	gsm48_decode_called(&called, hlr_number);
+
+	if (called.plan != 1) {
+		LOGMMCTXP(LOGL_ERROR, ctx,
+				"Numbering plan(%d) not allowed\n",
+				called.plan);
+		return;
+	}
+
+	if (called.type != 1) {
+		LOGMMCTXP(LOGL_ERROR, ctx,
+				"Numbering type(%d) not allowed\n",
+				called.type);
+		return;
+	}
+
+	strncpy(&ctx->hlr[0], called.number, sizeof(ctx->hlr) - 1);
+}
+
 /* Check if we can already authorize a subscriber */
 static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx)
 {
@@ -643,6 +680,7 @@
 	case GSM48_MT_GMM_ATTACH_REQ:
 
 		extract_subscr_msisdn(ctx);
+		extract_subscr_hlr(ctx);
 #ifdef PTMSI_ALLOC
 		/* Start T3350 and re-transmit up to 5 times until ATTACH COMPLETE */
 		mmctx_timer_start(ctx, 3350, GSM0408_T3350_SECS);
diff --git a/openbsc/src/gprs/gprs_gsup_messages.c b/openbsc/src/gprs/gprs_gsup_messages.c
index cb14fa1..bdcff5f 100644
--- a/openbsc/src/gprs/gprs_gsup_messages.c
+++ b/openbsc/src/gprs/gprs_gsup_messages.c
@@ -303,6 +303,11 @@
 			gsup_msg->msisdn_enc_len = value_len;
 			break;
 
+		case GPRS_GSUP_HLR_NUMBER_IE:
+			gsup_msg->hlr_enc = value;
+			gsup_msg->hlr_enc_len = value_len;
+			break;
+
 		default:
 			LOGP(DGPRS, LOGL_NOTICE,
 			     "GSUP IE type %d unknown\n", iei);
@@ -394,6 +399,9 @@
 	if (gsup_msg->msisdn_enc)
 		msgb_tlv_put(msg, GPRS_GSUP_MSISDN_IE,
 				gsup_msg->msisdn_enc_len, gsup_msg->msisdn_enc);
+	if (gsup_msg->hlr_enc)
+		msgb_tlv_put(msg, GPRS_GSUP_HLR_NUMBER_IE,
+				gsup_msg->hlr_enc_len, gsup_msg->hlr_enc);
 
 	if ((u8 = gsup_msg->cause))
 		msgb_tlv_put(msg, GPRS_GSUP_CAUSE_IE, sizeof(u8), &u8);
diff --git a/openbsc/src/gprs/gprs_subscriber.c b/openbsc/src/gprs/gprs_subscriber.c
index c2a3ae1..8231e8c 100644
--- a/openbsc/src/gprs/gprs_subscriber.c
+++ b/openbsc/src/gprs/gprs_subscriber.c
@@ -276,6 +276,18 @@
 		}
 	}
 
+	if (gsup_msg->hlr_enc) {
+		if (gsup_msg->hlr_enc_len > sizeof(sdata->hlr)) {
+			LOGP(DGPRS, LOGL_ERROR, "HLR-Number too long (%zu)\n",
+				gsup_msg->hlr_enc_len);
+			sdata->hlr_len = 0;
+		} else {
+			memcpy(sdata->hlr, gsup_msg->hlr_enc,
+				gsup_msg->hlr_enc_len);
+			sdata->hlr_len = gsup_msg->hlr_enc_len;
+		}
+	}
+
 	if (gsup_msg->pdp_info_compl) {
 		rc = gprs_subscr_pdp_data_clear(subscr);
 		if (rc > 0)
@@ -666,11 +678,17 @@
 
 int gprs_subscr_purge(struct gsm_subscriber *subscr)
 {
+	struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
 	struct gprs_gsup_message gsup_msg = {0};
 
 	LOGGSUBSCRP(LOGL_INFO, subscr, "purging MS subscriber\n");
 
 	gsup_msg.message_type = GPRS_GSUP_MSGT_PURGE_MS_REQUEST;
+
+	/* Provide the HLR number in case it is known */
+	gsup_msg.hlr_enc_len = sdata->hlr_len;
+	gsup_msg.hlr_enc = sdata->hlr;
+
 	return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
 }
 
diff --git a/openbsc/src/gprs/sgsn_cdr.c b/openbsc/src/gprs/sgsn_cdr.c
index 04084f5..d0cb712 100644
--- a/openbsc/src/gprs/sgsn_cdr.c
+++ b/openbsc/src/gprs/sgsn_cdr.c
@@ -64,7 +64,7 @@
 	if (ftell(cdr_file) != 0)
 		return;
 
-	fprintf(cdr_file, "timestamp,imsi,imei,msisdn,cell_id,lac,event,pdp_duration,ggsn_addr,sgsn_addr,apni,eua_addr,vol_in,vol_out,charging_id\n");
+	fprintf(cdr_file, "timestamp,imsi,imei,msisdn,cell_id,lac,hlr,event,pdp_duration,ggsn_addr,sgsn_addr,apni,eua_addr,vol_in,vol_out,charging_id\n");
 }
 
 static void cdr_log_mm(struct sgsn_instance *inst, const char *ev,
@@ -87,7 +87,7 @@
 	maybe_print_header(cdr_file);
 	gettimeofday(&tv, NULL);
 	gmtime_r(&tv.tv_sec, &tm);
-	fprintf(cdr_file, "%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s\n",
+	fprintf(cdr_file, "%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s,%s\n",
 		tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
 		tm.tm_hour, tm.tm_min, tm.tm_sec,
 		(int)(tv.tv_usec / 1000),
@@ -96,6 +96,7 @@
 		mmctx->msisdn,
 		mmctx->cell_id,
 		mmctx->ra.lac,
+		mmctx->hlr,
 		ev);
 
 	fclose(cdr_file);
@@ -171,7 +172,7 @@
 	duration = tp.tv_sec - pdp->cdr_start.tv_sec;
 
 	fprintf(cdr_file,
-		"%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s,%ld,%s,%s,%s,%s,%" PRIu64 ",%" PRIu64 ",%u\n",
+		"%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s,%s,%ld,%s,%s,%s,%s,%" PRIu64 ",%" PRIu64 ",%u\n",
 		tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
 		tm.tm_hour, tm.tm_min, tm.tm_sec,
 		(int)(tv.tv_usec / 1000),
@@ -180,6 +181,7 @@
 		pdp->mm ? pdp->mm->msisdn : "N/A",
 		pdp->mm ? pdp->mm->cell_id : -1,
 		pdp->mm ? pdp->mm->ra.lac : -1,
+		pdp->mm ? pdp->mm->hlr : "N/A",
 		ev,
 		(unsigned long ) duration,
 		ggsn_addr,
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
index 8b6e3ec..b7023ad 100644
--- a/openbsc/src/gprs/sgsn_vty.c
+++ b/openbsc/src/gprs/sgsn_vty.c
@@ -315,8 +315,8 @@
 {
 	vty_out(vty, "%sMM Context for IMSI %s, IMEI %s, P-TMSI %08x%s",
 		pfx, mm->imsi, mm->imei, mm->p_tmsi, VTY_NEWLINE);
-	vty_out(vty, "%s  MSISDN: %s, TLLI: %08x%s", pfx, mm->msisdn,
-		mm->tlli, VTY_NEWLINE);
+	vty_out(vty, "%s  MSISDN: %s, TLLI: %08x%s HLR: %s",
+		pfx, mm->msisdn, mm->tlli, mm->hlr, VTY_NEWLINE);
 	vty_out(vty, "%s  MM State: %s, Routeing Area: %u-%u-%u-%u, "
 		"Cell ID: %u%s", pfx,
 		get_value_string(gprs_mm_st_strs, mm->mm_state),
diff --git a/openbsc/tests/gprs/gprs_test.c b/openbsc/tests/gprs/gprs_test.c
index bbd1d8a..c78b98a 100644
--- a/openbsc/tests/gprs/gprs_test.c
+++ b/openbsc/tests/gprs/gprs_test.c
@@ -445,6 +445,8 @@
 		TEST_IMSI_IE,
 		0x08, 0x07, /* MSISDN of the subscriber */
 			0x91, 0x94, 0x61, 0x46, 0x32, 0x24, 0x43,
+		0x09, 0x07, /* HLR-Number of the subscriber */
+			0x91, 0x83, 0x52, 0x38, 0x48, 0x83, 0x93,
 		0x04, 0x00, /* PDP info complete */
 		0x05, 0x15,
 			0x10, 0x01, 0x01,
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index 879dfe3..a06c153 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -1155,6 +1155,8 @@
 			0x12, 0x09, 0x04, 't', 'e', 's', 't', 0x03, 'a', 'p', 'n',
 		0x08, 0x07, /* MSISDN 49166213323 encoded */
 			0x91, 0x94, 0x61, 0x26, 0x31, 0x23, 0xF3,
+		0x09, 0x07, /* MSISDN 38166213323 encoded */
+			0x91, 0x83, 0x61, 0x26, 0x31, 0x23, 0xF3,
 	};
 
 	OSMO_ASSERT(!mmctx || mmctx->subscr);