Use libvlr in libmsc (large refactoring)

Original libvlr code is by Harald Welte <laforge@gnumonks.org>,
polished and tweaked by Neels Hofmeyr <nhofmeyr@sysmocom.de>.

This is a long series of trial-and-error development collapsed in one patch.
This may be split in smaller commits if reviewers prefer that. If we can keep
it as one, we have saved ourselves the additional separation work.

SMS:

The SQL based lookup of SMS for attached subscribers no longer works since the
SQL database no longer has the subscriber data. Replace with a round-robin on
the SMS recipient MSISDNs paired with a VLR subscriber RAM lookup whether the
subscriber is currently attached.

If there are many SMS for not-attached subscribers in the SMS database, this
will become inefficient: a DB hit returns a pending SMS, the RAM lookup will
reveal that the subscriber is not attached, after which the DB is hit for the
next SMS. It would become more efficient e.g. by having an MSISDN based hash
list for the VLR subscribers and by marking non-attached SMS recipients in the
SMS database so that they can be excluded with the SQL query already.

There is a sanity limit to do at most 100 db hits per attempt to find a pending
SMS. So if there are more than 100 stored SMS waiting for their recipients to
actually attach to the MSC, it may take more than one SMS queue trigger to
deliver SMS for subscribers that are actually attached.

This is not very beautiful, but is merely intended to carry us over to a time
when we have a proper separate SMSC entity.

Introduce gsm_subscriber_connection ref-counting in libmsc.

Remove/Disable VTY and CTRL commands to create subscribers, which is now a task
of the OsmoHLR. Adjust the python tests accordingly.

Remove VTY cmd subscriber-keep-in-ram.

Use OSMO_GSUP_PORT = 4222 instead of 2222. See
I4222e21686c823985be8ff1f16b1182be8ad6175.

So far use the LAC from conn->bts, will be replaced by conn->lac in
Id3705236350d5f69e447046b0a764bbabc3d493c.

Related: OS#1592 OS#1974
Change-Id: I639544a6cdda77a3aafc4e3446a55393f60e4050
diff --git a/src/libmsc/sms_queue.c b/src/libmsc/sms_queue.c
index dc7f6e8..fe7a608 100644
--- a/src/libmsc/sms_queue.c
+++ b/src/libmsc/sms_queue.c
@@ -28,6 +28,8 @@
  * things up by collecting data from other parts of the system.
  */
 
+#include <limits.h>
+
 #include <openbsc/sms_queue.h>
 #include <openbsc/chan_alloc.h>
 #include <openbsc/db.h>
@@ -36,6 +38,7 @@
 #include <openbsc/gsm_04_11.h>
 #include <openbsc/gsm_subscriber.h>
 #include <openbsc/signal.h>
+#include <openbsc/vlr.h>
 
 #include <osmocom/core/talloc.h>
 
@@ -47,7 +50,7 @@
 struct gsm_sms_pending {
 	struct llist_head entry;
 
-	struct gsm_subscriber *subscr;
+	struct vlr_subscr *vsub;
 	unsigned long long sms_id;
 	int failed_attempts;
 	int resend;
@@ -62,7 +65,8 @@
 	int pending;
 
 	struct llist_head pending_sms;
-	unsigned long long last_subscr_id;
+
+	char last_msisdn[GSM_EXTENSION_LENGTH+1];
 };
 
 static int sms_subscr_cb(unsigned int, unsigned int, void *, void *);
