1/2: refactor: add and use lu_fsm, osmo_gsup_req, osmo_ipa_name

These are seemingly orthogonal changes in one patch, because they are in fact
sufficiently intertwined that we are not willing to spend the time to separate
them. They are also refactoring changes, unlikely to make sense on their own.

** lu_fsm:

Attempting to make luop.c keep state about incoming GSUP requests made me find
shortcomings in several places:
- since it predates osmo_fsm, it is a state machine that does not strictly
  enforce the order of state transitions or the right sequence of incoming
  events.
- several places OSMO_ASSERT() on data received from the network.
- modifies the subscriber state before a LU is accepted.
- dead code about canceling a subscriber in a previous VLR. That would be a
  good thing to actually do, which should also be trivial now that we record
  vlr_name and sgsn_name, but I decided to remove the dead code for now.

To both step up the LU game *and* make it easier for me to integrate
osmo_gsup_req handling, I decided to create a lu_fsm, drawing from my, by now,
ample experience of writing osmo_fsms.

** osmo_gsup_req:

Prepare for D-GSM, where osmo-hlr will do proxy routing for remote HLRs /
communicate with remote MSCs via a proxy:

a) It is important that a response that osmo-hlr generates and that is sent
back to a requesting MSC contains all IEs that are needed to route it back to
the requester. Particularly source_name must become destination_name in the
response to be able to even reach the requesting MSC. Other fields are also
necessary to match, which were so far taken care of in individual numerous code
paths.

b) For some operations, the response to a GSUP request is generated
asynchronously (like Update Location Request -> Response, or taking the
response from an EUSE, or the upcoming proxying to a remote HLR). To be able to
feed a request message's information back into the response, we must thus keep
the request data around. Since struct osmo_gsup_message references a lot of
external data, usually with pointers directly into the received msgb, it is not
so trivial to pass GSUP message data around asynchronously, on its own.

osmo_gsup_req is the combined solution for both a and b: it keeps all data for
a GSUP message by taking ownership of the incoming msgb, and it provides an
explicit API "forcing" callers to respond with osmo_gsup_req_respond(), so that
all code paths trivially are definitely responding with the correct IEs set to
match the request's routing (by using osmo_gsup_make_response() recently added
to libosmocore).

Adjust all osmo-hlr code paths to use *only* osmo_gsup_req to respond to
incoming requests received on the GSUP server (above LU code being one of
them).

In fact, the same should be done on the client side. Hence osmo_gsup_req is
implemented in a server/client agnostic way, and is placed in
libosmo-gsupclient. As soon as we see routing errors in complex GSUP setups,
using osmo_gsup_req in the related GSUP client is likely to resolve those
problems without much thinking required beyond making all code paths use it.

libosmo-gsupclient is hence added to osmo-hlr binary's own library
dependencies. It would have been added by the D-GSM proxy routing anyway, we
are just doing it a little sooner.

** gsup_peer_id.c / osmo_ipa_name:

We so far handle an IPA unit name as pointer + size, or as just pointer with
implicit talloc size. To ease working with GSUP peer identification data, I
require:

- a non-allocated storage of an IPA Name. It brings the drawback of being
  size limited, but our current implementation is anyway only able to handle
  MSC and SGSN names of 31 characters (see struct hlr_subscriber).
- a single-argument handle for IPA Name,
- easy to use utility functions like osmo_ipa_name_to_str(), osmo_ipa_name_cmp(), and copying
  by simple assignment, a = b.

Hence this patch adds a osmo_ipa_name in gsup_peer_id.h and gsup_peer_id.c. Heavily
used in LU and osmo_gsup_req.

