Create subscribers on demand

Add a new vty option and allow to optionally generate a random msisdn,
as well as setting the default NAM:

subscriber-create-on-demand (no-msisdn|<3-15>) (none|cs|ps|both)

Thanks to Vadim for the random MSISDN patch [1], which was squashed into
this one.

[1] Change-Id: I475c71f9902950fa7498855a616e1ec231fad6ac

Depends on: Idc74f4d94ad44b9fc1b6d43178f5f33d551ebfb1 (libosmocore)
Change-Id: I0c9fe93f5c24b5e9fefb513c4d049fb7ebd47ecd
Related: OS#2542
diff --git a/src/hlr.c b/src/hlr.c
index 0bea590..694ba14 100644
--- a/src/hlr.c
+++ b/src/hlr.c
@@ -34,6 +34,8 @@
 #include <osmocom/ctrl/control_vty.h>
 #include <osmocom/gsm/apn.h>
 #include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
 
 #include "db.h"
 #include "hlr.h"
@@ -148,6 +150,78 @@
 	}
 }
 
+static int generate_new_msisdn(char *msisdn, const char *imsi, unsigned int len)
+{
+	int i, j, rc;
+	uint8_t rand_buf[GSM23003_MSISDN_MAX_DIGITS];
+
+	OSMO_ASSERT(len <= sizeof(rand_buf));
+
+	/* Generate a random unique MSISDN (with retry) */
+	for (i = 0; i < 10; i++) {
+		/* Get a random number (with retry) */
+		for (j = 0; j < 10; j++) {
+			rc = osmo_get_rand_id(rand_buf, len);
+			if (!rc)
+				break;
+		}
+		if (rc) {
+			LOGP(DMAIN, LOGL_ERROR, "IMSI='%s': Failed to generate new MSISDN, random number generator"
+						" failed (rc=%d)\n", imsi, rc);
+			return rc;
+		}
+
+		/* Shift 0x00 ... 0xff range to 30 ... 39 (ASCII numbers) */
+		for (j = 0; j < len; j++)
+			msisdn[j] = 48 + (rand_buf[j] % 10);
+		msisdn[j] = '\0';
+
+		/* Ensure there is no subscriber with such MSISDN */
+		if (db_subscr_exists_by_msisdn(g_hlr->dbc, msisdn) == -ENOENT)
+			return 0;
+	}
+
+	/* Failure */
+	LOGP(DMAIN, LOGL_ERROR, "IMSI='%s': Failed to generate a new MSISDN, consider increasing "
+				"the length for the automatically assigned MSISDNs "
+				"(see 'subscriber-create-on-demand' command)\n", imsi);
+	return -1;
+}
+
+static int subscr_create_on_demand(const char *imsi)
+{
+	char msisdn[GSM23003_MSISDN_MAX_DIGITS + 1];
+	int rc;
+	unsigned int rand_msisdn_len = g_hlr->subscr_create_on_demand_rand_msisdn_len;
+
+	if (!g_hlr->subscr_create_on_demand)
+		return -1;
+	if (db_subscr_exists_by_imsi(g_hlr->dbc, imsi) == 0)
+		return -1;
+	if (rand_msisdn_len && generate_new_msisdn(msisdn, imsi, rand_msisdn_len) != 0)
+		return -1;
+
+	LOGP(DMAIN, LOGL_INFO, "IMSI='%s': Creating subscriber on demand\n", imsi);
+	rc = db_subscr_create(g_hlr->dbc, imsi, g_hlr->subscr_create_on_demand_flags);
+	if (rc) {
+		LOGP(DMAIN, LOGL_ERROR, "Failed to create subscriber on demand (rc=%d): IMSI='%s'\n", rc, imsi);
+		return rc;
+	}
+
+	if (!rand_msisdn_len)
+		return 0;
+
+	/* Update MSISDN of the new (just allocated) subscriber */
+	rc = db_subscr_update_msisdn_by_imsi(g_hlr->dbc, imsi, msisdn);
+	if (rc) {
+		LOGP(DMAIN, LOGL_ERROR, "IMSI='%s': Failed to assign MSISDN='%s' (rc=%d)\n", imsi, msisdn, rc);
+		return rc;
+	}
+	LOGP(DMAIN, LOGL_INFO, "IMSI='%s': Successfully assigned MSISDN='%s'\n", imsi, msisdn);
+
+	return 0;
+}
+
 /***********************************************************************
  * Send Auth Info handling
  ***********************************************************************/
@@ -161,6 +235,8 @@
 	struct msgb *msg_out;
 	int rc;
 
+	subscr_create_on_demand(gsup->imsi);
+
 	/* initialize return message structure */
 	memset(&gsup_out, 0, sizeof(gsup_out));
 	memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi));
@@ -291,6 +367,8 @@
 	}
 	llist_add(&luop->list, &g_lu_ops);
 
+	subscr_create_on_demand(gsup->imsi);
+
 	/* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
 
 	/* check if subscriber is known at all */
@@ -415,6 +493,8 @@
 		return -1;
 	}
 
+	subscr_create_on_demand(gsup->imsi);
+
 	/* Save in DB if desired */
 	if (g_hlr->store_imei) {
 		LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing IMEI = %s\n", gsup->imsi, imei);