@@ -88,12 +92,12 @@
 
 static struct gsm_sms_pending *sms_subscriber_find_pending(
 					struct gsm_sms_queue *smsq,
-					struct gsm_subscriber *subscr)
+					struct vlr_subscr *vsub)
 {
 	struct gsm_sms_pending *pending;
 
 	llist_for_each_entry(pending, &smsq->pending_sms, entry) {
-		if (pending->subscr == subscr)
+		if (pending->vsub == vsub)
 			return pending;
 	}
 
@@ -101,9 +105,9 @@
 }
 
 static int sms_subscriber_is_pending(struct gsm_sms_queue *smsq,
-				     struct gsm_subscriber *subscr)
+				     struct vlr_subscr *vsub)
 {
-	return sms_subscriber_find_pending(smsq, subscr) != NULL;
+	return sms_subscriber_find_pending(smsq, vsub) != NULL;
 }
 
 static struct gsm_sms_pending *sms_pending_from(struct gsm_sms_queue *smsq,
@@ -115,27 +119,28 @@
 	if (!pending)
 		return NULL;
 
-	pending->subscr = subscr_get(sms->receiver);
+	pending->vsub = vlr_subscr_get(sms->receiver);
 	pending->sms_id = sms->id;
 	return pending;
 }
 
 static void sms_pending_free(struct gsm_sms_pending *pending)
 {
-	subscr_put(pending->subscr);
+	vlr_subscr_put(pending->vsub);
 	llist_del(&pending->entry);
 	talloc_free(pending);
 }
 
 static void sms_pending_resend(struct gsm_sms_pending *pending)
 {
+	struct gsm_network *net = pending->vsub->vlr->user_ctx;
 	struct gsm_sms_queue *smsq;
 	LOGP(DLSMS, LOGL_DEBUG,
 	     "Scheduling resend of SMS %llu.\n", pending->sms_id);
 
 	pending->resend = 1;
 
-	smsq = pending->subscr->group->net->sms_queue;
+	smsq = net->sms_queue;
 	if (osmo_timer_pending(&smsq->resend_pending))
 		return;
 
@@ -144,12 +149,13 @@
 
 static void sms_pending_failed(struct gsm_sms_pending *pending, int paging_error)
 {
+	struct gsm_network *net = pending->vsub->vlr->user_ctx;
 	struct gsm_sms_queue *smsq;
 
 	LOGP(DLSMS, LOGL_NOTICE, "Sending SMS %llu failed %d times.\n",
 	     pending->sms_id, pending->failed_attempts);
 
-	smsq = pending->subscr->group->net->sms_queue;
+	smsq = net->sms_queue;
 	if (++pending->failed_attempts < smsq->max_fail)
 		return sms_pending_resend(pending);
 
@@ -186,23 +192,49 @@
 	}
 }
 
-static struct gsm_sms *take_next_sms(struct gsm_sms_queue *smsq)
+/* Find the next pending SMS by cycling through the recipients. We could also
+ * cycle through the pending SMS, but that might cause us to keep trying to
+ * send SMS to the same few subscribers repeatedly while not servicing other
+ * subscribers for a long time. By walking the list of recipient MSISDNs, we
+ * ensure that all subscribers get their fair time to receive SMS. */
+struct gsm_sms *smsq_take_next_sms(struct gsm_network *net,
+				   char *last_msisdn,
+				   size_t last_msisdn_buflen)
 {
 	struct gsm_sms *sms;
+	int wrapped = 0;
+	int sanity = 100;
+	char started_with_msisdn[last_msisdn_buflen];
 
-	sms = db_sms_get_unsent_by_subscr(smsq->network, smsq->last_subscr_id, 10);
-	if (sms) {
-		smsq->last_subscr_id = sms->receiver->id + 1;
+	osmo_strlcpy(started_with_msisdn, last_msisdn,
+		     sizeof(started_with_msisdn));
+
+	while (wrapped < 2 && (--sanity)) {
+		/* If we wrapped around and passed the first msisdn, we're
+		 * through the entire SMS DB; end it. */
+		if (wrapped && strcmp(last_msisdn, started_with_msisdn) >= 0)
+			break;
+
+		sms = db_sms_get_next_unsent_rr_msisdn(net, last_msisdn, 9);
+		if (!sms) {
+			last_msisdn[0] = '\0';
+			wrapped ++;
+			continue;
+		}
+
+		/* Whatever happens, next time around service another recipient
+		 */
+		osmo_strlcpy(last_msisdn, sms->dst.addr, last_msisdn_buflen);
+
+		/* Is the subscriber attached? If not, go to next SMS */
+		if (!sms->receiver || !sms->receiver->lu_complete)
+			continue;
+
 		return sms;
 	}
 
-	/* need to wrap around */
-	smsq->last_subscr_id = 0;
-	sms = db_sms_get_unsent_by_subscr(smsq->network,
-					  smsq->last_subscr_id, 10);
-	if (sms)
-		smsq->last_subscr_id = sms->receiver->id + 1;
-	return sms;
+	DEBUGP(DLSMS, "SMS queue: no SMS to be sent\n");
+	return NULL;
 }
 
 /**
@@ -224,7 +256,8 @@
 		struct gsm_sms *sms;
 
 
-		sms = take_next_sms(smsq);
+		sms = smsq_take_next_sms(smsq->network, smsq->last_msisdn,
+					 sizeof(smsq->last_msisdn));
 		if (!sms) {
 			LOGP(DLSMS, LOGL_DEBUG, "Sending SMS done (%d attempted)\n",
 			     attempted);
@@ -289,21 +322,22 @@
 /**
  * Send the next SMS or trigger the queue
  */
-static void sms_send_next(struct gsm_subscriber *subscr)
+static void sms_send_next(struct vlr_subscr *vsub)
 {
-	struct gsm_sms_queue *smsq = subscr->group->net->sms_queue;
+	struct gsm_network *net = vsub->vlr->user_ctx;
+	struct gsm_sms_queue *smsq = net->sms_queue;
 	struct gsm_sms_pending *pending;
 	struct gsm_sms *sms;
 
 	/* the subscriber should not be in the queue */
-	OSMO_ASSERT(!sms_subscriber_is_pending(smsq, subscr));
+	OSMO_ASSERT(!sms_subscriber_is_pending(smsq, vsub));
 
 	/* check for more messages for this subscriber */
-	sms = db_sms_get_unsent_for_subscr(subscr);
+	sms = db_sms_get_unsent_for_subscr(vsub, UINT_MAX);
 	if (!sms)
 		goto no_pending_sms;
 
-	/* No sms should be scheduled right now */
+	/* The sms should not be scheduled right now */
 	OSMO_ASSERT(!sms_is_in_pending(smsq, sms));
 
 	/* Remember that we deliver this SMS and send it */
@@ -322,7 +356,7 @@
 
 no_pending_sms:
 	/* Try to send the SMS to avoid the queue being stuck */
-	sms_submit_pending(subscr->group->net->sms_queue);
+	sms_submit_pending(net->sms_queue);
 }
 
 /*
@@ -362,7 +396,7 @@
 	return 0;
 }
 
-static int sub_ready_for_sm(struct gsm_network *net, struct gsm_subscriber *subscr)
+static int sub_ready_for_sm(struct gsm_network *net, struct vlr_subscr *vsub)
 {
 	struct gsm_sms *sms;
 	struct gsm_sms_pending *pending;
@@ -383,20 +417,20 @@
 	 */
 
 	/* check if we have pending requests */
-	pending = sms_subscriber_find_pending(net->sms_queue, subscr);
+	pending = sms_subscriber_find_pending(net->sms_queue, vsub);
 	if (pending) {
 		LOGP(DMSC, LOGL_NOTICE,
 		     "Pending paging while subscriber %llu attached.\n",
-		      subscr->id);
+		      vsub->id);
 		return 0;
 	}
 