Depends: libosmocore Id9692880079ea0f219f52d81b1923a76fc640566
Change-Id: I3a8dff3d4a1cbe10d6ab08257a0138d6b2a082d9
diff --git a/include/osmocom/gsupclient/gsup_req.h b/include/osmocom/gsupclient/gsup_req.h
new file mode 100644
index 0000000..09959f0
--- /dev/null
+++ b/include/osmocom/gsupclient/gsup_req.h
@@ -0,0 +1,115 @@
+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <osmocom/gsm/gsup.h>
+#include <osmocom/gsupclient/ipa_name.h>
+
+struct osmo_gsup_req;
+
+#define LOG_GSUP_REQ_CAT_SRC(req, subsys, level, file, line, fmt, args...) \
+	LOGPSRC(subsys, level, file, line, "GSUP %u: %s: IMSI-%s %s: " fmt, \
+		(req) ? (req)->nr : 0, \
+		(req) ? osmo_ipa_name_to_str(&(req)->source_name) : "NULL", \
+		(req) ? (req)->gsup.imsi : "NULL", \
+		(req) ? osmo_gsup_message_type_name((req)->gsup.message_type) : "NULL", \
+		##args)
+#define LOG_GSUP_REQ_CAT(req, subsys, level, fmt, args...) \
+	LOG_GSUP_REQ_CAT_SRC(req, subsys, level, __FILE__, __LINE__, fmt, ##args)
+
+#define LOG_GSUP_REQ_SRC(req, level, file, line, fmt, args...) \
+	LOG_GSUP_REQ_CAT_SRC(req, DLGSUP, level, file, line, fmt, ##args)
+
+#define LOG_GSUP_REQ(req, level, fmt, args...) \
+	LOG_GSUP_REQ_SRC(req, level, __FILE__, __LINE__, fmt, ##args)
+
+typedef void (*osmo_gsup_req_send_response_t)(struct osmo_gsup_req *req, struct osmo_gsup_message *response);
+
+/* Keep track of an incoming request, to route back a response when it is ready.
+ * Particularly, a GSUP response to a request must contain various bits of information that need to be copied from the
+ * request for proxy/routing to work and for session states to remain valid. That is the main reason why (almost) all
+ * GSUP request/response should go through an osmo_gsup_req, even if it is handled synchronously.
+ */
+struct osmo_gsup_req {
+	/* The incoming GSUP message in decoded form. */
+	const struct osmo_gsup_message gsup;
+
+	/* Decoding result code. If decoding failed, this will be != 0. */
+	int decode_rc;
+
+	/* The ultimate source of this message: the source_name form the GSUP message, or, if not present, then the
+	 * immediate GSUP peer. GSUP messages going via a proxy reflect the initial source in the source_name.
+	 * This source_name is implicitly added to the routes for the conn the message was received on. */
+	struct osmo_ipa_name source_name;
+
+	/* If the source_name is not an immediate GSUP peer, this is set to the closest intermediate peer between here
+	 * and source_name. */
+	struct osmo_ipa_name via_proxy;
+
+	/* Identify this request by number, for logging. */
+	unsigned int nr;
+
+	/* osmo_gsup_req can be used by both gsup_server and gsup_client. The individual method of actually sending a
+	 * GSUP message is provided by this callback. */
+	osmo_gsup_req_send_response_t send_response_cb;
+
+	/* User supplied data pointer, may be used to provide context to send_response_cb(). */
+	void *cb_data;
+
+	/* List entry that can be used to keep a list of osmo_gsup_req instances; not used directly by osmo_gsup_req.c,
+	 * it is up to using implementations to keep a list. If this is non-NULL, osmo_gsup_req_free() calls
+	 * llist_del() on this. */
+	struct llist_head entry;
+
+	/* A decoded GSUP message still points into the received msgb. For a decoded osmo_gsup_message to remain valid,
+	 * we also need to keep the msgb. */
+	struct msgb *msg;
+};
+
+struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_ipa_name *from_peer, struct msgb *msg,
+					osmo_gsup_req_send_response_t send_response_cb, void *cb_data,
+					struct llist_head *add_to_list);
+void osmo_gsup_req_free(struct osmo_gsup_req *req);
+
+/*! Call _osmo_gsup_req_respond() to convey the sender's source file and line in the logs. */
+#define osmo_gsup_req_respond(REQ, RESPONSE, ERROR, FINAL_RESPONSE) \
+	_osmo_gsup_req_respond(REQ, RESPONSE, ERROR, FINAL_RESPONSE, __FILE__, __LINE__)
+int _osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response,
+			   bool error, bool final_response, const char *file, int line);
+
+/*! Call _osmo_gsup_req_respond_msgt() to convey the sender's source file and line in the logs. */
+#define osmo_gsup_req_respond_msgt(REQ, MESSAGE_TYPE, FINAL_RESPONSE) \
+	_osmo_gsup_req_respond_msgt(REQ, MESSAGE_TYPE, FINAL_RESPONSE, __FILE__, __LINE__)
+int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type,
+				bool final_response, const char *file, int line);
+
+/*! Log an error message, and call _osmo_gsup_req_respond() to convey the sender's source file and line in the logs. */
+#define osmo_gsup_req_respond_err(REQ, CAUSE, FMT, args...) do { \
+		LOG_GSUP_REQ(REQ, LOGL_ERROR, "%s: " FMT "\n", \
+			     get_value_string(gsm48_gmm_cause_names, CAUSE), ##args); \
+		_osmo_gsup_req_respond_err(REQ, CAUSE, __FILE__, __LINE__); \
+	} while(0)
+void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause,
+				const char *file, int line);
+
+int osmo_gsup_make_response(struct osmo_gsup_message *reply,
+			    const struct osmo_gsup_message *rx, bool error, bool final_response);
+
+size_t osmo_gsup_message_to_str_buf(char *buf, size_t bufsize, const struct osmo_gsup_message *msg);
+char *osmo_gsup_message_to_str_c(void *ctx, const struct osmo_gsup_message *msg);
diff --git a/include/osmocom/gsupclient/ipa_name.h b/include/osmocom/gsupclient/ipa_name.h
new file mode 100644
index 0000000..5016f48
--- /dev/null
+++ b/include/osmocom/gsupclient/ipa_name.h
@@ -0,0 +1,37 @@
+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <unistd.h>
+#include <stdint.h>
+
+/*! IPA Name: Arbitrary length blob, not necessarily zero-terminated.
+ * In osmo-hlr, struct hlr_subscriber is mostly used as static reference and cannot serve as talloc context, which is
+ * why this is also implemented as a fixed-maximum-size buffer instead of a talloc'd arbitrary sized buffer.
+ * NOTE: The length of val may be extended in the future if it becomes necessary.
+ * At the time of writing, this holds IPA unit name strings of very limited length.
+ */
+struct osmo_ipa_name {
+	size_t len;
+	uint8_t val[128];
+};
+
+int osmo_ipa_name_set(struct osmo_ipa_name *ipa_name, const uint8_t *val, size_t len);
+int osmo_ipa_name_set_str(struct osmo_ipa_name *ipa_name, const char *str_fmt, ...);
+int osmo_ipa_name_cmp(const struct osmo_ipa_name *a, const struct osmo_ipa_name *b);
+const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name);
diff --git a/include/osmocom/hlr/Makefile.am b/include/osmocom/hlr/Makefile.am
index 77a8764..532fa5d 100644
--- a/include/osmocom/hlr/Makefile.am
+++ b/include/osmocom/hlr/Makefile.am
@@ -9,6 +9,6 @@
 	hlr_vty.h \
 	hlr_vty_subscr.h \
 	logging.h \
