implement periodic Location Update expiry in the VLR

Remove subscribers which fail to send periodic Location Updates from the
list of subscribers known to the VLR. This complements the IMSI detach
procedure: periodic LU expiry triggers an implicit IMSI detach.

Expired subscribers are purged from a periodic timer which iterates
over all subscribers once per minute.

Subscribers with an active connection do not expire. This is controlled
by the subscriber conn FSM which sets a subscriber's the LU expiry timeout
value to GSM_SUBSCRIBER_NO_EXPIRATION while a connection is active.

Add support for fake time with osmo_clock_gettime() to msc_vlr tests.

This functionality existed in OpenBSC but was lost during the nitb split.
This code took some inspiration from the OpenBSC implementation.

Related: OS#1976
Change-Id: Iebdee8b12d22acfcfb265ee41e71cfc8d9eb3ba9
diff --git a/src/libmsc/subscr_conn.c b/src/libmsc/subscr_conn.c
index 1b3b240..c1d0e11 100644
--- a/src/libmsc/subscr_conn.c
+++ b/src/libmsc/subscr_conn.c
@@ -202,6 +202,15 @@
 
 static void subscr_conn_fsm_accepted_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 {
+	struct gsm_subscriber_connection *conn = fi->priv;
+
+	/* Stop Location Update expiry for this subscriber. While the subscriber
+	 * has an open connection the LU expiry timer must remain disabled.
+	 * Otherwise we would kick the subscriber off the network when the timer
+	 * expires e.g. during a long phone call.
+	 * The LU expiry timer will restart once the connection is closed. */
+	conn->vsub->expire_lu = VLR_SUBSCRIBER_NO_EXPIRATION;
+
 	if (!subscr_conn_fsm_has_active_transactions(fi))
 		osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_UNUSED, NULL);
 }
@@ -278,6 +287,12 @@
 	/* Cancel all VLR FSMs, if any */
 	vlr_subscr_cancel_attach_fsm(conn->vsub, OSMO_FSM_TERM_ERROR, GSM48_REJECT_CONGESTION);
 
+	if (conn->vsub) {
+		/* The subscriber has no active connection anymore.
+		 * Restart the periodic Location Update expiry timer for this subscriber. */
+		vlr_subscr_enable_expire_lu(conn->vsub);
+	}
+
 	/* If we're closing in a middle of a trans, we need to clean up */
 	trans_conn_closed(conn);