-	conn = connection_for_subscr(subscr);
+	conn = connection_for_subscr(vsub);
 	if (!conn)
 		return -1;
 
 	/* Now try to deliver any pending SMS to this sub */
-	sms = db_sms_get_unsent_for_subscr(subscr);
+	sms = db_sms_get_unsent_for_subscr(vsub, UINT_MAX);
 	if (!sms)
 		return -1;
 	gsm411_send_sms(conn, sms);
@@ -406,13 +440,13 @@
 static int sms_subscr_cb(unsigned int subsys, unsigned int signal,
 			 void *handler_data, void *signal_data)
 {
-	struct gsm_subscriber *subscr = signal_data;
+	struct vlr_subscr *vsub = signal_data;
 
 	if (signal != S_SUBSCR_ATTACHED)
 		return 0;
 
 	/* this is readyForSM */
-	return sub_ready_for_sm(handler_data, subscr);
+	return sub_ready_for_sm(handler_data, vsub);
 }
 
 static int sms_sms_cb(unsigned int subsys, unsigned int signal,
@@ -421,7 +455,7 @@
 	struct gsm_network *network = handler_data;
 	struct sms_signal_data *sig_sms = signal_data;
 	struct gsm_sms_pending *pending;
-	struct gsm_subscriber *subscr;
+	struct vlr_subscr *vsub;
 
 	/* We got a new SMS and maybe should launch the queue again. */
 	if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) {
@@ -447,11 +481,11 @@
 	case S_SMS_DELIVERED:
 		/* Remember the subscriber and clear the pending entry */
 		network->sms_queue->pending -= 1;
-		subscr = subscr_get(pending->subscr);
+		vsub = vlr_subscr_get(pending->vsub);
 		sms_pending_free(pending);
 		/* Attempt to send another SMS to this subscriber */
-		sms_send_next(subscr);
-		subscr_put(subscr);
+		sms_send_next(vsub);
+		vlr_subscr_put(vsub);
 		break;
 	case S_SMS_MEM_EXCEEDED:
 		network->sms_queue->pending -= 1;
@@ -508,7 +542,7 @@
 
 	llist_for_each_entry(pending, &smsq->pending_sms, entry)
 		vty_out(vty, " SMS Pending for Subscriber: %llu SMS: %llu Failed: %d.%s",
-			pending->subscr->id, pending->sms_id,
+			pending->vsub->id, pending->sms_id,
 			pending->failed_attempts, VTY_NEWLINE);
 	return 0;
 }
@@ -535,7 +569,7 @@
 
 	llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) {
 		LOGP(DLSMS, LOGL_NOTICE,
-		     "SMSqueue clearing for sub %llu\n", pending->subscr->id);
+		     "SMSqueue clearing for sub %llu\n", pending->vsub->id);
 		sms_pending_free(pending);
 	}