-	luop.h \
+	lu_fsm.h \
 	rand.h \
 	$(NULL)
diff --git a/include/osmocom/hlr/db.h b/include/osmocom/hlr/db.h
index c927099..5c627be 100644
--- a/include/osmocom/hlr/db.h
+++ b/include/osmocom/hlr/db.h
@@ -3,6 +3,8 @@
 #include <stdbool.h>
 #include <sqlite3.h>
 
+#include <osmocom/gsupclient/ipa_name.h>
+
 struct hlr;
 
 enum stmt_idx {
@@ -151,13 +153,12 @@
 int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_subscriber *subscr);
 int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps);
 int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
-		 const char *vlr_or_sgsn_number, bool is_ps);
+		 const struct osmo_ipa_name *vlr_name, bool is_ps,
+		 const struct osmo_ipa_name *via_proxy);
 
 int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
 		    bool purge_val, bool is_ps);
 
-int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
-
 /*! Call sqlite3_column_text() and copy result to a char[].
  * \param[out] buf  A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
  * \param[in] stmt  An sqlite3_stmt*.
@@ -168,3 +169,14 @@
 		const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \
 		osmo_strlcpy(buf, _txt, sizeof(buf)); \
 	} while (0)
+
+/*! Call sqlite3_column_text() and copy result to a struct osmo_ipa_name.
+ * \param[out] ipa_name  A struct osmo_ipa_name* to write to.
+ * \param[in] stmt  An sqlite3_stmt*.
+ * \param[in] idx  Index in stmt's returned columns.
+ */
+#define copy_sqlite3_text_to_ipa_name(ipa_name, stmt, idx) \
+	do { \
+		const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \
+		osmo_ipa_name_set_str(ipa_name, _txt); \
+	} while (0)
diff --git a/include/osmocom/hlr/gsup_router.h b/include/osmocom/hlr/gsup_router.h
index 0fc10d0..ee12a2b 100644
--- a/include/osmocom/hlr/gsup_router.h
+++ b/include/osmocom/hlr/gsup_router.h
@@ -3,6 +3,8 @@
 #include <stdint.h>
 #include <osmocom/hlr/gsup_server.h>
 
