<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-hlr/+/16205">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">1/2: refactor: add and use lu_fsm, osmo_gsup_req, osmo_ipa_name<br><br>These are seemingly orthogonal changes in one patch, because they are in fact<br>sufficiently intertwined that we are not willing to spend the time to separate<br>them. They are also refactoring changes, unlikely to make sense on their own.<br><br>** lu_fsm:<br><br>Attempting to make luop.c keep state about incoming GSUP requests made me find<br>shortcomings in several places:<br>- since it predates osmo_fsm, it is a state machine that does not strictly<br>  enforce the order of state transitions or the right sequence of incoming<br>  events.<br>- several places OSMO_ASSERT() on data received from the network.<br>- modifies the subscriber state before a LU is accepted.<br>- dead code about canceling a subscriber in a previous VLR. That would be a<br>  good thing to actually do, which should also be trivial now that we record<br>  vlr_name and sgsn_name, but I decided to remove the dead code for now.<br><br>To both step up the LU game *and* make it easier for me to integrate<br>osmo_gsup_req handling, I decided to create a lu_fsm, drawing from my, by now,<br>ample experience of writing osmo_fsms.<br><br>** osmo_gsup_req:<br><br>Prepare for D-GSM, where osmo-hlr will do proxy routing for remote HLRs /<br>communicate with remote MSCs via a proxy:<br><br>a) It is important that a response that osmo-hlr generates and that is sent<br>back to a requesting MSC contains all IEs that are needed to route it back to<br>the requester. Particularly source_name must become destination_name in the<br>response to be able to even reach the requesting MSC. Other fields are also<br>necessary to match, which were so far taken care of in individual numerous code<br>paths.<br><br>b) For some operations, the response to a GSUP request is generated<br>asynchronously (like Update Location Request -> Response, or taking the<br>response from an EUSE, or the upcoming proxying to a remote HLR). To be able to<br>feed a request message's information back into the response, we must thus keep<br>the request data around. Since struct osmo_gsup_message references a lot of<br>external data, usually with pointers directly into the received msgb, it is not<br>so trivial to pass GSUP message data around asynchronously, on its own.<br><br>osmo_gsup_req is the combined solution for both a and b: it keeps all data for<br>a GSUP message by taking ownership of the incoming msgb, and it provides an<br>explicit API "forcing" callers to respond with osmo_gsup_req_respond(), so that<br>all code paths trivially are definitely responding with the correct IEs set to<br>match the request's routing (by using osmo_gsup_make_response() recently added<br>to libosmocore).<br><br>Adjust all osmo-hlr code paths to use *only* osmo_gsup_req to respond to<br>incoming requests received on the GSUP server (above LU code being one of<br>them).<br><br>In fact, the same should be done on the client side. Hence osmo_gsup_req is<br>implemented in a server/client agnostic way, and is placed in<br>libosmo-gsupclient. As soon as we see routing errors in complex GSUP setups,<br>using osmo_gsup_req in the related GSUP client is likely to resolve those<br>problems without much thinking required beyond making all code paths use it.<br><br>libosmo-gsupclient is hence added to osmo-hlr binary's own library<br>dependencies. It would have been added by the D-GSM proxy routing anyway, we<br>are just doing it a little sooner.<br><br>** cni_peer_id.c / osmo_ipa_name:<br><br>We so far handle an IPA unit name as pointer + size, or as just pointer with<br>implicit talloc size. To ease working with GSUP peer identification data, I<br>require:<br><br>- a non-allocated storage of an IPA Name. It brings the drawback of being<br>  size limited, but our current implementation is anyway only able to handle<br>  MSC and SGSN names of 31 characters (see struct hlr_subscriber).<br>- a single-argument handle for IPA Name,<br>- easy to use utility functions like osmo_ipa_name_to_str(), osmo_ipa_name_cmp(), and copying<br>  by simple assignment, a = b.<br><br>Hence this patch adds a osmo_ipa_name in cni_peer_id.h and cni_peer_id.c. Heavily<br>used in LU and osmo_gsup_req.<br><br>Depends: libosmocore Id9692880079ea0f219f52d81b1923a76fc640566<br>Change-Id: I3a8dff3d4a1cbe10d6ab08257a0138d6b2a082d9<br>---<br>M configure.ac<br>M include/Makefile.am<br>A include/osmocom/gsupclient/gsup_req.h<br>A include/osmocom/gsupclient/ipa_name.h<br>M include/osmocom/hlr/Makefile.am<br>M include/osmocom/hlr/db.h<br>M include/osmocom/hlr/gsup_router.h<br>M include/osmocom/hlr/gsup_server.h<br>M include/osmocom/hlr/hlr.h<br>M include/osmocom/hlr/hlr_ussd.h<br>M include/osmocom/hlr/logging.h<br>A include/osmocom/hlr/lu_fsm.h<br>D include/osmocom/hlr/luop.h<br>M src/Makefile.am<br>M src/db_hlr.c<br>M src/gsup_router.c<br>M src/gsup_send.c<br>M src/gsup_server.c<br>M src/gsupclient/Makefile.am<br>A src/gsupclient/gsup_req.c<br>A src/gsupclient/ipa_name.c<br>M src/hlr.c<br>M src/hlr_ussd.c<br>M src/logging.c<br>A src/lu_fsm.c<br>D src/luop.c<br>M tests/Makefile.am<br>M tests/db/Makefile.am<br>M tests/db/db_test.c<br>M tests/db/db_test.err<br>A tests/gsup/Makefile.am<br>A tests/gsup/gsup_test.c<br>A tests/gsup/gsup_test.err<br>A tests/gsup/gsup_test.ok<br>M tests/gsup_server/Makefile.am<br>M tests/test_nodes.vty<br>M tests/testsuite.at<br>37 files changed, 1,692 insertions(+), 865 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configure.ac b/configure.ac</span><br><span>index 73ff752..0e03ff0 100644</span><br><span>--- a/configure.ac</span><br><span>+++ b/configure.ac</span><br><span>@@ -204,6 +204,7 @@</span><br><span>      tests/auc/Makefile</span><br><span>   tests/auc/gen_ts_55_205_test_sets/Makefile</span><br><span>   tests/gsup_server/Makefile</span><br><span style="color: hsl(120, 100%, 40%);">+    tests/gsup/Makefile</span><br><span>  tests/db/Makefile</span><br><span>    tests/db_upgrade/Makefile</span><br><span>    tests/mslookup/Makefile</span><br><span>diff --git a/include/Makefile.am b/include/Makefile.am</span><br><span>index 9827950..aeeb03f 100644</span><br><span>--- a/include/Makefile.am</span><br><span>+++ b/include/Makefile.am</span><br><span>@@ -1,7 +1,9 @@</span><br><span> SUBDIRS = osmocom</span><br><span> </span><br><span> nobase_include_HEADERS = \</span><br><span style="color: hsl(120, 100%, 40%);">+     osmocom/gsupclient/ipa_name.h \</span><br><span>      osmocom/gsupclient/gsup_client.h \</span><br><span style="color: hsl(120, 100%, 40%);">+    osmocom/gsupclient/gsup_req.h \</span><br><span>      osmocom/mslookup/mdns.h \</span><br><span>    osmocom/mslookup/mdns_sock.h \</span><br><span>       osmocom/mslookup/mslookup_client_fake.h \</span><br><span>diff --git a/include/osmocom/gsupclient/gsup_req.h b/include/osmocom/gsupclient/gsup_req.h</span><br><span>new file mode 100644</span><br><span>index 0000000..c61483b</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/gsupclient/gsup_req.h</span><br><span>@@ -0,0 +1,119 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsup.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsupclient/ipa_name.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_gsup_req;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_GSUP_REQ_CAT_SRC(req, subsys, level, file, line, fmt, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+   LOGPSRC(subsys, level, file, line, "GSUP %u: %s: IMSI-%s %s: " fmt, \</span><br><span style="color: hsl(120, 100%, 40%);">+               (req) ? (req)->nr : 0, \</span><br><span style="color: hsl(120, 100%, 40%);">+           (req) ? osmo_ipa_name_to_str(&(req)->source_name) : "NULL", \</span><br><span style="color: hsl(120, 100%, 40%);">+                (req) ? (req)->gsup.imsi : "NULL", \</span><br><span style="color: hsl(120, 100%, 40%);">+             (req) ? osmo_gsup_message_type_name((req)->gsup.message_type) : "NULL", \</span><br><span style="color: hsl(120, 100%, 40%);">+                ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_GSUP_REQ_CAT(req, subsys, level, fmt, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+ LOG_GSUP_REQ_CAT_SRC(req, subsys, level, __FILE__, __LINE__, fmt, ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_GSUP_REQ_SRC(req, level, file, line, fmt, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+ LOG_GSUP_REQ_CAT_SRC(req, DLGSUP, level, file, line, fmt, ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_GSUP_REQ(req, level, fmt, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+ LOG_GSUP_REQ_SRC(req, level, __FILE__, __LINE__, fmt, ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+typedef void (*osmo_gsup_req_send_response_t)(struct osmo_gsup_req *req, struct osmo_gsup_message *response);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Keep track of an incoming request, to route back a response when it is ready.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Particularly, a GSUP response to a request must contain various bits of information that need to be copied from the</span><br><span style="color: hsl(120, 100%, 40%);">+ * request for proxy/routing to work and for session states to remain valid. That is the main reason why (almost) all</span><br><span style="color: hsl(120, 100%, 40%);">+ * GSUP request/response should go through an osmo_gsup_req, even if it is handled synchronously.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_gsup_req {</span><br><span style="color: hsl(120, 100%, 40%);">+     /* The incoming GSUP message in decoded form. */</span><br><span style="color: hsl(120, 100%, 40%);">+      const struct osmo_gsup_message gsup;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Decoding result code. If decoding failed, this will be != 0. */</span><br><span style="color: hsl(120, 100%, 40%);">+    int decode_rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* The ultimate source of this message: the source_name form the GSUP message, or, if not present, then the</span><br><span style="color: hsl(120, 100%, 40%);">+    * immediate GSUP peer. GSUP messages going via a proxy reflect the initial source in the source_name.</span><br><span style="color: hsl(120, 100%, 40%);">+         * This source_name is implicitly added to the routes for the conn the message was received on. */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_ipa_name source_name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If the source_name is not an immediate GSUP peer, this is set to the closest intermediate peer between here</span><br><span style="color: hsl(120, 100%, 40%);">+         * and source_name. */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_ipa_name via_proxy;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Identify this request by number, for logging. */</span><br><span style="color: hsl(120, 100%, 40%);">+   unsigned int nr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* osmo_gsup_req can be used by both gsup_server and gsup_client. The individual method of actually sending a</span><br><span style="color: hsl(120, 100%, 40%);">+  * GSUP message is provided by this callback. */</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_gsup_req_send_response_t send_response_cb;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* User supplied data pointer, may be used to provide context to send_response_cb(). */</span><br><span style="color: hsl(120, 100%, 40%);">+       void *cb_data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* List entry that can be used to keep a list of osmo_gsup_req instances; not used directly by osmo_gsup_req.c,</span><br><span style="color: hsl(120, 100%, 40%);">+        * it is up to using implementations to keep a list. If this is non-NULL, osmo_gsup_req_free() calls</span><br><span style="color: hsl(120, 100%, 40%);">+   * llist_del() on this. */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct llist_head entry;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* A decoded GSUP message still points into the received msgb. For a decoded osmo_gsup_message to remain valid,</span><br><span style="color: hsl(120, 100%, 40%);">+        * we also need to keep the msgb. */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct msgb *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_ipa_name *from_peer, struct msgb *msg,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  osmo_gsup_req_send_response_t send_response_cb, void *cb_data,</span><br><span style="color: hsl(120, 100%, 40%);">+                                        struct llist_head *add_to_list);</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_gsup_req_free(struct osmo_gsup_req *req);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! See _osmo_gsup_req_respond() for details.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Call _osmo_gsup_req_respond(), passing the caller's source file and line for logging. */</span><br><span style="color: hsl(120, 100%, 40%);">+#define osmo_gsup_req_respond(REQ, RESPONSE, ERROR, FINAL_RESPONSE) \</span><br><span style="color: hsl(120, 100%, 40%);">+        _osmo_gsup_req_respond(REQ, RESPONSE, ERROR, FINAL_RESPONSE, __FILE__, __LINE__)</span><br><span style="color: hsl(120, 100%, 40%);">+int _osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response,</span><br><span style="color: hsl(120, 100%, 40%);">+                      bool error, bool final_response, const char *file, int line);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! See _osmo_gsup_req_respond_msgt() for details.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Call _osmo_gsup_req_respond_msgt(), passing the caller's source file and line for logging. */</span><br><span style="color: hsl(120, 100%, 40%);">+#define osmo_gsup_req_respond_msgt(REQ, MESSAGE_TYPE, FINAL_RESPONSE) \</span><br><span style="color: hsl(120, 100%, 40%);">+ _osmo_gsup_req_respond_msgt(REQ, MESSAGE_TYPE, FINAL_RESPONSE, __FILE__, __LINE__)</span><br><span style="color: hsl(120, 100%, 40%);">+int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type,</span><br><span style="color: hsl(120, 100%, 40%);">+                              bool final_response, const char *file, int line);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! See _osmo_gsup_req_respond_err() for details.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Log an error message, and call _osmo_gsup_req_respond_err(), passing the caller's source file and line for logging.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define osmo_gsup_req_respond_err(REQ, CAUSE, FMT, args...) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+           LOG_GSUP_REQ(REQ, LOGL_ERROR, "%s: " FMT "\n", \</span><br><span style="color: hsl(120, 100%, 40%);">+                       get_value_string(gsm48_gmm_cause_names, CAUSE), ##args); \</span><br><span style="color: hsl(120, 100%, 40%);">+               _osmo_gsup_req_respond_err(REQ, CAUSE, __FILE__, __LINE__); \</span><br><span style="color: hsl(120, 100%, 40%);">+ } while(0)</span><br><span style="color: hsl(120, 100%, 40%);">+void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause,</span><br><span style="color: hsl(120, 100%, 40%);">+                            const char *file, int line);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_gsup_make_response(struct osmo_gsup_message *reply,</span><br><span style="color: hsl(120, 100%, 40%);">+                      const struct osmo_gsup_message *rx, bool error, bool final_response);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+size_t osmo_gsup_message_to_str_buf(char *buf, size_t bufsize, const struct osmo_gsup_message *msg);</span><br><span style="color: hsl(120, 100%, 40%);">+char *osmo_gsup_message_to_str_c(void *ctx, const struct osmo_gsup_message *msg);</span><br><span>diff --git a/include/osmocom/gsupclient/ipa_name.h b/include/osmocom/gsupclient/ipa_name.h</span><br><span>new file mode 100644</span><br><span>index 0000000..73cd1fb</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/gsupclient/ipa_name.h</span><br><span>@@ -0,0 +1,38 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+#include <unistd.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! IPA Name: Arbitrary length blob, not necessarily zero-terminated.</span><br><span style="color: hsl(120, 100%, 40%);">+ * In osmo-hlr, struct hlr_subscriber is mostly used as static reference and cannot serve as talloc context, which is</span><br><span style="color: hsl(120, 100%, 40%);">+ * why this is also implemented as a fixed-maximum-size buffer instead of a talloc'd arbitrary sized buffer.</span><br><span style="color: hsl(120, 100%, 40%);">+ * NOTE: The length of val may be extended in the future if it becomes necessary.</span><br><span style="color: hsl(120, 100%, 40%);">+ * At the time of writing, this holds IPA unit name strings of very limited length.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_ipa_name {</span><br><span style="color: hsl(120, 100%, 40%);">+    size_t len;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t val[128];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_ipa_name_set(struct osmo_ipa_name *ipa_name, const uint8_t *val, size_t len);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_ipa_name_set_str(struct osmo_ipa_name *ipa_name, const char *str_fmt, ...);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_ipa_name_cmp(const struct osmo_ipa_name *a, const struct osmo_ipa_name *b);</span><br><span style="color: hsl(120, 100%, 40%);">+const char *osmo_ipa_name_to_str_c(void *ctx, const struct osmo_ipa_name *ipa_name);</span><br><span style="color: hsl(120, 100%, 40%);">+const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name);</span><br><span>diff --git a/include/osmocom/hlr/Makefile.am b/include/osmocom/hlr/Makefile.am</span><br><span>index 77a8764..532fa5d 100644</span><br><span>--- a/include/osmocom/hlr/Makefile.am</span><br><span>+++ b/include/osmocom/hlr/Makefile.am</span><br><span>@@ -9,6 +9,6 @@</span><br><span>        hlr_vty.h \</span><br><span>  hlr_vty_subscr.h \</span><br><span>   logging.h \</span><br><span style="color: hsl(0, 100%, 40%);">-     luop.h \</span><br><span style="color: hsl(120, 100%, 40%);">+      lu_fsm.h \</span><br><span>   rand.h \</span><br><span>     $(NULL)</span><br><span>diff --git a/include/osmocom/hlr/db.h b/include/osmocom/hlr/db.h</span><br><span>index c927099..5c627be 100644</span><br><span>--- a/include/osmocom/hlr/db.h</span><br><span>+++ b/include/osmocom/hlr/db.h</span><br><span>@@ -3,6 +3,8 @@</span><br><span> #include <stdbool.h></span><br><span> #include <sqlite3.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsupclient/ipa_name.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct hlr;</span><br><span> </span><br><span> enum stmt_idx {</span><br><span>@@ -151,13 +153,12 @@</span><br><span> int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_subscriber *subscr);</span><br><span> int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps);</span><br><span> int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,</span><br><span style="color: hsl(0, 100%, 40%);">-           const char *vlr_or_sgsn_number, bool is_ps);</span><br><span style="color: hsl(120, 100%, 40%);">+          const struct osmo_ipa_name *vlr_name, bool is_ps,</span><br><span style="color: hsl(120, 100%, 40%);">+             const struct osmo_ipa_name *via_proxy);</span><br><span> </span><br><span> int db_subscr_purge(struct db_context *dbc, const char *by_imsi,</span><br><span>                 bool purge_val, bool is_ps);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /*! Call sqlite3_column_text() and copy result to a char[].</span><br><span>  * \param[out] buf  A char[] used as sizeof() arg(!) and osmo_strlcpy() target.</span><br><span>  * \param[in] stmt  An sqlite3_stmt*.</span><br><span>@@ -168,3 +169,14 @@</span><br><span>          const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \</span><br><span>          osmo_strlcpy(buf, _txt, sizeof(buf)); \</span><br><span>      } while (0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Call sqlite3_column_text() and copy result to a struct osmo_ipa_name.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] ipa_name  A struct osmo_ipa_name* to write to.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] stmt  An sqlite3_stmt*.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] idx  Index in stmt's returned columns.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define copy_sqlite3_text_to_ipa_name(ipa_name, stmt, idx) \</span><br><span style="color: hsl(120, 100%, 40%);">+   do { \</span><br><span style="color: hsl(120, 100%, 40%);">+                const char *_txt = (const char *) sqlite3_column_text(stmt, idx); \</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_ipa_name_set_str(ipa_name, _txt); \</span><br><span style="color: hsl(120, 100%, 40%);">+      } while (0)</span><br><span>diff --git a/include/osmocom/hlr/gsup_router.h b/include/osmocom/hlr/gsup_router.h</span><br><span>index 0fc10d0..ee12a2b 100644</span><br><span>--- a/include/osmocom/hlr/gsup_router.h</span><br><span>+++ b/include/osmocom/hlr/gsup_router.h</span><br><span>@@ -3,6 +3,8 @@</span><br><span> #include <stdint.h></span><br><span> #include <osmocom/hlr/gsup_server.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_ipa_name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct gsup_route {</span><br><span>        struct llist_head list;</span><br><span> </span><br><span>@@ -12,10 +14,12 @@</span><br><span> </span><br><span> struct osmo_gsup_conn *gsup_route_find(struct osmo_gsup_server *gs,</span><br><span>                                         const uint8_t *addr, size_t addrlen);</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_gsup_conn *gsup_route_find_by_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name);</span><br><span> </span><br><span> struct gsup_route *gsup_route_find_by_conn(const struct osmo_gsup_conn *conn);</span><br><span> </span><br><span> /* add a new route for the given address to the given conn */</span><br><span style="color: hsl(120, 100%, 40%);">+int gsup_route_add_ipa_name(struct osmo_gsup_conn *conn, const struct osmo_ipa_name *ipa_name);</span><br><span> int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen);</span><br><span> </span><br><span> /* delete all routes for the given connection */</span><br><span>@@ -24,3 +28,6 @@</span><br><span> int osmo_gsup_addr_send(struct osmo_gsup_server *gs,</span><br><span>                       const uint8_t *addr, size_t addrlen,</span><br><span>                         struct msgb *msg);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_gsup_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name, struct msgb *msg);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_gsup_enc_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                         const struct osmo_gsup_message *gsup);</span><br><span>diff --git a/include/osmocom/hlr/gsup_server.h b/include/osmocom/hlr/gsup_server.h</span><br><span>index 14f5013..149971a 100644</span><br><span>--- a/include/osmocom/hlr/gsup_server.h</span><br><span>+++ b/include/osmocom/hlr/gsup_server.h</span><br><span>@@ -5,6 +5,8 @@</span><br><span> #include <osmocom/abis/ipa.h></span><br><span> #include <osmocom/abis/ipaccess.h></span><br><span> #include <osmocom/gsm/gsup.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsupclient/ipa_name.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsupclient/gsup_req.h></span><br><span> </span><br><span> #ifndef OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN</span><br><span> #define OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN   43 /* TS 24.008 10.5.4.7 */</span><br><span>@@ -22,9 +24,6 @@</span><br><span>      /* list of osmo_gsup_conn */</span><br><span>         struct llist_head clients;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  /* lu_operations list */</span><br><span style="color: hsl(0, 100%, 40%);">-        struct llist_head *luop;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>     struct ipa_server_link *link;</span><br><span>        osmo_gsup_read_cb_t read_cb;</span><br><span>         struct llist_head routes;</span><br><span>@@ -45,10 +44,15 @@</span><br><span>      /* Set when Location Update is received: */</span><br><span>  bool supports_cs; /* client supports OSMO_GSUP_CN_DOMAIN_CS */</span><br><span>       bool supports_ps; /* client supports OSMO_GSUP_CN_DOMAIN_PS */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* The IPA unit name received on this link. Routes with more unit names serviced by this link may exist in</span><br><span style="color: hsl(120, 100%, 40%);">+     * osmo_gsup_server->routes, but this is the name the immediate peer identified as in the IPA handshake. */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_ipa_name peer_name;</span><br><span> };</span><br><span> </span><br><span> struct msgb *osmo_gsup_msgb_alloc(const char *label);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb *msg);</span><br><span> int osmo_gsup_conn_send(struct osmo_gsup_conn *conn, struct msgb *msg);</span><br><span> int osmo_gsup_conn_ccm_get(const struct osmo_gsup_conn *clnt, uint8_t **addr,</span><br><span>                     uint8_t tag);</span><br><span>@@ -57,7 +61,6 @@</span><br><span>                                                  const char *ip_addr,</span><br><span>                                                 uint16_t tcp_port,</span><br><span>                                           osmo_gsup_read_cb_t read_cb,</span><br><span style="color: hsl(0, 100%, 40%);">-                                            struct llist_head *lu_op_lst,</span><br><span>                                                void *priv);</span><br><span> </span><br><span> void osmo_gsup_server_destroy(struct osmo_gsup_server *gsups);</span><br><span>diff --git a/include/osmocom/hlr/hlr.h b/include/osmocom/hlr/hlr.h</span><br><span>index 0564518..5885600 100644</span><br><span>--- a/include/osmocom/hlr/hlr.h</span><br><span>+++ b/include/osmocom/hlr/hlr.h</span><br><span>@@ -24,10 +24,16 @@</span><br><span> </span><br><span> #include <stdbool.h></span><br><span> #include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/ipa.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/tdef.h></span><br><span> </span><br><span> #define HLR_DEFAULT_DB_FILE_PATH "hlr.db"</span><br><span> </span><br><span> struct hlr_euse;</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_gsup_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+enum osmo_gsup_message_type;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+extern struct osmo_tdef g_hlr_tdefs[];</span><br><span> </span><br><span> struct hlr {</span><br><span>   /* GSUP server pointer */</span><br><span>@@ -43,6 +49,7 @@</span><br><span> </span><br><span>    /* Local bind addr */</span><br><span>        char *gsup_bind_addr;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ipaccess_unit gsup_unit_name;</span><br><span> </span><br><span>     struct llist_head euse_list;</span><br><span>         struct hlr_euse *euse_default;</span><br><span>@@ -67,3 +74,4 @@</span><br><span> struct hlr_subscriber;</span><br><span> </span><br><span> void osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr);</span><br><span style="color: hsl(120, 100%, 40%);">+int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);</span><br><span>diff --git a/include/osmocom/hlr/hlr_ussd.h b/include/osmocom/hlr/hlr_ussd.h</span><br><span>index 08e810e..8b2e837 100644</span><br><span>--- a/include/osmocom/hlr/hlr_ussd.h</span><br><span>+++ b/include/osmocom/hlr/hlr_ussd.h</span><br><span>@@ -46,8 +46,8 @@</span><br><span>                                              struct hlr_euse *euse);</span><br><span> void ussd_route_del(struct hlr_ussd_route *rt);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);</span><br><span style="color: hsl(120, 100%, 40%);">+void rx_proc_ss_req(struct osmo_gsup_req *req);</span><br><span style="color: hsl(120, 100%, 40%);">+void rx_proc_ss_error(struct osmo_gsup_req *req);</span><br><span> </span><br><span> struct ss_session;</span><br><span> struct ss_request;</span><br><span>@@ -56,6 +56,5 @@</span><br><span> struct hlr_iuse {</span><br><span>         const char *name;</span><br><span>    /* call-back to be called for any incoming USSD messages for this IUSE */</span><br><span style="color: hsl(0, 100%, 40%);">-       int (*handle_ussd)(struct osmo_gsup_conn *conn, struct ss_session *ss,</span><br><span style="color: hsl(0, 100%, 40%);">-                     const struct osmo_gsup_message *gsup, const struct ss_request *req);</span><br><span style="color: hsl(120, 100%, 40%);">+       int (*handle_ussd)(struct ss_session *ss, const struct osmo_gsup_message *gsup, const struct ss_request *req);</span><br><span> };</span><br><span>diff --git a/include/osmocom/hlr/logging.h b/include/osmocom/hlr/logging.h</span><br><span>index 83f1acd..4e0a25c 100644</span><br><span>--- a/include/osmocom/hlr/logging.h</span><br><span>+++ b/include/osmocom/hlr/logging.h</span><br><span>@@ -9,6 +9,7 @@</span><br><span>      DAUC,</span><br><span>        DSS,</span><br><span>         DMSLOOKUP,</span><br><span style="color: hsl(120, 100%, 40%);">+    DLU,</span><br><span> };</span><br><span> </span><br><span> extern const struct log_info hlr_log_info;</span><br><span>diff --git a/include/osmocom/hlr/lu_fsm.h b/include/osmocom/hlr/lu_fsm.h</span><br><span>new file mode 100644</span><br><span>index 0000000..2440185</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/hlr/lu_fsm.h</span><br><span>@@ -0,0 +1,22 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lu_rx_gsup(struct osmo_gsup_req *req);</span><br><span>diff --git a/include/osmocom/hlr/luop.h b/include/osmocom/hlr/luop.h</span><br><span>deleted file mode 100644</span><br><span>index 77a1dec..0000000</span><br><span>--- a/include/osmocom/hlr/luop.h</span><br><span>+++ /dev/null</span><br><span>@@ -1,81 +0,0 @@</span><br><span style="color: hsl(0, 100%, 40%);">-/* OsmoHLR TX/RX lu operations */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(0, 100%, 40%);">- * All Rights Reserved</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * Author: Harald Welte <laforge@gnumonks.org></span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(0, 100%, 40%);">- * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(0, 100%, 40%);">- * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(0, 100%, 40%);">- * (at your option) any later version.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(0, 100%, 40%);">- * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(0, 100%, 40%);">- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(0, 100%, 40%);">- * GNU Affero General Public License for more details.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(0, 100%, 40%);">- * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#pragma once</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include <stdbool.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/core/timer.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/gsm/gsup.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/hlr/db.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/hlr/gsup_server.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#define CANCEL_TIMEOUT_SECS     30</span><br><span style="color: hsl(0, 100%, 40%);">-#define ISD_TIMEOUT_SECS      30</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-enum lu_state {</span><br><span style="color: hsl(0, 100%, 40%);">-       LU_S_NULL,</span><br><span style="color: hsl(0, 100%, 40%);">-      LU_S_LU_RECEIVED,</span><br><span style="color: hsl(0, 100%, 40%);">-       LU_S_CANCEL_SENT,</span><br><span style="color: hsl(0, 100%, 40%);">-       LU_S_CANCEL_ACK_RECEIVED,</span><br><span style="color: hsl(0, 100%, 40%);">-       LU_S_ISD_SENT,</span><br><span style="color: hsl(0, 100%, 40%);">-  LU_S_ISD_ACK_RECEIVED,</span><br><span style="color: hsl(0, 100%, 40%);">-  LU_S_COMPLETE,</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-extern const struct value_string lu_state_names[];</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct lu_operation {</span><br><span style="color: hsl(0, 100%, 40%);">- /*! entry in global list of location update operations */</span><br><span style="color: hsl(0, 100%, 40%);">-       struct llist_head list;</span><br><span style="color: hsl(0, 100%, 40%);">- /*! to which gsup_server do we belong */</span><br><span style="color: hsl(0, 100%, 40%);">-        struct osmo_gsup_server *gsup_server;</span><br><span style="color: hsl(0, 100%, 40%);">-   /*! state of the location update */</span><br><span style="color: hsl(0, 100%, 40%);">-     enum lu_state state;</span><br><span style="color: hsl(0, 100%, 40%);">-    /*! CS (false) or PS (true) Location Update? */</span><br><span style="color: hsl(0, 100%, 40%);">- bool is_ps;</span><br><span style="color: hsl(0, 100%, 40%);">-     /*! currently running timer */</span><br><span style="color: hsl(0, 100%, 40%);">-  struct osmo_timer_list timer;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   /*! subscriber related to this operation */</span><br><span style="color: hsl(0, 100%, 40%);">-     struct hlr_subscriber subscr;</span><br><span style="color: hsl(0, 100%, 40%);">-   /*! peer VLR/SGSN starting the request */</span><br><span style="color: hsl(0, 100%, 40%);">-       uint8_t *peer;</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv);</span><br><span style="color: hsl(0, 100%, 40%);">-struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn);</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state);</span><br><span style="color: hsl(0, 100%, 40%);">-bool lu_op_fill_subscr(struct lu_operation *luop, struct db_context *dbc,</span><br><span style="color: hsl(0, 100%, 40%);">-                    const char *imsi);</span><br><span style="color: hsl(0, 100%, 40%);">-struct lu_operation *lu_op_by_imsi(const char *imsi,</span><br><span style="color: hsl(0, 100%, 40%);">-                              const struct llist_head *lst);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause);</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_tx_ack(struct lu_operation *luop);</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_tx_cancel_old(struct lu_operation *luop);</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_tx_insert_subscr_data(struct lu_operation *luop);</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_tx_del_subscr_data(struct lu_operation *luop);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_free(struct lu_operation *luop);</span><br><span>diff --git a/src/Makefile.am b/src/Makefile.am</span><br><span>index f858ff0..bfbe775 100644</span><br><span>--- a/src/Makefile.am</span><br><span>+++ b/src/Makefile.am</span><br><span>@@ -41,7 +41,6 @@</span><br><span>   auc.c \</span><br><span>      ctrl.c \</span><br><span>     db.c \</span><br><span style="color: hsl(0, 100%, 40%);">-  luop.c \</span><br><span>     db_auc.c \</span><br><span>   db_hlr.c \</span><br><span>   gsup_router.c \</span><br><span>@@ -53,9 +52,11 @@</span><br><span>         hlr_vty_subscr.c \</span><br><span>   gsup_send.c \</span><br><span>        hlr_ussd.c \</span><br><span style="color: hsl(120, 100%, 40%);">+  lu_fsm.c \</span><br><span>   $(NULL)</span><br><span> </span><br><span> osmo_hlr_LDADD = \</span><br><span style="color: hsl(120, 100%, 40%);">+     $(top_builddir)/src/gsupclient/libosmo-gsup-client.la \</span><br><span>      $(LIBOSMOCORE_LIBS) \</span><br><span>        $(LIBOSMOGSM_LIBS) \</span><br><span>         $(LIBOSMOVTY_LIBS) \</span><br><span>@@ -71,6 +72,7 @@</span><br><span>     logging.c \</span><br><span>  rand_urandom.c \</span><br><span>     dbd_decode_binary.c \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(srcdir)/gsupclient/ipa_name.c \</span><br><span>    $(NULL)</span><br><span> </span><br><span> osmo_hlr_db_tool_LDADD = \</span><br><span>diff --git a/src/db_hlr.c b/src/db_hlr.c</span><br><span>index b3e3887..fdac75f 100644</span><br><span>--- a/src/db_hlr.c</span><br><span>+++ b/src/db_hlr.c</span><br><span>@@ -28,6 +28,7 @@</span><br><span> #include <time.h></span><br><span> </span><br><span> #include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/timer.h></span><br><span> #include <osmocom/crypt/auth.h></span><br><span> #include <osmocom/gsm/gsm23003.h></span><br><span> </span><br><span>@@ -36,8 +37,7 @@</span><br><span> #include <osmocom/hlr/logging.h></span><br><span> #include <osmocom/hlr/hlr.h></span><br><span> #include <osmocom/hlr/db.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/hlr/gsup_server.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/hlr/luop.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsupclient/ipa_name.h></span><br><span> </span><br><span> #define LOGHLR(imsi, level, fmt, args ...)       LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)</span><br><span> </span><br><span>@@ -734,7 +734,8 @@</span><br><span>  *         -EIO on database errors.</span><br><span>  */</span><br><span> int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,</span><br><span style="color: hsl(0, 100%, 40%);">-             const char *vlr_or_sgsn_number, bool is_ps)</span><br><span style="color: hsl(120, 100%, 40%);">+           const struct osmo_ipa_name *vlr_name, bool is_ps,</span><br><span style="color: hsl(120, 100%, 40%);">+             const struct osmo_ipa_name *via_proxy)</span><br><span> {</span><br><span>         sqlite3_stmt *stmt;</span><br><span>  int rc, ret = 0;</span><br><span>@@ -746,7 +747,7 @@</span><br><span>       if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))</span><br><span>             return -EIO;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        if (!db_bind_text(stmt, "$number", vlr_or_sgsn_number))</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!db_bind_text(stmt, "$number", (char*)vlr_name->val))</span><br><span>               return -EIO;</span><br><span> </span><br><span>     /* execute the statement */</span><br><span>@@ -873,51 +874,3 @@</span><br><span> </span><br><span>       return ret;</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Update nam_cs/nam_ps in the db and trigger notifications to GSUP clients.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in,out] hlr  Global hlr context.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] subscr   Subscriber from a fresh db_subscr_get_by_*() call.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] nam_val  True to enable CS/PS, false to disable.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] is_ps    True to enable/disable PS, false for CS.</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns 0 on success, ENOEXEC if there is no need to change, a negative</span><br><span style="color: hsl(0, 100%, 40%);">- *          value on error.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-     int rc;</span><br><span style="color: hsl(0, 100%, 40%);">-        struct lu_operation *luop;</span><br><span style="color: hsl(0, 100%, 40%);">-        struct osmo_gsup_conn *co;</span><br><span style="color: hsl(0, 100%, 40%);">-     bool is_val = is_ps? subscr->nam_ps : subscr->nam_cs;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     if (is_val == nam_val) {</span><br><span style="color: hsl(0, 100%, 40%);">-                LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                 nam_val ? "enable" : "disable", is_ps ? "PS" : "CS");</span><br><span style="color: hsl(0, 100%, 40%);">-            return ENOEXEC;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       rc = db_subscr_nam(hlr->dbc, subscr->imsi, nam_val, is_ps);</span><br><span style="color: hsl(0, 100%, 40%);">-       if (rc)</span><br><span style="color: hsl(0, 100%, 40%);">-         return rc > 0? -rc : rc;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     /* If we're disabling, send a notice out to the GSUP client that is</span><br><span style="color: hsl(0, 100%, 40%);">-  * responsible. Otherwise no need. */</span><br><span style="color: hsl(0, 100%, 40%);">-   if (nam_val)</span><br><span style="color: hsl(0, 100%, 40%);">-            return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* FIXME: only send to single SGSN where latest update for IMSI came from */</span><br><span style="color: hsl(0, 100%, 40%);">-    llist_for_each_entry(co, &hlr->gs->clients, list) {</span><br><span style="color: hsl(0, 100%, 40%);">-           luop = lu_op_alloc_conn(co);</span><br><span style="color: hsl(0, 100%, 40%);">-            if (!luop) {</span><br><span style="color: hsl(0, 100%, 40%);">-                    LOGHLR(subscr->imsi, LOGL_ERROR,</span><br><span style="color: hsl(0, 100%, 40%);">-                            "Cannot notify GSUP client, cannot allocate lu_operation,"</span><br><span style="color: hsl(0, 100%, 40%);">-                            " for %s:%u\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                               co && co->conn && co->conn->server? co->conn->server->addr : "unset",</span><br><span style="color: hsl(0, 100%, 40%);">-                               co && co->conn && co->conn->server? co->conn->server->port : 0);</span><br><span style="color: hsl(0, 100%, 40%);">-                       continue;</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-               luop->subscr = *subscr;</span><br><span style="color: hsl(0, 100%, 40%);">-              lu_op_tx_del_subscr_data(luop);</span><br><span style="color: hsl(0, 100%, 40%);">-         lu_op_free(luop);</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-       return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span>diff --git a/src/gsup_router.c b/src/gsup_router.c</span><br><span>index adf3af7..ba71fe4 100644</span><br><span>--- a/src/gsup_router.c</span><br><span>+++ b/src/gsup_router.c</span><br><span>@@ -47,6 +47,11 @@</span><br><span>     return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_gsup_conn *gsup_route_find_by_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       return gsup_route_find(gs, ipa_name->val, ipa_name->len);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! Find a GSUP connection's route (to read the IPA address from the route).</span><br><span>  * \param[in] conn GSUP connection</span><br><span>  * \return GSUP route</span><br><span>@@ -67,10 +72,15 @@</span><br><span> int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addrlen)</span><br><span> {</span><br><span>   struct gsup_route *gr;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_gsup_conn *exists_on_conn;</span><br><span> </span><br><span>   /* Check if we already have a route for this address */</span><br><span style="color: hsl(0, 100%, 40%);">- if (gsup_route_find(conn->server, addr, addrlen))</span><br><span style="color: hsl(0, 100%, 40%);">-            return -EEXIST;</span><br><span style="color: hsl(120, 100%, 40%);">+       exists_on_conn = gsup_route_find(conn->server, addr, addrlen);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (exists_on_conn) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (exists_on_conn != conn)</span><br><span style="color: hsl(120, 100%, 40%);">+                   return -EEXIST;</span><br><span style="color: hsl(120, 100%, 40%);">+               return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span> </span><br><span>        /* allocate new route and populate it */</span><br><span>     gr = talloc_zero(conn->server, struct gsup_route);</span><br><span>@@ -86,6 +96,11 @@</span><br><span>   return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int gsup_route_add_ipa_name(struct osmo_gsup_conn *conn, const struct osmo_ipa_name *ipa_name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return gsup_route_add(conn, ipa_name->val, ipa_name->len);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* delete all routes for the given connection */</span><br><span> int gsup_route_del_conn(struct osmo_gsup_conn *conn)</span><br><span> {</span><br><span>@@ -95,7 +110,7 @@</span><br><span>  llist_for_each_entry_safe(gr, gr2, &conn->server->routes, list) {</span><br><span>          if (gr->conn == conn) {</span><br><span>                   LOGP(DMAIN, LOGL_INFO, "Removing GSUP route for %s (GSUP disconnect)\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                           gr->addr);</span><br><span style="color: hsl(120, 100%, 40%);">+                         osmo_quote_str_c(OTC_SELECT, (char*)gr->addr, talloc_total_size(gr->addr)));</span><br><span>                      llist_del(&gr->list);</span><br><span>                         talloc_free(gr);</span><br><span>                     num_deleted++;</span><br><span>diff --git a/src/gsup_send.c b/src/gsup_send.c</span><br><span>index 29aeaa5..99fae93 100644</span><br><span>--- a/src/gsup_send.c</span><br><span>+++ b/src/gsup_send.c</span><br><span>@@ -42,7 +42,8 @@</span><br><span> </span><br><span>      conn = gsup_route_find(gs, addr, addrlen);</span><br><span>   if (!conn) {</span><br><span style="color: hsl(0, 100%, 40%);">-            DEBUGP(DLGSUP, "Cannot find route for addr %s\n", osmo_quote_str((const char*)addr, addrlen));</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DLGSUP, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                   "Cannot find route for addr %s\n", osmo_quote_str((const char*)addr, addrlen));</span><br><span>               msgb_free(msg);</span><br><span>              return -ENODEV;</span><br><span>      }</span><br><span>@@ -50,3 +51,41 @@</span><br><span>       return osmo_gsup_conn_send(conn, msg);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! Send a msgb to a given address using routing.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] gs  gsup server</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] ipa_name  IPA unit name of the client (SGSN, MSC/VLR, proxy).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] msg  message buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_gsup_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name, struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        if (ipa_name->val[ipa_name->len - 1]) {</span><br><span style="color: hsl(120, 100%, 40%);">+         /* Is not nul terminated. But for legacy reasons we (still) require that. */</span><br><span style="color: hsl(120, 100%, 40%);">+          if (ipa_name->len >= sizeof(ipa_name->val)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        LOGP(DLGSUP, LOGL_ERROR, "IPA unit name is too long: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                      osmo_ipa_name_to_str(ipa_name));</span><br><span style="color: hsl(120, 100%, 40%);">+                 return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             struct osmo_ipa_name ipa_name2 = *ipa_name;</span><br><span style="color: hsl(120, 100%, 40%);">+           ipa_name2.val[ipa_name->len] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+               ipa_name2.len++;</span><br><span style="color: hsl(120, 100%, 40%);">+              return osmo_gsup_addr_send(gs, ipa_name2.val, ipa_name2.len, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     return osmo_gsup_addr_send(gs, ipa_name->val, ipa_name->len, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_gsup_enc_send_to_ipa_name(struct osmo_gsup_server *gs, const struct osmo_ipa_name *ipa_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                    const struct osmo_gsup_message *gsup)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct msgb *msg = osmo_gsup_msgb_alloc("GSUP Tx");</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       rc = osmo_gsup_encode(msg, gsup);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (rc) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGP(DLGSUP, LOGL_ERROR, "IMSI-%s: Cannot encode GSUP: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                    gsup->imsi, osmo_gsup_message_type_name(gsup->message_type));</span><br><span style="color: hsl(120, 100%, 40%);">+              msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+               return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   LOGP(DLGSUP, LOGL_DEBUG, "IMSI-%s: Tx: %s\n", gsup->imsi, osmo_gsup_message_type_name(gsup->message_type));</span><br><span style="color: hsl(120, 100%, 40%);">+   return osmo_gsup_send_to_ipa_name(gs, ipa_name, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/gsup_server.c b/src/gsup_server.c</span><br><span>index ed1b285..ba2d456 100644</span><br><span>--- a/src/gsup_server.c</span><br><span>+++ b/src/gsup_server.c</span><br><span>@@ -26,10 +26,15 @@</span><br><span> #include <osmocom/abis/ipaccess.h></span><br><span> #include <osmocom/gsm/gsm48_ie.h></span><br><span> #include <osmocom/gsm/apn.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm23003.h></span><br><span> </span><br><span> #include <osmocom/hlr/gsup_server.h></span><br><span> #include <osmocom/hlr/gsup_router.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_GSUP_CONN(conn, level, fmt, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGP(DLGSUP, level, "GSUP peer %s: " fmt, \</span><br><span style="color: hsl(120, 100%, 40%);">+      (conn) ? osmo_ipa_name_to_str(&(conn)->peer_name) : "NULL", ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct msgb *osmo_gsup_msgb_alloc(const char *label)</span><br><span> {</span><br><span>     struct msgb *msg = msgb_alloc_headroom(1024+16, 16, label);</span><br><span>@@ -57,6 +62,57 @@</span><br><span>     return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void gsup_server_send_req_response(struct osmo_gsup_req *req, struct osmo_gsup_message *response)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_gsup_server *server = req->cb_data;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_gsup_conn *conn;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct msgb *msg = osmo_gsup_msgb_alloc("GSUP Tx");</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     conn = gsup_route_find_by_ipa_name(server, &req->source_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!conn) {</span><br><span style="color: hsl(120, 100%, 40%);">+          LOG_GSUP_REQ(req, LOGL_ERROR, "GSUP client that sent this request was disconnected, cannot respond\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = osmo_gsup_encode(msg, response);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOG_GSUP_REQ(req, LOGL_ERROR, "Unable to encode: {%s}\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                        osmo_gsup_message_to_str_c(OTC_SELECT, response));</span><br><span style="color: hsl(120, 100%, 40%);">+               msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = osmo_gsup_conn_send(conn, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (rc)</span><br><span style="color: hsl(120, 100%, 40%);">+               LOG_GSUP_CONN(conn, LOGL_ERROR, "Unable to send: %s\n", osmo_gsup_message_to_str_c(OTC_SELECT, response));</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_gsup_req *osmo_gsup_conn_rx(struct osmo_gsup_conn *conn, struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_gsup_req *req = osmo_gsup_req_new(conn->server, &conn->peer_name, msg, gsup_server_send_req_response,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                 conn->server, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!req)</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (req->via_proxy.len) {</span><br><span style="color: hsl(120, 100%, 40%);">+          /* The source of the GSUP message is not the immediate GSUP peer, but that peer is our proxy for that</span><br><span style="color: hsl(120, 100%, 40%);">+          * source. Add it to the routes for this conn (so we can route responses back). */</span><br><span style="color: hsl(120, 100%, 40%);">+            if (gsup_route_add_ipa_name(conn, &req->source_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        LOG_GSUP_REQ(req, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                              "GSUP message received from %s via peer %s, but there already exists a"</span><br><span style="color: hsl(120, 100%, 40%);">+                                     " different route to this source, message is not routable\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                               osmo_ipa_name_to_str(&req->source_name),</span><br><span style="color: hsl(120, 100%, 40%);">+                               osmo_ipa_name_to_str(&conn->peer_name));</span><br><span style="color: hsl(120, 100%, 40%);">+                  osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);</span><br><span style="color: hsl(120, 100%, 40%);">+                  return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return req;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int osmo_gsup_conn_oap_handle(struct osmo_gsup_conn *conn,</span><br><span>                                struct msgb *msg_rx)</span><br><span> {</span><br><span>@@ -202,10 +258,18 @@</span><br><span>            return -EINVAL;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   gsup_route_add(clnt, addr, addr_len);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_ipa_name_set(&clnt->peer_name, addr, addr_len);</span><br><span style="color: hsl(120, 100%, 40%);">+   gsup_route_add_ipa_name(clnt, &clnt->peer_name);</span><br><span>      return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void osmo_gsup_conn_free(struct osmo_gsup_conn *conn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   gsup_route_del_conn(conn);</span><br><span style="color: hsl(120, 100%, 40%);">+    llist_del(&conn->list);</span><br><span style="color: hsl(120, 100%, 40%);">+        talloc_free(conn);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int osmo_gsup_server_closed_cb(struct ipa_server_conn *conn)</span><br><span> {</span><br><span>  struct osmo_gsup_conn *clnt = (struct osmo_gsup_conn *)conn->data;</span><br><span>@@ -213,10 +277,7 @@</span><br><span>         LOGP(DLGSUP, LOGL_INFO, "Lost GSUP client %s:%d\n",</span><br><span>                conn->addr, conn->port);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      gsup_route_del_conn(clnt);</span><br><span style="color: hsl(0, 100%, 40%);">-      llist_del(&clnt->list);</span><br><span style="color: hsl(0, 100%, 40%);">-  talloc_free(clnt);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_gsup_conn_free(clnt);</span><br><span>   return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -298,8 +359,7 @@</span><br><span> </span><br><span> struct osmo_gsup_server *</span><br><span> osmo_gsup_server_create(void *ctx, const char *ip_addr, uint16_t tcp_port,</span><br><span style="color: hsl(0, 100%, 40%);">-                 osmo_gsup_read_cb_t read_cb,</span><br><span style="color: hsl(0, 100%, 40%);">-                    struct llist_head *lu_op_lst, void *priv)</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_gsup_read_cb_t read_cb, void *priv)</span><br><span> {</span><br><span>        struct osmo_gsup_server *gsups;</span><br><span>      int rc;</span><br><span>@@ -325,8 +385,6 @@</span><br><span>        if (rc < 0)</span><br><span>               goto failed;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        gsups->luop = lu_op_lst;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>  return gsups;</span><br><span> </span><br><span> failed:</span><br><span>@@ -390,8 +448,10 @@</span><br><span>  int len;</span><br><span> </span><br><span>         OSMO_ASSERT(gsup);</span><br><span style="color: hsl(120, 100%, 40%);">+    *gsup = (struct osmo_gsup_message){</span><br><span style="color: hsl(120, 100%, 40%);">+           .message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST,</span><br><span style="color: hsl(120, 100%, 40%);">+   };</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  gsup->message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST;</span><br><span>  osmo_strlcpy(gsup->imsi, imsi, sizeof(gsup->imsi));</span><br><span> </span><br><span>        if (msisdn_enc_size < OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN)</span><br><span>diff --git a/src/gsupclient/Makefile.am b/src/gsupclient/Makefile.am</span><br><span>index 4a449ec..38b1582 100644</span><br><span>--- a/src/gsupclient/Makefile.am</span><br><span>+++ b/src/gsupclient/Makefile.am</span><br><span>@@ -8,7 +8,11 @@</span><br><span> </span><br><span> lib_LTLIBRARIES = libosmo-gsup-client.la</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-libosmo_gsup_client_la_SOURCES = gsup_client.c</span><br><span style="color: hsl(120, 100%, 40%);">+libosmo_gsup_client_la_SOURCES = \</span><br><span style="color: hsl(120, 100%, 40%);">+    ipa_name.c \</span><br><span style="color: hsl(120, 100%, 40%);">+  gsup_client.c \</span><br><span style="color: hsl(120, 100%, 40%);">+       gsup_req.c \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(NULL)</span><br><span> </span><br><span> libosmo_gsup_client_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined</span><br><span> libosmo_gsup_client_la_LIBADD = $(TALLOC_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS)</span><br><span>diff --git a/src/gsupclient/gsup_req.c b/src/gsupclient/gsup_req.c</span><br><span>new file mode 100644</span><br><span>index 0000000..4a2ff23</span><br><span>--- /dev/null</span><br><span>+++ b/src/gsupclient/gsup_req.c</span><br><span>@@ -0,0 +1,410 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <inttypes.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm23003.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsupclient/gsup_req.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Create a new osmo_gsup_req record, decode GSUP and add to a provided list of requests.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Rationales:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * - osmo_gsup_req makes it easy to handle GSUP requests asynchronously. Before this, a GSUP message struct would be</span><br><span style="color: hsl(120, 100%, 40%);">+ *   valid only within a read callback function, and would not survive asynchronous handling, because the struct often</span><br><span style="color: hsl(120, 100%, 40%);">+ *   points directly into the received msgb. An osmo_gsup_req takes ownership of the msgb and ensures that the data</span><br><span style="color: hsl(120, 100%, 40%);">+ *   remains valid, so that it can easily be queued for later handling.</span><br><span style="color: hsl(120, 100%, 40%);">+ * - osmo_gsup_req unifies the composition of response messages to ensure that all IEs that identify it to belong to</span><br><span style="color: hsl(120, 100%, 40%);">+ *   the initial request are preserved / derived, like the source_name, destination_name, session_id, etc (see</span><br><span style="color: hsl(120, 100%, 40%);">+ *   osmo_gsup_make_response() for details).</span><br><span style="color: hsl(120, 100%, 40%);">+ * - Deallocation of an osmo_gsup_req is implicit upon sending a response. The idea is that msgb memory leaks are a</span><br><span style="color: hsl(120, 100%, 40%);">+ *   recurring source of bugs. By enforcing a request-response relation with implicit deallocation, osmo_gsup_req aims</span><br><span style="color: hsl(120, 100%, 40%);">+ *   to help avoid most such memory leaks implicitly.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The typical GSUP message sequence is:</span><br><span style="color: hsl(120, 100%, 40%);">+ *   -> rx request,</span><br><span style="color: hsl(120, 100%, 40%);">+ *   <- tx response.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * With osmo_gsup_req we can easily expand to:</span><br><span style="color: hsl(120, 100%, 40%);">+ *   -> rx request,</span><br><span style="color: hsl(120, 100%, 40%);">+ *   ... wait asynchronously,</span><br><span style="color: hsl(120, 100%, 40%);">+ *   <- tx response.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Only few GSUP conversations go beyond a 1:1 request-response match. But some have a session (e.g. USSD) or more</span><br><span style="color: hsl(120, 100%, 40%);">+ * negotiation may happen before the initial request is completed (e.g. Update Location with interleaved Insert</span><br><span style="color: hsl(120, 100%, 40%);">+ * Subscriber Data), so osmo_gsup_req also allows passing non-final responses.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The final_response flag allows for:</span><br><span style="color: hsl(120, 100%, 40%);">+ *    -> rx request,</span><br><span style="color: hsl(120, 100%, 40%);">+ *    ... wait async,</span><br><span style="color: hsl(120, 100%, 40%);">+ *    <- tx intermediate message to same peer (final_response = false, req remains open),</span><br><span style="color: hsl(120, 100%, 40%);">+ *    ... wait async,</span><br><span style="color: hsl(120, 100%, 40%);">+ *    -> rx intermediate response,</span><br><span style="color: hsl(120, 100%, 40%);">+ *    ... wait async,</span><br><span style="color: hsl(120, 100%, 40%);">+ *    <- tx final response (final_response = true, req is deallocated).</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This function takes ownership of the msgb, which will, on success, be owned by the returned osmo_gsup_req instance</span><br><span style="color: hsl(120, 100%, 40%);">+ * until osmo_gsup_req_free(). If a decoding error occurs, send an error response immediately, and return NULL.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The original CNI entity that sent the message is found in req->source_name. If the message was passed on by an</span><br><span style="color: hsl(120, 100%, 40%);">+ * intermediate CNI peer, then req->via_proxy is set to the immediate peer, and it is the responsibility of the caller</span><br><span style="color: hsl(120, 100%, 40%);">+ * to add req->source_name to the GSUP routes that are serviced by req->via_proxy (usually not relevant for clients with</span><br><span style="color: hsl(120, 100%, 40%);">+ * a single GSUP conn).</span><br><span style="color: hsl(120, 100%, 40%);">+ * Examples:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   "msc" ---> here</span><br><span style="color: hsl(120, 100%, 40%);">+ *   source_name = "msc"</span><br><span style="color: hsl(120, 100%, 40%);">+ *   via_proxy = <empty></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   "msc" ---> "proxy-HLR" ---> here (e.g. home HLR)</span><br><span style="color: hsl(120, 100%, 40%);">+ *   source_name = "msc"</span><br><span style="color: hsl(120, 100%, 40%);">+ *   via_proxy = "proxy-HLR"</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   "msc" ---> "proxy-HLR" ---> "home-HLR" ---> here (e.g. EUSE)</span><br><span style="color: hsl(120, 100%, 40%);">+ *   source_name = "msc"</span><br><span style="color: hsl(120, 100%, 40%);">+ *   via_proxy = "home-HLR"</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * An osmo_gsup_req must be concluded (and deallocated) by calling one of the osmo_gsup_req_respond* functions.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Note: osmo_gsup_req API makes use of OTC_SELECT to allocate volatile buffers for logging. Use of</span><br><span style="color: hsl(120, 100%, 40%);">+ * osmo_select_main_ctx() is mandatory when using osmo_gsup_req.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] ctx  Talloc context for allocation of the new request.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] from_peer  The IPA unit name of the immediate GSUP peer from which this msgb was received.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] msg  The message buffer containing the received GSUP message, where msgb_l2() shall point to the GSUP</span><br><span style="color: hsl(120, 100%, 40%);">+ *                 message start. The caller no longer owns the msgb when it is passed to this function: on error, the</span><br><span style="color: hsl(120, 100%, 40%);">+ *                 msgb is freed immediately, and on success, the msgb is owned by the returned osmo_gsup_req.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] send_response_cb  User specific method to send a GSUP response message, invoked upon</span><br><span style="color: hsl(120, 100%, 40%);">+ *                             osmo_gsup_req_respond*() functions. Typically this invokes encoding and transmitting the</span><br><span style="color: hsl(120, 100%, 40%);">+ *                            GSUP message over a network socket. See for example gsup_server_send_req_response().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[inout] cb_data  Context data to be used freely by the caller.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[inout] add_to_list  List to which to append this request, or NULL for no list.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return a newly allocated osmo_gsup_req, or NULL on error. If NULL is returned, an error response has already been</span><br><span style="color: hsl(120, 100%, 40%);">+ *         dispatched to the send_response_cb.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_gsup_req *osmo_gsup_req_new(void *ctx, const struct osmo_ipa_name *from_peer, struct msgb *msg,</span><br><span style="color: hsl(120, 100%, 40%);">+                                      osmo_gsup_req_send_response_t send_response_cb, void *cb_data,</span><br><span style="color: hsl(120, 100%, 40%);">+                                        struct llist_head *add_to_list)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    static unsigned int next_req_nr = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_gsup_req *req;</span><br><span style="color: hsl(120, 100%, 40%);">+    int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!msgb_l2(msg) || !msgb_l2len(msg)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: missing or empty L2 data\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  osmo_ipa_name_to_str(from_peer));</span><br><span style="color: hsl(120, 100%, 40%);">+                msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   req = talloc_zero(ctx, struct osmo_gsup_req);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(req);</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Note: req->gsup is declared const, so that the incoming message cannot be modified by handlers. */</span><br><span style="color: hsl(120, 100%, 40%);">+      req->nr = next_req_nr++;</span><br><span style="color: hsl(120, 100%, 40%);">+   req->msg = msg;</span><br><span style="color: hsl(120, 100%, 40%);">+    req->send_response_cb = send_response_cb;</span><br><span style="color: hsl(120, 100%, 40%);">+  req->cb_data = cb_data;</span><br><span style="color: hsl(120, 100%, 40%);">+    if (from_peer)</span><br><span style="color: hsl(120, 100%, 40%);">+                req->source_name = *from_peer;</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = osmo_gsup_decode(msgb_l2(req->msg), msgb_l2len(req->msg), (struct osmo_gsup_message*)&req->gsup);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DLGSUP, LOGL_ERROR, "Rx GSUP from %s: cannot decode (rc=%d)\n", osmo_ipa_name_to_str(from_peer), rc);</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_gsup_req_free(req);</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   LOG_GSUP_REQ(req, LOGL_DEBUG, "new request: {%s}\n", osmo_gsup_message_to_str_c(OTC_SELECT, &req->gsup));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (req->gsup.source_name_len) {</span><br><span style="color: hsl(120, 100%, 40%);">+           if (osmo_ipa_name_set(&req->source_name, req->gsup.source_name, req->gsup.source_name_len)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    LOGP(DLGSUP, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                           "Rx GSUP from %s: failed to decode source_name, message is not routable\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                         osmo_ipa_name_to_str(from_peer));</span><br><span style="color: hsl(120, 100%, 40%);">+                        osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_ROUTING_ERROR, true);</span><br><span style="color: hsl(120, 100%, 40%);">+                  return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           /* The source of the GSUP message is not the immediate GSUP peer; the peer is our proxy for that source.</span><br><span style="color: hsl(120, 100%, 40%);">+               */</span><br><span style="color: hsl(120, 100%, 40%);">+           if (osmo_ipa_name_cmp(&req->source_name, from_peer))</span><br><span style="color: hsl(120, 100%, 40%);">+                   req->via_proxy = *from_peer;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!osmo_imsi_str_valid(req->gsup.imsi)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "invalid IMSI: %s",</span><br><span style="color: hsl(120, 100%, 40%);">+                                   osmo_quote_str(req->gsup.imsi, -1));</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (add_to_list)</span><br><span style="color: hsl(120, 100%, 40%);">+              llist_add_tail(&req->entry, add_to_list);</span><br><span style="color: hsl(120, 100%, 40%);">+      return req;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Free an osmo_gsup_req and its msgb -- this is usually implicit in osmo_gsup_req_resond_*(), it should not be</span><br><span style="color: hsl(120, 100%, 40%);">+ * necessary to call this directly. */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_gsup_req_free(struct osmo_gsup_req *req)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     LOG_GSUP_REQ(req, LOGL_DEBUG, "free\n");</span><br><span style="color: hsl(120, 100%, 40%);">+    if (req->msg)</span><br><span style="color: hsl(120, 100%, 40%);">+              msgb_free(req->msg);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (req->entry.prev)</span><br><span style="color: hsl(120, 100%, 40%);">+               llist_del(&req->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+        talloc_free(req);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Send a response to a GSUP request.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Ensure that the response message contains all GSUP IEs that identify it as a response for the request req, by calling</span><br><span style="color: hsl(120, 100%, 40%);">+ * osmo_gsup_make_response().</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The final complete response message is passed to req->send_response_cb() to take care of the transmission.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param req  Request as previously initialized by osmo_gsup_req_new().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param response  Buffer to compose the response, possibly with some pre-configured IEs.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                  Any missing IEs are added via osmo_gsup_make_response().</span><br><span style="color: hsl(120, 100%, 40%);">+ *                  Must not be NULL. Does not need to remain valid memory beyond the function call,</span><br><span style="color: hsl(120, 100%, 40%);">+ *                  i.e. this can just be a local variable in the calling function.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param error  True when the response message indicates an error response (error message type).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param final_response  True when the request is concluded by this response, which deallocates the req.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                        False when the request should remain open after this response.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                        For most plain request->response GSUP messages, this should be True.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param file  Source file for logging as in __FILE__, added by osmo_gsup_req_respond() macro.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param line  Source line for logging as in __LINE__, added by osmo_gsup_req_respond() macro.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int _osmo_gsup_req_respond(struct osmo_gsup_req *req, struct osmo_gsup_message *response,</span><br><span style="color: hsl(120, 100%, 40%);">+                        bool error, bool final_response, const char *file, int line)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = osmo_gsup_make_response(response, &req->gsup, error, final_response);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (rc) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOG_GSUP_REQ_SRC(req, LOGL_ERROR, file, line, "Invalid response (rc=%d): {%s}\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                            rc, osmo_gsup_message_to_str_c(OTC_SELECT, response));</span><br><span style="color: hsl(120, 100%, 40%);">+               rc = -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+         goto exit_cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!req->send_response_cb) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_GSUP_REQ_SRC(req, LOGL_ERROR, file, line, "No send_response_cb set, cannot send: {%s}\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                osmo_gsup_message_to_str_c(OTC_SELECT, response));</span><br><span style="color: hsl(120, 100%, 40%);">+           rc = -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+         goto exit_cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   LOG_GSUP_REQ_SRC(req, LOGL_DEBUG, file, line, "Tx response: {%s}\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                         osmo_gsup_message_to_str_c(OTC_SELECT, response));</span><br><span style="color: hsl(120, 100%, 40%);">+   req->send_response_cb(req, response);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+exit_cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+     if (final_response)</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_gsup_req_free(req);</span><br><span style="color: hsl(120, 100%, 40%);">+      return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Shorthand for _osmo_gsup_req_respond() with no additional IEs and a fixed message type.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Set the message type in a local osmo_gsup_message and feed it to _osmo_gsup_req_respond().</span><br><span style="color: hsl(120, 100%, 40%);">+ * That will ensure to add all IEs that identify it as a response to req.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param req  Request as previously initialized by osmo_gsup_req_new().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message_type  The GSUP message type discriminator to respond with.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param final_response  True when the request is concluded by this response, which deallocates the req.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                        False when the request should remain open after this response.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                        For most plain request->response GSUP messages, this should be True.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param file  Source file for logging as in __FILE__, added by osmo_gsup_req_respond_msgt() macro.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param line  Source line for logging as in __LINE__, added by osmo_gsup_req_respond_msgt() macro.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int _osmo_gsup_req_respond_msgt(struct osmo_gsup_req *req, enum osmo_gsup_message_type message_type,</span><br><span style="color: hsl(120, 100%, 40%);">+                             bool final_response, const char *file, int line)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_gsup_message response = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .message_type = message_type,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+    return _osmo_gsup_req_respond(req, &response, OSMO_GSUP_IS_MSGT_ERROR(message_type), final_response,</span><br><span style="color: hsl(120, 100%, 40%);">+                                    file, line);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Shorthand for _osmo_gsup_req_respond() with an error cause IEs and using the req's matched error message type.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Set the error cause in a local osmo_gsup_message and feed it to _osmo_gsup_req_respond().</span><br><span style="color: hsl(120, 100%, 40%);">+ * That will ensure to add all IEs that identify it as a response to req.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Responding with an error always implies a final response: req is implicitly deallocated.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param req  Request as previously initialized by osmo_gsup_req_new().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param cause  The error cause to include in a OSMO_GSUP_CAUSE_IE.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param file  Source file for logging as in __FILE__, added by osmo_gsup_req_respond_err() macro.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param line  Source line for logging as in __LINE__, added by osmo_gsup_req_respond_err() macro.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void _osmo_gsup_req_respond_err(struct osmo_gsup_req *req, enum gsm48_gmm_cause cause,</span><br><span style="color: hsl(120, 100%, 40%);">+                          const char *file, int line)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_gsup_message response = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .cause = cause,</span><br><span style="color: hsl(120, 100%, 40%);">+       };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* No need to answer if we couldn't parse an ERROR message type, only REQUESTs need an error reply. */</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!OSMO_GSUP_IS_MSGT_REQUEST(req->gsup.message_type)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_gsup_req_free(req);</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_gsup_req_respond(req, &response, true, true);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! This function is implicitly called by the osmo_gsup_req API, if at all possible rather use osmo_gsup_req_respond().</span><br><span style="color: hsl(120, 100%, 40%);">+ * This function is non-static mostly to allow unit testing.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Set fields, if still unset, that need to be copied from a received message over to its response message, to ensure</span><br><span style="color: hsl(120, 100%, 40%);">+ * the response can be routed back to the requesting peer even via GSUP proxies.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Note: after calling this function, fields in the reply may reference the same memory as rx and are not deep-copied,</span><br><span style="color: hsl(120, 100%, 40%);">+ * as is the usual way we are handling decoded GSUP messages.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * These fields are set in the reply message, iff they are still unset:</span><br><span style="color: hsl(120, 100%, 40%);">+ * - Set reply->message_type to the rx's matching RESULT code (or ERROR code if error == true).</span><br><span style="color: hsl(120, 100%, 40%);">+ * - IMSI,</span><br><span style="color: hsl(120, 100%, 40%);">+ * - Set reply->destination_name to rx->source_name (for proxy routing),</span><br><span style="color: hsl(120, 100%, 40%);">+ * - sm_rp_mr (for SMS),</span><br><span style="color: hsl(120, 100%, 40%);">+ * - session_id (for SS/USSD),</span><br><span style="color: hsl(120, 100%, 40%);">+ * - if rx->session_state is not NONE, set tx->session_state depending on the final_response argument:</span><br><span style="color: hsl(120, 100%, 40%);">+ *   If false, set to OSMO_GSUP_SESSION_STATE_CONTINUE, else OSMO_GSUP_SESSION_STATE_END.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * If values in reply are already set, they will not be overwritten. The return code is an optional way of finding out</span><br><span style="color: hsl(120, 100%, 40%);">+ * whether all values that were already set in 'reply' are indeed matching the 'rx' values that would have been set.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] rx  Received GSUP message that is being replied to.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[inout] reply  The message that should be the response to rx, either empty or with some values already set up.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return 0 if the resulting message is a valid response for rx, nonzero otherwise. A nonzero rc has no effect on the</span><br><span style="color: hsl(120, 100%, 40%);">+ *         values set in the reply message: all unset fields are first updated, and then the rc is determined.</span><br><span style="color: hsl(120, 100%, 40%);">+ *         The rc is intended to merely warn if the reply message already contained data that is incompatible with rx,</span><br><span style="color: hsl(120, 100%, 40%);">+ *         e.g. a mismatching IMSI.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_gsup_make_response(struct osmo_gsup_message *reply,</span><br><span style="color: hsl(120, 100%, 40%);">+                          const struct osmo_gsup_message *rx, bool error, bool final_response)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   int rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!reply->message_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (error)</span><br><span style="color: hsl(120, 100%, 40%);">+                    reply->message_type = OSMO_GSUP_TO_MSGT_ERROR(rx->message_type);</span><br><span style="color: hsl(120, 100%, 40%);">+                else</span><br><span style="color: hsl(120, 100%, 40%);">+                  reply->message_type = OSMO_GSUP_TO_MSGT_RESULT(rx->message_type);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (*reply->imsi == '\0')</span><br><span style="color: hsl(120, 100%, 40%);">+          OSMO_STRLCPY_ARRAY(reply->imsi, rx->imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (reply->message_class == OSMO_GSUP_MESSAGE_CLASS_UNSET)</span><br><span style="color: hsl(120, 100%, 40%);">+         reply->message_class = rx->message_class;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!reply->destination_name || !reply->destination_name_len) {</span><br><span style="color: hsl(120, 100%, 40%);">+         reply->destination_name = rx->source_name;</span><br><span style="color: hsl(120, 100%, 40%);">+              reply->destination_name_len = rx->source_name_len;</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* RP-Message-Reference is mandatory for SM Service */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!reply->sm_rp_mr)</span><br><span style="color: hsl(120, 100%, 40%);">+              reply->sm_rp_mr = rx->sm_rp_mr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* For SS/USSD, it's important to keep both session state and ID IEs */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!reply->session_id)</span><br><span style="color: hsl(120, 100%, 40%);">+            reply->session_id = rx->session_id;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (rx->session_state != OSMO_GSUP_SESSION_STATE_NONE</span><br><span style="color: hsl(120, 100%, 40%);">+          && reply->session_state == OSMO_GSUP_SESSION_STATE_NONE) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (final_response || rx->session_state == OSMO_GSUP_SESSION_STATE_END)</span><br><span style="color: hsl(120, 100%, 40%);">+                    reply->session_state = OSMO_GSUP_SESSION_STATE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+                else</span><br><span style="color: hsl(120, 100%, 40%);">+                  reply->session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (strcmp(reply->imsi, rx->imsi))</span><br><span style="color: hsl(120, 100%, 40%);">+              rc |= 1 << 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (reply->message_class != rx->message_class)</span><br><span style="color: hsl(120, 100%, 40%);">+          rc |= 1 << 1;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (rx->sm_rp_mr && (!reply->sm_rp_mr || *rx->sm_rp_mr != *reply->sm_rp_mr))</span><br><span style="color: hsl(120, 100%, 40%);">+              rc |= 1 << 2;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (reply->session_id != rx->session_id)</span><br><span style="color: hsl(120, 100%, 40%);">+                rc |= 1 << 3;</span><br><span style="color: hsl(120, 100%, 40%);">+   return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Print the most important value of a GSUP message to a string buffer in human readable form.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] buf  The buffer to write to.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] buflen  sizeof(buf).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] msg  GSUP message to print.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+size_t osmo_gsup_message_to_str_buf(char *buf, size_t buflen, const struct osmo_gsup_message *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_strbuf sb = { .buf = buf, .len = buflen };</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           OSMO_STRBUF_PRINTF(sb, "NULL");</span><br><span style="color: hsl(120, 100%, 40%);">+             return sb.chars_needed;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (msg->message_class)</span><br><span style="color: hsl(120, 100%, 40%);">+            OSMO_STRBUF_PRINTF(sb, "%s ", osmo_gsup_message_class_name(msg->message_class));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_STRBUF_PRINTF(sb, "%s:", osmo_gsup_message_type_name(msg->message_type));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_STRBUF_PRINTF(sb, " imsi=");</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_STRBUF_APPEND(sb, osmo_quote_cstr_buf, msg->imsi, strnlen(msg->imsi, sizeof(msg->imsi)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (msg->cause)</span><br><span style="color: hsl(120, 100%, 40%);">+            OSMO_STRBUF_PRINTF(sb, " cause=%s", get_value_string(gsm48_gmm_cause_names, msg->cause));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      switch (msg->cn_domain) {</span><br><span style="color: hsl(120, 100%, 40%);">+  case OSMO_GSUP_CN_DOMAIN_CS:</span><br><span style="color: hsl(120, 100%, 40%);">+          OSMO_STRBUF_PRINTF(sb, " cn_domain=CS");</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case OSMO_GSUP_CN_DOMAIN_PS:</span><br><span style="color: hsl(120, 100%, 40%);">+          OSMO_STRBUF_PRINTF(sb, " cn_domain=PS");</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              if (msg->cn_domain)</span><br><span style="color: hsl(120, 100%, 40%);">+                        OSMO_STRBUF_PRINTF(sb, " cn_domain=?(%d)", msg->cn_domain);</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (msg->source_name_len) {</span><br><span style="color: hsl(120, 100%, 40%);">+                OSMO_STRBUF_PRINTF(sb, " source_name=");</span><br><span style="color: hsl(120, 100%, 40%);">+            OSMO_STRBUF_APPEND(sb, osmo_quote_cstr_buf, (char*)msg->source_name, msg->source_name_len);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (msg->destination_name_len) {</span><br><span style="color: hsl(120, 100%, 40%);">+           OSMO_STRBUF_PRINTF(sb, " destination_name=");</span><br><span style="color: hsl(120, 100%, 40%);">+               OSMO_STRBUF_APPEND(sb, osmo_quote_cstr_buf, (char*)msg->destination_name, msg->destination_name_len);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (msg->session_id)</span><br><span style="color: hsl(120, 100%, 40%);">+               OSMO_STRBUF_PRINTF(sb, " session_id=%" PRIu32, msg->session_id);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (msg->session_state)</span><br><span style="color: hsl(120, 100%, 40%);">+            OSMO_STRBUF_PRINTF(sb, " session_state=%s", osmo_gsup_session_state_name(msg->session_state));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (msg->sm_rp_mr)</span><br><span style="color: hsl(120, 100%, 40%);">+         OSMO_STRBUF_PRINTF(sb, " sm_rp_mr=%" PRIu8, *msg->sm_rp_mr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return sb.chars_needed;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Same as  osmo_gsup_message_to_str_buf() but returns a talloc allocated string. */</span><br><span style="color: hsl(120, 100%, 40%);">+char *osmo_gsup_message_to_str_c(void *ctx, const struct osmo_gsup_message *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_gsup_message_to_str_buf, msg)</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/gsupclient/ipa_name.c b/src/gsupclient/ipa_name.c</span><br><span>new file mode 100644</span><br><span>index 0000000..2db069f</span><br><span>--- /dev/null</span><br><span>+++ b/src/gsupclient/ipa_name.c</span><br><span>@@ -0,0 +1,97 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsupclient/ipa_name.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_ipa_name_set(struct osmo_ipa_name *ipa_name, const uint8_t *val, size_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!val || !len) {</span><br><span style="color: hsl(120, 100%, 40%);">+           *ipa_name = (struct osmo_ipa_name){};</span><br><span style="color: hsl(120, 100%, 40%);">+         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (len > sizeof(ipa_name->val))</span><br><span style="color: hsl(120, 100%, 40%);">+                return -ENOSPC;</span><br><span style="color: hsl(120, 100%, 40%);">+       ipa_name->len = len;</span><br><span style="color: hsl(120, 100%, 40%);">+       memcpy(ipa_name->val, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_ipa_name_set_str(struct osmo_ipa_name *ipa_name, const char *str_fmt, ...)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        va_list ap;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!str_fmt)</span><br><span style="color: hsl(120, 100%, 40%);">+         return osmo_ipa_name_set(ipa_name, NULL, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        va_start(ap, str_fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+        vsnprintf((char*)(ipa_name->val), sizeof(ipa_name->val), str_fmt, ap);</span><br><span style="color: hsl(120, 100%, 40%);">+  va_end(ap);</span><br><span style="color: hsl(120, 100%, 40%);">+   ipa_name->len = strlen((char*)(ipa_name->val))+1;</span><br><span style="color: hsl(120, 100%, 40%);">+       return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_ipa_name_cmp(const struct osmo_ipa_name *a, const struct osmo_ipa_name *b)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        int cmp;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (a == b)</span><br><span style="color: hsl(120, 100%, 40%);">+           return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!a)</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!b)</span><br><span style="color: hsl(120, 100%, 40%);">+               return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!a->len && !b->len)</span><br><span style="color: hsl(120, 100%, 40%);">+         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!a->len && b->len)</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!b->len && a->len)</span><br><span style="color: hsl(120, 100%, 40%);">+          return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (a->len == b->len)</span><br><span style="color: hsl(120, 100%, 40%);">+           return memcmp(a->val, b->val, a->len);</span><br><span style="color: hsl(120, 100%, 40%);">+       else if (a->len < b->len) {</span><br><span style="color: hsl(120, 100%, 40%);">+          cmp = memcmp(a->val, b->val, a->len);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!cmp)</span><br><span style="color: hsl(120, 100%, 40%);">+                     cmp = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+             return cmp;</span><br><span style="color: hsl(120, 100%, 40%);">+   } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* a->len > b->len */</span><br><span style="color: hsl(120, 100%, 40%);">+                cmp = memcmp(a->val, b->val, b->len);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!cmp)</span><br><span style="color: hsl(120, 100%, 40%);">+                     cmp = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+              return cmp;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Call osmo_ipa_name_to_str_c with OTC_SELECT. */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *osmo_ipa_name_to_str(const struct osmo_ipa_name *ipa_name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return osmo_ipa_name_to_str_c(OTC_SELECT, ipa_name);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Return an unquoted string, not including the terminating zero. Used for writing VTY config. */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *osmo_ipa_name_to_str_c(void *ctx, const struct osmo_ipa_name *ipa_name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  size_t len = ipa_name->len;</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!len)</span><br><span style="color: hsl(120, 100%, 40%);">+             return talloc_strdup(ctx, "");</span><br><span style="color: hsl(120, 100%, 40%);">+      if (ipa_name->val[len-1] == '\0')</span><br><span style="color: hsl(120, 100%, 40%);">+          len--;</span><br><span style="color: hsl(120, 100%, 40%);">+        return osmo_escape_str_c(ctx, (char*)ipa_name->val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/hlr.c b/src/hlr.c</span><br><span>index ac6afc3..47b3211 100644</span><br><span>--- a/src/hlr.c</span><br><span>+++ b/src/hlr.c</span><br><span>@@ -35,8 +35,9 @@</span><br><span> #include <osmocom/gsm/apn.h></span><br><span> #include <osmocom/gsm/gsm48_ie.h></span><br><span> #include <osmocom/gsm/gsm_utils.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/gsm/protocol/gsm_23_003.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm23003.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsupclient/ipa_name.h></span><br><span> #include <osmocom/hlr/db.h></span><br><span> #include <osmocom/hlr/hlr.h></span><br><span> #include <osmocom/hlr/ctrl.h></span><br><span>@@ -44,14 +45,20 @@</span><br><span> #include <osmocom/hlr/gsup_server.h></span><br><span> #include <osmocom/hlr/gsup_router.h></span><br><span> #include <osmocom/hlr/rand.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/hlr/luop.h></span><br><span> #include <osmocom/hlr/hlr_vty.h></span><br><span> #include <osmocom/hlr/hlr_ussd.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/hlr/lu_fsm.h></span><br><span> </span><br><span> struct hlr *g_hlr;</span><br><span> static void *hlr_ctx = NULL;</span><br><span> static int quit = 0;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef g_hlr_tdefs[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+  /* 4222 is also the OSMO_GSUP_PORT */</span><br><span style="color: hsl(120, 100%, 40%);">+ { .T = -4222, .default_val = 30, .desc = "GSUP Update Location timeout" },</span><br><span style="color: hsl(120, 100%, 40%);">+  {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Trigger 'Insert Subscriber Data' messages to all connected GSUP clients.</span><br><span>  *</span><br><span>  * \param[in] subscr  A subscriber we have new data to send for.</span><br><span>@@ -69,6 +76,8 @@</span><br><span>                return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* FIXME: send only to current vlr_number and sgsn_number */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>       llist_for_each_entry(co, &g_hlr->gs->clients, list) {</span><br><span>              struct osmo_gsup_message gsup = { };</span><br><span>                 uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];</span><br><span>@@ -222,145 +231,102 @@</span><br><span>    return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! Update nam_cs/nam_ps in the db and trigger notifications to GSUP clients.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in,out] hlr  Global hlr context.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] subscr   Subscriber from a fresh db_subscr_get_by_*() call.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] nam_val  True to enable CS/PS, false to disable.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] is_ps    True to enable/disable PS, false for CS.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, ENOEXEC if there is no need to change, a negative</span><br><span style="color: hsl(120, 100%, 40%);">+ *          value on error.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       bool is_val = is_ps? subscr->nam_ps : subscr->nam_cs;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_ipa_name vlr_name;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_gsup_message gsup_del_data = {</span><br><span style="color: hsl(120, 100%, 40%);">+            .message_type = OSMO_GSUP_MSGT_DELETE_DATA_REQUEST,</span><br><span style="color: hsl(120, 100%, 40%);">+   };</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_STRLCPY_ARRAY(gsup_del_data.imsi, subscr->imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (is_val == nam_val) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DAUC, LOGL_DEBUG, "IMSI-%s: Already has the requested value when asked to %s %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                 subscr->imsi, nam_val ? "enable" : "disable", is_ps ? "PS" : "CS");</span><br><span style="color: hsl(120, 100%, 40%);">+           return ENOEXEC;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = db_subscr_nam(hlr->dbc, subscr->imsi, nam_val, is_ps);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (rc)</span><br><span style="color: hsl(120, 100%, 40%);">+               return rc > 0? -rc : rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If we're disabling, send a notice out to the GSUP client that is</span><br><span style="color: hsl(120, 100%, 40%);">+        * responsible. Otherwise no need. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (nam_val)</span><br><span style="color: hsl(120, 100%, 40%);">+          return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (subscr->vlr_number && osmo_ipa_name_set_str(&vlr_name, subscr->vlr_number))</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &vlr_name, &gsup_del_data);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (subscr->sgsn_number && osmo_ipa_name_set_str(&vlr_name, subscr->sgsn_number))</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_gsup_enc_send_to_ipa_name(g_hlr->gs, &vlr_name, &gsup_del_data);</span><br><span style="color: hsl(120, 100%, 40%);">+      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /***********************************************************************</span><br><span>  * Send Auth Info handling</span><br><span>  ***********************************************************************/</span><br><span> </span><br><span> /* process an incoming SAI request */</span><br><span style="color: hsl(0, 100%, 40%);">-static int rx_send_auth_info(struct osmo_gsup_conn *conn,</span><br><span style="color: hsl(0, 100%, 40%);">-                          const struct osmo_gsup_message *gsup,</span><br><span style="color: hsl(0, 100%, 40%);">-                           struct db_context *dbc)</span><br><span style="color: hsl(120, 100%, 40%);">+static int rx_send_auth_info(unsigned int auc_3g_ind, struct osmo_gsup_req *req)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-     struct osmo_gsup_message gsup_out;</span><br><span style="color: hsl(0, 100%, 40%);">-      struct msgb *msg_out;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_gsup_message gsup_out = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span>   bool separation_bit = false;</span><br><span>         int num_auth_vectors = OSMO_GSUP_MAX_NUM_AUTH_INFO;</span><br><span>  int rc;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     subscr_create_on_demand(gsup->imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+       subscr_create_on_demand(req->gsup.imsi);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- /* initialize return message structure */</span><br><span style="color: hsl(0, 100%, 40%);">-       memset(&gsup_out, 0, sizeof(gsup_out));</span><br><span style="color: hsl(0, 100%, 40%);">-     memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  if (gsup->current_rat_type == OSMO_RAT_EUTRAN_SGS)</span><br><span style="color: hsl(120, 100%, 40%);">+ if (req->gsup.current_rat_type == OSMO_RAT_EUTRAN_SGS)</span><br><span>            separation_bit = true;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      if (gsup->num_auth_vectors > 0 &&</span><br><span style="color: hsl(0, 100%, 40%);">-                 gsup->num_auth_vectors <= OSMO_GSUP_MAX_NUM_AUTH_INFO)</span><br><span style="color: hsl(0, 100%, 40%);">-            num_auth_vectors = gsup->num_auth_vectors;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (req->gsup.num_auth_vectors > 0 &&</span><br><span style="color: hsl(120, 100%, 40%);">+                   req->gsup.num_auth_vectors <= OSMO_GSUP_MAX_NUM_AUTH_INFO)</span><br><span style="color: hsl(120, 100%, 40%);">+              num_auth_vectors = req->gsup.num_auth_vectors;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   rc = db_get_auc(dbc, gsup->imsi, conn->auc_3g_ind,</span><br><span style="color: hsl(120, 100%, 40%);">+      rc = db_get_auc(g_hlr->dbc, req->gsup.imsi, auc_3g_ind,</span><br><span>                        gsup_out.auth_vectors,</span><br><span>                       num_auth_vectors,</span><br><span style="color: hsl(0, 100%, 40%);">-                       gsup->rand, gsup->auts, separation_bit);</span><br><span style="color: hsl(120, 100%, 40%);">+                        req->gsup.rand, req->gsup.auts, separation_bit);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     if (rc <= 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-               gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;</span><br><span>                 switch (rc) {</span><br><span>                case 0:</span><br><span>                      /* 0 means "0 tuples generated", which shouldn't happen.</span><br><span>                        * Treat the same as "no auth data". */</span><br><span>            case -ENOKEY:</span><br><span style="color: hsl(0, 100%, 40%);">-                   LOGP(DAUC, LOGL_NOTICE, "%s: IMSI known, but has no auth data;"</span><br><span style="color: hsl(0, 100%, 40%);">-                            " Returning slightly inaccurate cause 'IMSI Unknown' via GSUP\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                             gsup->imsi);</span><br><span style="color: hsl(0, 100%, 40%);">-                    gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;</span><br><span style="color: hsl(0, 100%, 40%);">-                        break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                  "IMSI known, but has no auth data;"</span><br><span style="color: hsl(120, 100%, 40%);">+                                                 " Returning slightly inaccurate cause 'IMSI Unknown' via GSUP");</span><br><span style="color: hsl(120, 100%, 40%);">+                  return rc;</span><br><span>           case -ENOENT:</span><br><span style="color: hsl(0, 100%, 40%);">-                   LOGP(DAUC, LOGL_NOTICE, "%s: IMSI not known\n", gsup->imsi);</span><br><span style="color: hsl(0, 100%, 40%);">-                       gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;</span><br><span style="color: hsl(0, 100%, 40%);">-                        break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown");</span><br><span style="color: hsl(120, 100%, 40%);">+                     return rc;</span><br><span>           default:</span><br><span style="color: hsl(0, 100%, 40%);">-                        LOGP(DAUC, LOGL_ERROR, "%s: failure to look up IMSI in db\n", gsup->imsi);</span><br><span style="color: hsl(0, 100%, 40%);">-                 gsup_out.cause = GMM_CAUSE_NET_FAIL;</span><br><span style="color: hsl(0, 100%, 40%);">-                    break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "failure to look up IMSI in db");</span><br><span style="color: hsl(120, 100%, 40%);">+                        return rc;</span><br><span>           }</span><br><span style="color: hsl(0, 100%, 40%);">-       } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT;</span><br><span style="color: hsl(0, 100%, 40%);">-           gsup_out.num_auth_vectors = rc;</span><br><span>      }</span><br><span style="color: hsl(120, 100%, 40%);">+     gsup_out.num_auth_vectors = rc;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     msg_out = osmo_gsup_msgb_alloc("GSUP AUC response");</span><br><span style="color: hsl(0, 100%, 40%);">-  osmo_gsup_encode(msg_out, &gsup_out);</span><br><span style="color: hsl(0, 100%, 40%);">-       return osmo_gsup_conn_send(conn, msg_out);</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_gsup_req_respond(req, &gsup_out, false, true);</span><br><span style="color: hsl(120, 100%, 40%);">+       return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/***********************************************************************</span><br><span style="color: hsl(0, 100%, 40%);">- * LU Operation State / Structure</span><br><span style="color: hsl(0, 100%, 40%);">- ***********************************************************************/</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static LLIST_HEAD(g_lu_ops);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Receive Cancel Location Result from old VLR/SGSN */</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_rx_cancel_old_ack(struct lu_operation *luop,</span><br><span style="color: hsl(0, 100%, 40%);">-                       const struct osmo_gsup_message *gsup)</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Receive Update Location Request, creates new lu_operation */</span><br><span style="color: hsl(120, 100%, 40%);">+static int rx_upd_loc_req(struct osmo_gsup_conn *conn, struct osmo_gsup_req *req)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(luop->state == LU_S_CANCEL_SENT);</span><br><span style="color: hsl(0, 100%, 40%);">-        /* FIXME: Check for spoofing */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- osmo_timer_del(&luop->timer);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    /* FIXME */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     lu_op_tx_insert_subscr_data(luop);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Receive Insert Subscriber Data Result from new VLR/SGSN */</span><br><span style="color: hsl(0, 100%, 40%);">-static void lu_op_rx_insert_subscr_data_ack(struct lu_operation *luop,</span><br><span style="color: hsl(0, 100%, 40%);">-                             const struct osmo_gsup_message *gsup)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      OSMO_ASSERT(luop->state == LU_S_ISD_SENT);</span><br><span style="color: hsl(0, 100%, 40%);">-   /* FIXME: Check for spoofing */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- osmo_timer_del(&luop->timer);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    /* Subscriber_Present_HLR */</span><br><span style="color: hsl(0, 100%, 40%);">-    /* CS only: Check_SS_required? -> MAP-FW-CHECK_SS_IND.req */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- /* Send final ACK towards inquiring VLR/SGSN */</span><br><span style="color: hsl(0, 100%, 40%);">- lu_op_tx_ack(luop);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Receive GSUP message for given \ref lu_operation */</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_rx_gsup(struct lu_operation *luop,</span><br><span style="color: hsl(0, 100%, 40%);">-                  const struct osmo_gsup_message *gsup)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        switch (gsup->message_type) {</span><br><span style="color: hsl(0, 100%, 40%);">-        case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:</span><br><span style="color: hsl(0, 100%, 40%);">-          /* FIXME */</span><br><span style="color: hsl(0, 100%, 40%);">-             break;</span><br><span style="color: hsl(0, 100%, 40%);">-  case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:</span><br><span style="color: hsl(0, 100%, 40%);">-         lu_op_rx_insert_subscr_data_ack(luop, gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-            break;</span><br><span style="color: hsl(0, 100%, 40%);">-  case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:</span><br><span style="color: hsl(0, 100%, 40%);">-              /* FIXME */</span><br><span style="color: hsl(0, 100%, 40%);">-             break;</span><br><span style="color: hsl(0, 100%, 40%);">-  case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:</span><br><span style="color: hsl(0, 100%, 40%);">-             lu_op_rx_cancel_old_ack(luop, gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-            break;</span><br><span style="color: hsl(0, 100%, 40%);">-  default:</span><br><span style="color: hsl(0, 100%, 40%);">-                LOGP(DMAIN, LOGL_ERROR, "Unhandled GSUP msg_type 0x%02x\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                   gsup->message_type);</span><br><span style="color: hsl(0, 100%, 40%);">-         break;</span><br><span style="color: hsl(0, 100%, 40%);">-  }</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Receive Update Location Request, creates new \ref lu_operation */</span><br><span style="color: hsl(0, 100%, 40%);">-static int rx_upd_loc_req(struct osmo_gsup_conn *conn,</span><br><span style="color: hsl(0, 100%, 40%);">-                     const struct osmo_gsup_message *gsup)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        struct hlr_subscriber *subscr;</span><br><span style="color: hsl(0, 100%, 40%);">-  struct lu_operation *luop = lu_op_alloc_conn(conn);</span><br><span style="color: hsl(0, 100%, 40%);">-     if (!luop) {</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");</span><br><span style="color: hsl(0, 100%, 40%);">-          return -EINVAL;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       subscr = &luop->subscr;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  lu_op_statechg(luop, LU_S_LU_RECEIVED);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- switch (gsup->cn_domain) {</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (req->gsup.cn_domain) {</span><br><span>    case OSMO_GSUP_CN_DOMAIN_CS:</span><br><span>                 conn->supports_cs = true;</span><br><span>                 break;</span><br><span>@@ -371,143 +337,64 @@</span><br><span>               * a request, the PS Domain is assumed." */</span><br><span>     case OSMO_GSUP_CN_DOMAIN_PS:</span><br><span>                 conn->supports_ps = true;</span><br><span style="color: hsl(0, 100%, 40%);">-            luop->is_ps = true;</span><br><span>               break;</span><br><span>       }</span><br><span style="color: hsl(0, 100%, 40%);">-       llist_add(&luop->list, &g_lu_ops);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       subscr_create_on_demand(gsup->imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+       subscr_create_on_demand(req->gsup.imsi);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      /* check if subscriber is known at all */</span><br><span style="color: hsl(0, 100%, 40%);">-       if (!lu_op_fill_subscr(luop, g_hlr->dbc, gsup->imsi)) {</span><br><span style="color: hsl(0, 100%, 40%);">-           /* Send Error back: Subscriber Unknown in HLR */</span><br><span style="color: hsl(0, 100%, 40%);">-                osmo_strlcpy(luop->subscr.imsi, gsup->imsi, sizeof(luop->subscr.imsi));</span><br><span style="color: hsl(0, 100%, 40%);">-                lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);</span><br><span style="color: hsl(0, 100%, 40%);">-           return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* Check if subscriber is generally permitted on CS or PS</span><br><span style="color: hsl(0, 100%, 40%);">-        * service (as requested) */</span><br><span style="color: hsl(0, 100%, 40%);">-    if (!luop->is_ps && !luop->subscr.nam_cs) {</span><br><span style="color: hsl(0, 100%, 40%);">-               lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED);</span><br><span style="color: hsl(0, 100%, 40%);">-                return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-       } else if (luop->is_ps && !luop->subscr.nam_ps) {</span><br><span style="color: hsl(0, 100%, 40%);">-         lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED);</span><br><span style="color: hsl(0, 100%, 40%);">-                return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* TODO: Set subscriber tracing = deactive in VLR/SGSN */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#if 0</span><br><span style="color: hsl(0, 100%, 40%);">-  /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old (FIXME: OS#4491) */</span><br><span style="color: hsl(0, 100%, 40%);">- if (luop->is_ps == false &&</span><br><span style="color: hsl(0, 100%, 40%);">-      strcmp(subscr->vlr_number, vlr_number)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                lu_op_tx_cancel_old(luop);</span><br><span style="color: hsl(0, 100%, 40%);">-      } else if (luop->is_ps == true &&</span><br><span style="color: hsl(0, 100%, 40%);">-               strcmp(subscr->sgsn_number, sgsn_number)) {</span><br><span style="color: hsl(0, 100%, 40%);">-               lu_op_tx_cancel_old(luop);</span><br><span style="color: hsl(0, 100%, 40%);">-      } else</span><br><span style="color: hsl(0, 100%, 40%);">-#endif</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    /* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */</span><br><span style="color: hsl(0, 100%, 40%);">-       LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-             subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",</span><br><span style="color: hsl(0, 100%, 40%);">-             osmo_quote_str((const char*)luop->peer, -1));</span><br><span style="color: hsl(0, 100%, 40%);">-   if (db_subscr_lu(g_hlr->dbc, subscr->id, (const char *)luop->peer, luop->is_ps))</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGP(DAUC, LOGL_ERROR, "IMSI='%s': Cannot update %s in the database\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                    subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number");</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* TODO: Subscriber allowed to roam in PLMN? */</span><br><span style="color: hsl(0, 100%, 40%);">- /* TODO: Update RoutingInfo */</span><br><span style="color: hsl(0, 100%, 40%);">-  /* TODO: Reset Flag MS Purged (cs/ps) */</span><br><span style="color: hsl(0, 100%, 40%);">-        /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */</span><br><span style="color: hsl(0, 100%, 40%);">- lu_op_tx_insert_subscr_data(luop);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+    lu_rx_gsup(req);</span><br><span>     return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int rx_purge_ms_req(struct osmo_gsup_conn *conn,</span><br><span style="color: hsl(0, 100%, 40%);">-                          const struct osmo_gsup_message *gsup)</span><br><span style="color: hsl(120, 100%, 40%);">+static int rx_purge_ms_req(struct osmo_gsup_req *req)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-    struct osmo_gsup_message gsup_reply = {0};</span><br><span style="color: hsl(0, 100%, 40%);">-      struct msgb *msg_out;</span><br><span style="color: hsl(0, 100%, 40%);">-   bool is_ps = false;</span><br><span style="color: hsl(120, 100%, 40%);">+   bool is_ps = (req->gsup.cn_domain != OSMO_GSUP_CN_DOMAIN_CS);</span><br><span>     int rc;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     LOGP(DAUC, LOGL_INFO, "%s: Purge MS (%s)\n", gsup->imsi,</span><br><span style="color: hsl(0, 100%, 40%);">-           is_ps ? "PS" : "CS");</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)</span><br><span style="color: hsl(0, 100%, 40%);">-               is_ps = true;</span><br><span style="color: hsl(120, 100%, 40%);">+ LOG_GSUP_REQ_CAT(req, DAUC, LOGL_INFO, "Purge MS (%s)\n", is_ps ? "PS" : "CS");</span><br><span> </span><br><span>    /* FIXME: check if the VLR that sends the purge is the same that</span><br><span>      * we have on record. Only update if yes */</span><br><span> </span><br><span>      /* Perform the actual update of the DB */</span><br><span style="color: hsl(0, 100%, 40%);">-       rc = db_subscr_purge(g_hlr->dbc, gsup->imsi, true, is_ps);</span><br><span style="color: hsl(120, 100%, 40%);">+      rc = db_subscr_purge(g_hlr->dbc, req->gsup.imsi, true, is_ps);</span><br><span> </span><br><span>     if (rc == 0)</span><br><span style="color: hsl(0, 100%, 40%);">-            gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_RESULT;</span><br><span style="color: hsl(0, 100%, 40%);">-       else if (rc == -ENOENT) {</span><br><span style="color: hsl(0, 100%, 40%);">-               gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;</span><br><span style="color: hsl(0, 100%, 40%);">-                gsup_reply.cause = GMM_CAUSE_IMSI_UNKNOWN;</span><br><span style="color: hsl(0, 100%, 40%);">-      } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;</span><br><span style="color: hsl(0, 100%, 40%);">-                gsup_reply.cause = GMM_CAUSE_NET_FAIL;</span><br><span style="color: hsl(0, 100%, 40%);">-  }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       msg_out = osmo_gsup_msgb_alloc("GSUP AUC response");</span><br><span style="color: hsl(0, 100%, 40%);">-  osmo_gsup_encode(msg_out, &gsup_reply);</span><br><span style="color: hsl(0, 100%, 40%);">-     return osmo_gsup_conn_send(conn, msg_out);</span><br><span style="color: hsl(120, 100%, 40%);">+            osmo_gsup_req_respond_msgt(req, OSMO_GSUP_MSGT_PURGE_MS_RESULT, true);</span><br><span style="color: hsl(120, 100%, 40%);">+        else if (rc == -ENOENT)</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown");</span><br><span style="color: hsl(120, 100%, 40%);">+     else</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_gsup_req_respond_err(req, GMM_CAUSE_NET_FAIL, "db error");</span><br><span style="color: hsl(120, 100%, 40%);">+     return rc;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int gsup_send_err_reply(struct osmo_gsup_conn *conn, const char *imsi,</span><br><span style="color: hsl(0, 100%, 40%);">-                                enum osmo_gsup_message_type type_in, uint8_t err_cause)</span><br><span style="color: hsl(120, 100%, 40%);">+static int rx_check_imei_req(struct osmo_gsup_req *req)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-   int type_err = OSMO_GSUP_TO_MSGT_ERROR(type_in);</span><br><span style="color: hsl(0, 100%, 40%);">-        struct osmo_gsup_message gsup_reply = {0};</span><br><span style="color: hsl(0, 100%, 40%);">-      struct msgb *msg_out;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   OSMO_STRLCPY_ARRAY(gsup_reply.imsi, imsi);</span><br><span style="color: hsl(0, 100%, 40%);">-      gsup_reply.message_type = type_err;</span><br><span style="color: hsl(0, 100%, 40%);">-     gsup_reply.cause = err_cause;</span><br><span style="color: hsl(0, 100%, 40%);">-   msg_out = osmo_gsup_msgb_alloc("GSUP ERR response");</span><br><span style="color: hsl(0, 100%, 40%);">-  osmo_gsup_encode(msg_out, &gsup_reply);</span><br><span style="color: hsl(0, 100%, 40%);">-     LOGP(DMAIN, LOGL_NOTICE, "Tx %s\n", osmo_gsup_message_type_name(type_err));</span><br><span style="color: hsl(0, 100%, 40%);">-   return osmo_gsup_conn_send(conn, msg_out);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static int rx_check_imei_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-     struct osmo_gsup_message gsup_reply = {0};</span><br><span style="color: hsl(0, 100%, 40%);">-      struct msgb *msg_out;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_gsup_message gsup_reply;</span><br><span>         char imei[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1] = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+   const struct osmo_gsup_message *gsup = &req->gsup;</span><br><span>    int rc;</span><br><span> </span><br><span>  /* Require IMEI */</span><br><span>   if (!gsup->imei_enc) {</span><br><span style="color: hsl(0, 100%, 40%);">-               LOGP(DMAIN, LOGL_ERROR, "%s: missing IMEI\n", gsup->imsi);</span><br><span style="color: hsl(0, 100%, 40%);">-         gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "missing IMEI");</span><br><span>           return -1;</span><br><span>   }</span><br><span> </span><br><span>        /* Decode IMEI (fails if IMEI is too long) */</span><br><span>        rc = gsm48_decode_bcd_number2(imei, sizeof(imei), gsup->imei_enc, gsup->imei_enc_len, 0);</span><br><span>      if (rc < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                LOGP(DMAIN, LOGL_ERROR, "%s: failed to decode IMEI (rc: %i)\n", gsup->imsi, rc);</span><br><span style="color: hsl(0, 100%, 40%);">-           gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,</span><br><span style="color: hsl(120, 100%, 40%);">+                                         "failed to decode IMEI %s (rc: %d)",</span><br><span style="color: hsl(120, 100%, 40%);">+                                        osmo_hexdump_c(OTC_SELECT, gsup->imei_enc, gsup->imei_enc_len),</span><br><span style="color: hsl(120, 100%, 40%);">+                                         rc);</span><br><span>               return -1;</span><br><span>   }</span><br><span> </span><br><span>        /* Check if IMEI is too short */</span><br><span style="color: hsl(0, 100%, 40%);">-        if (strlen(imei) != GSM23003_IMEI_NUM_DIGITS_NO_CHK) {</span><br><span style="color: hsl(0, 100%, 40%);">-          LOGP(DMAIN, LOGL_ERROR, "%s: wrong encoded IMEI length (IMEI: '%s', %lu, %i)\n", gsup->imsi, imei,</span><br><span style="color: hsl(0, 100%, 40%);">-              strlen(imei), GSM23003_IMEI_NUM_DIGITS_NO_CHK);</span><br><span style="color: hsl(0, 100%, 40%);">-            gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!osmo_imei_str_valid(imei, false)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,</span><br><span style="color: hsl(120, 100%, 40%);">+                                         "invalid IMEI: %s", osmo_quote_str_c(OTC_SELECT, imei, -1));</span><br><span>             return -1;</span><br><span>   }</span><br><span> </span><br><span>@@ -517,7 +404,7 @@</span><br><span>  if (g_hlr->store_imei) {</span><br><span>          LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing IMEI = %s\n", gsup->imsi, imei);</span><br><span>             if (db_subscr_update_imei_by_imsi(g_hlr->dbc, gsup->imsi, imei) < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                 gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "Failed to store IMEI in HLR db");</span><br><span>                         return -1;</span><br><span>           }</span><br><span>    } else {</span><br><span>@@ -525,18 +412,17 @@</span><br><span>             LOGP(DMAIN, LOGL_INFO, "IMSI='%s': has IMEI = %s (consider setting 'store-imei')\n", gsup->imsi, imei);</span><br><span>                 struct hlr_subscriber subscr;</span><br><span>                if (db_subscr_get_by_imsi(g_hlr->dbc, gsup->imsi, &subscr) < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                  gsup_send_err_reply(conn, gsup->imsi, gsup->message_type, GMM_CAUSE_INV_MAND_INFO);</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO, "IMSI unknown");</span><br><span>                   return -1;</span><br><span>           }</span><br><span>    }</span><br><span> </span><br><span>        /* Accept all IMEIs */</span><br><span style="color: hsl(0, 100%, 40%);">-  gsup_reply.imei_result = OSMO_GSUP_IMEI_RESULT_ACK;</span><br><span style="color: hsl(0, 100%, 40%);">-     gsup_reply.message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT;</span><br><span style="color: hsl(0, 100%, 40%);">-     msg_out = osmo_gsup_msgb_alloc("GSUP Check_IMEI response");</span><br><span style="color: hsl(0, 100%, 40%);">-   memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));</span><br><span style="color: hsl(0, 100%, 40%);">-        osmo_gsup_encode(msg_out, &gsup_reply);</span><br><span style="color: hsl(0, 100%, 40%);">-     return osmo_gsup_conn_send(conn, msg_out);</span><br><span style="color: hsl(120, 100%, 40%);">+    gsup_reply = (struct osmo_gsup_message){</span><br><span style="color: hsl(120, 100%, 40%);">+              .message_type = OSMO_GSUP_MSGT_CHECK_IMEI_RESULT,</span><br><span style="color: hsl(120, 100%, 40%);">+             .imei_result = OSMO_GSUP_IMEI_RESULT_ACK,</span><br><span style="color: hsl(120, 100%, 40%);">+     };</span><br><span style="color: hsl(120, 100%, 40%);">+    return osmo_gsup_req_respond(req, &gsup_reply, false, true);</span><br><span> }</span><br><span> </span><br><span> static char namebuf[255];</span><br><span>@@ -549,151 +435,112 @@</span><br><span>          osmo_quote_str_buf2(namebuf, sizeof(namebuf), (const char *)(gsup)->destination_name, (gsup)->destination_name_len), \</span><br><span>         ## args)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int read_cb_forward(struct osmo_gsup_conn *conn, struct msgb *msg, const struct osmo_gsup_message *gsup)</span><br><span style="color: hsl(120, 100%, 40%);">+static int read_cb_forward(struct osmo_gsup_req *req)</span><br><span> {</span><br><span>         int ret = -EINVAL;</span><br><span style="color: hsl(0, 100%, 40%);">-      struct osmo_gsup_message *gsup_err;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     /* FIXME: it would be better if the msgb never were deallocated immediately by osmo_gsup_addr_send(), which a</span><br><span style="color: hsl(0, 100%, 40%);">-    * select-loop volatile talloc context could facilitate. Then we would still be able to access gsup-> members</span><br><span style="color: hsl(0, 100%, 40%);">-         * (pointing into the msgb) even after sending failed, and we wouldn't need to copy this data before sending: */</span><br><span style="color: hsl(0, 100%, 40%);">-    /* Prepare error message (before IEs get deallocated) */</span><br><span style="color: hsl(0, 100%, 40%);">-        gsup_err = talloc_zero(hlr_ctx, struct osmo_gsup_message);</span><br><span style="color: hsl(0, 100%, 40%);">-      OSMO_STRLCPY_ARRAY(gsup_err->imsi, gsup->imsi);</span><br><span style="color: hsl(0, 100%, 40%);">-   gsup_err->message_class = gsup->message_class;</span><br><span style="color: hsl(0, 100%, 40%);">-    gsup_err->destination_name = talloc_memdup(gsup_err, gsup->destination_name, gsup->destination_name_len);</span><br><span style="color: hsl(0, 100%, 40%);">-      gsup_err->destination_name_len = gsup->destination_name_len;</span><br><span style="color: hsl(0, 100%, 40%);">-      gsup_err->message_type = gsup->message_type;</span><br><span style="color: hsl(0, 100%, 40%);">-      gsup_err->session_state = gsup->session_state;</span><br><span style="color: hsl(0, 100%, 40%);">-    gsup_err->session_id = gsup->session_id;</span><br><span style="color: hsl(0, 100%, 40%);">-  gsup_err->source_name = talloc_memdup(gsup_err, gsup->source_name, gsup->source_name_len);</span><br><span style="color: hsl(0, 100%, 40%);">-     gsup_err->source_name_len = gsup->source_name_len;</span><br><span style="color: hsl(120, 100%, 40%);">+      const struct osmo_gsup_message *gsup = &req->gsup;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_gsup_message gsup_err;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct msgb *forward_msg;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_ipa_name destination_name;</span><br><span> </span><br><span>   /* Check for routing IEs */</span><br><span style="color: hsl(0, 100%, 40%);">-     if (!gsup->source_name || !gsup->source_name_len || !gsup->destination_name || !gsup->destination_name_len) {</span><br><span style="color: hsl(0, 100%, 40%);">-               LOGP_GSUP_FWD(gsup, LOGL_ERROR, "missing routing IEs\n");</span><br><span style="color: hsl(0, 100%, 40%);">-             goto end;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!req->gsup.source_name || !req->gsup.source_name_len</span><br><span style="color: hsl(120, 100%, 40%);">+            || !req->gsup.destination_name || !req->gsup.destination_name_len) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGP_GSUP_FWD(&req->gsup, LOGL_ERROR, "missing routing IEs\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              goto routing_error;</span><br><span>  }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* Verify source name (e.g. "MSC-00-00-00-00-00-00") */</span><br><span style="color: hsl(0, 100%, 40%);">-       if (gsup_route_find(conn->server, gsup->source_name, gsup->source_name_len) != conn) {</span><br><span style="color: hsl(0, 100%, 40%);">-         LOGP_GSUP_FWD(gsup, LOGL_ERROR, "mismatching source name\n");</span><br><span style="color: hsl(0, 100%, 40%);">-         goto end;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (osmo_ipa_name_set(&destination_name, req->gsup.destination_name, req->gsup.destination_name_len)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGP_GSUP_FWD(&req->gsup, LOGL_ERROR, "invalid destination name\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         goto routing_error;</span><br><span>  }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* Forward message without re-encoding (so we don't remove unknown IEs) */</span><br><span style="color: hsl(0, 100%, 40%);">-  LOGP_GSUP_FWD(gsup, LOGL_INFO, "checks passed, forwarding\n");</span><br><span style="color: hsl(120, 100%, 40%);">+      LOG_GSUP_REQ(req, LOGL_INFO, "Forwarding to %s\n", osmo_ipa_name_to_str(&destination_name));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  /* Remove incoming IPA header to be able to prepend an outgoing IPA header */</span><br><span style="color: hsl(0, 100%, 40%);">-   msgb_pull_to_l2(msg);</span><br><span style="color: hsl(0, 100%, 40%);">-   ret = osmo_gsup_addr_send(g_hlr->gs, gsup->destination_name, gsup->destination_name_len, msg);</span><br><span style="color: hsl(0, 100%, 40%);">- /* AT THIS POINT, THE msg MAY BE DEALLOCATED and the data like gsup->imsi, gsup->source_name etc may all be</span><br><span style="color: hsl(0, 100%, 40%);">-        * invalid and cause segfaults. */</span><br><span style="color: hsl(0, 100%, 40%);">-      msg = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-     gsup = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    if (ret == -ENODEV)</span><br><span style="color: hsl(0, 100%, 40%);">-             LOGP_GSUP_FWD(gsup_err, LOGL_ERROR, "destination not connected\n");</span><br><span style="color: hsl(0, 100%, 40%);">-   else if (ret)</span><br><span style="color: hsl(0, 100%, 40%);">-           LOGP_GSUP_FWD(gsup_err, LOGL_ERROR, "unknown error %i\n", ret);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-end:</span><br><span style="color: hsl(0, 100%, 40%);">-   /* Send error back to source */</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Forward message without re-encoding (so we don't remove unknown IEs).</span><br><span style="color: hsl(120, 100%, 40%);">+   * Copy GSUP part to forward, removing incoming IPA header to be able to prepend an outgoing IPA header */</span><br><span style="color: hsl(120, 100%, 40%);">+    forward_msg = osmo_gsup_msgb_alloc("GSUP forward");</span><br><span style="color: hsl(120, 100%, 40%);">+ forward_msg->l2h = msgb_put(forward_msg, msgb_l2len(req->msg));</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(forward_msg->l2h, msgb_l2(req->msg), msgb_l2len(req->msg));</span><br><span style="color: hsl(120, 100%, 40%);">+   ret = osmo_gsup_send_to_ipa_name(g_hlr->gs, &destination_name, forward_msg);</span><br><span>  if (ret) {</span><br><span style="color: hsl(0, 100%, 40%);">-              struct msgb *msg_err = osmo_gsup_msgb_alloc("GSUP forward ERR response");</span><br><span style="color: hsl(0, 100%, 40%);">-             gsup_err->message_type = OSMO_GSUP_MSGT_E_ROUTING_ERROR;</span><br><span style="color: hsl(0, 100%, 40%);">-             osmo_gsup_encode(msg_err, gsup_err);</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGP_GSUP_FWD(gsup_err, LOGL_NOTICE, "Tx %s\n", osmo_gsup_message_type_name(gsup_err->message_type));</span><br><span style="color: hsl(0, 100%, 40%);">-              osmo_gsup_conn_send(conn, msg_err);</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP_GSUP_FWD(gsup, LOGL_ERROR, "%s (rc=%d)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                           ret == -ENODEV ? "destination not connected" : "unknown error",</span><br><span style="color: hsl(120, 100%, 40%);">+                           ret);</span><br><span style="color: hsl(120, 100%, 40%);">+           goto routing_error;</span><br><span>  }</span><br><span style="color: hsl(0, 100%, 40%);">-       talloc_free(gsup_err);</span><br><span style="color: hsl(0, 100%, 40%);">-  if (msg)</span><br><span style="color: hsl(0, 100%, 40%);">-                msgb_free(msg);</span><br><span style="color: hsl(0, 100%, 40%);">- return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_gsup_req_free(req);</span><br><span style="color: hsl(120, 100%, 40%);">+      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+routing_error:</span><br><span style="color: hsl(120, 100%, 40%);">+   gsup_err = (struct osmo_gsup_message){</span><br><span style="color: hsl(120, 100%, 40%);">+                .message_type = OSMO_GSUP_MSGT_ROUTING_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+         .source_name = gsup->destination_name,</span><br><span style="color: hsl(120, 100%, 40%);">+             .source_name_len = gsup->destination_name_len,</span><br><span style="color: hsl(120, 100%, 40%);">+     };</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_gsup_req_respond(req, &gsup_err, true, true);</span><br><span style="color: hsl(120, 100%, 40%);">+        return -1;</span><br><span> }</span><br><span> </span><br><span> static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-   static struct osmo_gsup_message gsup;</span><br><span style="color: hsl(0, 100%, 40%);">-   int rc;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (!msgb_l2(msg) || !msgb_l2len(msg)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                LOGP(DMAIN, LOGL_ERROR, "missing or empty L2 data\n");</span><br><span style="color: hsl(0, 100%, 40%);">-                msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_gsup_req *req = osmo_gsup_conn_rx(conn, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!req)</span><br><span>            return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* If the GSUP recipient is other than this HLR, forward. */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (req->gsup.destination_name_len) {</span><br><span style="color: hsl(120, 100%, 40%);">+              struct osmo_ipa_name destination_name;</span><br><span style="color: hsl(120, 100%, 40%);">+                struct osmo_ipa_name my_name;</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_ipa_name_set_str(&my_name, g_hlr->gsup_unit_name.serno);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!osmo_ipa_name_set(&destination_name, req->gsup.destination_name, req->gsup.destination_name_len)</span><br><span style="color: hsl(120, 100%, 40%);">+                   && osmo_ipa_name_cmp(&destination_name, &my_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      return read_cb_forward(req);</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-        if (rc < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);</span><br><span style="color: hsl(0, 100%, 40%);">-            msgb_free(msg);</span><br><span style="color: hsl(0, 100%, 40%);">-         return rc;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* 3GPP TS 23.003 Section 2.2 clearly states that an IMSI with less than 5</span><br><span style="color: hsl(0, 100%, 40%);">-       * digits is impossible.  Even 5 digits is a highly theoretical case */</span><br><span style="color: hsl(0, 100%, 40%);">- if (strlen(gsup.imsi) < 5) { /* TODO: move this check to libosmogsm/gsup.c? */</span><br><span style="color: hsl(0, 100%, 40%);">-               LOGP(DMAIN, LOGL_ERROR, "IMSI too short: %s\n", osmo_quote_str(gsup.imsi, -1));</span><br><span style="color: hsl(0, 100%, 40%);">-               gsup_send_err_reply(conn, gsup.imsi, gsup.message_type, GMM_CAUSE_INV_MAND_INFO);</span><br><span style="color: hsl(0, 100%, 40%);">-               msgb_free(msg);</span><br><span style="color: hsl(0, 100%, 40%);">-         return -EINVAL;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (gsup.destination_name_len)</span><br><span style="color: hsl(0, 100%, 40%);">-          return read_cb_forward(conn, msg, &gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   switch (gsup.message_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (req->gsup.message_type) {</span><br><span>         /* requests sent to us */</span><br><span>    case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:</span><br><span style="color: hsl(0, 100%, 40%);">-             rx_send_auth_info(conn, &gsup, g_hlr->dbc);</span><br><span style="color: hsl(120, 100%, 40%);">+            rx_send_auth_info(conn->auc_3g_ind, req);</span><br><span>                 break;</span><br><span>       case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:</span><br><span style="color: hsl(0, 100%, 40%);">-            rx_upd_loc_req(conn, &gsup);</span><br><span style="color: hsl(120, 100%, 40%);">+              rx_upd_loc_req(conn, req);</span><br><span>           break;</span><br><span>       case OSMO_GSUP_MSGT_PURGE_MS_REQUEST:</span><br><span style="color: hsl(0, 100%, 40%);">-           rx_purge_ms_req(conn, &gsup);</span><br><span style="color: hsl(120, 100%, 40%);">+             rx_purge_ms_req(req);</span><br><span>                break;</span><br><span>       /* responses to requests sent by us */</span><br><span>       case OSMO_GSUP_MSGT_DELETE_DATA_ERROR:</span><br><span style="color: hsl(0, 100%, 40%);">-          LOGP(DMAIN, LOGL_ERROR, "Error while deleting subscriber data "</span><br><span style="color: hsl(0, 100%, 40%);">-                    "for IMSI %s\n", gsup.imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+           LOG_GSUP_REQ(req, LOGL_ERROR, "Peer responds with: Error while deleting subscriber data\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_gsup_req_free(req);</span><br><span>             break;</span><br><span>       case OSMO_GSUP_MSGT_DELETE_DATA_RESULT:</span><br><span style="color: hsl(0, 100%, 40%);">-         LOGP(DMAIN, LOGL_ERROR, "Deleting subscriber data for IMSI %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                  gsup.imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_GSUP_REQ(req, LOGL_DEBUG, "Peer responds with: Subscriber data deleted\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_gsup_req_free(req);</span><br><span>             break;</span><br><span>       case OSMO_GSUP_MSGT_PROC_SS_REQUEST:</span><br><span>         case OSMO_GSUP_MSGT_PROC_SS_RESULT:</span><br><span style="color: hsl(0, 100%, 40%);">-             rx_proc_ss_req(conn, &gsup);</span><br><span style="color: hsl(120, 100%, 40%);">+              rx_proc_ss_req(req);</span><br><span>                 break;</span><br><span>       case OSMO_GSUP_MSGT_PROC_SS_ERROR:</span><br><span style="color: hsl(0, 100%, 40%);">-              rx_proc_ss_error(conn, &gsup);</span><br><span style="color: hsl(120, 100%, 40%);">+            rx_proc_ss_error(req);</span><br><span>               break;</span><br><span>       case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:</span><br><span>       case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:</span><br><span>      case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:</span><br><span>   case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:</span><br><span style="color: hsl(0, 100%, 40%);">-             {</span><br><span style="color: hsl(0, 100%, 40%);">-                       struct lu_operation *luop = lu_op_by_imsi(gsup.imsi,</span><br><span style="color: hsl(0, 100%, 40%);">-                                                              &g_lu_ops);</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (!luop) {</span><br><span style="color: hsl(0, 100%, 40%);">-                            LOGP(DMAIN, LOGL_ERROR, "GSUP message %s for "</span><br><span style="color: hsl(0, 100%, 40%);">-                                     "unknown IMSI %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                                  osmo_gsup_message_type_name(gsup.message_type),</span><br><span style="color: hsl(0, 100%, 40%);">-                                    gsup.imsi);</span><br><span style="color: hsl(0, 100%, 40%);">-                             break;</span><br><span style="color: hsl(0, 100%, 40%);">-                  }</span><br><span style="color: hsl(0, 100%, 40%);">-                       lu_op_rx_gsup(luop, &gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-         }</span><br><span style="color: hsl(120, 100%, 40%);">+             lu_rx_gsup(req);</span><br><span>             break;</span><br><span>       case OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST:</span><br><span style="color: hsl(0, 100%, 40%);">-         rx_check_imei_req(conn, &gsup);</span><br><span style="color: hsl(120, 100%, 40%);">+           rx_check_imei_req(req);</span><br><span>              break;</span><br><span>       default:</span><br><span>             LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                osmo_gsup_message_type_name(gsup.message_type));</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_gsup_message_type_name(req->gsup.message_type));</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_gsup_req_free(req);</span><br><span>             break;</span><br><span>       }</span><br><span style="color: hsl(0, 100%, 40%);">-       msgb_free(msg);</span><br><span>      return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -908,7 +755,7 @@</span><br><span> </span><br><span> </span><br><span>     g_hlr->gs = osmo_gsup_server_create(hlr_ctx, g_hlr->gsup_bind_addr, OSMO_GSUP_PORT,</span><br><span style="color: hsl(0, 100%, 40%);">-                                           read_cb, &g_lu_ops, g_hlr);</span><br><span style="color: hsl(120, 100%, 40%);">+                                       read_cb, g_hlr);</span><br><span>         if (!g_hlr->gs) {</span><br><span>                 LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");</span><br><span>           exit(1);</span><br><span>@@ -931,7 +778,8 @@</span><br><span>       }</span><br><span> </span><br><span>        while (!quit)</span><br><span style="color: hsl(0, 100%, 40%);">-           osmo_select_main(0);</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_select_main_ctx(0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> </span><br><span>       osmo_gsup_server_destroy(g_hlr->gs);</span><br><span>      db_close(g_hlr->dbc);</span><br><span>diff --git a/src/hlr_ussd.c b/src/hlr_ussd.c</span><br><span>index 8cdc15c..aa7614e 100644</span><br><span>--- a/src/hlr_ussd.c</span><br><span>+++ b/src/hlr_ussd.c</span><br><span>@@ -170,12 +170,14 @@</span><br><span>        /* subscriber's vlr_number</span><br><span>        * MO USSD: originating MSC's vlr_number</span><br><span>          * MT USSD: looked up once per session and cached here */</span><br><span style="color: hsl(0, 100%, 40%);">-       uint8_t *vlr_number;</span><br><span style="color: hsl(0, 100%, 40%);">-    size_t vlr_number_len;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_ipa_name vlr_name;</span><br><span> </span><br><span>   /* we don't keep a pointer to the osmo_gsup_{route,conn} towards the MSC/VLR here,</span><br><span>        * as this might change during inter-VLR hand-over, and we simply look-up the serving MSC/VLR</span><br><span>         * every time we receive an USSD component from the EUSE */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_gsup_req *initial_req_from_ms;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_gsup_req *initial_req_from_euse;</span><br><span> };</span><br><span> </span><br><span> struct ss_session *ss_session_find(struct hlr *hlr, const char *imsi, uint32_t session_id)</span><br><span>@@ -191,6 +193,10 @@</span><br><span> void ss_session_free(struct ss_session *ss)</span><br><span> {</span><br><span>      osmo_timer_del(&ss->timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (ss->initial_req_from_ms)</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_gsup_req_free(ss->initial_req_from_ms);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (ss->initial_req_from_euse)</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_gsup_req_free(ss->initial_req_from_euse);</span><br><span>    llist_del(&ss->list);</span><br><span>         talloc_free(ss);</span><br><span> }</span><br><span>@@ -230,32 +236,46 @@</span><br><span>  ***********************************************************************/</span><br><span> </span><br><span> /* Resolve the target MSC by ss->imsi and send GSUP message. */</span><br><span style="color: hsl(0, 100%, 40%);">-static int ss_gsup_send(struct ss_session *ss, struct osmo_gsup_server *gs, struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+static int ss_gsup_send_to_ms(struct ss_session *ss, struct osmo_gsup_server *gs, struct osmo_gsup_message *gsup)</span><br><span> {</span><br><span>    struct hlr_subscriber subscr = {};</span><br><span style="color: hsl(120, 100%, 40%);">+    struct msgb *msg;</span><br><span>    int rc;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+   if (ss->initial_req_from_ms) {</span><br><span style="color: hsl(120, 100%, 40%);">+             /* Use non-final osmo_gsup_req_respond() to not deallocate the ss->initial_req_from_ms */</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_gsup_req_respond(ss->initial_req_from_ms, gsup, false, false);</span><br><span style="color: hsl(120, 100%, 40%);">+                return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   msg = osmo_gsup_msgb_alloc("GSUP USSD FW");</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = osmo_gsup_encode(msg, gsup);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (rc) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGPSS(ss, LOGL_ERROR, "Failed to encode GSUP message\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+               return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  /* Use vlr_number as looked up by the caller, or look up now. */</span><br><span style="color: hsl(0, 100%, 40%);">-        if (!ss->vlr_number) {</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!ss->vlr_name.len) {</span><br><span>          rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);</span><br><span>                 if (rc < 0) {</span><br><span>                     LOGPSS(ss, LOGL_ERROR, "Cannot find subscriber, cannot route GSUP message\n");</span><br><span>                     msgb_free(msg);</span><br><span>                      return -EINVAL;</span><br><span>              }</span><br><span style="color: hsl(0, 100%, 40%);">-               ss->vlr_number = (uint8_t *)talloc_strdup(ss, subscr.vlr_number);</span><br><span style="color: hsl(0, 100%, 40%);">-            ss->vlr_number_len = strlen(subscr.vlr_number) + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_ipa_name_set_str(&ss->vlr_name, subscr.vlr_number);</span><br><span>      }</span><br><span> </span><br><span>        /* Check for empty string (all vlr_number strings end in "\0", because otherwise gsup_route_find() fails) */</span><br><span style="color: hsl(0, 100%, 40%);">-  if (ss->vlr_number_len == 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+     if (ss->vlr_name.len <= 1) {</span><br><span>           LOGPSS(ss, LOGL_ERROR, "Cannot send GSUP message, no VLR number stored for subscriber\n");</span><br><span>                 msgb_free(msg);</span><br><span>              return -EINVAL;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   LOGPSS(ss, LOGL_DEBUG, "Tx SS/USSD to VLR %s\n", osmo_quote_str((char *)ss->vlr_number, ss->vlr_number_len));</span><br><span style="color: hsl(0, 100%, 40%);">-   return osmo_gsup_addr_send(gs, ss->vlr_number, ss->vlr_number_len, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGPSS(ss, LOGL_DEBUG, "Tx SS/USSD to VLR %s\n", osmo_ipa_name_to_str(&ss->vlr_name));</span><br><span style="color: hsl(120, 100%, 40%);">+       return osmo_gsup_send_to_ipa_name(gs, &ss->vlr_name, msg);</span><br><span> }</span><br><span> </span><br><span> static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_msg_type,</span><br><span>@@ -263,7 +283,7 @@</span><br><span> </span><br><span> {</span><br><span>        struct osmo_gsup_message resp = {0};</span><br><span style="color: hsl(0, 100%, 40%);">-    struct msgb *resp_msg;</span><br><span style="color: hsl(120, 100%, 40%);">+        int rc;</span><br><span> </span><br><span>  resp.message_type = gsup_msg_type;</span><br><span>   OSMO_STRLCPY_ARRAY(resp.imsi, ss->imsi);</span><br><span>@@ -277,12 +297,10 @@</span><br><span>          resp.ss_info_len = msgb_length(ss_msg);</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   resp_msg = msgb_alloc_headroom(4000, 64, __func__);</span><br><span style="color: hsl(0, 100%, 40%);">-     OSMO_ASSERT(resp_msg);</span><br><span style="color: hsl(0, 100%, 40%);">-  osmo_gsup_encode(resp_msg, &resp);</span><br><span style="color: hsl(0, 100%, 40%);">-  msgb_free(ss_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+    rc = ss_gsup_send_to_ms(ss, g_hlr->gs, &resp);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       return ss_gsup_send(ss, g_hlr->gs, resp_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+      msgb_free(ss_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+    return rc;</span><br><span> }</span><br><span> </span><br><span> #if 0</span><br><span>@@ -297,7 +315,7 @@</span><br><span> }</span><br><span> #endif</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int ss_tx_error(struct ss_session *ss, uint8_t invoke_id, uint8_t error_code)</span><br><span style="color: hsl(120, 100%, 40%);">+static int ss_tx_to_ms_error(struct ss_session *ss, uint8_t invoke_id, uint8_t error_code)</span><br><span> {</span><br><span>   struct msgb *msg = gsm0480_gen_return_error(invoke_id, error_code);</span><br><span>  LOGPSS(ss, LOGL_NOTICE, "Tx ReturnError(%u, 0x%02x)\n", invoke_id, error_code);</span><br><span>@@ -305,7 +323,7 @@</span><br><span>      return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int ss_tx_ussd_7bit(struct ss_session *ss, bool final, uint8_t invoke_id, const char *text)</span><br><span style="color: hsl(120, 100%, 40%);">+static int ss_tx_to_ms_ussd_7bit(struct ss_session *ss, bool final, uint8_t invoke_id, const char *text)</span><br><span> {</span><br><span>  struct msgb *msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text);</span><br><span>      LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text);</span><br><span>@@ -319,7 +337,7 @@</span><br><span> </span><br><span> #include <osmocom/hlr/db.h></span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int handle_ussd_own_msisdn(struct osmo_gsup_conn *conn, struct ss_session *ss,</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_ussd_own_msisdn(struct ss_session *ss,</span><br><span>                             const struct osmo_gsup_message *gsup, const struct ss_request *req)</span><br><span> {</span><br><span>   struct hlr_subscriber subscr;</span><br><span>@@ -333,25 +351,25 @@</span><br><span>                        snprintf(buf, sizeof(buf), "You have no MSISDN!");</span><br><span>                 else</span><br><span>                         snprintf(buf, sizeof(buf), "Your extension is %s", subscr.msisdn);</span><br><span style="color: hsl(0, 100%, 40%);">-            ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);</span><br><span style="color: hsl(120, 100%, 40%);">+            ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, buf);</span><br><span>             break;</span><br><span>       case -ENOENT:</span><br><span style="color: hsl(0, 100%, 40%);">-           ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);</span><br><span style="color: hsl(120, 100%, 40%);">+              ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);</span><br><span>               break;</span><br><span>       case -EIO:</span><br><span>   default:</span><br><span style="color: hsl(0, 100%, 40%);">-                ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);</span><br><span style="color: hsl(120, 100%, 40%);">+          ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);</span><br><span>           break;</span><br><span>       }</span><br><span>    return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int handle_ussd_own_imsi(struct osmo_gsup_conn *conn, struct ss_session *ss,</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_ussd_own_imsi(struct ss_session *ss,</span><br><span>                                const struct osmo_gsup_message *gsup, const struct ss_request *req)</span><br><span> {</span><br><span>     char buf[GSM0480_USSD_7BIT_STRING_LEN+1];</span><br><span>    snprintf(buf, sizeof(buf), "Your IMSI is %s", ss->imsi);</span><br><span style="color: hsl(0, 100%, 40%);">-   ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);</span><br><span style="color: hsl(120, 100%, 40%);">+    ss_tx_to_ms_ussd_7bit(ss, true, req->invoke_id, buf);</span><br><span>     return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -398,37 +416,26 @@</span><br><span> }</span><br><span> </span><br><span> /* is this GSUP connection an EUSE (true) or not (false)? */</span><br><span style="color: hsl(0, 100%, 40%);">-static bool conn_is_euse(struct osmo_gsup_conn *conn)</span><br><span style="color: hsl(120, 100%, 40%);">+static bool peer_name_is_euse(const struct osmo_ipa_name *peer_name)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-     int rc;</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *addr;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  rc = osmo_gsup_conn_ccm_get(conn, &addr, IPAC_IDTAG_SERNR);</span><br><span style="color: hsl(0, 100%, 40%);">- if (rc <= 5)</span><br><span style="color: hsl(120, 100%, 40%);">+       if (peer_name->len <= 5)</span><br><span>               return false;</span><br><span style="color: hsl(0, 100%, 40%);">-   if (!strncmp((char *)addr, "EUSE-", 5))</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!strncmp((char *)(peer_name->val), "EUSE-", 5))</span><br><span>             return true;</span><br><span>         else</span><br><span>                 return false;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct hlr_euse *euse_by_conn(struct osmo_gsup_conn *conn)</span><br><span style="color: hsl(120, 100%, 40%);">+static struct hlr_euse *euse_by_name(const struct osmo_ipa_name *peer_name)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       int rc;</span><br><span style="color: hsl(0, 100%, 40%);">- char *addr;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct hlr *hlr = conn->server->priv;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &addr, IPAC_IDTAG_SERNR);</span><br><span style="color: hsl(0, 100%, 40%);">-    if (rc <= 5)</span><br><span style="color: hsl(0, 100%, 40%);">-         return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    if (strncmp(addr, "EUSE-", 5))</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!peer_name_is_euse(peer_name))</span><br><span>           return NULL;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        return euse_find(hlr, addr+5);</span><br><span style="color: hsl(120, 100%, 40%);">+        return euse_find(g_hlr, (const char*)(peer_name->val)+5);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int handle_ss(struct ss_session *ss, const struct osmo_gsup_message *gsup,</span><br><span style="color: hsl(0, 100%, 40%);">-                  const struct ss_request *req)</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_ss(struct ss_session *ss, bool is_euse_originated, const struct osmo_gsup_message *gsup,</span><br><span style="color: hsl(120, 100%, 40%);">+                  const struct ss_request *req)</span><br><span> {</span><br><span>      uint8_t comp_type = gsup->ss_info[0];</span><br><span> </span><br><span>@@ -441,17 +448,16 @@</span><br><span>          * we don't handle "structured" SS requests at all.</span><br><span>     */</span><br><span>  LOGPSS(ss, LOGL_NOTICE, "Structured SS requests are not supported, rejecting...\n");</span><br><span style="color: hsl(0, 100%, 40%);">-  ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED);</span><br><span style="color: hsl(120, 100%, 40%);">+  ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED);</span><br><span>   return -ENOTSUP;</span><br><span> }</span><br><span> </span><br><span> /* Handle a USSD GSUP message for a given SS Session received from VLR or EUSE */</span><br><span style="color: hsl(0, 100%, 40%);">-static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss,</span><br><span style="color: hsl(0, 100%, 40%);">-                        const struct osmo_gsup_message *gsup, const struct ss_request *req)</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_ussd(struct ss_session *ss, bool is_euse_originated, const struct osmo_gsup_message *gsup,</span><br><span style="color: hsl(120, 100%, 40%);">+                    const struct ss_request *req)</span><br><span> {</span><br><span>    uint8_t comp_type = gsup->ss_info[0];</span><br><span>     struct msgb *msg_out;</span><br><span style="color: hsl(0, 100%, 40%);">-   bool is_euse_originated = conn_is_euse(conn);</span><br><span> </span><br><span>    LOGPSS(ss, LOGL_INFO, "USSD CompType=%s, OpCode=%s '%s'\n",</span><br><span>                gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode),</span><br><span>@@ -459,26 +465,27 @@</span><br><span> </span><br><span>         if ((ss->is_external && !ss->u.euse) || !ss->u.iuse) {</span><br><span>              LOGPSS(ss, LOGL_NOTICE, "USSD for unknown code '%s'\n", req->ussd_text);</span><br><span style="color: hsl(0, 100%, 40%);">-           ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SS_NOT_AVAILABLE);</span><br><span style="color: hsl(120, 100%, 40%);">+                ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_SS_NOT_AVAILABLE);</span><br><span>                 return 0;</span><br><span>    }</span><br><span> </span><br><span>        if (is_euse_originated) {</span><br><span style="color: hsl(0, 100%, 40%);">-               msg_out = osmo_gsup_msgb_alloc("GSUP USSD FW");</span><br><span>            /* Received from EUSE, Forward to VLR */</span><br><span style="color: hsl(0, 100%, 40%);">-                osmo_gsup_encode(msg_out, gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-                ss_gsup_send(ss, conn->server, msg_out);</span><br><span style="color: hsl(120, 100%, 40%);">+           /* Need a non-const osmo_gsup_message, because sending might modify some (routing related?) parts. */</span><br><span style="color: hsl(120, 100%, 40%);">+         struct osmo_gsup_message forward = *gsup;</span><br><span style="color: hsl(120, 100%, 40%);">+             ss_gsup_send_to_ms(ss, g_hlr->gs, &forward);</span><br><span>  } else {</span><br><span>             /* Received from VLR (MS) */</span><br><span>                 if (ss->is_external) {</span><br><span>                    /* Forward to EUSE */</span><br><span style="color: hsl(0, 100%, 40%);">-                   char addr[128];</span><br><span style="color: hsl(0, 100%, 40%);">-                 strcpy(addr, "EUSE-");</span><br><span style="color: hsl(0, 100%, 40%);">-                        osmo_strlcpy(addr+5, ss->u.euse->name, sizeof(addr)-5);</span><br><span style="color: hsl(0, 100%, 40%);">-                   conn = gsup_route_find(conn->server, (uint8_t *)addr, strlen(addr)+1);</span><br><span style="color: hsl(120, 100%, 40%);">+                     struct osmo_ipa_name euse_name;</span><br><span style="color: hsl(120, 100%, 40%);">+                       struct osmo_gsup_conn *conn;</span><br><span style="color: hsl(120, 100%, 40%);">+                  osmo_ipa_name_set_str(&euse_name, "EUSE-%s", ss->u.euse->name);</span><br><span style="color: hsl(120, 100%, 40%);">+                   conn = gsup_route_find_by_ipa_name(g_hlr->gs, &euse_name);</span><br><span>                    if (!conn) {</span><br><span style="color: hsl(0, 100%, 40%);">-                            LOGPSS(ss, LOGL_ERROR, "Cannot find conn for EUSE %s\n", addr);</span><br><span style="color: hsl(0, 100%, 40%);">-                               ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);</span><br><span style="color: hsl(120, 100%, 40%);">+                          LOGPSS(ss, LOGL_ERROR, "Cannot find conn for EUSE %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                   osmo_ipa_name_to_str(&euse_name));</span><br><span style="color: hsl(120, 100%, 40%);">+                         ss_tx_to_ms_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);</span><br><span>                   } else {</span><br><span>                             msg_out = osmo_gsup_msgb_alloc("GSUP USSD FW");</span><br><span>                            osmo_gsup_encode(msg_out, gsup);</span><br><span>@@ -486,7 +493,7 @@</span><br><span>                       }</span><br><span>            } else {</span><br><span>                     /* Handle internally */</span><br><span style="color: hsl(0, 100%, 40%);">-                 ss->u.iuse->handle_ussd(conn, ss, gsup, req);</span><br><span style="color: hsl(120, 100%, 40%);">+                   ss->u.iuse->handle_ussd(ss, gsup, req);</span><br><span>                        /* Release session immediately */</span><br><span>                    ss_session_free(ss);</span><br><span>                 }</span><br><span>@@ -498,12 +505,16 @@</span><br><span> </span><br><span> /* this function is called for any SS_REQ/SS_RESP messages from both the MSC/VLR side as well</span><br><span>  * as from the EUSE side */</span><br><span style="color: hsl(0, 100%, 40%);">-int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)</span><br><span style="color: hsl(120, 100%, 40%);">+void rx_proc_ss_req(struct osmo_gsup_req *gsup_req)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-    struct hlr *hlr = conn->server->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct hlr *hlr = g_hlr;</span><br><span>     struct ss_session *ss;</span><br><span>       struct ss_request req = {0};</span><br><span style="color: hsl(0, 100%, 40%);">-    struct gsup_route *gsup_rt;</span><br><span style="color: hsl(120, 100%, 40%);">+   const struct osmo_gsup_message *gsup = &gsup_req->gsup;</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Remember whether this function should free the incoming gsup_req: if it is placed as ss->initial_req_from_*,</span><br><span style="color: hsl(120, 100%, 40%);">+     * do not free it here. If not, free it here. */</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_gsup_req *free_gsup_req = gsup_req;</span><br><span style="color: hsl(120, 100%, 40%);">+       bool is_euse_originated = peer_name_is_euse(&gsup_req->source_name);</span><br><span> </span><br><span>      LOGP(DSS, LOGL_DEBUG, "%s/0x%08x: Process SS (%s)\n", gsup->imsi, gsup->session_id,</span><br><span>          osmo_gsup_session_state_name(gsup->session_state));</span><br><span>@@ -514,14 +525,15 @@</span><br><span>                       LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Unable to parse SS request: %s\n",</span><br><span>                              gsup->imsi, gsup->session_id,</span><br><span>                          osmo_hexdump(gsup->ss_info, gsup->ss_info_len));</span><br><span style="color: hsl(0, 100%, 40%);">-                  /* FIXME: Send a Reject component? */</span><br><span style="color: hsl(0, 100%, 40%);">-                   goto out_err;</span><br><span style="color: hsl(120, 100%, 40%);">+                 osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "error parsing SS request");</span><br><span style="color: hsl(120, 100%, 40%);">+                   return;</span><br><span>              }</span><br><span>    } else if (gsup->session_state != OSMO_GSUP_SESSION_STATE_END) {</span><br><span>          LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Missing SS payload for '%s'\n",</span><br><span>                      gsup->imsi, gsup->session_id,</span><br><span>                  osmo_gsup_session_state_name(gsup->session_state));</span><br><span style="color: hsl(0, 100%, 40%);">-             goto out_err;</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "missing SS payload");</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span>      }</span><br><span> </span><br><span>        switch (gsup->session_state) {</span><br><span>@@ -530,32 +542,29 @@</span><br><span>            if (ss_session_find(hlr, gsup->imsi, gsup->session_id)) {</span><br><span>                      LOGP(DSS, LOGL_ERROR, "%s/0x%08x: BEGIN with non-unique session ID!\n",</span><br><span>                            gsup->imsi, gsup->session_id);</span><br><span style="color: hsl(0, 100%, 40%);">-                    goto out_err;</span><br><span style="color: hsl(120, 100%, 40%);">+                 osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "BEGIN with non-unique session ID");</span><br><span style="color: hsl(120, 100%, 40%);">+                   return;</span><br><span>              }</span><br><span>            ss = ss_session_alloc(hlr, gsup->imsi, gsup->session_id);</span><br><span>              if (!ss) {</span><br><span>                   LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unable to allocate SS session\n",</span><br><span>                                gsup->imsi, gsup->session_id);</span><br><span style="color: hsl(0, 100%, 40%);">-                    goto out_err;</span><br><span style="color: hsl(120, 100%, 40%);">+                 osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_NET_FAIL, "Unable to allocate SS session");</span><br><span style="color: hsl(120, 100%, 40%);">+                   return;</span><br><span>              }</span><br><span>            /* Get IPA name from VLR conn and save as ss->vlr_number */</span><br><span style="color: hsl(0, 100%, 40%);">-          if (!conn_is_euse(conn)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                      gsup_rt = gsup_route_find_by_conn(conn);</span><br><span style="color: hsl(0, 100%, 40%);">-                        if (gsup_rt) {</span><br><span style="color: hsl(0, 100%, 40%);">-                          ss->vlr_number = (uint8_t *)talloc_strdup(ss, (const char *)gsup_rt->addr);</span><br><span style="color: hsl(0, 100%, 40%);">-                               ss->vlr_number_len = strlen((const char *)gsup_rt->addr) + 1;</span><br><span style="color: hsl(0, 100%, 40%);">-                             LOGPSS(ss, LOGL_DEBUG, "Destination IPA name retrieved from GSUP route: %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                                       osmo_quote_str((const char *)ss->vlr_number, ss->vlr_number_len));</span><br><span style="color: hsl(0, 100%, 40%);">-                 } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                LOGPSS(ss, LOGL_NOTICE, "Could not find GSUP route, therefore can't set the destination"</span><br><span style="color: hsl(0, 100%, 40%);">-                                                  " IPA name. We'll try to look it up later, but this should not"</span><br><span style="color: hsl(0, 100%, 40%);">-                                                   " have happened.\n");</span><br><span style="color: hsl(0, 100%, 40%);">-                 }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!is_euse_originated) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    ss->initial_req_from_ms = gsup_req;</span><br><span style="color: hsl(120, 100%, 40%);">+                        free_gsup_req = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                 ss->vlr_name = gsup_req->source_name;</span><br><span style="color: hsl(120, 100%, 40%);">+           } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ss->initial_req_from_euse = gsup_req;</span><br><span style="color: hsl(120, 100%, 40%);">+                      free_gsup_req = NULL;</span><br><span>                }</span><br><span>            if (ss_op_is_ussd(req.opcode)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        if (conn_is_euse(conn)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (is_euse_originated) {</span><br><span>                            /* EUSE->VLR: MT USSD. EUSE is known ('conn'), VLR is to be resolved */</span><br><span style="color: hsl(0, 100%, 40%);">-                              ss->u.euse = euse_by_conn(conn);</span><br><span style="color: hsl(120, 100%, 40%);">+                           ss->u.euse = euse_by_name(&gsup_req->source_name);</span><br><span>                         } else {</span><br><span>                             /* VLR->EUSE: MO USSD. VLR is known ('conn'), EUSE is to be resolved */</span><br><span>                           struct hlr_ussd_route *rt;</span><br><span>@@ -576,10 +585,10 @@</span><br><span>                           }</span><br><span>                    }</span><br><span>                    /* dispatch unstructured SS to routing */</span><br><span style="color: hsl(0, 100%, 40%);">-                       handle_ussd(conn, ss, gsup, &req);</span><br><span style="color: hsl(120, 100%, 40%);">+                        handle_ussd(ss, is_euse_originated, &gsup_req->gsup, &req);</span><br><span>               } else {</span><br><span>                     /* dispatch non-call SS to internal code */</span><br><span style="color: hsl(0, 100%, 40%);">-                     handle_ss(ss, gsup, &req);</span><br><span style="color: hsl(120, 100%, 40%);">+                        handle_ss(ss, is_euse_originated, &gsup_req->gsup, &req);</span><br><span>                 }</span><br><span>            break;</span><br><span>       case OSMO_GSUP_SESSION_STATE_CONTINUE:</span><br><span>@@ -587,7 +596,8 @@</span><br><span>                 if (!ss) {</span><br><span>                   LOGP(DSS, LOGL_ERROR, "%s/0x%08x: CONTINUE for unknown SS session\n",</span><br><span>                              gsup->imsi, gsup->session_id);</span><br><span style="color: hsl(0, 100%, 40%);">-                    goto out_err;</span><br><span style="color: hsl(120, 100%, 40%);">+                 osmo_gsup_req_respond_err(gsup_req, GMM_CAUSE_INV_MAND_INFO, "CONTINUE for unknown SS session");</span><br><span style="color: hsl(120, 100%, 40%);">+                    return;</span><br><span>              }</span><br><span> </span><br><span>                /* Reschedule self-destruction timer */</span><br><span>@@ -596,10 +606,10 @@</span><br><span> </span><br><span>          if (ss_op_is_ussd(req.opcode)) {</span><br><span>                     /* dispatch unstructured SS to routing */</span><br><span style="color: hsl(0, 100%, 40%);">-                       handle_ussd(conn, ss, gsup, &req);</span><br><span style="color: hsl(120, 100%, 40%);">+                        handle_ussd(ss, is_euse_originated, &gsup_req->gsup, &req);</span><br><span>               } else {</span><br><span>                     /* dispatch non-call SS to internal code */</span><br><span style="color: hsl(0, 100%, 40%);">-                     handle_ss(ss, gsup, &req);</span><br><span style="color: hsl(120, 100%, 40%);">+                        handle_ss(ss, is_euse_originated, &gsup_req->gsup, &req);</span><br><span>                 }</span><br><span>            break;</span><br><span>       case OSMO_GSUP_SESSION_STATE_END:</span><br><span>@@ -607,17 +617,17 @@</span><br><span>            if (!ss) {</span><br><span>                   LOGP(DSS, LOGL_ERROR, "%s/0x%08x: END for unknown SS session\n",</span><br><span>                           gsup->imsi, gsup->session_id);</span><br><span style="color: hsl(0, 100%, 40%);">-                    goto out_err;</span><br><span style="color: hsl(120, 100%, 40%);">+                 return;</span><br><span>              }</span><br><span> </span><br><span>                /* SS payload is optional for END */</span><br><span>                 if (gsup->ss_info && gsup->ss_info_len) {</span><br><span>                      if (ss_op_is_ussd(req.opcode)) {</span><br><span>                             /* dispatch unstructured SS to routing */</span><br><span style="color: hsl(0, 100%, 40%);">-                               handle_ussd(conn, ss, gsup, &req);</span><br><span style="color: hsl(120, 100%, 40%);">+                                handle_ussd(ss, is_euse_originated, &gsup_req->gsup, &req);</span><br><span>                       } else {</span><br><span>                             /* dispatch non-call SS to internal code */</span><br><span style="color: hsl(0, 100%, 40%);">-                             handle_ss(ss, gsup, &req);</span><br><span style="color: hsl(120, 100%, 40%);">+                                handle_ss(ss, is_euse_originated, &gsup_req->gsup, &req);</span><br><span>                         }</span><br><span>            }</span><br><span> </span><br><span>@@ -626,18 +636,15 @@</span><br><span>        default:</span><br><span>             LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unknown SS State %d\n", gsup->imsi,</span><br><span>                   gsup->session_id, gsup->session_state);</span><br><span style="color: hsl(0, 100%, 40%);">-           goto out_err;</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span>       }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-out_err:</span><br><span style="color: hsl(0, 100%, 40%);">-       return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (free_gsup_req)</span><br><span style="color: hsl(120, 100%, 40%);">+            osmo_gsup_req_free(free_gsup_req);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)</span><br><span style="color: hsl(120, 100%, 40%);">+void rx_proc_ss_error(struct osmo_gsup_req *req)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       LOGP(DSS, LOGL_NOTICE, "%s/0x%08x: Process SS ERROR (%s)\n", gsup->imsi, gsup->session_id,</span><br><span style="color: hsl(0, 100%, 40%);">-              osmo_gsup_session_state_name(gsup->session_state));</span><br><span style="color: hsl(0, 100%, 40%);">-  return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     LOGP(DSS, LOGL_NOTICE, "%s/0x%08x: Process SS ERROR (%s)\n", req->gsup.imsi, req->gsup.session_id,</span><br><span style="color: hsl(120, 100%, 40%);">+            osmo_gsup_session_state_name(req->gsup.session_state));</span><br><span> }</span><br><span>diff --git a/src/logging.c b/src/logging.c</span><br><span>index d0b79cf..15ef596 100644</span><br><span>--- a/src/logging.c</span><br><span>+++ b/src/logging.c</span><br><span>@@ -31,6 +31,12 @@</span><br><span>                .color = "\033[1;35m",</span><br><span>             .enabled = 1, .loglevel = LOGL_NOTICE,</span><br><span>       },</span><br><span style="color: hsl(120, 100%, 40%);">+    [DLU] = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .name = "DLU",</span><br><span style="color: hsl(120, 100%, 40%);">+              .description = "Location Updating",</span><br><span style="color: hsl(120, 100%, 40%);">+         .color = "\033[1;33m",</span><br><span style="color: hsl(120, 100%, 40%);">+              .enabled = 1, .loglevel = LOGL_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+        },</span><br><span> };</span><br><span> </span><br><span> const struct log_info hlr_log_info = {</span><br><span>diff --git a/src/lu_fsm.c b/src/lu_fsm.c</span><br><span>new file mode 100644</span><br><span>index 0000000..bded4ef</span><br><span>--- /dev/null</span><br><span>+++ b/src/lu_fsm.c</span><br><span>@@ -0,0 +1,308 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Roughly following "Process Update_Location_HLR" of TS 09.02 */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/tdef.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/apn.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm48_ie.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsupclient/ipa_name.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsupclient/gsup_req.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/hlr/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/hlr/hlr.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/hlr/gsup_server.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/hlr/db.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_LU(lu, level, fmt, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGPFSML((lu)? (lu)->fi : NULL, level, fmt, ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_LU_REQ(lu, req, level, fmt, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+  LOGPFSML((lu)? (lu)->fi : NULL, level, "%s:" fmt, \</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_gsup_message_type_name((req)->gsup.message_type), ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct lu {</span><br><span style="color: hsl(120, 100%, 40%);">+     struct llist_head entry;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_fsm_inst *fi;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_gsup_req *update_location_req;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Subscriber state at time of initial Update Location Request */</span><br><span style="color: hsl(120, 100%, 40%);">+     struct hlr_subscriber subscr;</span><br><span style="color: hsl(120, 100%, 40%);">+ bool is_ps;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* VLR requesting the LU. */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_ipa_name vlr_name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* If the LU request was received via a proxy and not immediately from a local VLR, this indicates the closest</span><br><span style="color: hsl(120, 100%, 40%);">+         * peer that forwarded the GSUP message. */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_ipa_name via_proxy;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+LLIST_HEAD(g_all_lu);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum lu_fsm_event {</span><br><span style="color: hsl(120, 100%, 40%);">+     LU_EV_RX_GSUP,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum lu_fsm_state {</span><br><span style="color: hsl(120, 100%, 40%);">+     LU_ST_UNVALIDATED,</span><br><span style="color: hsl(120, 100%, 40%);">+    LU_ST_WAIT_INSERT_DATA_RESULT,</span><br><span style="color: hsl(120, 100%, 40%);">+        LU_ST_WAIT_LOCATION_CANCEL_RESULT,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct value_string lu_fsm_event_names[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_VALUE_STRING(LU_EV_RX_GSUP),</span><br><span style="color: hsl(120, 100%, 40%);">+     {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_tdef_state_timeout lu_fsm_timeouts[32] = {</span><br><span style="color: hsl(120, 100%, 40%);">+       [LU_ST_WAIT_INSERT_DATA_RESULT] = { .T = -4222 },</span><br><span style="color: hsl(120, 100%, 40%);">+     [LU_ST_WAIT_LOCATION_CANCEL_RESULT] = { .T = -4222 },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define lu_state_chg(lu, state) \</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_tdef_fsm_inst_state_chg((lu)->fi, state, lu_fsm_timeouts, g_hlr_tdefs, 5)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lu_success(struct lu *lu)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!lu->update_location_req)</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_LU(lu, LOGL_ERROR, "No request for this LU\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_gsup_req_respond_msgt(lu->update_location_req, OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT, true);</span><br><span style="color: hsl(120, 100%, 40%);">+  lu->update_location_req = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_fsm_inst_term(lu->fi, OSMO_FSM_TERM_REGULAR, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define lu_failure(LU, CAUSE, log_msg, args...) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!(LU)->update_location_req) \</span><br><span style="color: hsl(120, 100%, 40%);">+                  LOG_LU(LU, LOGL_ERROR, "No request for this LU\n"); \</span><br><span style="color: hsl(120, 100%, 40%);">+               else \</span><br><span style="color: hsl(120, 100%, 40%);">+                        osmo_gsup_req_respond_err((LU)->update_location_req, CAUSE, log_msg, ##args); \</span><br><span style="color: hsl(120, 100%, 40%);">+            (LU)->update_location_req = NULL; \</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_fsm_inst_term((LU)->fi, OSMO_FSM_TERM_REGULAR, NULL); \</span><br><span style="color: hsl(120, 100%, 40%);">+       } while(0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_fsm lu_fsm;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lu_start(struct osmo_gsup_req *update_location_req)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_fsm_inst *fi;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct lu *lu;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(update_location_req);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(update_location_req->gsup.message_type == OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   fi = osmo_fsm_inst_alloc(&lu_fsm, g_hlr, NULL, LOGL_DEBUG, update_location_req->gsup.imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    lu = talloc(fi, struct lu);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(lu);</span><br><span style="color: hsl(120, 100%, 40%);">+      fi->priv = lu;</span><br><span style="color: hsl(120, 100%, 40%);">+     *lu = (struct lu){</span><br><span style="color: hsl(120, 100%, 40%);">+            .fi = fi,</span><br><span style="color: hsl(120, 100%, 40%);">+             .update_location_req = update_location_req,</span><br><span style="color: hsl(120, 100%, 40%);">+           .vlr_name = update_location_req->source_name,</span><br><span style="color: hsl(120, 100%, 40%);">+              .via_proxy = update_location_req->via_proxy,</span><br><span style="color: hsl(120, 100%, 40%);">+               /* According to GSUP specs, OSMO_GSUP_CN_DOMAIN_PS is the default. */</span><br><span style="color: hsl(120, 100%, 40%);">+         .is_ps = (update_location_req->gsup.cn_domain != OSMO_GSUP_CN_DOMAIN_CS),</span><br><span style="color: hsl(120, 100%, 40%);">+  };</span><br><span style="color: hsl(120, 100%, 40%);">+    llist_add(&lu->entry, &g_all_lu);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_fsm_inst_update_id_f_sanitize(fi, '_', "%s:IMSI-%s", lu->is_ps ? "PS" : "CS", update_location_req->gsup.imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!lu->vlr_name.len) {</span><br><span style="color: hsl(120, 100%, 40%);">+           lu_failure(lu, GMM_CAUSE_NET_FAIL, "LU without a VLR");</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (db_subscr_get_by_imsi(g_hlr->dbc, update_location_req->gsup.imsi, &lu->subscr) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             lu_failure(lu, GMM_CAUSE_IMSI_UNKNOWN, "Subscriber does not exist");</span><br><span style="color: hsl(120, 100%, 40%);">+                return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Check if subscriber is generally permitted on CS or PS</span><br><span style="color: hsl(120, 100%, 40%);">+      * service (as requested) */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!lu->is_ps && !lu->subscr.nam_cs) {</span><br><span style="color: hsl(120, 100%, 40%);">+         lu_failure(lu, GMM_CAUSE_PLMN_NOTALLOWED, "nam_cs == false");</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (lu->is_ps && !lu->subscr.nam_ps) {</span><br><span style="color: hsl(120, 100%, 40%);">+          lu_failure(lu, GMM_CAUSE_GPRS_NOTALLOWED, "nam_ps == false");</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* TODO: Set subscriber tracing = deactive in VLR/SGSN */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#if 0</span><br><span style="color: hsl(120, 100%, 40%);">+    /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old (FIXME: OS#4491) */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!lu->is_ps && strcmp(subscr->vlr_number, vlr_number)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             lu_op_tx_cancel_old(lu);</span><br><span style="color: hsl(120, 100%, 40%);">+      } else if (lu->is_ps && strcmp(subscr->sgsn_number, sgsn_number)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             lu_op_tx_cancel_old(lu);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (lu->via_proxy.len) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s, via proxy %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                         lu->is_ps ? "SGSN number" : "VLR number",</span><br><span style="color: hsl(120, 100%, 40%);">+                      osmo_ipa_name_to_str(&lu->vlr_name),</span><br><span style="color: hsl(120, 100%, 40%);">+                           osmo_ipa_name_to_str(&lu->via_proxy));</span><br><span style="color: hsl(120, 100%, 40%);">+    } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+               lu->is_ps ? "SGSN number" : "VLR number",</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_ipa_name_to_str(&lu->vlr_name));</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (db_subscr_lu(g_hlr->dbc, lu->subscr.id, &lu->vlr_name, lu->is_ps, &lu->via_proxy)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               lu_failure(lu, GMM_CAUSE_NET_FAIL, "Cannot update %s in the database",</span><br><span style="color: hsl(120, 100%, 40%);">+                         lu->is_ps ? "SGSN number" : "VLR number");</span><br><span style="color: hsl(120, 100%, 40%);">+          return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* TODO: Subscriber allowed to roam in PLMN? */</span><br><span style="color: hsl(120, 100%, 40%);">+       /* TODO: Update RoutingInfo */</span><br><span style="color: hsl(120, 100%, 40%);">+        /* TODO: Reset Flag MS Purged (cs/ps) */</span><br><span style="color: hsl(120, 100%, 40%);">+      /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     lu_state_chg(lu, LU_ST_WAIT_INSERT_DATA_RESULT);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lu_rx_gsup(struct osmo_gsup_req *req)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct lu *lu;</span><br><span style="color: hsl(120, 100%, 40%);">+        if (req->gsup.message_type == OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST)</span><br><span style="color: hsl(120, 100%, 40%);">+              return lu_start(req);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       llist_for_each_entry(lu, &g_all_lu, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (strcmp(lu->subscr.imsi, req->gsup.imsi))</span><br><span style="color: hsl(120, 100%, 40%);">+                    continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             if (osmo_fsm_inst_dispatch(lu->fi, LU_EV_RX_GSUP, req)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  LOG_LU_REQ(lu, req, LOGL_ERROR, "Cannot receive GSUP messages in this state\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                    osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE,</span><br><span style="color: hsl(120, 100%, 40%);">+                                           "LU does not accept GSUP rx");</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE, "No Location Updating in progress for this IMSI");</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int lu_fsm_timer_cb(struct osmo_fsm_inst *fi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct lu *lu = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+  lu_failure(lu, GSM_CAUSE_NET_FAIL, "Timeout");</span><br><span style="color: hsl(120, 100%, 40%);">+      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lu_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct lu *lu = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+  if (lu->update_location_req)</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_gsup_req_respond_err(lu->update_location_req, GSM_CAUSE_NET_FAIL, "LU aborted");</span><br><span style="color: hsl(120, 100%, 40%);">+    lu->update_location_req = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    llist_del(&lu->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lu_fsm_wait_insert_data_result_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Transmit Insert Data Request to the VLR */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct lu *lu = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct hlr_subscriber *subscr = &lu->subscr;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_gsup_message gsup;</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];</span><br><span style="color: hsl(120, 100%, 40%);">+       uint8_t apn[APN_MAXLEN];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                   subscr->msisdn, msisdn_enc, sizeof(msisdn_enc),</span><br><span style="color: hsl(120, 100%, 40%);">+                                                    apn, sizeof(apn),</span><br><span style="color: hsl(120, 100%, 40%);">+                                                     lu->is_ps? OSMO_GSUP_CN_DOMAIN_PS : OSMO_GSUP_CN_DOMAIN_CS)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             lu_failure(lu, GMM_CAUSE_NET_FAIL, "cannot encode Insert Subscriber Data message");</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (osmo_gsup_req_respond(lu->update_location_req, &gsup, false, false))</span><br><span style="color: hsl(120, 100%, 40%);">+               lu_failure(lu, GMM_CAUSE_NET_FAIL, "cannot send %s", osmo_gsup_message_type_name(gsup.message_type));</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lu_fsm_wait_insert_data_result(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct lu *lu = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_gsup_req *req;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case LU_EV_RX_GSUP:</span><br><span style="color: hsl(120, 100%, 40%);">+           req = data;</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   switch (req->gsup.message_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+  case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_gsup_req_free(req);</span><br><span style="color: hsl(120, 100%, 40%);">+              lu_success(lu);</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:</span><br><span style="color: hsl(120, 100%, 40%);">+                lu_failure(lu, GMM_CAUSE_NET_FAIL, "Rx %s", osmo_gsup_message_type_name(req->gsup.message_type));</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      default:</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE, "unexpected message type in this state");</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define S(x) (1 << (x))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct osmo_fsm_state lu_fsm_states[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+       [LU_ST_UNVALIDATED] = {</span><br><span style="color: hsl(120, 100%, 40%);">+               .name = "UNVALIDATED",</span><br><span style="color: hsl(120, 100%, 40%);">+              .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LU_ST_WAIT_INSERT_DATA_RESULT)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+    [LU_ST_WAIT_INSERT_DATA_RESULT] = {</span><br><span style="color: hsl(120, 100%, 40%);">+           .name = "WAIT_INSERT_DATA_RESULT",</span><br><span style="color: hsl(120, 100%, 40%);">+          .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LU_EV_RX_GSUP)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .onenter = lu_fsm_wait_insert_data_result_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+            .action = lu_fsm_wait_insert_data_result,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_fsm lu_fsm = {</span><br><span style="color: hsl(120, 100%, 40%);">+   .name = "lu",</span><br><span style="color: hsl(120, 100%, 40%);">+       .states = lu_fsm_states,</span><br><span style="color: hsl(120, 100%, 40%);">+      .num_states = ARRAY_SIZE(lu_fsm_states),</span><br><span style="color: hsl(120, 100%, 40%);">+      .log_subsys = DLU,</span><br><span style="color: hsl(120, 100%, 40%);">+    .event_names = lu_fsm_event_names,</span><br><span style="color: hsl(120, 100%, 40%);">+    .timer_cb = lu_fsm_timer_cb,</span><br><span style="color: hsl(120, 100%, 40%);">+  .cleanup = lu_fsm_cleanup,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static __attribute__((constructor)) void lu_fsm_init()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(osmo_fsm_register(&lu_fsm) == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/luop.c b/src/luop.c</span><br><span>deleted file mode 100644</span><br><span>index e63ba91..0000000</span><br><span>--- a/src/luop.c</span><br><span>+++ /dev/null</span><br><span>@@ -1,258 +0,0 @@</span><br><span style="color: hsl(0, 100%, 40%);">-/* OsmoHLR TX/RX lu operations */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(0, 100%, 40%);">- * All Rights Reserved</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * Author: Harald Welte <laforge@gnumonks.org></span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(0, 100%, 40%);">- * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(0, 100%, 40%);">- * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(0, 100%, 40%);">- * (at your option) any later version.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(0, 100%, 40%);">- * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(0, 100%, 40%);">- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(0, 100%, 40%);">- * GNU Affero General Public License for more details.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(0, 100%, 40%);">- * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include <stdbool.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <string.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <errno.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/core/logging.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/gsm/gsup.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/gsm/apn.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/hlr/gsup_server.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/hlr/gsup_router.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/hlr/logging.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/hlr/luop.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-const struct value_string lu_state_names[] = {</span><br><span style="color: hsl(0, 100%, 40%);">-    { LU_S_NULL,                    "NULL" },</span><br><span style="color: hsl(0, 100%, 40%);">-     { LU_S_LU_RECEIVED,             "LU RECEIVED" },</span><br><span style="color: hsl(0, 100%, 40%);">-      { LU_S_CANCEL_SENT,             "CANCEL SENT" },</span><br><span style="color: hsl(0, 100%, 40%);">-      { LU_S_CANCEL_ACK_RECEIVED,     "CANCEL-ACK RECEIVED" },</span><br><span style="color: hsl(0, 100%, 40%);">-      { LU_S_ISD_SENT,                "ISD SENT" },</span><br><span style="color: hsl(0, 100%, 40%);">- { LU_S_ISD_ACK_RECEIVED,        "ISD-ACK RECEIVED" },</span><br><span style="color: hsl(0, 100%, 40%);">- { LU_S_COMPLETE,                "COMPLETE" },</span><br><span style="color: hsl(0, 100%, 40%);">- { 0, NULL }</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* Transmit a given GSUP message for the given LU operation */</span><br><span style="color: hsl(0, 100%, 40%);">-static void _luop_tx_gsup(struct lu_operation *luop,</span><br><span style="color: hsl(0, 100%, 40%);">-                   const struct osmo_gsup_message *gsup)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        struct msgb *msg_out;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   msg_out = osmo_gsup_msgb_alloc("GSUP LUOP");</span><br><span style="color: hsl(0, 100%, 40%);">-  osmo_gsup_encode(msg_out, gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        osmo_gsup_addr_send(luop->gsup_server, luop->peer,</span><br><span style="color: hsl(0, 100%, 40%);">-                            talloc_total_size(luop->peer),</span><br><span style="color: hsl(0, 100%, 40%);">-                       msg_out);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static inline void fill_gsup_msg(struct osmo_gsup_message *out,</span><br><span style="color: hsl(0, 100%, 40%);">-                            const struct lu_operation *lu,</span><br><span style="color: hsl(0, 100%, 40%);">-                          enum osmo_gsup_message_type mt)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       memset(out, 0, sizeof(struct osmo_gsup_message));</span><br><span style="color: hsl(0, 100%, 40%);">-       if (lu)</span><br><span style="color: hsl(0, 100%, 40%);">-         osmo_strlcpy(out->imsi, lu->subscr.imsi,</span><br><span style="color: hsl(0, 100%, 40%);">-                       GSM23003_IMSI_MAX_DIGITS + 1);</span><br><span style="color: hsl(0, 100%, 40%);">-     out->message_type = mt;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* timer call-back in case LU operation doesn't receive an response */</span><br><span style="color: hsl(0, 100%, 40%);">-static void lu_op_timer_cb(void *data)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    struct lu_operation *luop = data;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       DEBUGP(DMAIN, "LU OP timer expired in state %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-            get_value_string(lu_state_names, luop->state));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      switch (luop->state) {</span><br><span style="color: hsl(0, 100%, 40%);">-       case LU_S_CANCEL_SENT:</span><br><span style="color: hsl(0, 100%, 40%);">-          break;</span><br><span style="color: hsl(0, 100%, 40%);">-  case LU_S_ISD_SENT:</span><br><span style="color: hsl(0, 100%, 40%);">-             break;</span><br><span style="color: hsl(0, 100%, 40%);">-  default:</span><br><span style="color: hsl(0, 100%, 40%);">-                break;</span><br><span style="color: hsl(0, 100%, 40%);">-  }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-bool lu_op_fill_subscr(struct lu_operation *luop, struct db_context *dbc,</span><br><span style="color: hsl(0, 100%, 40%);">-                    const char *imsi)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       struct hlr_subscriber *subscr = &luop->subscr;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   if (db_subscr_get_by_imsi(dbc, imsi, subscr) < 0)</span><br><span style="color: hsl(0, 100%, 40%);">-            return false;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   return true;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    struct lu_operation *luop;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      luop = talloc_zero(srv, struct lu_operation);</span><br><span style="color: hsl(0, 100%, 40%);">-   OSMO_ASSERT(luop);</span><br><span style="color: hsl(0, 100%, 40%);">-      luop->gsup_server = srv;</span><br><span style="color: hsl(0, 100%, 40%);">-     osmo_timer_setup(&luop->timer, lu_op_timer_cb, luop);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    return luop;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_free(struct lu_operation *luop)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        /* Only attempt to remove when it was ever added to a list. */</span><br><span style="color: hsl(0, 100%, 40%);">-  if (luop->list.next)</span><br><span style="color: hsl(0, 100%, 40%);">-         llist_del(&luop->list);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  /* Delete timer just in case it is still pending. */</span><br><span style="color: hsl(0, 100%, 40%);">-    osmo_timer_del(&luop->timer);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    talloc_free(luop);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct lu_operation *lu_op_alloc_conn(struct osmo_gsup_conn *conn)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  uint8_t *peer_addr;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct lu_operation *luop = lu_op_alloc(conn->server);</span><br><span style="color: hsl(0, 100%, 40%);">-       int rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR);</span><br><span style="color: hsl(0, 100%, 40%);">-        if (rc < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                lu_op_free(luop);</span><br><span style="color: hsl(0, 100%, 40%);">-               return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       luop->peer = talloc_memdup(luop, peer_addr, rc);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     return luop;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* FIXME: this doesn't seem to work at all */</span><br><span style="color: hsl(0, 100%, 40%);">-struct lu_operation *lu_op_by_imsi(const char *imsi,</span><br><span style="color: hsl(0, 100%, 40%);">-                                 const struct llist_head *lst)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       struct lu_operation *luop;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      llist_for_each_entry(luop, lst, list) {</span><br><span style="color: hsl(0, 100%, 40%);">-         if (!strcmp(imsi, luop->subscr.imsi))</span><br><span style="color: hsl(0, 100%, 40%);">-                        return luop;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-       return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   enum lu_state old_state = luop->state;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       DEBUGP(DMAIN, "LU OP state change: %s -> ",</span><br><span style="color: hsl(0, 100%, 40%);">-                get_value_string(lu_state_names, old_state));</span><br><span style="color: hsl(0, 100%, 40%);">-   DEBUGPC(DMAIN, "%s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                get_value_string(lu_state_names, new_state));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   luop->state = new_state;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Transmit UPD_LOC_ERROR and destroy lu_operation */</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   struct osmo_gsup_message gsup;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  DEBUGP(DMAIN, "%s: LU OP Tx Error (cause %s)\n",</span><br><span style="color: hsl(0, 100%, 40%);">-             luop->subscr.imsi, get_value_string(gsm48_gmm_cause_names,</span><br><span style="color: hsl(0, 100%, 40%);">-                                               cause));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR);</span><br><span style="color: hsl(0, 100%, 40%);">-   gsup.cause = cause;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     _luop_tx_gsup(luop, &gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- lu_op_free(luop);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Transmit UPD_LOC_RESULT and destroy lu_operation */</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_tx_ack(struct lu_operation *luop)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  struct osmo_gsup_message gsup;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT);</span><br><span style="color: hsl(0, 100%, 40%);">-  //FIXME gsup.hlr_enc;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   _luop_tx_gsup(luop, &gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- lu_op_free(luop);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Send Cancel Location to old VLR/SGSN (FIXME: OS#4491) */</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_tx_cancel_old(struct lu_operation *luop)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      struct osmo_gsup_message gsup;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        fill_gsup_msg(&gsup, NULL, OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST);</span><br><span style="color: hsl(0, 100%, 40%);">- //gsup.cause = FIXME;</span><br><span style="color: hsl(0, 100%, 40%);">-   //gsup.cancel_type = FIXME;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     _luop_tx_gsup(luop, &gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- lu_op_statechg(luop, LU_S_CANCEL_SENT);</span><br><span style="color: hsl(0, 100%, 40%);">- osmo_timer_schedule(&luop->timer, CANCEL_TIMEOUT_SECS, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Transmit Insert Subscriber Data to new VLR/SGSN */</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_tx_insert_subscr_data(struct lu_operation *luop)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    struct hlr_subscriber *subscr = &luop->subscr;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct osmo_gsup_message gsup = { };</span><br><span style="color: hsl(0, 100%, 40%);">-    uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t apn[APN_MAXLEN];</span><br><span style="color: hsl(0, 100%, 40%);">-        enum osmo_gsup_cn_domain cn_domain;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED ||</span><br><span style="color: hsl(0, 100%, 40%);">-                   luop->state == LU_S_CANCEL_ACK_RECEIVED);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        if (luop->is_ps)</span><br><span style="color: hsl(0, 100%, 40%);">-             cn_domain = OSMO_GSUP_CN_DOMAIN_PS;</span><br><span style="color: hsl(0, 100%, 40%);">-     else</span><br><span style="color: hsl(0, 100%, 40%);">-            cn_domain = OSMO_GSUP_CN_DOMAIN_CS;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi, subscr->msisdn, msisdn_enc,</span><br><span style="color: hsl(0, 100%, 40%);">-                                                      sizeof(msisdn_enc), apn, sizeof(apn), cn_domain) != 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                LOGP(DMAIN, LOGL_ERROR,</span><br><span style="color: hsl(0, 100%, 40%);">-                "IMSI='%s': Cannot notify GSUP client; could not create gsup message "</span><br><span style="color: hsl(0, 100%, 40%);">-                "for %s\n", subscr->imsi, luop->peer);</span><br><span style="color: hsl(0, 100%, 40%);">-           return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* Send ISD to new VLR/SGSN */</span><br><span style="color: hsl(0, 100%, 40%);">-  _luop_tx_gsup(luop, &gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- lu_op_statechg(luop, LU_S_ISD_SENT);</span><br><span style="color: hsl(0, 100%, 40%);">-    osmo_timer_schedule(&luop->timer, ISD_TIMEOUT_SECS, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Transmit Delete Subscriber Data to new VLR/SGSN.</span><br><span style="color: hsl(0, 100%, 40%);">- * The luop is not freed. */</span><br><span style="color: hsl(0, 100%, 40%);">-void lu_op_tx_del_subscr_data(struct lu_operation *luop)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        struct osmo_gsup_message gsup;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  fill_gsup_msg(&gsup, luop, OSMO_GSUP_MSGT_DELETE_DATA_REQUEST);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     gsup.cn_domain = OSMO_GSUP_CN_DOMAIN_PS;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        /* Send ISD to new VLR/SGSN */</span><br><span style="color: hsl(0, 100%, 40%);">-  _luop_tx_gsup(luop, &gsup);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span>diff --git a/tests/Makefile.am b/tests/Makefile.am</span><br><span>index 776f8a9..9015494 100644</span><br><span>--- a/tests/Makefile.am</span><br><span>+++ b/tests/Makefile.am</span><br><span>@@ -2,6 +2,7 @@</span><br><span>  auc \</span><br><span>        gsup_server \</span><br><span>        db \</span><br><span style="color: hsl(120, 100%, 40%);">+  gsup \</span><br><span>       db_upgrade \</span><br><span>         mslookup \</span><br><span>   $(NULL)</span><br><span>diff --git a/tests/db/Makefile.am b/tests/db/Makefile.am</span><br><span>index f13824d..0194957 100644</span><br><span>--- a/tests/db/Makefile.am</span><br><span>+++ b/tests/db/Makefile.am</span><br><span>@@ -30,6 +30,7 @@</span><br><span>     $(top_builddir)/src/db_auc.o \</span><br><span>       $(top_builddir)/src/db_hlr.o \</span><br><span>       $(top_builddir)/src/db.o \</span><br><span style="color: hsl(120, 100%, 40%);">+    $(top_builddir)/src/ipa_name.o \</span><br><span>     $(LIBOSMOCORE_LIBS) \</span><br><span>        $(LIBOSMOGSM_LIBS) \</span><br><span>         $(LIBOSMOABIS_LIBS) \</span><br><span>diff --git a/tests/db/db_test.c b/tests/db/db_test.c</span><br><span>index b9b263d..c53baed 100644</span><br><span>--- a/tests/db/db_test.c</span><br><span>+++ b/tests/db/db_test.c</span><br><span>@@ -27,6 +27,7 @@</span><br><span> #include <osmocom/core/utils.h></span><br><span> #include <osmocom/core/logging.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsupclient/ipa_name.h></span><br><span> #include <osmocom/hlr/db.h></span><br><span> #include <osmocom/hlr/logging.h></span><br><span> </span><br><span>@@ -145,6 +146,8 @@</span><br><span> #define Ps(name) \</span><br><span>        if (*subscr->name) \</span><br><span>              Pfo(name, "'%s'", subscr)</span><br><span style="color: hsl(120, 100%, 40%);">+#define Pgt(name) \</span><br><span style="color: hsl(120, 100%, 40%);">+      Pfv(name, "%s", osmo_ipa_name_to_str(&subscr->name))</span><br><span> #define Pd(name) \</span><br><span>  Pfv(name, "%"PRId64, (int64_t)subscr->name)</span><br><span> #define Pd_nonzero(name) \</span><br><span>@@ -235,6 +238,14 @@</span><br><span> static const char *short_imsi = "123456";</span><br><span> static const char *unknown_imsi = "999999999";</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int db_subscr_lu_str(struct db_context *dbc, int64_t subscr_id,</span><br><span style="color: hsl(120, 100%, 40%);">+                      const char *vlr_or_sgsn_number, bool is_ps)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_ipa_name vlr_nr;</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_ipa_name_set_str(&vlr_nr, vlr_or_sgsn_number);</span><br><span style="color: hsl(120, 100%, 40%);">+       return db_subscr_lu(dbc, subscr_id, &vlr_nr, is_ps, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void test_subscr_create_update_sel_delete()</span><br><span> {</span><br><span>       int64_t id0, id1, id2, id_short;</span><br><span>@@ -386,39 +397,39 @@</span><br><span> </span><br><span>         comment("Record LU for PS and CS (SGSN and VLR names)");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  ASSERT_RC(db_subscr_lu(dbc, id0, "5952", true), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ASSERT_RC(db_subscr_lu_str(dbc, id0, "5952", true), 0);</span><br><span>    ASSERT_SEL(id, id0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">- ASSERT_RC(db_subscr_lu(dbc, id0, "712", false), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ASSERT_RC(db_subscr_lu_str(dbc, id0, "712", false), 0);</span><br><span>    ASSERT_SEL(id, id0, 0);</span><br><span> </span><br><span>  comment("Record LU for PS and CS (SGSN and VLR names) *again*");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+  ASSERT_RC(db_subscr_lu_str(dbc, id0, "111", true), 0);</span><br><span>     ASSERT_SEL(id, id0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">- ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+  ASSERT_RC(db_subscr_lu_str(dbc, id0, "111", true), 0);</span><br><span>     ASSERT_SEL(id, id0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">- ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ASSERT_RC(db_subscr_lu_str(dbc, id0, "222", false), 0);</span><br><span>    ASSERT_SEL(id, id0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">- ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ASSERT_RC(db_subscr_lu_str(dbc, id0, "222", false), 0);</span><br><span>    ASSERT_SEL(id, id0, 0);</span><br><span> </span><br><span>  comment("Unset LU info for PS and CS (SGSN and VLR names)");</span><br><span style="color: hsl(0, 100%, 40%);">-  ASSERT_RC(db_subscr_lu(dbc, id0, "", true), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+     ASSERT_RC(db_subscr_lu_str(dbc, id0, "", true), 0);</span><br><span>        ASSERT_SEL(id, id0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">- ASSERT_RC(db_subscr_lu(dbc, id0, "", false), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+    ASSERT_RC(db_subscr_lu_str(dbc, id0, "", false), 0);</span><br><span>       ASSERT_SEL(id, id0, 0);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     ASSERT_RC(db_subscr_lu(dbc, id0, "111", true), 0);</span><br><span style="color: hsl(0, 100%, 40%);">-    ASSERT_RC(db_subscr_lu(dbc, id0, "222", false), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ASSERT_RC(db_subscr_lu_str(dbc, id0, "111", true), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+      ASSERT_RC(db_subscr_lu_str(dbc, id0, "222", false), 0);</span><br><span>    ASSERT_SEL(id, id0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">- ASSERT_RC(db_subscr_lu(dbc, id0, NULL, true), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+     ASSERT_RC(db_subscr_lu_str(dbc, id0, NULL, true), 0);</span><br><span>        ASSERT_SEL(id, id0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">- ASSERT_RC(db_subscr_lu(dbc, id0, NULL, false), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+    ASSERT_RC(db_subscr_lu_str(dbc, id0, NULL, false), 0);</span><br><span>       ASSERT_SEL(id, id0, 0);</span><br><span> </span><br><span>  comment("Record LU for non-existent ID");</span><br><span style="color: hsl(0, 100%, 40%);">-     ASSERT_RC(db_subscr_lu(dbc, 99999, "5952", true), -ENOENT);</span><br><span style="color: hsl(0, 100%, 40%);">-   ASSERT_RC(db_subscr_lu(dbc, 99999, "712", false), -ENOENT);</span><br><span style="color: hsl(120, 100%, 40%);">+ ASSERT_RC(db_subscr_lu_str(dbc, 99999, "5952", true), -ENOENT);</span><br><span style="color: hsl(120, 100%, 40%);">+     ASSERT_RC(db_subscr_lu_str(dbc, 99999, "712", false), -ENOENT);</span><br><span>    ASSERT_SEL(id, 99999, -ENOENT);</span><br><span> </span><br><span>  comment("Purge and un-purge PS and CS");</span><br><span>diff --git a/tests/db/db_test.err b/tests/db/db_test.err</span><br><span>index 6423550..e4d43c9 100644</span><br><span>--- a/tests/db/db_test.err</span><br><span>+++ b/tests/db/db_test.err</span><br><span>@@ -435,7 +435,7 @@</span><br><span> </span><br><span> --- Record LU for PS and CS (SGSN and VLR names)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, id0, "5952", true) --> 0</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, id0, "5952", true) --> 0</span><br><span> </span><br><span> db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0</span><br><span> struct hlr_subscriber {</span><br><span>@@ -445,7 +445,7 @@</span><br><span>   .sgsn_number = '5952',</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, id0, "712", false) --> 0</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, id0, "712", false) --> 0</span><br><span> </span><br><span> db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0</span><br><span> struct hlr_subscriber {</span><br><span>@@ -459,7 +459,7 @@</span><br><span> </span><br><span> --- Record LU for PS and CS (SGSN and VLR names) *again*</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, id0, "111", true) --> 0</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, id0, "111", true) --> 0</span><br><span> </span><br><span> db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0</span><br><span> struct hlr_subscriber {</span><br><span>@@ -470,7 +470,7 @@</span><br><span>   .sgsn_number = '111',</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, id0, "111", true) --> 0</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, id0, "111", true) --> 0</span><br><span> </span><br><span> db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0</span><br><span> struct hlr_subscriber {</span><br><span>@@ -481,7 +481,7 @@</span><br><span>   .sgsn_number = '111',</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, id0, "222", false) --> 0</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, id0, "222", false) --> 0</span><br><span> </span><br><span> db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0</span><br><span> struct hlr_subscriber {</span><br><span>@@ -492,7 +492,7 @@</span><br><span>   .sgsn_number = '111',</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, id0, "222", false) --> 0</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, id0, "222", false) --> 0</span><br><span> </span><br><span> db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0</span><br><span> struct hlr_subscriber {</span><br><span>@@ -506,7 +506,7 @@</span><br><span> </span><br><span> --- Unset LU info for PS and CS (SGSN and VLR names)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, id0, "", true) --> 0</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, id0, "", true) --> 0</span><br><span> </span><br><span> db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0</span><br><span> struct hlr_subscriber {</span><br><span>@@ -516,7 +516,7 @@</span><br><span>   .vlr_number = '222',</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, id0, "", false) --> 0</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, id0, "", false) --> 0</span><br><span> </span><br><span> db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0</span><br><span> struct hlr_subscriber {</span><br><span>@@ -525,9 +525,9 @@</span><br><span>   .msisdn = '543210123456789',</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, id0, "111", true) --> 0</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, id0, "111", true) --> 0</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, id0, "222", false) --> 0</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, id0, "222", false) --> 0</span><br><span> </span><br><span> db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0</span><br><span> struct hlr_subscriber {</span><br><span>@@ -538,7 +538,7 @@</span><br><span>   .sgsn_number = '111',</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, id0, NULL, true) --> 0</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, id0, NULL, true) --> 0</span><br><span> </span><br><span> db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0</span><br><span> struct hlr_subscriber {</span><br><span>@@ -548,7 +548,7 @@</span><br><span>   .vlr_number = '222',</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, id0, NULL, false) --> 0</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, id0, NULL, false) --> 0</span><br><span> </span><br><span> db_subscr_get_by_id(dbc, id0, &g_subscr) --> 0</span><br><span> struct hlr_subscriber {</span><br><span>@@ -560,10 +560,10 @@</span><br><span> </span><br><span> --- Record LU for non-existent ID</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, 99999, "5952", true) --> -ENOENT</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, 99999, "5952", true) --> -ENOENT</span><br><span> DAUC Cannot update SGSN number for subscriber ID=99999: no such subscriber</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-db_subscr_lu(dbc, 99999, "712", false) --> -ENOENT</span><br><span style="color: hsl(120, 100%, 40%);">+db_subscr_lu_str(dbc, 99999, "712", false) --> -ENOENT</span><br><span> DAUC Cannot update VLR number for subscriber ID=99999: no such subscriber</span><br><span> </span><br><span> db_subscr_get_by_id(dbc, 99999, &g_subscr) --> -ENOENT</span><br><span>diff --git a/tests/gsup/Makefile.am b/tests/gsup/Makefile.am</span><br><span>new file mode 100644</span><br><span>index 0000000..5dbb180</span><br><span>--- /dev/null</span><br><span>+++ b/tests/gsup/Makefile.am</span><br><span>@@ -0,0 +1,36 @@</span><br><span style="color: hsl(120, 100%, 40%);">+AM_CPPFLAGS = \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(all_includes) \</span><br><span style="color: hsl(120, 100%, 40%);">+     $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AM_CFLAGS = \</span><br><span style="color: hsl(120, 100%, 40%);">+      -I$(top_srcdir)/include \</span><br><span style="color: hsl(120, 100%, 40%);">+     $(LIBOSMOCORE_CFLAGS) \</span><br><span style="color: hsl(120, 100%, 40%);">+       $(LIBOSMOGSM_CFLAGS) \</span><br><span style="color: hsl(120, 100%, 40%);">+        $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AM_LDFLAGS = \</span><br><span style="color: hsl(120, 100%, 40%);">+     -no-install \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+EXTRA_DIST = \</span><br><span style="color: hsl(120, 100%, 40%);">+     gsup_test.ok \</span><br><span style="color: hsl(120, 100%, 40%);">+        gsup_test.err \</span><br><span style="color: hsl(120, 100%, 40%);">+       $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+noinst_PROGRAMS = \</span><br><span style="color: hsl(120, 100%, 40%);">+        gsup_test \</span><br><span style="color: hsl(120, 100%, 40%);">+   $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+gsup_test_SOURCES = \</span><br><span style="color: hsl(120, 100%, 40%);">+      gsup_test.c \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+gsup_test_LDADD = \</span><br><span style="color: hsl(120, 100%, 40%);">+        $(top_builddir)/src/gsupclient/libosmo-gsup-client.la \</span><br><span style="color: hsl(120, 100%, 40%);">+       $(LIBOSMOCORE_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+.PHONY: update_exp</span><br><span style="color: hsl(120, 100%, 40%);">+update_exp:</span><br><span style="color: hsl(120, 100%, 40%);">+    $(builddir)/gsup_test >"$(srcdir)/gsup_test.ok" 2>"$(srcdir)/gsup_test.err"</span><br><span>diff --git a/tests/gsup/gsup_test.c b/tests/gsup/gsup_test.c</span><br><span>new file mode 100644</span><br><span>index 0000000..1a7bb76</span><br><span>--- /dev/null</span><br><span>+++ b/tests/gsup/gsup_test.c</span><br><span>@@ -0,0 +1,113 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/application.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsupclient/gsup_req.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *ctx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void test_gsup_make_response(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      char *source_name = "incoming-source-name";</span><br><span style="color: hsl(120, 100%, 40%);">+ char *destination_name = "preset-destination-name";</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t sm_rp_mr = 23;</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t other_sm_rp_mr = 17;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_gsup_message rx = {</span><br><span style="color: hsl(120, 100%, 40%);">+               .message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST,</span><br><span style="color: hsl(120, 100%, 40%);">+               .imsi = "1234567",</span><br><span style="color: hsl(120, 100%, 40%);">+          .message_class = OSMO_GSUP_MESSAGE_CLASS_SUBSCRIBER_MANAGEMENT,</span><br><span style="color: hsl(120, 100%, 40%);">+               .source_name = (uint8_t*)source_name,</span><br><span style="color: hsl(120, 100%, 40%);">+         .source_name_len = strlen(source_name) + 1,</span><br><span style="color: hsl(120, 100%, 40%);">+           .sm_rp_mr = &sm_rp_mr,</span><br><span style="color: hsl(120, 100%, 40%);">+            .session_id = 42,</span><br><span style="color: hsl(120, 100%, 40%);">+             .session_state = OSMO_GSUP_SESSION_STATE_BEGIN,</span><br><span style="color: hsl(120, 100%, 40%);">+       };</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_gsup_message nonempty = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .message_type = OSMO_GSUP_MSGT_ROUTING_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+         .imsi = "987654321",</span><br><span style="color: hsl(120, 100%, 40%);">+                .message_class = OSMO_GSUP_MESSAGE_CLASS_INTER_MSC,</span><br><span style="color: hsl(120, 100%, 40%);">+           .destination_name = (uint8_t*)destination_name,</span><br><span style="color: hsl(120, 100%, 40%);">+               .destination_name_len = strlen(destination_name) + 1,</span><br><span style="color: hsl(120, 100%, 40%);">+         .sm_rp_mr = &other_sm_rp_mr,</span><br><span style="color: hsl(120, 100%, 40%);">+              .session_id = 11,</span><br><span style="color: hsl(120, 100%, 40%);">+             .session_state = OSMO_GSUP_SESSION_STATE_END,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+    void *name_ctx = talloc_named_const(ctx, 0, __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+        int error;</span><br><span style="color: hsl(120, 100%, 40%);">+    int final;</span><br><span style="color: hsl(120, 100%, 40%);">+    char *nonempty_str;</span><br><span style="color: hsl(120, 100%, 40%);">+   int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("\n%s()\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("rx = %s\n", osmo_gsup_message_to_str_c(name_ctx, &rx));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("\nwriting to an empty struct osmo_gsup_message should populate values as needed:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+       for (error = 0; error <= 1; error++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             for (final = 0; final <= 1; final++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     struct osmo_gsup_message target = {};</span><br><span style="color: hsl(120, 100%, 40%);">+                 printf("- args (error=%d, final=%d)\n", error, final);</span><br><span style="color: hsl(120, 100%, 40%);">+                      rc = osmo_gsup_make_response(&target, &rx, error, final);</span><br><span style="color: hsl(120, 100%, 40%);">+                     printf("  %s\n", osmo_gsup_message_to_str_c(name_ctx, &target));</span><br><span style="color: hsl(120, 100%, 40%);">+                        printf("  rc = %d\n", rc);</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   printf("\nwriting to an already populated struct osmo_gsup_message, should have no effect:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+     nonempty_str = osmo_gsup_message_to_str_c(name_ctx, &nonempty);</span><br><span style="color: hsl(120, 100%, 40%);">+   for (error = 0; error <= 1; error++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             for (final = 0; final <= 1; final++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     struct osmo_gsup_message target = nonempty;</span><br><span style="color: hsl(120, 100%, 40%);">+                   char *result;</span><br><span style="color: hsl(120, 100%, 40%);">+                 printf("- args (error=%d, final=%d)\n", error, final);</span><br><span style="color: hsl(120, 100%, 40%);">+                      rc = osmo_gsup_make_response(&target, &rx, error, final);</span><br><span style="color: hsl(120, 100%, 40%);">+                     result = osmo_gsup_message_to_str_c(name_ctx, &target);</span><br><span style="color: hsl(120, 100%, 40%);">+                   printf("  %s\n", result);</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (strcmp(result, nonempty_str))</span><br><span style="color: hsl(120, 100%, 40%);">+                             printf("  ERROR: expected: %s\n", nonempty_str);</span><br><span style="color: hsl(120, 100%, 40%);">+                    printf("  rc = %d\n", rc);</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct log_info_cat default_categories[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct log_info info = {</span><br><span style="color: hsl(120, 100%, 40%);">+     .cat = default_categories,</span><br><span style="color: hsl(120, 100%, 40%);">+    .num_cat = ARRAY_SIZE(default_categories),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int main(int argc, char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  ctx = talloc_named_const(NULL, 0, "gsup_test");</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_init_logging2(ctx, &info);</span><br><span style="color: hsl(120, 100%, 40%);">+   log_set_print_filename(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        log_set_print_timestamp(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+       log_set_use_color(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+     log_set_print_category(osmo_stderr_target, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      test_gsup_make_response();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  printf("Done.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+  return EXIT_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/gsup/gsup_test.err b/tests/gsup/gsup_test.err</span><br><span>new file mode 100644</span><br><span>index 0000000..e69de29</span><br><span>--- /dev/null</span><br><span>+++ b/tests/gsup/gsup_test.err</span><br><span>diff --git a/tests/gsup/gsup_test.ok b/tests/gsup/gsup_test.ok</span><br><span>new file mode 100644</span><br><span>index 0000000..0899633</span><br><span>--- /dev/null</span><br><span>+++ b/tests/gsup/gsup_test.ok</span><br><span>@@ -0,0 +1,32 @@</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+test_gsup_make_response()</span><br><span style="color: hsl(120, 100%, 40%);">+rx = Subscriber-Management OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST: imsi="1234567" source_name="incoming-source-name\0" session_id=42 session_state=BEGIN sm_rp_mr=23</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+writing to an empty struct osmo_gsup_message should populate values as needed:</span><br><span style="color: hsl(120, 100%, 40%);">+- args (error=0, final=0)</span><br><span style="color: hsl(120, 100%, 40%);">+  Subscriber-Management OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: imsi="1234567" destination_name="incoming-source-name\0" session_id=42 session_state=CONTINUE sm_rp_mr=23</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = 0</span><br><span style="color: hsl(120, 100%, 40%);">+- args (error=0, final=1)</span><br><span style="color: hsl(120, 100%, 40%);">+  Subscriber-Management OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: imsi="1234567" destination_name="incoming-source-name\0" session_id=42 session_state=END sm_rp_mr=23</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = 0</span><br><span style="color: hsl(120, 100%, 40%);">+- args (error=1, final=0)</span><br><span style="color: hsl(120, 100%, 40%);">+  Subscriber-Management OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: imsi="1234567" destination_name="incoming-source-name\0" session_id=42 session_state=CONTINUE sm_rp_mr=23</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = 0</span><br><span style="color: hsl(120, 100%, 40%);">+- args (error=1, final=1)</span><br><span style="color: hsl(120, 100%, 40%);">+  Subscriber-Management OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: imsi="1234567" destination_name="incoming-source-name\0" session_id=42 session_state=END sm_rp_mr=23</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+writing to an already populated struct osmo_gsup_message, should have no effect:</span><br><span style="color: hsl(120, 100%, 40%);">+- args (error=0, final=0)</span><br><span style="color: hsl(120, 100%, 40%);">+  Inter-MSC OSMO_GSUP_MSGT_ROUTING_ERROR: imsi="987654321" destination_name="preset-destination-name\0" session_id=11 session_state=END sm_rp_mr=17</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = 15</span><br><span style="color: hsl(120, 100%, 40%);">+- args (error=0, final=1)</span><br><span style="color: hsl(120, 100%, 40%);">+  Inter-MSC OSMO_GSUP_MSGT_ROUTING_ERROR: imsi="987654321" destination_name="preset-destination-name\0" session_id=11 session_state=END sm_rp_mr=17</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = 15</span><br><span style="color: hsl(120, 100%, 40%);">+- args (error=1, final=0)</span><br><span style="color: hsl(120, 100%, 40%);">+  Inter-MSC OSMO_GSUP_MSGT_ROUTING_ERROR: imsi="987654321" destination_name="preset-destination-name\0" session_id=11 session_state=END sm_rp_mr=17</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = 15</span><br><span style="color: hsl(120, 100%, 40%);">+- args (error=1, final=1)</span><br><span style="color: hsl(120, 100%, 40%);">+  Inter-MSC OSMO_GSUP_MSGT_ROUTING_ERROR: imsi="987654321" destination_name="preset-destination-name\0" session_id=11 session_state=END sm_rp_mr=17</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = 15</span><br><span style="color: hsl(120, 100%, 40%);">+Done.</span><br><span>diff --git a/tests/gsup_server/Makefile.am b/tests/gsup_server/Makefile.am</span><br><span>index e64ac4a..48fda88 100644</span><br><span>--- a/tests/gsup_server/Makefile.am</span><br><span>+++ b/tests/gsup_server/Makefile.am</span><br><span>@@ -31,6 +31,8 @@</span><br><span> gsup_server_test_LDADD = \</span><br><span>   $(top_srcdir)/src/gsup_server.c \</span><br><span>    $(top_srcdir)/src/gsup_router.c \</span><br><span style="color: hsl(120, 100%, 40%);">+     $(top_srcdir)/src/gsupclient/ipa_name.c \</span><br><span style="color: hsl(120, 100%, 40%);">+     $(top_srcdir)/src/gsupclient/gsup_req.c \</span><br><span>    $(LIBOSMOCORE_LIBS) \</span><br><span>        $(LIBOSMOGSM_LIBS) \</span><br><span>         $(LIBOSMOABIS_LIBS) \</span><br><span>diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty</span><br><span>index a752c93..3f31b0d 100644</span><br><span>--- a/tests/test_nodes.vty</span><br><span>+++ b/tests/test_nodes.vty</span><br><span>@@ -148,6 +148,8 @@</span><br><span>  logging level db notice</span><br><span>  logging level auc notice</span><br><span>  logging level ss info</span><br><span style="color: hsl(120, 100%, 40%);">+ logging level mslookup notice</span><br><span style="color: hsl(120, 100%, 40%);">+ logging level lu notice</span><br><span> ...</span><br><span> hlr</span><br><span>  store-imei</span><br><span>diff --git a/tests/testsuite.at b/tests/testsuite.at</span><br><span>index d30b5e9..956ef87 100644</span><br><span>--- a/tests/testsuite.at</span><br><span>+++ b/tests/testsuite.at</span><br><span>@@ -15,6 +15,13 @@</span><br><span> AT_CHECK([$abs_top_builddir/tests/auc/auc_ts_55_205_test_sets], [], [expout], [experr])</span><br><span> AT_CLEANUP</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+AT_SETUP([gsup])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_KEYWORDS([gsup])</span><br><span style="color: hsl(120, 100%, 40%);">+cat $abs_srcdir/gsup/gsup_test.ok > expout</span><br><span style="color: hsl(120, 100%, 40%);">+cat $abs_srcdir/gsup/gsup_test.err > experr</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CHECK([$abs_top_builddir/tests/gsup/gsup_test], [], [expout], [experr])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CLEANUP</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> AT_SETUP([gsup_server])</span><br><span> AT_KEYWORDS([gsup_server])</span><br><span> cat $abs_srcdir/gsup_server/gsup_server_test.ok > expout</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-hlr/+/16205">change 16205</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-hlr/+/16205"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-hlr </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I3a8dff3d4a1cbe10d6ab08257a0138d6b2a082d9 </div>
<div style="display:none"> Gerrit-Change-Number: 16205 </div>
<div style="display:none"> Gerrit-PatchSet: 31 </div>
<div style="display:none"> Gerrit-Owner: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: fixeria <axilirator@gmail.com> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: osmith <osmith@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>