+struct osmo_ipa_name;
+
 struct gsup_route {
 	struct llist_head list;
 
@@ -12,10 +14,12 @@
 
 struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,
 					const uint8_t *addr, size_t addrlen);
+struct osmo_gsup_conn *gsup_route_find_by_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name);
 
 struct gsup_route *gsup_route_find_by_conn(const struct osmo_gsup_conn *conn);
 
 /* add a new route for the given address to the given conn */
+int gsup_route_add_ipa_name(struct osmo_gsup_conn *conn, const struct osmo_ipa_name *ipa_name);
 int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen);
 
 /* delete all routes for the given connection */
@@ -24,3 +28,6 @@
 int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
 			const uint8_t *addr, size_t addrlen,
 			struct msgb *msg);
+int osmo_gsup_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name, struct msgb *msg);
+int osmo_gsup_enc_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name,
+			  const struct osmo_gsup_message *gsup);
diff --git a/include/osmocom/hlr/gsup_server.h b/include/osmocom/hlr/gsup_server.h
index 14f5013..149971a 100644
--- a/include/osmocom/hlr/gsup_server.h
+++ b/include/osmocom/hlr/gsup_server.h
@@ -5,6 +5,8 @@
 #include <osmocom/abis/ipa.h>
 #include <osmocom/abis/ipaccess.h>
 #include <osmocom/gsm/gsup.h>
+#include <osmocom/gsupclient/ipa_name.h>
+#include <osmocom/gsupclient/gsup_req.h>
 
 #ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN
 #define OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN	43 /* TS 24.008 10.5.4.7 */
@@ -22,9 +24,6 @@
 	/* list of osmo_gsup_conn */
 	struct llist_head clients;
 
-	/* lu_operations list */
-	struct llist_head *luop;
-
 	struct ipa_server_link *link;
 	osmo_gsup_read_cb_t read_cb;
 	struct llist_head routes;
@@ -45,10 +44,15 @@
 	/* Set when Location Update is received: */
 	bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */
 	bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */
+
+	/* The IPA unit name received on this link. Routes with more unit names serviced by this link may exist in
+	 * osmo_gsup_server->routes, but this is the name the immediate peer identified as in the IPA handshake. */
+	struct osmo_ipa_name peer_name;
 };
 
 struct msgb *osmo_gsup_msgb_alloc(const char *label);
 
+struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb *msg);
 int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg);
 int osmo_gsup_conn_ccm_get(const struct osmo_gsup_conn *clnt, uint8_t **addr,
 			   uint8_t tag);
@@ -57,7 +61,6 @@
 						 const char *ip_addr,
 						 uint16_t tcp_port,
 						 osmo_gsup_read_cb_t read_cb,
-						 struct llist_head *lu_op_lst,
 						 void *priv);
 
 void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups);
diff --git a/include/osmocom/hlr/hlr.h b/include/osmocom/hlr/hlr.h
index 18c4a1d..2214a8b 100644
--- a/include/osmocom/hlr/hlr.h
+++ b/include/osmocom/hlr/hlr.h
@@ -24,10 +24,16 @@
 
 #include <stdbool.h>
 #include <osmocom/core/linuxlist.h>
+#include <osmocom/gsm/ipa.h>
+#include <osmocom/core/tdef.h>
 
 #define HLR_DEFAULT_DB_FILE_PATH "hlr.db"
 
 struct hlr_euse;
+struct osmo_gsup_conn;
+enum osmo_gsup_message_type;
+
+extern struct osmo_tdef g_hlr_tdefs[];
 
 struct hlr {
 	/* GSUP server pointer */
@@ -43,6 +49,7 @@
 
 	/* Local bind addr */
 	char *gsup_bind_addr;
+	struct ipaccess_unit gsup_unit_name;
 
 	struct llist_head euse_list;
 	struct hlr_euse *euse_default;
@@ -68,3 +75,4 @@
 struct hlr_subscriber;
 
 void osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr);
+int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
diff --git a/include/osmocom/hlr/hlr_ussd.h b/include/osmocom/hlr/hlr_ussd.h
index 08e810e..8b2e837 100644
--- a/include/osmocom/hlr/hlr_ussd.h
+++ b/include/osmocom/hlr/hlr_ussd.h
@@ -46,8 +46,8 @@
 						   struct hlr_euse *euse);
 void ussd_route_del(struct hlr_ussd_route *rt);
 
-int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
-int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
+void rx_proc_ss_req(struct osmo_gsup_req *req);
+void rx_proc_ss_error(struct osmo_gsup_req *req);
 
 struct ss_session;
 struct ss_request;
@@ -56,6 +56,5 @@
 struct hlr_iuse {
 	const char *name;
 	/* call-back to be called for any incoming USSD messages for this IUSE */
-	int (*handle_ussd)(struct osmo_gsup_conn *conn, struct ss_session *ss,
-			   const struct osmo_gsup_message *gsup, const struct ss_request *req);
+	int (*handle_ussd)(struct ss_session *ss, const struct osmo_gsup_message *gsup, const struct ss_request *req);
 };
diff --git a/include/osmocom/hlr/logging.h b/include/osmocom/hlr/logging.h
index 83f1acd..4e0a25c 100644
--- a/include/osmocom/hlr/logging.h
+++ b/include/osmocom/hlr/logging.h
@@ -9,6 +9,7 @@
 	DAUC,
 	DSS,
 	DMSLOOKUP,
+	DLU,
 };
 
 extern const struct log_info hlr_log_info;
diff --git a/include/osmocom/hlr/lu_fsm.h b/include/osmocom/hlr/lu_fsm.h
new file mode 100644
index 0000000..2440185
--- /dev/null
+++ b/include/osmocom/hlr/lu_fsm.h
@@ -0,0 +1,22 @@
+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+void lu_rx_gsup(struct osmo_gsup_req *req);
diff --git a/include/osmocom/hlr/luop.h b/include/osmocom/hlr/luop.h
deleted file mode 100644
index 77a1dec..0000000
--- a/include/osmocom/hlr/luop.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/* OsmoHLR TX/RX lu operations */
-
-/* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * Author: Harald Welte <laforge@gnumonks.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#pragma once
-
-#include <stdbool.h>
-
-#include <osmocom/core/timer.h>
-#include <osmocom/gsm/gsup.h>
-
-#include <osmocom/hlr/db.h>
-#include <osmocom/hlr/gsup_server.h>
-
-#define CANCEL_TIMEOUT_SECS	30
-#define ISD_TIMEOUT_SECS	30
-
-enum lu_state {
-	LU_S_NULL,
-	LU_S_LU_RECEIVED,
-	LU_S_CANCEL_SENT,
-	LU_S_CANCEL_ACK_RECEIVED,
-	LU_S_ISD_SENT,
-	LU_S_ISD_ACK_RECEIVED,
-	LU_S_COMPLETE,
-};
-
-extern const struct value_string lu_state_names[];
-
-struct lu_operation {
-	/*! entry in global list of location update operations */
-	struct llist_head list;
-	/*! to which gsup_server do we belong */
-	struct osmo_gsup_server *gsup_server;
-	/*! state of the location update */
-	enum lu_state state;
-	/*! CS (false) or PS (true) Location Update? */
-	bool is_ps;
-	/*! currently running timer */
-	struct osmo_timer_list timer;
-
-	/*! subscriber related to this operation */
-	struct hlr_subscriber subscr;
-	/*! peer VLR/SGSN starting the request */
-	uint8_t *peer;
-};
-
-
-struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv);
-struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn);
-void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state);
-bool lu_op_fill_subscr(struct lu_operation *luop, struct db_context *dbc,
-		       const char *imsi);
-struct lu_operation *lu_op_by_imsi(const char *imsi,
-				   const struct llist_head *lst);
-
-void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause);
-void lu_op_tx_ack(struct lu_operation *luop);
-void lu_op_tx_cancel_old(struct lu_operation *luop);
-void lu_op_tx_insert_subscr_data(struct lu_operation *luop);
-void lu_op_tx_del_subscr_data(struct lu_operation *luop);
-
-void lu_op_free(struct lu_operation *luop);