<p>neels <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-smlc/+/20470">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;">initial working osmo-smlc implementation<br><br>The lower level Lb/SCCP interface conn handling is essentially a copy of<br>OsmoMSC's A/SCCP infrastructure (OsmoMSC also connects to multiple BSCs).<br><br>The smlc_subscr is mostly a copy of OsmoBSC's bsc_subscr.<br><br>smlc_loc_req FSM is the SMLC side of OsmoBSC's new lcs_loc_req FSM.<br><br>cell_locations configures geographic coordinates of individual cells.<br><br>Change-Id: I917ba8fc51a1f1150be77ae01e12a7b16a853052<br>---<br>M configure.ac<br>M doc/examples/osmo-smlc/osmo-smlc.cfg<br>M include/osmocom/smlc/Makefile.am<br>A include/osmocom/smlc/cell_locations.h<br>A include/osmocom/smlc/debug.h<br>A include/osmocom/smlc/lb_conn.h<br>A include/osmocom/smlc/lb_peer.h<br>A include/osmocom/smlc/sccp_lb_inst.h<br>M include/osmocom/smlc/smlc_data.h<br>A include/osmocom/smlc/smlc_loc_req.h<br>M include/osmocom/smlc/smlc_sigtran.h<br>A include/osmocom/smlc/smlc_subscr.h<br>A include/osmocom/smlc/smlc_vty.h<br>M src/osmo-smlc/Makefile.am<br>A src/osmo-smlc/cell_locations.c<br>A src/osmo-smlc/lb_conn.c<br>A src/osmo-smlc/lb_peer.c<br>A src/osmo-smlc/sccp_lb_inst.c<br>A src/osmo-smlc/smlc_data.c<br>A src/osmo-smlc/smlc_loc_req.c<br>M src/osmo-smlc/smlc_main.c<br>D src/osmo-smlc/smlc_sigtran.c<br>A src/osmo-smlc/smlc_subscr.c<br>M tests/Makefile.am<br>A tests/cell_locations.vty<br>A tests/osmo-smlc.cfg<br>A tests/smlc_subscr/Makefile.am<br>A tests/smlc_subscr/smlc_subscr_test.c<br>A tests/smlc_subscr/smlc_subscr_test.err<br>A tests/smlc_subscr/smlc_subscr_test.ok<br>M tests/testsuite.at<br>31 files changed, 2,684 insertions(+), 114 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 e9c4ffc..186721b 100644</span><br><span>--- a/configure.ac</span><br><span>+++ b/configure.ac</span><br><span>@@ -212,6 +212,7 @@</span><br><span>     src/osmo-smlc/Makefile</span><br><span>     tests/Makefile</span><br><span>     tests/atlocal</span><br><span style="color: hsl(120, 100%, 40%);">+    tests/smlc_subscr/Makefile</span><br><span>     doc/Makefile</span><br><span>     doc/examples/Makefile</span><br><span>     doc/manuals/Makefile</span><br><span>diff --git a/doc/examples/osmo-smlc/osmo-smlc.cfg b/doc/examples/osmo-smlc/osmo-smlc.cfg</span><br><span>index e69de29..6585d47 100644</span><br><span>--- a/doc/examples/osmo-smlc/osmo-smlc.cfg</span><br><span>+++ b/doc/examples/osmo-smlc/osmo-smlc.cfg</span><br><span>@@ -0,0 +1,3 @@</span><br><span style="color: hsl(120, 100%, 40%);">+cells</span><br><span style="color: hsl(120, 100%, 40%);">+ lac-ci 23 42 lat 12.34567 lon 34.56789</span><br><span style="color: hsl(120, 100%, 40%);">+ cgi 262 42 17 5 lat 12.34765 lon 34.56987</span><br><span>diff --git a/include/osmocom/smlc/Makefile.am b/include/osmocom/smlc/Makefile.am</span><br><span>index 599651d..4933441 100644</span><br><span>--- a/include/osmocom/smlc/Makefile.am</span><br><span>+++ b/include/osmocom/smlc/Makefile.am</span><br><span>@@ -1,4 +1,12 @@</span><br><span> noinst_HEADERS = \</span><br><span style="color: hsl(120, 100%, 40%);">+  cell_locations.h \</span><br><span style="color: hsl(120, 100%, 40%);">+    debug.h \</span><br><span style="color: hsl(120, 100%, 40%);">+     lb_conn.h \</span><br><span style="color: hsl(120, 100%, 40%);">+   lb_peer.h \</span><br><span style="color: hsl(120, 100%, 40%);">+   sccp_lb_inst.h \</span><br><span>     smlc_data.h \</span><br><span style="color: hsl(120, 100%, 40%);">+ smlc_loc_req.h \</span><br><span>     smlc_sigtran.h \</span><br><span style="color: hsl(120, 100%, 40%);">+      smlc_subscr.h \</span><br><span style="color: hsl(120, 100%, 40%);">+       smlc_vty.h \</span><br><span>         $(NULL)</span><br><span>diff --git a/include/osmocom/smlc/cell_locations.h b/include/osmocom/smlc/cell_locations.h</span><br><span>new file mode 100644</span><br><span>index 0000000..33023bb</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/smlc/cell_locations.h</span><br><span>@@ -0,0 +1,49 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* OsmoSMLC cell locations configuration */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></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%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</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, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</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%);">+#include <stdint.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/sigtran/sccp_sap.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_gad;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct cell_location {</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%);">+    struct gsm0808_cell_id cell_id;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! latitude in micro degrees (degrees * 1e6) */</span><br><span style="color: hsl(120, 100%, 40%);">+      int32_t lat;</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! longitude in micro degrees (degrees * 1e6) */</span><br><span style="color: hsl(120, 100%, 40%);">+     int32_t lon;</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 cell_location_from_ta(struct osmo_gad *location_estimate,</span><br><span style="color: hsl(120, 100%, 40%);">+                       const struct gsm0808_cell_id *cell_id,</span><br><span style="color: hsl(120, 100%, 40%);">+                        uint8_t ta);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int cell_locations_vty_init();</span><br><span>diff --git a/include/osmocom/smlc/debug.h b/include/osmocom/smlc/debug.h</span><br><span>new file mode 100644</span><br><span>index 0000000..0c64323</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/smlc/debug.h</span><br><span>@@ -0,0 +1,13 @@</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%);">+#define DEBUG</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Debug Areas of the code */</span><br><span style="color: hsl(120, 100%, 40%);">+enum {</span><br><span style="color: hsl(120, 100%, 40%);">+  DSMLC,</span><br><span style="color: hsl(120, 100%, 40%);">+        DREF,</span><br><span style="color: hsl(120, 100%, 40%);">+ DLB,</span><br><span style="color: hsl(120, 100%, 40%);">+  DLCS,</span><br><span style="color: hsl(120, 100%, 40%);">+ Debug_LastEntry,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span>diff --git a/include/osmocom/smlc/lb_conn.h b/include/osmocom/smlc/lb_conn.h</span><br><span>new file mode 100644</span><br><span>index 0000000..5640780</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/smlc/lb_conn.h</span><br><span>@@ -0,0 +1,55 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+/* SMLC Lb connection implementation */</span><br><span style="color: hsl(120, 100%, 40%);">+</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%);">+#include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_subscr.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct lb_peer;</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fsm_inst;</span><br><span style="color: hsl(120, 100%, 40%);">+struct msgb;</span><br><span style="color: hsl(120, 100%, 40%);">+struct bssmap_le_pdu;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_LB_CONN_SL(CONN, CAT, LEVEL, file, line, FMT, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+    LOGPSRC(CAT, LEVEL, file, line, "Lb-%d %s %s: " FMT, (CONN) ? (CONN)->sccp_conn_id : 0, \</span><br><span style="color: hsl(120, 100%, 40%);">+             ((CONN) && (CONN)->smlc_subscr) ? smlc_subscr_to_str_c(OTC_SELECT, (CONN)->smlc_subscr) : "no-subscr", \</span><br><span style="color: hsl(120, 100%, 40%);">+      (CONN) ? osmo_use_count_to_str_c(OTC_SELECT, &(CONN)->use_count) : "-", \</span><br><span style="color: hsl(120, 100%, 40%);">+            ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_LB_CONN_S(CONN, CAT, LEVEL, FMT, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+       LOG_LB_CONN_SL(CONN, CAT, LEVEL, NULL, 0, FMT, ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_LB_CONN(CONN, LEVEL, FMT, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+    LOG_LB_CONN_S(CONN, DLB, LEVEL, FMT, ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define SMLC_SUBSCR_USE_LB_CONN "Lb-conn"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct lb_conn {</span><br><span style="color: hsl(120, 100%, 40%);">+       struct llist_head entry;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_use_count use_count;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    struct lb_peer *lb_peer;</span><br><span style="color: hsl(120, 100%, 40%);">+      uint32_t sccp_conn_id;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      bool closing;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       struct smlc_subscr *smlc_subscr;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct smlc_loc_req *smlc_loc_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%);">+#define lb_conn_get(lb_conn, use) \</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(osmo_use_count_get_put(&(lb_conn)->use_count, use, 1) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+#define lb_conn_put(lb_conn, use) \</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(osmo_use_count_get_put(&(lb_conn)->use_count, use, -1) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct lb_conn *lb_conn_create_incoming(struct lb_peer *lb_peer, uint32_t sccp_conn_id, const char *use_token);</span><br><span style="color: hsl(120, 100%, 40%);">+struct lb_conn *lb_conn_create_outgoing(struct lb_peer *lb_peer, const char *use_token);</span><br><span style="color: hsl(120, 100%, 40%);">+struct lb_conn *lb_conn_find_by_smlc_subscr(struct smlc_subscr *smlc_subscr, const char *use_token);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lb_conn_msc_role_gone(struct lb_conn *lb_conn, struct osmo_fsm_inst *msc_role);</span><br><span style="color: hsl(120, 100%, 40%);">+void lb_conn_close(struct lb_conn *lb_conn);</span><br><span style="color: hsl(120, 100%, 40%);">+void lb_conn_discard(struct lb_conn *lb_conn);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int lb_conn_rx(struct lb_conn *lb_conn, struct msgb *msg, bool initial);</span><br><span style="color: hsl(120, 100%, 40%);">+int lb_conn_send_bssmap_le(struct lb_conn *lb_conn, const struct bssmap_le_pdu *bssmap_le);</span><br><span>diff --git a/include/osmocom/smlc/lb_peer.h b/include/osmocom/smlc/lb_peer.h</span><br><span>new file mode 100644</span><br><span>index 0000000..b5ac6d6</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/smlc/lb_peer.h</span><br><span>@@ -0,0 +1,67 @@</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/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm0808.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/sigtran/sccp_sap.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/debug.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/lb_conn.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct vlr_subscr;</span><br><span style="color: hsl(120, 100%, 40%);">+struct lb_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+struct neighbor_ident_entry;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_LB_PEER_CAT(LB_PEER, subsys, loglevel, fmt, args ...) \</span><br><span style="color: hsl(120, 100%, 40%);">+      LOGPFSMSL((LB_PEER)? (LB_PEER)->fi : NULL, subsys, loglevel, fmt, ## args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_LB_PEER(LB_PEER, loglevel, fmt, args ...) \</span><br><span style="color: hsl(120, 100%, 40%);">+      LOG_LB_PEER_CAT(LB_PEER, DLB, loglevel, fmt, ## args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct lb_peer {</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 sccp_lb_inst *sli;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_sccp_addr peer_addr;</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 lb_peer_for_each_lb_conn(LB_CONN, LB_PEER) \</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_for_each_entry(LB_CONN, &(LB_PEER)->sli->lb_conns, entry) \</span><br><span style="color: hsl(120, 100%, 40%);">+           if ((LB_CONN)->lb_peer == (LB_PEER))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define lb_peer_for_each_lb_conn_safe(LB_CONN, LB_CONN_NEXT, LB_PEER) \</span><br><span style="color: hsl(120, 100%, 40%);">+    llist_for_each_entry_safe(LB_CONN, LB_CONN_NEXT, &(LB_PEER)->sli->lb_conns, entry) \</span><br><span style="color: hsl(120, 100%, 40%);">+                if ((LB_CONN)->lb_peer == (LB_PEER))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum lb_peer_state {</span><br><span style="color: hsl(120, 100%, 40%);">+       LB_PEER_ST_WAIT_RX_RESET = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ LB_PEER_ST_WAIT_RX_RESET_ACK,</span><br><span style="color: hsl(120, 100%, 40%);">+ LB_PEER_ST_READY,</span><br><span style="color: hsl(120, 100%, 40%);">+     LB_PEER_ST_DISCARDING,</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 lb_peer_event {</span><br><span style="color: hsl(120, 100%, 40%);">+    LB_PEER_EV_MSG_UP_CL = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+     LB_PEER_EV_MSG_UP_CO_INITIAL,</span><br><span style="color: hsl(120, 100%, 40%);">+ LB_PEER_EV_MSG_UP_CO,</span><br><span style="color: hsl(120, 100%, 40%);">+ LB_PEER_EV_MSG_DOWN_CL,</span><br><span style="color: hsl(120, 100%, 40%);">+       LB_PEER_EV_MSG_DOWN_CO_INITIAL,</span><br><span style="color: hsl(120, 100%, 40%);">+       LB_PEER_EV_MSG_DOWN_CO,</span><br><span style="color: hsl(120, 100%, 40%);">+       LB_PEER_EV_RX_RESET,</span><br><span style="color: hsl(120, 100%, 40%);">+  LB_PEER_EV_RX_RESET_ACK,</span><br><span style="color: hsl(120, 100%, 40%);">+      LB_PEER_EV_CONNECTION_SUCCESS,</span><br><span style="color: hsl(120, 100%, 40%);">+        LB_PEER_EV_CONNECTION_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%);">+struct lb_peer_ev_ctx {</span><br><span style="color: hsl(120, 100%, 40%);">+ uint32_t conn_id;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct lb_conn *lb_conn;</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 lb_peer *lb_peer_find_or_create(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr);</span><br><span style="color: hsl(120, 100%, 40%);">+struct lb_peer *lb_peer_find(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int lb_peer_up_l2(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *calling_addr, bool co, uint32_t conn_id,</span><br><span style="color: hsl(120, 100%, 40%);">+                struct msgb *l2);</span><br><span style="color: hsl(120, 100%, 40%);">+void lb_peer_disconnect(struct sccp_lb_inst *sli, uint32_t conn_id);</span><br><span>diff --git a/include/osmocom/smlc/sccp_lb_inst.h b/include/osmocom/smlc/sccp_lb_inst.h</span><br><span>new file mode 100644</span><br><span>index 0000000..525eac0</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/smlc/sccp_lb_inst.h</span><br><span>@@ -0,0 +1,63 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Lb: BSSAP-LE/SCCP */</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 <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+</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/gsm/gsm_utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm0808_utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/sigtran/sccp_sap.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct msgb;</span><br><span style="color: hsl(120, 100%, 40%);">+struct sccp_lb_inst;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_SCCP_LB_CO(sli, peer_addr, conn_id, level, fmt, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+  LOGP(DLB, level, "(Lb-%u%s%s) " fmt, \</span><br><span style="color: hsl(120, 100%, 40%);">+           conn_id, peer_addr ? " from " : "", \</span><br><span style="color: hsl(120, 100%, 40%);">+             peer_addr ? osmo_sccp_inst_addr_name((sli)->sccp, peer_addr) : "", \</span><br><span style="color: hsl(120, 100%, 40%);">+             ## args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_SCCP_LB_CL_CAT(sli, peer_addr, subsys, level, fmt, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+    LOGP(subsys, level, "(Lb%s%s) " fmt, \</span><br><span style="color: hsl(120, 100%, 40%);">+           peer_addr ? " from " : "", \</span><br><span style="color: hsl(120, 100%, 40%);">+      peer_addr ? osmo_sccp_inst_addr_name((sli)->sccp, peer_addr) : "", \</span><br><span style="color: hsl(120, 100%, 40%);">+             ## args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_SCCP_LB_CL(sli, peer_addr, level, fmt, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+        LOG_SCCP_LB_CL_CAT(sli, peer_addr, DLB, level, fmt, ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_SCCP_LB_CAT(sli, subsys, level, fmt, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+    LOG_SCCP_LB_CL_CAT(sli, NULL, subsys, level, fmt, ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_SCCP_LB(sli, level, fmt, args...) \</span><br><span style="color: hsl(120, 100%, 40%);">+  LOG_SCCP_LB_CL(sli, NULL, level, fmt, ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum reset_msg_type {</span><br><span style="color: hsl(120, 100%, 40%);">+        SCCP_LB_MSG_NON_RESET = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+    SCCP_LB_MSG_RESET,</span><br><span style="color: hsl(120, 100%, 40%);">+    SCCP_LB_MSG_RESET_ACK,</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 sccp_lb_inst {</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_sccp_instance *sccp;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_sccp_user *scu;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_sccp_addr local_sccp_addr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      struct llist_head lb_peers;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct llist_head lb_conns;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ void *user_data;</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 sccp_lb_inst *sccp_lb_init(void *talloc_ctx, struct osmo_sccp_instance *sccp, enum osmo_sccp_ssn ssn,</span><br><span style="color: hsl(120, 100%, 40%);">+                            const char *sccp_user_name);</span><br><span style="color: hsl(120, 100%, 40%);">+int sccp_lb_inst_next_conn_id();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int sccp_lb_down_l2_co_initial(struct sccp_lb_inst *sli,</span><br><span style="color: hsl(120, 100%, 40%);">+                         const struct osmo_sccp_addr *called_addr,</span><br><span style="color: hsl(120, 100%, 40%);">+                             uint32_t conn_id, struct msgb *l2);</span><br><span style="color: hsl(120, 100%, 40%);">+int sccp_lb_down_l2_co(struct sccp_lb_inst *sli, uint32_t conn_id, struct msgb *l2);</span><br><span style="color: hsl(120, 100%, 40%);">+int sccp_lb_down_l2_cl(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *called_addr, struct msgb *l2);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int sccp_lb_disconnect(struct sccp_lb_inst *sli, uint32_t conn_id, uint32_t cause);</span><br><span>diff --git a/include/osmocom/smlc/smlc_data.h b/include/osmocom/smlc/smlc_data.h</span><br><span>index 2506fdc..dc77507 100644</span><br><span>--- a/include/osmocom/smlc/smlc_data.h</span><br><span>+++ b/include/osmocom/smlc/smlc_data.h</span><br><span>@@ -8,25 +8,26 @@</span><br><span> </span><br><span> #include <osmocom/ctrl/control_if.h></span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/sigtran/sccp_sap.h></span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_sccp_instance;</span><br><span style="color: hsl(120, 100%, 40%);">+struct sccp_lb_inst;</span><br><span> </span><br><span> struct smlc_state {</span><br><span style="color: hsl(0, 100%, 40%);">-    struct osmo_sccp_user *sccp_user;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_sccp_instance *sccp_inst;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct sccp_lb_inst *lb;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   struct ctrl_handle *ctrl;</span><br><span> </span><br><span>        struct rate_ctr_group *ctrs;</span><br><span>         struct osmo_stat_item_group *statg;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct osmo_tdef *T_defs;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   struct llist_head subscribers;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct llist_head cell_locations;</span><br><span> };</span><br><span> </span><br><span> extern struct smlc_state *g_smlc;</span><br><span style="color: hsl(120, 100%, 40%);">+struct smlc_state *smlc_state_alloc(void *ctx);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-enum {</span><br><span style="color: hsl(0, 100%, 40%);">-     DSMLC,</span><br><span style="color: hsl(0, 100%, 40%);">-  DLB,            /* Lb interface */</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+extern struct osmo_tdef g_smlc_tdefs[];</span><br><span> </span><br><span> int smlc_ctrl_node_lookup(void *data, vector vline, int *node_type,</span><br><span>                       void **node_data, int *i);</span><br><span>@@ -35,3 +36,25 @@</span><br><span>    CTRL_NODE_SMLC = _LAST_CTRL_NODE,</span><br><span>    _LAST_CTRL_NODE_SMLC</span><br><span> };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum {</span><br><span style="color: hsl(120, 100%, 40%);">+  SMLC_CTR_BSSMAP_LE_RX_UDT_RESET,</span><br><span style="color: hsl(120, 100%, 40%);">+      SMLC_CTR_BSSMAP_LE_RX_UDT_RESET_ACK,</span><br><span style="color: hsl(120, 100%, 40%);">+  SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG,</span><br><span style="color: hsl(120, 100%, 40%);">+    SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG,</span><br><span style="color: hsl(120, 100%, 40%);">+    SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_REQUEST,</span><br><span style="color: hsl(120, 100%, 40%);">+   SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_TA_RESPONSE,</span><br><span style="color: hsl(120, 100%, 40%);">+ SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_REJECT,</span><br><span style="color: hsl(120, 100%, 40%);">+      SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_RESET,</span><br><span style="color: hsl(120, 100%, 40%);">+       SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_ABORT,</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     SMLC_CTR_BSSMAP_LE_TX_ERR_INVALID_MSG,</span><br><span style="color: hsl(120, 100%, 40%);">+        SMLC_CTR_BSSMAP_LE_TX_ERR_CONN_NOT_READY,</span><br><span style="color: hsl(120, 100%, 40%);">+     SMLC_CTR_BSSMAP_LE_TX_ERR_SEND,</span><br><span style="color: hsl(120, 100%, 40%);">+       SMLC_CTR_BSSMAP_LE_TX_SUCCESS,</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      SMLC_CTR_BSSMAP_LE_TX_UDT_RESET,</span><br><span style="color: hsl(120, 100%, 40%);">+      SMLC_CTR_BSSMAP_LE_TX_UDT_RESET_ACK,</span><br><span style="color: hsl(120, 100%, 40%);">+  SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_RESPONSE,</span><br><span style="color: hsl(120, 100%, 40%);">+  SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_TA_REQUEST,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span>diff --git a/include/osmocom/smlc/smlc_loc_req.h b/include/osmocom/smlc/smlc_loc_req.h</span><br><span>new file mode 100644</span><br><span>index 0000000..a8aa27e</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/smlc/smlc_loc_req.h</span><br><span>@@ -0,0 +1,65 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Handle LCS BSSMAP-LE Perform Location Request */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></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%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</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, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</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/smlc/debug.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/bssmap_le.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_SMLC_LOC_REQ(LOC_REQ, level, fmt, args...) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+         if (LOC_REQ) \</span><br><span style="color: hsl(120, 100%, 40%);">+                        LOGPFSML(LOC_REQ->fi, level, fmt, ## args); \</span><br><span style="color: hsl(120, 100%, 40%);">+              else \</span><br><span style="color: hsl(120, 100%, 40%);">+                        LOGP(DLCS, level, "LCS Perf Loc Req: " fmt, ## args); \</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%);">+struct smlc_ta_req;</span><br><span style="color: hsl(120, 100%, 40%);">+struct lb_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+struct msgb;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LB_CONN_USE_SMLC_LOC_REQ "smlc_loc_req"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum smlc_loc_req_fsm_event {</span><br><span style="color: hsl(120, 100%, 40%);">+        SMLC_LOC_REQ_EV_RX_TA_RESPONSE,</span><br><span style="color: hsl(120, 100%, 40%);">+       SMLC_LOC_REQ_EV_RX_BSSLAP_RESET,</span><br><span style="color: hsl(120, 100%, 40%);">+      SMLC_LOC_REQ_EV_RX_LE_PERFORM_LOCATION_ABORT,</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 smlc_loc_req {</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 smlc_subscr *smlc_subscr;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct lb_conn *lb_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    struct bssmap_le_perform_loc_req req;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       bool ta_present;</span><br><span style="color: hsl(120, 100%, 40%);">+      uint8_t ta;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct gsm0808_cell_id latest_cell_id;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      struct lcs_cause_ie lcs_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%);">+int smlc_loc_req_rx_bssap_le(struct lb_conn *conn, const struct bssap_le_pdu *bssap_le);</span><br><span>diff --git a/include/osmocom/smlc/smlc_sigtran.h b/include/osmocom/smlc/smlc_sigtran.h</span><br><span>index aeaf2c5..f89f284 100644</span><br><span>--- a/include/osmocom/smlc/smlc_sigtran.h</span><br><span>+++ b/include/osmocom/smlc/smlc_sigtran.h</span><br><span>@@ -1,3 +1,5 @@</span><br><span> #pragma once</span><br><span> </span><br><span> int smlc_sigtran_init(void);</span><br><span style="color: hsl(120, 100%, 40%);">+int smlc_sigtran_send(uint32_t sccp_conn_id, struct msgb *msg);</span><br><span style="color: hsl(120, 100%, 40%);">+int smlc_sigtran_send_udt(uint32_t sccp_conn_id, struct msgb *msg);</span><br><span>diff --git a/include/osmocom/smlc/smlc_subscr.h b/include/osmocom/smlc/smlc_subscr.h</span><br><span>new file mode 100644</span><br><span>index 0000000..fbc4103</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/smlc/smlc_subscr.h</span><br><span>@@ -0,0 +1,30 @@</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/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/core/use_count.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm48.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm0808.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct smlc_subscr {</span><br><span style="color: hsl(120, 100%, 40%);">+     struct llist_head entry;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_use_count use_count;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_mobile_identity imsi;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm0808_cell_id cell_id;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_fsm_inst *loc_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%);">+struct smlc_subscr *smlc_subscr_find_or_create(const struct osmo_mobile_identity *imsi, const char *use_token);</span><br><span style="color: hsl(120, 100%, 40%);">+struct smlc_subscr *smlc_subscr_find(const struct osmo_mobile_identity *imsi, const char *use_token);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int smlc_subscr_to_str_buf(char *buf, size_t buf_len, const struct smlc_subscr *smlc_subscr);</span><br><span style="color: hsl(120, 100%, 40%);">+char *smlc_subscr_to_str_c(void *ctx, const struct smlc_subscr *smlc_subscr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct smlc_subscr *smlc_subscr_find_or_create(const struct osmo_mobile_identity *imsi, const char *use_token);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define smlc_subscr_get(smlc_subscr, use) \</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(osmo_use_count_get_put(&(smlc_subscr)->use_count, use, 1) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+#define smlc_subscr_put(smlc_subscr, use) \</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(osmo_use_count_get_put(&(smlc_subscr)->use_count, use, -1) == 0)</span><br><span>diff --git a/include/osmocom/smlc/smlc_vty.h b/include/osmocom/smlc/smlc_vty.h</span><br><span>new file mode 100644</span><br><span>index 0000000..d5d82f8</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/smlc/smlc_vty.h</span><br><span>@@ -0,0 +1,7 @@</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/vty/command.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum smlc_vty_node {</span><br><span style="color: hsl(120, 100%, 40%);">+        CELLS_NODE = _LAST_OSMOVTY_NODE + 1,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span>diff --git a/src/osmo-smlc/Makefile.am b/src/osmo-smlc/Makefile.am</span><br><span>index 269c23d..37fe042 100644</span><br><span>--- a/src/osmo-smlc/Makefile.am</span><br><span>+++ b/src/osmo-smlc/Makefile.am</span><br><span>@@ -23,9 +23,15 @@</span><br><span>       $(NULL)</span><br><span> </span><br><span> osmo_smlc_SOURCES = \</span><br><span style="color: hsl(120, 100%, 40%);">+  cell_locations.c \</span><br><span style="color: hsl(120, 100%, 40%);">+    lb_conn.c \</span><br><span style="color: hsl(120, 100%, 40%);">+   lb_peer.c \</span><br><span style="color: hsl(120, 100%, 40%);">+   sccp_lb_inst.c \</span><br><span>     smlc_ctrl.c \</span><br><span style="color: hsl(120, 100%, 40%);">+ smlc_data.c \</span><br><span style="color: hsl(120, 100%, 40%);">+ smlc_loc_req.c \</span><br><span>     smlc_main.c \</span><br><span style="color: hsl(0, 100%, 40%);">-   smlc_sigtran.c \</span><br><span style="color: hsl(120, 100%, 40%);">+      smlc_subscr.c \</span><br><span>      $(NULL)</span><br><span> </span><br><span> osmo_smlc_LDADD = \</span><br><span>diff --git a/src/osmo-smlc/cell_locations.c b/src/osmo-smlc/cell_locations.c</span><br><span>new file mode 100644</span><br><span>index 0000000..e563720</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-smlc/cell_locations.c</span><br><span>@@ -0,0 +1,318 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* OsmoSMLC cell locations configuration */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></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%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</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, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</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 <limits.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <inttypes.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></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/gsm/protocol/gsm_08_08.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm0808_utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gad.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_vty.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/cell_locations.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static uint32_t ta_to_m(uint8_t ta)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      return ((uint32_t)ta) * 550;</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 cell_location *cell_location_find(const struct gsm0808_cell_id *cell_id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct cell_location *cell_location;</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_for_each_entry(cell_location, &g_smlc->cell_locations, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (gsm0808_cell_ids_match(&cell_location->cell_id, cell_id, true))</span><br><span style="color: hsl(120, 100%, 40%);">+                    return cell_location;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry(cell_location, &g_smlc->cell_locations, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (gsm0808_cell_ids_match(&cell_location->cell_id, cell_id, false))</span><br><span style="color: hsl(120, 100%, 40%);">+                   return cell_location;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</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%);">+int cell_location_from_ta(struct osmo_gad *location_estimate,</span><br><span style="color: hsl(120, 100%, 40%);">+                        const struct gsm0808_cell_id *cell_id,</span><br><span style="color: hsl(120, 100%, 40%);">+                        uint8_t ta)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      const struct cell_location *cell;</span><br><span style="color: hsl(120, 100%, 40%);">+     cell = cell_location_find(cell_id);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!cell)</span><br><span style="color: hsl(120, 100%, 40%);">+            return -ENOENT;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     *location_estimate = (struct osmo_gad){</span><br><span style="color: hsl(120, 100%, 40%);">+               .type = GAD_TYPE_ELL_POINT_UNC_CIRCLE,</span><br><span style="color: hsl(120, 100%, 40%);">+                .ell_point_unc_circle = {</span><br><span style="color: hsl(120, 100%, 40%);">+                     .lat = cell->lat,</span><br><span style="color: hsl(120, 100%, 40%);">+                  .lon = cell->lon,</span><br><span style="color: hsl(120, 100%, 40%);">+                  .unc = osmo_gad_dec_unc(osmo_gad_enc_unc(ta_to_m(ta) * 1000)),</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 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 struct cell_location *cell_location_find_or_create(const struct gsm0808_cell_id *cell_id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct cell_location *cell_location = cell_location_find(cell_id);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!cell_location) {</span><br><span style="color: hsl(120, 100%, 40%);">+         cell_location = talloc_zero(g_smlc, struct cell_location);</span><br><span style="color: hsl(120, 100%, 40%);">+            OSMO_ASSERT(cell_location);</span><br><span style="color: hsl(120, 100%, 40%);">+           cell_location->cell_id = *cell_id;</span><br><span style="color: hsl(120, 100%, 40%);">+         llist_add_tail(&cell_location->entry, &g_smlc->cell_locations);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     return cell_location;</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 const struct cell_location *cell_location_set(const struct gsm0808_cell_id *cell_id, int32_t lat, int32_t lon)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct cell_location *cell_location = cell_location_find_or_create(cell_id);</span><br><span style="color: hsl(120, 100%, 40%);">+  cell_location->lat = lat;</span><br><span style="color: hsl(120, 100%, 40%);">+  cell_location->lon = lon;</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 int cell_location_remove(const struct gsm0808_cell_id *cell_id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct cell_location *cell_location = cell_location_find(cell_id);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!cell_location)</span><br><span style="color: hsl(120, 100%, 40%);">+           return -ENOENT;</span><br><span style="color: hsl(120, 100%, 40%);">+       llist_del(&cell_location->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+      talloc_free(cell_location);</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%);">+#define LAC_CI_PARAMS "lac-ci <0-65535> <0-65535>"</span><br><span style="color: hsl(120, 100%, 40%);">+#define LAC_CI_DOC "Cell location by LAC and CI\n" "LAC\n" "CI\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define CGI_PARAMS "cgi <0-999> <0-999> <0-65535> <0-65535>"</span><br><span style="color: hsl(120, 100%, 40%);">+#define CGI_DOC "Cell location by Cell-Global ID\n" "MCC\n" "MNC\n" "LAC\n" "CI\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LAT_LON_PARAMS "lat LATITUDE lon LONGITUDE"</span><br><span style="color: hsl(120, 100%, 40%);">+#define LAT_LON_DOC "Global latitute coordinate\n" "Latitude floating-point number, -90.0 (S) to 90.0 (N)\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+                "Global longitude coordinate\n" "Longitude as floating-point number, -180.0 (W) to 180.0 (E)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int vty_parse_lac_ci(struct vty *vty, struct gsm0808_cell_id *dst, const char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        *dst = (struct gsm0808_cell_id){</span><br><span style="color: hsl(120, 100%, 40%);">+              .id_discr = CELL_IDENT_LAC_AND_CI,</span><br><span style="color: hsl(120, 100%, 40%);">+            .id.lac_and_ci = {</span><br><span style="color: hsl(120, 100%, 40%);">+                    .lac = atoi(argv[0]),</span><br><span style="color: hsl(120, 100%, 40%);">+                 .ci = atoi(argv[1]),</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 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 int vty_parse_cgi(struct vty *vty, struct gsm0808_cell_id *dst, const char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  *dst = (struct gsm0808_cell_id){</span><br><span style="color: hsl(120, 100%, 40%);">+              .id_discr = CELL_IDENT_WHOLE_GLOBAL,</span><br><span style="color: hsl(120, 100%, 40%);">+  };</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_cell_global_id *cgi = &dst->id.global;</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *mcc = argv[0];</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *mnc = argv[1];</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *lac = argv[2];</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *ci = argv[3];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (osmo_mcc_from_str(mcc, &cgi->lai.plmn.mcc)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              vty_out(vty, "%% Error decoding MCC: %s%s", mcc, VTY_NEWLINE);</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%);">+   if (osmo_mnc_from_str(mnc, &cgi->lai.plmn.mnc, &cgi->lai.plmn.mnc_3_digits)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          vty_out(vty, "%% Error decoding MNC: %s%s", mnc, VTY_NEWLINE);</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%);">+   cgi->lai.lac = atoi(lac);</span><br><span style="color: hsl(120, 100%, 40%);">+  cgi->cell_identity = atoi(ci);</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 int vty_parse_location(struct vty *vty, const struct gsm0808_cell_id *cell_id, const char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *lat_str = argv[0];</span><br><span style="color: hsl(120, 100%, 40%);">+        const char *lon_str = argv[1];</span><br><span style="color: hsl(120, 100%, 40%);">+        int64_t val;</span><br><span style="color: hsl(120, 100%, 40%);">+  int32_t lat, lon;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (osmo_float_str_to_int(&val, lat_str, 6)</span><br><span style="color: hsl(120, 100%, 40%);">+           || val < -90000000 || val > 90000000) {</span><br><span style="color: hsl(120, 100%, 40%);">+             vty_out(vty, "%% Invalid latitude: '%s'%s", lat_str, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+          return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     lat = val;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (osmo_float_str_to_int(&val, lon_str, 6)</span><br><span style="color: hsl(120, 100%, 40%);">+           || val < -180000000 || val > 180000000) {</span><br><span style="color: hsl(120, 100%, 40%);">+           vty_out(vty, "%% Invalid longitude: '%s'%s", lon_str, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+         return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     lon = val;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (cell_location_set(cell_id, lat, lon)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           vty_out(vty, "%% Failed to add cell location%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+              return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     return CMD_SUCCESS;</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%);">+DEFUN(cfg_cells, cfg_cells_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "cells",</span><br><span style="color: hsl(120, 100%, 40%);">+      "Configure cell locations\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ vty->node = CELLS_NODE;</span><br><span style="color: hsl(120, 100%, 40%);">+    return CMD_SUCCESS;</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%);">+DEFUN(cfg_cells_lac_ci, cfg_cells_lac_ci_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      LAC_CI_PARAMS " " LAT_LON_PARAMS,</span><br><span style="color: hsl(120, 100%, 40%);">+      LAC_CI_DOC LAT_LON_DOC)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gsm0808_cell_id cell_id;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (vty_parse_lac_ci(vty, &cell_id, argv))</span><br><span style="color: hsl(120, 100%, 40%);">+                return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return vty_parse_location(vty, &cell_id, argv + 2);</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%);">+DEFUN(cfg_cells_no_lac_ci, cfg_cells_no_lac_ci_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "no " LAC_CI_PARAMS,</span><br><span style="color: hsl(120, 100%, 40%);">+      NO_STR "Remove " LAC_CI_DOC)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gsm0808_cell_id cell_id;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (vty_parse_lac_ci(vty, &cell_id, argv))</span><br><span style="color: hsl(120, 100%, 40%);">+                return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (cell_location_remove(&cell_id)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             vty_out(vty, "%% cannot remove, no such entry%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+             return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     return CMD_SUCCESS;</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%);">+DEFUN(cfg_cells_cgi, cfg_cells_cgi_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      CGI_PARAMS " " LAT_LON_PARAMS,</span><br><span style="color: hsl(120, 100%, 40%);">+      CGI_DOC LAT_LON_DOC)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gsm0808_cell_id cell_id;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (vty_parse_cgi(vty, &cell_id, argv))</span><br><span style="color: hsl(120, 100%, 40%);">+           return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return vty_parse_location(vty, &cell_id, argv + 4);</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%);">+DEFUN(cfg_cells_no_cgi, cfg_cells_no_cgi_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "no " CGI_PARAMS,</span><br><span style="color: hsl(120, 100%, 40%);">+      NO_STR "Remove " CGI_DOC)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gsm0808_cell_id cell_id;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (vty_parse_cgi(vty, &cell_id, argv))</span><br><span style="color: hsl(120, 100%, 40%);">+           return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (cell_location_remove(&cell_id)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             vty_out(vty, "%% cannot remove, no such entry%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+             return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     return CMD_SUCCESS;</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 above are omnidirectional cells. If we add configuration sector antennae, it would add arguments to the above,</span><br><span style="color: hsl(120, 100%, 40%);">+ * something like this:</span><br><span style="color: hsl(120, 100%, 40%);">+ *  cgi 001 01 23 42 lat 23.23 lon 42.42 arc 270 30</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 cmd_node cells_node = {</span><br><span style="color: hsl(120, 100%, 40%);">+  CELLS_NODE,</span><br><span style="color: hsl(120, 100%, 40%);">+   "%s(config-cells)# ",</span><br><span style="color: hsl(120, 100%, 40%);">+       1,</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 config_write_cells(struct vty *vty)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct cell_location *cell;</span><br><span style="color: hsl(120, 100%, 40%);">+   const struct osmo_cell_global_id *cgi;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (llist_empty(&g_smlc->cell_locations))</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%);">+   vty_out(vty, "cells%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry(cell, &g_smlc->cell_locations, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+           switch (cell->cell_id.id_discr) {</span><br><span style="color: hsl(120, 100%, 40%);">+          case CELL_IDENT_LAC_AND_CI:</span><br><span style="color: hsl(120, 100%, 40%);">+                   vty_out(vty, " lac-ci %u %u", cell->cell_id.id.lac_and_ci.lac, cell->cell_id.id.lac_and_ci.ci);</span><br><span style="color: hsl(120, 100%, 40%);">+                       break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case CELL_IDENT_WHOLE_GLOBAL:</span><br><span style="color: hsl(120, 100%, 40%);">+                 cgi = &cell->cell_id.id.global;</span><br><span style="color: hsl(120, 100%, 40%);">+                        vty_out(vty, " cgi %s %s %u %u",</span><br><span style="color: hsl(120, 100%, 40%);">+                            osmo_mcc_name(cgi->lai.plmn.mcc),</span><br><span style="color: hsl(120, 100%, 40%);">+                          osmo_mnc_name(cgi->lai.plmn.mnc, cgi->lai.plmn.mnc_3_digits),</span><br><span style="color: hsl(120, 100%, 40%);">+                           cgi->lai.lac, cgi->cell_identity);</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%);">+                      vty_out(vty, " %% [unsupported cell id type: %d]",</span><br><span style="color: hsl(120, 100%, 40%);">+                          cell->cell_id.id_discr);</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%);">+           vty_out(vty, " lat %s lon %s%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                    osmo_int_to_float_str_c(OTC_SELECT, cell->lat, 6),</span><br><span style="color: hsl(120, 100%, 40%);">+                 osmo_int_to_float_str_c(OTC_SELECT, cell->lon, 6),</span><br><span style="color: hsl(120, 100%, 40%);">+                 VTY_NEWLINE);</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 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%);">+DEFUN(ve_show_cells, ve_show_cells_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+      "show cells",</span><br><span style="color: hsl(120, 100%, 40%);">+      SHOW_STR "Show configured cell locations\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       if (llist_empty(&g_smlc->cell_locations)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            vty_out(vty, "%% No cell locations are configured%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+         return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     config_write_cells(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+      return CMD_SUCCESS;</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 cell_locations_vty_init()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    install_element(CONFIG_NODE, &cfg_cells_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+     install_node(&cells_node, config_write_cells);</span><br><span style="color: hsl(120, 100%, 40%);">+    install_element(CELLS_NODE, &cfg_cells_lac_ci_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+       install_element(CELLS_NODE, &cfg_cells_no_lac_ci_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+    install_element(CELLS_NODE, &cfg_cells_cgi_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+  install_element(CELLS_NODE, &cfg_cells_no_cgi_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+       install_element_ve(&ve_show_cells_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/osmo-smlc/lb_conn.c b/src/osmo-smlc/lb_conn.c</span><br><span>new file mode 100644</span><br><span>index 0000000..7e79de0</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-smlc/lb_conn.c</span><br><span>@@ -0,0 +1,198 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* SMLC Lb connection implementation */</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%);">+ * (C) 2020 by sysmocom s.m.f.c. <info@sysmocom.de></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%);">+ * Author: Neels Hofmeyr</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 <errno.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/fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/signal.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/bssmap_le.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/debug.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/sccp_lb_inst.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/lb_peer.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/lb_conn.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_loc_req.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int lb_conn_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct lb_conn *lb_conn = e->use_count->talloc_object;</span><br><span style="color: hsl(120, 100%, 40%);">+  int32_t total;</span><br><span style="color: hsl(120, 100%, 40%);">+        int level;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!e->use)</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%);">+     total = osmo_use_count_total(&lb_conn->use_count);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (total == 0</span><br><span style="color: hsl(120, 100%, 40%);">+            || (total == 1 && old_use_count == 0 && e->count == 1))</span><br><span style="color: hsl(120, 100%, 40%);">+                level = LOGL_INFO;</span><br><span style="color: hsl(120, 100%, 40%);">+    else</span><br><span style="color: hsl(120, 100%, 40%);">+          level = LOGL_DEBUG;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ LOG_LB_CONN_SL(lb_conn, DREF, level, file, line, "%s %s: now used by %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+         (e->count - old_use_count) > 0? "+" : "-", e->use,</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_use_count_to_str_c(OTC_SELECT, &lb_conn->use_count));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (e->count < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+               return -ERANGE;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (total == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+               lb_conn_close(lb_conn);</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 struct lb_conn *lb_conn_alloc(struct lb_peer *lb_peer, uint32_t sccp_conn_id, const char *use_token)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct lb_conn *lb_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    lb_conn = talloc(lb_peer, struct lb_conn);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(lb_conn);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       *lb_conn = (struct lb_conn){</span><br><span style="color: hsl(120, 100%, 40%);">+          .lb_peer = lb_peer,</span><br><span style="color: hsl(120, 100%, 40%);">+           .sccp_conn_id = sccp_conn_id,</span><br><span style="color: hsl(120, 100%, 40%);">+         .use_count = {</span><br><span style="color: hsl(120, 100%, 40%);">+                        .talloc_object = lb_conn,</span><br><span style="color: hsl(120, 100%, 40%);">+                     .use_cb = lb_conn_use_cb,</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%);">+  llist_add(&lb_conn->entry, &lb_peer->sli->lb_conns);</span><br><span style="color: hsl(120, 100%, 40%);">+ lb_conn_get(lb_conn, use_token);</span><br><span style="color: hsl(120, 100%, 40%);">+      return lb_conn;</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 lb_conn *lb_conn_create_incoming(struct lb_peer *lb_peer, uint32_t sccp_conn_id, const char *use_token)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       LOG_LB_PEER(lb_peer, LOGL_DEBUG, "Incoming lb_conn id: %u\n", sccp_conn_id);</span><br><span style="color: hsl(120, 100%, 40%);">+        return lb_conn_alloc(lb_peer, sccp_conn_id, use_token);</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 lb_conn *lb_conn_create_outgoing(struct lb_peer *lb_peer, const char *use_token)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int new_conn_id = sccp_lb_inst_next_conn_id();</span><br><span style="color: hsl(120, 100%, 40%);">+        if (new_conn_id < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  LOG_LB_PEER(lb_peer, LOGL_DEBUG, "Outgoing lb_conn id: %u\n", new_conn_id);</span><br><span style="color: hsl(120, 100%, 40%);">+ return lb_conn_alloc(lb_peer, new_conn_id, use_token);</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 lb_conn *lb_conn_find_by_smlc_subscr(struct smlc_subscr *smlc_subscr, const char *use_token)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct lb_conn *lb_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+      llist_for_each_entry(lb_conn, &g_smlc->lb->lb_conns, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+               if (lb_conn->smlc_subscr == smlc_subscr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 lb_conn_get(lb_conn, use_token);</span><br><span style="color: hsl(120, 100%, 40%);">+                      return lb_conn;</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 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%);">+int lb_conn_down_l2_co(struct lb_conn *lb_conn, struct msgb *l3, bool initial)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct lb_peer_ev_ctx co = {</span><br><span style="color: hsl(120, 100%, 40%);">+          .conn_id = lb_conn->sccp_conn_id,</span><br><span style="color: hsl(120, 100%, 40%);">+          .lb_conn = lb_conn,</span><br><span style="color: hsl(120, 100%, 40%);">+           .msg = l3,</span><br><span style="color: hsl(120, 100%, 40%);">+    };</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!lb_conn->lb_peer)</span><br><span style="color: hsl(120, 100%, 40%);">+             return -EIO;</span><br><span style="color: hsl(120, 100%, 40%);">+  return osmo_fsm_inst_dispatch(lb_conn->lb_peer->fi,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   initial ? LB_PEER_EV_MSG_DOWN_CO_INITIAL : LB_PEER_EV_MSG_DOWN_CO,</span><br><span style="color: hsl(120, 100%, 40%);">+                                    &co);</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 lb_conn_rx(struct lb_conn *lb_conn, struct msgb *msg, bool initial)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct bssap_le_pdu bssap_le;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_bssap_le_err *err;</span><br><span style="color: hsl(120, 100%, 40%);">+        if (osmo_bssap_le_dec(&bssap_le, &err, msg, msg)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOG_LB_CONN(lb_conn, LOGL_ERROR, "Rx BSSAP-LE with error: %s\n", err->logmsg);</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%);">+   return smlc_loc_req_rx_bssap_le(lb_conn, &bssap_le);</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 lb_conn_send_bssmap_le(struct lb_conn *lb_conn, const struct bssmap_le_pdu *bssmap_le)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct msgb *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+     int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct bssap_le_pdu bssap_le = {</span><br><span style="color: hsl(120, 100%, 40%);">+              .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,</span><br><span style="color: hsl(120, 100%, 40%);">+                .bssmap_le = *bssmap_le,</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_bssap_le_enc(&bssap_le);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOG_LB_CONN(lb_conn, LOGL_ERROR, "Unable to encode %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le));</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%);">+     rc = lb_conn_down_l2_co(lb_conn, msg, false);</span><br><span style="color: hsl(120, 100%, 40%);">+ msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (rc)</span><br><span style="color: hsl(120, 100%, 40%);">+               LOG_LB_CONN(lb_conn, LOGL_ERROR, "Unable to send %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                         osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le));</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%);">+/* Regularly close the lb_conn */</span><br><span style="color: hsl(120, 100%, 40%);">+void lb_conn_close(struct lb_conn *lb_conn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!lb_conn)</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+       if (lb_conn->closing)</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+       lb_conn->closing = true;</span><br><span style="color: hsl(120, 100%, 40%);">+   LOG_LB_PEER(lb_conn->lb_peer, LOGL_DEBUG, "Closing lb_conn\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (lb_conn->lb_peer) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* Todo: pass a useful SCCP cause? */</span><br><span style="color: hsl(120, 100%, 40%);">+         sccp_lb_disconnect(lb_conn->lb_peer->sli, lb_conn->sccp_conn_id, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+         lb_conn->lb_peer = 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 (lb_conn->smlc_loc_req)</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_fsm_inst_term(lb_conn->smlc_loc_req->fi, OSMO_FSM_TERM_REGULAR, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (lb_conn->smlc_subscr)</span><br><span style="color: hsl(120, 100%, 40%);">+          smlc_subscr_put(lb_conn->smlc_subscr, SMLC_SUBSCR_USE_LB_CONN);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_del(&lb_conn->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+    talloc_free(lb_conn);</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 lb_conn_close() but without sending any SCCP messages (e.g. after RESET) */</span><br><span style="color: hsl(120, 100%, 40%);">+void lb_conn_discard(struct lb_conn *lb_conn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!lb_conn)</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Make sure to drop dead and don't dispatch things like DISCONNECT requests on SCCP. */</span><br><span style="color: hsl(120, 100%, 40%);">+  lb_conn->lb_peer = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+   lb_conn_close(lb_conn);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/osmo-smlc/lb_peer.c b/src/osmo-smlc/lb_peer.c</span><br><span>new file mode 100644</span><br><span>index 0000000..986ab44</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-smlc/lb_peer.c</span><br><span>@@ -0,0 +1,495 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de></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%);">+ * SPDX-License-Identifier: AGPL-3.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr</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%);">+#include <osmocom/core/linuxlist.h></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/fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/bssmap_le.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/sigtran/sccp_helpers.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/sccp_lb_inst.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/lb_peer.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_fsm lb_peer_fsm;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static __attribute__((constructor)) void lb_peer_init()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT( osmo_fsm_register(&lb_peer_fsm) == 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 struct lb_peer *lb_peer_alloc(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct lb_peer *lbp;</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%);">+   fi = osmo_fsm_inst_alloc(&lb_peer_fsm, sli, NULL, LOGL_DEBUG, NULL);</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%);">+    osmo_fsm_inst_update_id(fi, osmo_sccp_addr_to_id_c(OTC_SELECT, osmo_sccp_get_ss7(sli->sccp), peer_addr));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        lbp = talloc_zero(fi, struct lb_peer);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(lbp);</span><br><span style="color: hsl(120, 100%, 40%);">+     *lbp = (struct lb_peer){</span><br><span style="color: hsl(120, 100%, 40%);">+              .fi = fi,</span><br><span style="color: hsl(120, 100%, 40%);">+             .sli = sli,</span><br><span style="color: hsl(120, 100%, 40%);">+           .peer_addr = *peer_addr,</span><br><span style="color: hsl(120, 100%, 40%);">+      };</span><br><span style="color: hsl(120, 100%, 40%);">+    fi->priv = lbp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_add(&lbp->entry, &sli->lb_peers);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return lbp;</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 lb_peer *lb_peer_find_or_create(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct lb_peer *lbp = lb_peer_find(sli, peer_addr);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (lbp)</span><br><span style="color: hsl(120, 100%, 40%);">+              return lbp;</span><br><span style="color: hsl(120, 100%, 40%);">+   return lb_peer_alloc(sli, peer_addr);</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 lb_peer *lb_peer_find(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct lb_peer *lbp;</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_for_each_entry(lbp, &sli->lb_peers, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (osmo_sccp_addr_ri_cmp(peer_addr, &lbp->peer_addr))</span><br><span style="color: hsl(120, 100%, 40%);">+                 continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             return lbp;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</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%);">+static const struct osmo_tdef_state_timeout lb_peer_fsm_timeouts[32] = {</span><br><span style="color: hsl(120, 100%, 40%);">+   [LB_PEER_ST_WAIT_RX_RESET_ACK] = { .T = -13 },</span><br><span style="color: hsl(120, 100%, 40%);">+        [LB_PEER_ST_DISCARDING] = { .T = -14 },</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 lb_peer_state_chg(LB_PEER, NEXT_STATE) \</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_tdef_fsm_inst_state_chg((LB_PEER)->fi, NEXT_STATE, lb_peer_fsm_timeouts, g_smlc_tdefs, 5)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lb_peer_discard_all_conns(struct lb_peer *lbp)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct lb_conn *lb_conn, *next;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     lb_peer_for_each_lb_conn_safe(lb_conn, next, lbp) {</span><br><span style="color: hsl(120, 100%, 40%);">+           lb_conn_discard(lb_conn);</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%);">+/* Drop all SCCP connections for this lb_peer, respond with RESET ACKNOWLEDGE and move to READY state. */</span><br><span style="color: hsl(120, 100%, 40%);">+static void lb_peer_rx_reset(struct lb_peer *lbp, struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct msgb *resp;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct bssap_le_pdu reset_ack = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,</span><br><span style="color: hsl(120, 100%, 40%);">+                .bssmap_le = {</span><br><span style="color: hsl(120, 100%, 40%);">+                        .msg_type = BSSMAP_LE_MSGT_RESET_ACK,</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%);">+  lb_peer_discard_all_conns(lbp);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     resp = osmo_bssap_le_enc(&reset_ack);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!resp) {</span><br><span style="color: hsl(120, 100%, 40%);">+          LOG_LB_PEER(lbp, LOGL_ERROR, "Failed to compose RESET ACKNOWLEDGE message\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);</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 (sccp_lb_down_l2_cl(lbp->sli, &lbp->peer_addr, resp)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          LOG_LB_PEER(lbp, LOGL_ERROR, "Failed to send RESET ACKNOWLEDGE message\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);</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%);">+   LOG_LB_PEER(lbp, LOGL_INFO, "Sent RESET ACKNOWLEDGE\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* sccp_lb_down_l2_cl() doesn't free msgb */</span><br><span style="color: hsl(120, 100%, 40%);">+      msgb_free(resp);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    lb_peer_state_chg(lbp, LB_PEER_ST_READY);</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 lb_peer_rx_reset_ack(struct lb_peer *lbp, struct msgb* msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    lb_peer_state_chg(lbp, LB_PEER_ST_READY);</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 lb_peer_reset(struct lb_peer *lbp)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct bssap_le_pdu reset = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,</span><br><span style="color: hsl(120, 100%, 40%);">+                .bssmap_le = {</span><br><span style="color: hsl(120, 100%, 40%);">+                        .msg_type = BSSMAP_LE_MSGT_RESET,</span><br><span style="color: hsl(120, 100%, 40%);">+                     .reset = GSM0808_CAUSE_EQUIPMENT_FAILURE,</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 msgb *msg;</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%);">+     lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET_ACK);</span><br><span style="color: hsl(120, 100%, 40%);">+ lb_peer_discard_all_conns(lbp);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     msg = osmo_bssap_le_enc(&reset);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOG_LB_PEER(lbp, LOGL_ERROR, "Failed to compose RESET message\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);</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 = sccp_lb_down_l2_cl(lbp->sli, &lbp->peer_addr, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+    msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (rc) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOG_LB_PEER(lbp, LOGL_ERROR, "Failed to send RESET message\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);</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%);">+void lb_peer_allstate_action(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 lb_peer *lbp = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct lb_peer_ev_ctx *ctx = data;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct msgb *msg = ctx->msg;</span><br><span style="color: hsl(120, 100%, 40%);">+       enum bssmap_le_msgt msg_type;</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 LB_PEER_EV_MSG_UP_CL:</span><br><span style="color: hsl(120, 100%, 40%);">+            msg_type = osmo_bssmap_le_msgt(msgb_l2(msg), msgb_l2len(msg));</span><br><span style="color: hsl(120, 100%, 40%);">+                switch (msg_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+           case BSSMAP_LE_MSGT_RESET:</span><br><span style="color: hsl(120, 100%, 40%);">+                    osmo_fsm_inst_dispatch(fi, LB_PEER_EV_RX_RESET, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+                 return;</span><br><span style="color: hsl(120, 100%, 40%);">+               case BSSMAP_LE_MSGT_RESET_ACK:</span><br><span style="color: hsl(120, 100%, 40%);">+                        osmo_fsm_inst_dispatch(fi, LB_PEER_EV_RX_RESET_ACK, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return;</span><br><span style="color: hsl(120, 100%, 40%);">+               default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled ConnectionLess message received: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                 osmo_bssmap_le_msgt_name(msg_type));</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%);">+   default:</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&lb_peer_fsm, event));</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lb_peer_st_wait_rx_reset(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 lb_peer *lbp = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct lb_peer_ev_ctx *ctx;</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%);">+   switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LB_PEER_EV_MSG_UP_CO:</span><br><span style="color: hsl(120, 100%, 40%);">+    case LB_PEER_EV_MSG_UP_CO_INITIAL:</span><br><span style="color: hsl(120, 100%, 40%);">+            ctx = data;</span><br><span style="color: hsl(120, 100%, 40%);">+           OSMO_ASSERT(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+             LOG_LB_PEER(lbp, LOGL_ERROR, "Receiving CO message on Lb peer that has not done a proper RESET yet."</span><br><span style="color: hsl(120, 100%, 40%);">+                             " Disconnecting on incoming message, sending RESET to Lb peer.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            /* No valid RESET procedure has happened here yet. Usually, we're expecting the Lb peer (BSC,</span><br><span style="color: hsl(120, 100%, 40%);">+              * RNC) to first send a RESET message before sending Connection Oriented messages. So if we're</span><br><span style="color: hsl(120, 100%, 40%);">+             * getting a CO message, likely we've just restarted or something. Send a RESET to the peer. */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         lb_peer_disconnect(lbp->sli, ctx->conn_id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           lb_peer_reset(lbp);</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%);">+     case LB_PEER_EV_RX_RESET:</span><br><span style="color: hsl(120, 100%, 40%);">+             msg = (struct msgb*)data;</span><br><span style="color: hsl(120, 100%, 40%);">+             lb_peer_rx_reset(lbp, 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%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&lb_peer_fsm, event));</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lb_peer_st_wait_rx_reset_ack(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 lb_peer *lbp = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct lb_peer_ev_ctx *ctx;</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%);">+   switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LB_PEER_EV_RX_RESET_ACK:</span><br><span style="color: hsl(120, 100%, 40%);">+         msg = (struct msgb*)data;</span><br><span style="color: hsl(120, 100%, 40%);">+             lb_peer_rx_reset_ack(lbp, 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%);">+     case LB_PEER_EV_MSG_UP_CO:</span><br><span style="color: hsl(120, 100%, 40%);">+    case LB_PEER_EV_MSG_UP_CO_INITIAL:</span><br><span style="color: hsl(120, 100%, 40%);">+            ctx = data;</span><br><span style="color: hsl(120, 100%, 40%);">+           OSMO_ASSERT(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+             LOG_LB_PEER(lbp, LOGL_ERROR, "Receiving CO message on Lb peer that has not done a proper RESET yet."</span><br><span style="color: hsl(120, 100%, 40%);">+                             " Disconnecting on incoming message, sending RESET to Lb peer.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            sccp_lb_disconnect(lbp->sli, ctx->conn_id, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+          /* No valid RESET procedure has happened here yet. */</span><br><span style="color: hsl(120, 100%, 40%);">+         lb_peer_reset(lbp);</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%);">+     case LB_PEER_EV_RX_RESET:</span><br><span style="color: hsl(120, 100%, 40%);">+             msg = (struct msgb*)data;</span><br><span style="color: hsl(120, 100%, 40%);">+             lb_peer_rx_reset(lbp, 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%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&lb_peer_fsm, event));</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lb_peer_st_ready(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 lb_peer *lbp = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct lb_peer_ev_ctx *ctx;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct lb_conn *lb_conn;</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%);">+   switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LB_PEER_EV_MSG_UP_CO_INITIAL:</span><br><span style="color: hsl(120, 100%, 40%);">+            ctx = data;</span><br><span style="color: hsl(120, 100%, 40%);">+           OSMO_ASSERT(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+             OSMO_ASSERT(!ctx->lb_conn);</span><br><span style="color: hsl(120, 100%, 40%);">+                OSMO_ASSERT(ctx->msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           lb_conn = lb_conn_create_incoming(lbp, ctx->conn_id, __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!lb_conn) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       LOG_LB_PEER(lbp, LOGL_ERROR, "Cannot allocate lb_conn\n");</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%);">+           lb_conn_rx(lb_conn, ctx->msg, true);</span><br><span style="color: hsl(120, 100%, 40%);">+               lb_conn_put(lb_conn, __func__);</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%);">+     case LB_PEER_EV_MSG_UP_CO:</span><br><span style="color: hsl(120, 100%, 40%);">+            ctx = data;</span><br><span style="color: hsl(120, 100%, 40%);">+           OSMO_ASSERT(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+             OSMO_ASSERT(ctx->lb_conn);</span><br><span style="color: hsl(120, 100%, 40%);">+         OSMO_ASSERT(ctx->msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           lb_conn_rx(ctx->lb_conn, ctx->msg, 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%);">+     case LB_PEER_EV_MSG_DOWN_CO_INITIAL:</span><br><span style="color: hsl(120, 100%, 40%);">+          ctx = data;</span><br><span style="color: hsl(120, 100%, 40%);">+           OSMO_ASSERT(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+             OSMO_ASSERT(ctx->msg);</span><br><span style="color: hsl(120, 100%, 40%);">+             sccp_lb_down_l2_co_initial(lbp->sli, &lbp->peer_addr, ctx->conn_id, ctx->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%);">+     case LB_PEER_EV_MSG_DOWN_CO:</span><br><span style="color: hsl(120, 100%, 40%);">+          ctx = data;</span><br><span style="color: hsl(120, 100%, 40%);">+           OSMO_ASSERT(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+             OSMO_ASSERT(ctx->msg);</span><br><span style="color: hsl(120, 100%, 40%);">+             sccp_lb_down_l2_co(lbp->sli, ctx->conn_id, ctx->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%);">+     case LB_PEER_EV_MSG_DOWN_CL:</span><br><span style="color: hsl(120, 100%, 40%);">+          OSMO_ASSERT(data);</span><br><span style="color: hsl(120, 100%, 40%);">+            sccp_lb_down_l2_cl(lbp->sli, &lbp->peer_addr, (struct msgb*)data);</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%);">+     case LB_PEER_EV_RX_RESET:</span><br><span style="color: hsl(120, 100%, 40%);">+             msg = (struct msgb*)data;</span><br><span style="color: hsl(120, 100%, 40%);">+             lb_peer_rx_reset(lbp, 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%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&lb_peer_fsm, event));</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int lb_peer_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 lb_peer *lbp = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+    lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);</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%);">+void lb_peer_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 lb_peer *lbp = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+    lb_peer_discard_all_conns(lbp);</span><br><span style="color: hsl(120, 100%, 40%);">+       llist_del(&lbp->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 const struct value_string lb_peer_fsm_event_names[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_VALUE_STRING(LB_PEER_EV_MSG_UP_CL),</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_VALUE_STRING(LB_PEER_EV_MSG_UP_CO_INITIAL),</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_VALUE_STRING(LB_PEER_EV_MSG_UP_CO),</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_VALUE_STRING(LB_PEER_EV_MSG_DOWN_CL),</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_VALUE_STRING(LB_PEER_EV_MSG_DOWN_CO_INITIAL),</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_VALUE_STRING(LB_PEER_EV_MSG_DOWN_CO),</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_VALUE_STRING(LB_PEER_EV_RX_RESET),</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_VALUE_STRING(LB_PEER_EV_RX_RESET_ACK),</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_VALUE_STRING(LB_PEER_EV_CONNECTION_SUCCESS),</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_VALUE_STRING(LB_PEER_EV_CONNECTION_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 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 lb_peer_fsm_states[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+       [LB_PEER_ST_WAIT_RX_RESET] = {</span><br><span style="color: hsl(120, 100%, 40%);">+                .name = "WAIT_RX_RESET",</span><br><span style="color: hsl(120, 100%, 40%);">+            .action = lb_peer_st_wait_rx_reset,</span><br><span style="color: hsl(120, 100%, 40%);">+           .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LB_PEER_EV_RX_RESET)</span><br><span style="color: hsl(120, 100%, 40%);">+                      | S(LB_PEER_EV_MSG_UP_CO_INITIAL)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LB_PEER_EV_MSG_UP_CO)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LB_PEER_EV_CONNECTION_TIMEOUT)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LB_PEER_ST_WAIT_RX_RESET)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LB_PEER_ST_WAIT_RX_RESET_ACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LB_PEER_ST_READY)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LB_PEER_ST_DISCARDING)</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%);">+    [LB_PEER_ST_WAIT_RX_RESET_ACK] = {</span><br><span style="color: hsl(120, 100%, 40%);">+            .name = "WAIT_RX_RESET_ACK",</span><br><span style="color: hsl(120, 100%, 40%);">+                .action = lb_peer_st_wait_rx_reset_ack,</span><br><span style="color: hsl(120, 100%, 40%);">+               .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LB_PEER_EV_RX_RESET)</span><br><span style="color: hsl(120, 100%, 40%);">+                      | S(LB_PEER_EV_RX_RESET_ACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                  | S(LB_PEER_EV_MSG_UP_CO_INITIAL)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LB_PEER_EV_MSG_UP_CO)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LB_PEER_EV_CONNECTION_TIMEOUT)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LB_PEER_ST_WAIT_RX_RESET)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LB_PEER_ST_WAIT_RX_RESET_ACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LB_PEER_ST_READY)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LB_PEER_ST_DISCARDING)</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%);">+    [LB_PEER_ST_READY] = {</span><br><span style="color: hsl(120, 100%, 40%);">+                .name = "READY",</span><br><span style="color: hsl(120, 100%, 40%);">+            .action = lb_peer_st_ready,</span><br><span style="color: hsl(120, 100%, 40%);">+           .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LB_PEER_EV_RX_RESET)</span><br><span style="color: hsl(120, 100%, 40%);">+                      | S(LB_PEER_EV_MSG_UP_CO_INITIAL)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LB_PEER_EV_MSG_UP_CO)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LB_PEER_EV_MSG_DOWN_CO_INITIAL)</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LB_PEER_EV_MSG_DOWN_CO)</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LB_PEER_EV_MSG_DOWN_CL)</span><br><span style="color: hsl(120, 100%, 40%);">+                   ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LB_PEER_ST_WAIT_RX_RESET)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LB_PEER_ST_WAIT_RX_RESET_ACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LB_PEER_ST_READY)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LB_PEER_ST_DISCARDING)</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%);">+    [LB_PEER_ST_DISCARDING] = {</span><br><span style="color: hsl(120, 100%, 40%);">+           .name = "DISCARDING",</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 lb_peer_fsm = {</span><br><span style="color: hsl(120, 100%, 40%);">+      .name = "lb_peer",</span><br><span style="color: hsl(120, 100%, 40%);">+  .states = lb_peer_fsm_states,</span><br><span style="color: hsl(120, 100%, 40%);">+ .num_states = ARRAY_SIZE(lb_peer_fsm_states),</span><br><span style="color: hsl(120, 100%, 40%);">+ .log_subsys = DLB,</span><br><span style="color: hsl(120, 100%, 40%);">+    .event_names = lb_peer_fsm_event_names,</span><br><span style="color: hsl(120, 100%, 40%);">+       .timer_cb = lb_peer_fsm_timer_cb,</span><br><span style="color: hsl(120, 100%, 40%);">+     .cleanup = lb_peer_fsm_cleanup,</span><br><span style="color: hsl(120, 100%, 40%);">+       .allstate_action = lb_peer_allstate_action,</span><br><span style="color: hsl(120, 100%, 40%);">+   .allstate_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+              | S(LB_PEER_EV_MSG_UP_CL)</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%);">+int lb_peer_up_l2(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *calling_addr, bool co, uint32_t conn_id,</span><br><span style="color: hsl(120, 100%, 40%);">+              struct msgb *l2)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct lb_peer *lb_peer = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint32_t event;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct lb_peer_ev_ctx ctx = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .conn_id = conn_id,</span><br><span style="color: hsl(120, 100%, 40%);">+           .msg = l2,</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 (co) {</span><br><span style="color: hsl(120, 100%, 40%);">+             struct lb_conn *lb_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+              llist_for_each_entry(lb_conn, &sli->lb_conns, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (lb_conn->sccp_conn_id == conn_id) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            lb_peer = lb_conn->lb_peer;</span><br><span style="color: hsl(120, 100%, 40%);">+                                ctx.lb_conn = lb_conn;</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%);">+           if (lb_peer && calling_addr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        LOG_SCCP_LB_CO(sli, calling_addr, conn_id, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                                        "Connection-Oriented Initial message for already existing conn_id."</span><br><span style="color: hsl(120, 100%, 40%);">+                                 " Dropping message.\n");</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%);">+           if (!lb_peer && !calling_addr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      LOG_SCCP_LB_CO(sli, calling_addr, conn_id, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                                        "Connection-Oriented non-Initial message for unknown conn_id %u."</span><br><span style="color: hsl(120, 100%, 40%);">+                                   " Dropping message.\n", conn_id);</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (calling_addr) {</span><br><span style="color: hsl(120, 100%, 40%);">+           lb_peer = lb_peer_find_or_create(sli, calling_addr);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!lb_peer) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       LOG_SCCP_LB_CL(sli, calling_addr, LOGL_ERROR, "Cannot register Lb peer\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                 return -EIO;</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%);">+   OSMO_ASSERT(lb_peer && lb_peer->fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (co)</span><br><span style="color: hsl(120, 100%, 40%);">+               event = calling_addr ? LB_PEER_EV_MSG_UP_CO_INITIAL : LB_PEER_EV_MSG_UP_CO;</span><br><span style="color: hsl(120, 100%, 40%);">+   else</span><br><span style="color: hsl(120, 100%, 40%);">+          event = LB_PEER_EV_MSG_UP_CL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return osmo_fsm_inst_dispatch(lb_peer->fi, event, &ctx);</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 lb_peer_disconnect(struct sccp_lb_inst *sli, uint32_t conn_id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct lb_conn *lb_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+      llist_for_each_entry(lb_conn, &sli->lb_conns, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (lb_conn->sccp_conn_id == conn_id) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    lb_conn_discard(lb_conn);</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%);">+}</span><br><span>diff --git a/src/osmo-smlc/sccp_lb_inst.c b/src/osmo-smlc/sccp_lb_inst.c</span><br><span>new file mode 100644</span><br><span>index 0000000..5a5c5e9</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-smlc/sccp_lb_inst.c</span><br><span>@@ -0,0 +1,253 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2020 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de></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%);">+ * SPDX-License-Identifier: AGPL-3.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr</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%);">+#include <osmocom/core/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/sccp/sccp_types.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/sigtran/sccp_sap.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/sigtran/sccp_helpers.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/debug.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/sccp_lb_inst.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/lb_peer.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* We need an unused SCCP conn_id across all SCCP users. */</span><br><span style="color: hsl(120, 100%, 40%);">+int sccp_lb_inst_next_conn_id()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    static uint32_t next_id = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* This looks really suboptimal, but in most cases the static next_id should indicate exactly the next unused</span><br><span style="color: hsl(120, 100%, 40%);">+  * conn_id, and we only iterate all conns once to make super sure that it is not already in use. */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < 0xFFFFFF; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+           struct lb_peer *lb_peer;</span><br><span style="color: hsl(120, 100%, 40%);">+              uint32_t conn_id = next_id;</span><br><span style="color: hsl(120, 100%, 40%);">+           bool conn_id_already_used = false;</span><br><span style="color: hsl(120, 100%, 40%);">+            next_id = (next_id + 1) & 0xffffff;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             llist_for_each_entry(lb_peer, &g_smlc->lb->lb_peers, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       struct lb_conn *conn;</span><br><span style="color: hsl(120, 100%, 40%);">+                 lb_peer_for_each_lb_conn(conn, lb_peer) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             if (conn_id == conn->sccp_conn_id) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                       conn_id_already_used = true;</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 (conn_id_already_used)</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 (!conn_id_already_used)</span><br><span style="color: hsl(120, 100%, 40%);">+                    return conn_id;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int sccp_lb_sap_up(struct osmo_prim_hdr *oph, void *_scu);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct sccp_lb_inst *sccp_lb_init(void *talloc_ctx, struct osmo_sccp_instance *sccp, enum osmo_sccp_ssn ssn,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const char *sccp_user_name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct sccp_lb_inst *sli = talloc(talloc_ctx, struct sccp_lb_inst);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sli);</span><br><span style="color: hsl(120, 100%, 40%);">+     *sli = (struct sccp_lb_inst){</span><br><span style="color: hsl(120, 100%, 40%);">+         .sccp = sccp,</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%);">+  INIT_LLIST_HEAD(&sli->lb_peers);</span><br><span style="color: hsl(120, 100%, 40%);">+       INIT_LLIST_HEAD(&sli->lb_conns);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_sccp_local_addr_by_instance(&sli->local_sccp_addr, sccp, ssn);</span><br><span style="color: hsl(120, 100%, 40%);">+    sli->scu = osmo_sccp_user_bind(sccp, sccp_user_name, sccp_lb_sap_up, ssn);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_sccp_user_set_priv(sli->scu, sli);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return sli;</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 sccp_lb_sap_up(struct osmo_prim_hdr *oph, void *_scu)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_sccp_user *scu = _scu;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct sccp_lb_inst *sli = osmo_sccp_user_get_priv(scu);</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_sccp_addr *my_addr;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_sccp_addr *peer_addr;</span><br><span style="color: hsl(120, 100%, 40%);">+     uint32_t conn_id;</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%);">+     switch (OSMO_PRIM_HDR(oph)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):</span><br><span style="color: hsl(120, 100%, 40%);">+          /* indication of new inbound connection request */</span><br><span style="color: hsl(120, 100%, 40%);">+            conn_id = prim->u.connect.conn_id;</span><br><span style="color: hsl(120, 100%, 40%);">+         my_addr = &prim->u.connect.called_addr;</span><br><span style="color: hsl(120, 100%, 40%);">+                peer_addr = &prim->u.connect.calling_addr;</span><br><span style="color: hsl(120, 100%, 40%);">+             LOG_SCCP_LB_CO(sli, peer_addr, conn_id, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               if (!msgb_l2(oph->msg) || msgb_l2len(oph->msg) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  LOG_SCCP_LB_CO(sli, peer_addr, conn_id, LOGL_NOTICE, "Received invalid N-CONNECT.ind\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                   rc = -1;</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 (osmo_sccp_addr_ri_cmp(&sli->local_sccp_addr, my_addr))</span><br><span style="color: hsl(120, 100%, 40%);">+                     LOG_SCCP_LB_CO(sli, peer_addr, conn_id, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  "Rx N-CONNECT: Called address is %s != local address %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                 osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sli->sccp, my_addr),</span><br><span style="color: hsl(120, 100%, 40%);">+                                      osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sli->sccp, &sli->local_sccp_addr));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               /* ensure the local SCCP socket is ACTIVE */</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_sccp_tx_conn_resp(scu, conn_id, my_addr, NULL, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             rc = lb_peer_up_l2(sli, peer_addr, true, conn_id, oph->msg);</span><br><span style="color: hsl(120, 100%, 40%);">+               if (rc)</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmo_sccp_tx_disconn(scu, conn_id, my_addr, SCCP_RETURN_CAUSE_UNQUALIFIED);</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_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):</span><br><span style="color: hsl(120, 100%, 40%);">+             /* connection-oriented data received */</span><br><span style="color: hsl(120, 100%, 40%);">+               conn_id = prim->u.data.conn_id;</span><br><span style="color: hsl(120, 100%, 40%);">+            LOG_SCCP_LB_CO(sli, NULL, conn_id, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            rc = lb_peer_up_l2(sli, NULL, true, conn_id, oph->msg);</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_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):</span><br><span style="color: hsl(120, 100%, 40%);">+               /* indication of disconnect */</span><br><span style="color: hsl(120, 100%, 40%);">+                conn_id = prim->u.disconnect.conn_id;</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_SCCP_LB_CO(sli, NULL, conn_id, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            /* If there is no L2 payload in the N-DISCONNECT, no need to dispatch up_l2(). */</span><br><span style="color: hsl(120, 100%, 40%);">+             if (msgb_l2len(oph->msg))</span><br><span style="color: hsl(120, 100%, 40%);">+                  rc = lb_peer_up_l2(sli, NULL, true, conn_id, oph->msg);</span><br><span style="color: hsl(120, 100%, 40%);">+            else</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%);">+             /* Make sure the lb_conn is dropped. It might seem more optimal to combine the disconnect() into</span><br><span style="color: hsl(120, 100%, 40%);">+               * up_l2(), but since an up_l2() dispatch might already cause the lb_conn to be discarded for other</span><br><span style="color: hsl(120, 100%, 40%);">+            * reasons, a separate disconnect() with a separate conn_id lookup is actually necessary. */</span><br><span style="color: hsl(120, 100%, 40%);">+          sccp_lb_disconnect(sli, conn_id, 0);</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_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):</span><br><span style="color: hsl(120, 100%, 40%);">+         /* connection-less data received */</span><br><span style="color: hsl(120, 100%, 40%);">+           my_addr = &prim->u.unitdata.called_addr;</span><br><span style="color: hsl(120, 100%, 40%);">+               peer_addr = &prim->u.unitdata.calling_addr;</span><br><span style="color: hsl(120, 100%, 40%);">+            LOG_SCCP_LB_CL(sli, peer_addr, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                if (osmo_sccp_addr_ri_cmp(&sli->local_sccp_addr, my_addr))</span><br><span style="color: hsl(120, 100%, 40%);">+                     LOG_SCCP_LB_CL(sli, peer_addr, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                                    "Rx N-UNITDATA: Called address is %s != local address %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                        osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sli->sccp, my_addr),</span><br><span style="color: hsl(120, 100%, 40%);">+                                      osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sli->sccp, &sli->local_sccp_addr));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              rc = lb_peer_up_l2(sli, peer_addr, false, 0, oph->msg);</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%);">+              LOG_SCCP_LB_CL(sli, NULL, LOGL_ERROR, "%s(%s) unsupported\n", __func__, osmo_scu_prim_name(oph));</span><br><span style="color: hsl(120, 100%, 40%);">+           rc = -1;</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%);">+   msgb_free(oph->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 style="color: hsl(120, 100%, 40%);">+/* Push some padding if necessary to reach a multiple-of-eight offset to be msgb_push() an osmo_scu_prim that will then</span><br><span style="color: hsl(120, 100%, 40%);">+ * be 8-byte aligned. */</span><br><span style="color: hsl(120, 100%, 40%);">+static void msgb_pad_mod8(struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    uint8_t mod8 = (intptr_t)(msg->data) % 8;</span><br><span style="color: hsl(120, 100%, 40%);">+  if (mod8)</span><br><span style="color: hsl(120, 100%, 40%);">+             msgb_push(msg, mod8);</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 sccp_lb_sap_down(struct sccp_lb_inst *sli, struct osmo_prim_hdr *oph)</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%);">+       if (!sli->scu) {</span><br><span style="color: hsl(120, 100%, 40%);">+           rate_ctr_inc(&g_smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_ERR_CONN_NOT_READY]);</span><br><span style="color: hsl(120, 100%, 40%);">+         return -EIO;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = osmo_sccp_user_sap_down_nofree(sli->scu, oph);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (rc >= 0)</span><br><span style="color: hsl(120, 100%, 40%);">+               rate_ctr_inc(&g_smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_SUCCESS]);</span><br><span style="color: hsl(120, 100%, 40%);">+    else</span><br><span style="color: hsl(120, 100%, 40%);">+          rate_ctr_inc(&g_smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_ERR_SEND]);</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%);">+int sccp_lb_down_l2_co_initial(struct sccp_lb_inst *sli,</span><br><span style="color: hsl(120, 100%, 40%);">+                             const struct osmo_sccp_addr *called_addr,</span><br><span style="color: hsl(120, 100%, 40%);">+                             uint32_t conn_id, struct msgb *l2)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_scu_prim *prim;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ l2->l2h = l2->data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   msgb_pad_mod8(l2);</span><br><span style="color: hsl(120, 100%, 40%);">+    prim = (struct osmo_scu_prim *) msgb_push(l2, sizeof(*prim));</span><br><span style="color: hsl(120, 100%, 40%);">+ prim->u.connect = (struct osmo_scu_connect_param){</span><br><span style="color: hsl(120, 100%, 40%);">+         .called_addr = *called_addr,</span><br><span style="color: hsl(120, 100%, 40%);">+          .calling_addr = sli->local_sccp_addr,</span><br><span style="color: hsl(120, 100%, 40%);">+              .sccp_class = 2,</span><br><span style="color: hsl(120, 100%, 40%);">+              //.importance = ?,</span><br><span style="color: hsl(120, 100%, 40%);">+            .conn_id = conn_id,</span><br><span style="color: hsl(120, 100%, 40%);">+   };</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST, l2);</span><br><span style="color: hsl(120, 100%, 40%);">+       return sccp_lb_sap_down(sli, &prim->oph);</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 sccp_lb_down_l2_co(struct sccp_lb_inst *sli, uint32_t conn_id, struct msgb *l2)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_scu_prim *prim;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ l2->l2h = l2->data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   msgb_pad_mod8(l2);</span><br><span style="color: hsl(120, 100%, 40%);">+    prim = (struct osmo_scu_prim *) msgb_push(l2, sizeof(*prim));</span><br><span style="color: hsl(120, 100%, 40%);">+ prim->u.data.conn_id = conn_id;</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, l2);</span><br><span style="color: hsl(120, 100%, 40%);">+  return sccp_lb_sap_down(sli, &prim->oph);</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 sccp_lb_down_l2_cl(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *called_addr, struct msgb *l2)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_scu_prim *prim;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ l2->l2h = l2->data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   msgb_pad_mod8(l2);</span><br><span style="color: hsl(120, 100%, 40%);">+    prim = (struct osmo_scu_prim *) msgb_push(l2, sizeof(*prim));</span><br><span style="color: hsl(120, 100%, 40%);">+ prim->u.unitdata = (struct osmo_scu_unitdata_param){</span><br><span style="color: hsl(120, 100%, 40%);">+               .called_addr = *called_addr,</span><br><span style="color: hsl(120, 100%, 40%);">+          .calling_addr = sli->local_sccp_addr,</span><br><span style="color: hsl(120, 100%, 40%);">+      };</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_REQUEST, l2);</span><br><span style="color: hsl(120, 100%, 40%);">+      return sccp_lb_sap_down(sli, &prim->oph);</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 sccp_lb_disconnect(struct sccp_lb_inst *sli, uint32_t conn_id, uint32_t cause)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  return osmo_sccp_tx_disconn(sli->scu, conn_id, NULL, cause);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/osmo-smlc/smlc_data.c b/src/osmo-smlc/smlc_data.c</span><br><span>new file mode 100644</span><br><span>index 0000000..d51bceb</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-smlc/smlc_data.c</span><br><span>@@ -0,0 +1,65 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* (C) 2020 by Harald Welte <laforge@gnumonks.org></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/lienses/>.</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/stats.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef g_smlc_tdefs[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+    { .T=-12, .default_val=5, .desc="Timeout for BSSLAP TA Response from BSC" },</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 const struct rate_ctr_desc smlc_ctr_description[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+        [SMLC_CTR_BSSMAP_LE_RX_UDT_RESET] =     { "bssmap_le:rx_udt_reset", "Rx BSSMAP-LE Reset" },</span><br><span style="color: hsl(120, 100%, 40%);">+       [SMLC_CTR_BSSMAP_LE_RX_UDT_RESET_ACK] = { "bssmap_le:rx_udt_reset_ack", "Rx BSSMAP-LE Reset Acknowledge" },</span><br><span style="color: hsl(120, 100%, 40%);">+       [SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG] =   { "bssmap_le:rx_udt_err_invalid_msg", "Receive invalid UnitData message" },</span><br><span style="color: hsl(120, 100%, 40%);">+       [SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG] =   { "bssmap_le:rx_dt1_err_invalid_msg", "Receive invalid DirectTransfer1 message" },</span><br><span style="color: hsl(120, 100%, 40%);">+        [SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_REQUEST] =  { "bssmap_le:rx_dt1_perform_location_request", "Receive Perform Location Request from BSC" },</span><br><span style="color: hsl(120, 100%, 40%);">+     [SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_TA_RESPONSE] =        { "bssmap_le:rx_dt1_bsslap_ta_response", "Receive BSSLAP TA Response from BSC" },</span><br><span style="color: hsl(120, 100%, 40%);">+ [SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_REJECT] =     { "bssmap_le:rx_dt1_bsslap_reject", "Rx BSSLAP Reject from BSC" },</span><br><span style="color: hsl(120, 100%, 40%);">+        [SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_RESET] =      { "bssmap_le:rx_dt1_bsslap_reset", "Rx BSSLAP Reset (handover) from BSC" },</span><br><span style="color: hsl(120, 100%, 40%);">+       [SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_ABORT] =      { "bssmap_le:rx_dt1_bsslap_abort", "Rx BSSLAP Abort from BSC" },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        [SMLC_CTR_BSSMAP_LE_TX_ERR_INVALID_MSG] =       { "bssmap_le:tx_err_invalid_msg", "BSSMAP-LE send error: invalid message" },</span><br><span style="color: hsl(120, 100%, 40%);">+      [SMLC_CTR_BSSMAP_LE_TX_ERR_CONN_NOT_READY] =    { "bssmap_le:tx_err_conn_not_ready", "BSSMAP-LE send error: conn not ready" },</span><br><span style="color: hsl(120, 100%, 40%);">+    [SMLC_CTR_BSSMAP_LE_TX_ERR_SEND] =      { "bssmap_le:tx_err_send", "BSSMAP-LE send error" },</span><br><span style="color: hsl(120, 100%, 40%);">+      [SMLC_CTR_BSSMAP_LE_TX_SUCCESS] =       { "bssmap_le:tx_success", "BSSMAP-LE send success" },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   [SMLC_CTR_BSSMAP_LE_TX_UDT_RESET] =     { "bssmap_le:tx_udt_reset", "Transmit UnitData Reset" },</span><br><span style="color: hsl(120, 100%, 40%);">+  [SMLC_CTR_BSSMAP_LE_TX_UDT_RESET_ACK] = { "bssmap_le:tx_udt_reset_ack", "Transmit UnitData Reset Acknowledge" },</span><br><span style="color: hsl(120, 100%, 40%);">+  [SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_RESPONSE] = { "bssmap_le:tx_dt1_perform_location_response", "Tx Perform Location Response to BSC" },</span><br><span style="color: hsl(120, 100%, 40%);">+  [SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_TA_REQUEST] = { "bssmap_le:tx_dt1_bsslap_ta_request", "Tx BSSLAP TA Request to BSC" },</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 rate_ctr_group_desc smlc_ctrg_desc = {</span><br><span style="color: hsl(120, 100%, 40%);">+        "smlc",</span><br><span style="color: hsl(120, 100%, 40%);">+     "serving mobile location center",</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_STATS_CLASS_GLOBAL,</span><br><span style="color: hsl(120, 100%, 40%);">+      ARRAY_SIZE(smlc_ctr_description),</span><br><span style="color: hsl(120, 100%, 40%);">+     smlc_ctr_description,</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 smlc_state *smlc_state_alloc(void *ctx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct smlc_state *smlc = talloc_zero(ctx, struct smlc_state);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(smlc);</span><br><span style="color: hsl(120, 100%, 40%);">+    INIT_LLIST_HEAD(&smlc->subscribers);</span><br><span style="color: hsl(120, 100%, 40%);">+   INIT_LLIST_HEAD(&smlc->cell_locations);</span><br><span style="color: hsl(120, 100%, 40%);">+        smlc->ctrs = rate_ctr_group_alloc(smlc, &smlc_ctrg_desc, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+   return smlc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/osmo-smlc/smlc_loc_req.c b/src/osmo-smlc/smlc_loc_req.c</span><br><span>new file mode 100644</span><br><span>index 0000000..1365bfa</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-smlc/smlc_loc_req.c</span><br><span>@@ -0,0 +1,445 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Handle LCS BSSMAP-LE Perform Location Request */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></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%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</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, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</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/smlc/smlc_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_loc_req.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_subscr.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/lb_conn.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/cell_locations.h></span><br><span style="color: hsl(120, 100%, 40%);">+</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/core/tdef.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/gsm/bsslap.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/bssmap_le.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gad.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum smlc_loc_req_fsm_state {</span><br><span style="color: hsl(120, 100%, 40%);">+     SMLC_LOC_REQ_ST_INIT,</span><br><span style="color: hsl(120, 100%, 40%);">+ SMLC_LOC_REQ_ST_WAIT_TA,</span><br><span style="color: hsl(120, 100%, 40%);">+      SMLC_LOC_REQ_ST_GOT_TA,</span><br><span style="color: hsl(120, 100%, 40%);">+       SMLC_LOC_REQ_ST_FAILED,</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 smlc_loc_req_fsm_event_names[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_VALUE_STRING(SMLC_LOC_REQ_EV_RX_TA_RESPONSE),</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_VALUE_STRING(SMLC_LOC_REQ_EV_RX_BSSLAP_RESET),</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_VALUE_STRING(SMLC_LOC_REQ_EV_RX_LE_PERFORM_LOCATION_ABORT),</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 smlc_loc_req_fsm;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct osmo_tdef_state_timeout smlc_loc_req_fsm_timeouts[32] = {</span><br><span style="color: hsl(120, 100%, 40%);">+   [SMLC_LOC_REQ_ST_WAIT_TA] = { .T = -12 },</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%);">+/* Transition to a state, using the T timer defined in smlc_loc_req_fsm_timeouts.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The actual timeout value is in turn obtained from network->T_defs.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Assumes local variable fi exists. */</span><br><span style="color: hsl(120, 100%, 40%);">+#define smlc_loc_req_fsm_state_chg(FI, STATE) \</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_tdef_fsm_inst_state_chg(FI, STATE, \</span><br><span style="color: hsl(120, 100%, 40%);">+                                  smlc_loc_req_fsm_timeouts, \</span><br><span style="color: hsl(120, 100%, 40%);">+                                  g_smlc_tdefs, \</span><br><span style="color: hsl(120, 100%, 40%);">+                               5)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define smlc_loc_req_fail(cause, fmt, args...) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_ERROR, "Perform Location Request failed in state %s: " fmt "\n", \</span><br><span style="color: hsl(120, 100%, 40%);">+                             smlc_loc_req ? osmo_fsm_inst_state_name(smlc_loc_req->fi) : "NULL", ## args); \</span><br><span style="color: hsl(120, 100%, 40%);">+         smlc_loc_req->lcs_cause = (struct lcs_cause_ie){ \</span><br><span style="color: hsl(120, 100%, 40%);">+                 .present = true, \</span><br><span style="color: hsl(120, 100%, 40%);">+                    .cause_val = cause, \</span><br><span style="color: hsl(120, 100%, 40%);">+         }; \</span><br><span style="color: hsl(120, 100%, 40%);">+          smlc_loc_req_fsm_state_chg(smlc_loc_req->fi, SMLC_LOC_REQ_ST_FAILED); \</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 smlc_loc_req *smlc_loc_req_alloc(void *ctx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct smlc_loc_req *smlc_loc_req;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc(&smlc_loc_req_fsm, ctx, NULL, LOGL_DEBUG, "no-id");</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%);">+    smlc_loc_req = talloc(fi, struct smlc_loc_req);</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(smlc_loc_req);</span><br><span style="color: hsl(120, 100%, 40%);">+    fi->priv = smlc_loc_req;</span><br><span style="color: hsl(120, 100%, 40%);">+   *smlc_loc_req = (struct smlc_loc_req){</span><br><span style="color: hsl(120, 100%, 40%);">+                .fi = fi,</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 smlc_loc_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%);">+static int smlc_loc_req_start(struct lb_conn *lb_conn, const struct bssmap_le_perform_loc_req *loc_req_pdu)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct smlc_loc_req *smlc_loc_req;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  rate_ctr_inc(&g_smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_REQUEST]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (lb_conn->smlc_loc_req) {</span><br><span style="color: hsl(120, 100%, 40%);">+               /* Another request is already pending. If we send Perform Location Abort, the peer doesn't know which</span><br><span style="color: hsl(120, 100%, 40%);">+              * request we would mean. Just drop this on the floor. */</span><br><span style="color: hsl(120, 100%, 40%);">+             LOG_SMLC_LOC_REQ(lb_conn->smlc_loc_req, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                                "Ignoring Perform Location Request, another request is still pending\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           return -EAGAIN;</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 (loc_req_pdu->imsi.type == GSM_MI_TYPE_IMSI</span><br><span style="color: hsl(120, 100%, 40%);">+         && (!lb_conn->smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+              || osmo_mobile_identity_cmp(&loc_req_pdu->imsi, &lb_conn->smlc_subscr->imsi))) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           struct smlc_subscr *smlc_subscr;</span><br><span style="color: hsl(120, 100%, 40%);">+              struct lb_conn *other_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+           smlc_subscr = smlc_subscr_find_or_create(&loc_req_pdu->imsi, __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+                OSMO_ASSERT(smlc_subscr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (lb_conn->smlc_subscr && lb_conn->smlc_subscr != smlc_subscr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      LOG_LB_CONN(lb_conn, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  "IMSI mismatch: lb_conn has %s, Rx Perform Location Request has %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                              smlc_subscr_to_str_c(OTC_SELECT, lb_conn->smlc_subscr),</span><br><span style="color: hsl(120, 100%, 40%);">+                                    smlc_subscr_to_str_c(OTC_SELECT, smlc_subscr));</span><br><span style="color: hsl(120, 100%, 40%);">+                   smlc_subscr_put(smlc_subscr, __func__);</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%);">+           /* Find another conn before setting this conn's subscriber */</span><br><span style="color: hsl(120, 100%, 40%);">+             other_conn = lb_conn_find_by_smlc_subscr(lb_conn->smlc_subscr, __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                /* Set the subscriber before logging about it, so that it shows as log context */</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!lb_conn->smlc_subscr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       lb_conn->smlc_subscr = smlc_subscr;</span><br><span style="color: hsl(120, 100%, 40%);">+                        smlc_subscr_get(lb_conn->smlc_subscr, SMLC_SUBSCR_USE_LB_CONN);</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 (other_conn && other_conn != lb_conn) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    LOG_LB_CONN(lb_conn, LOGL_ERROR, "Another conn already active for this subscriber\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                      LOG_LB_CONN(other_conn, LOGL_ERROR, "Another conn opened for this subscriber, discarding\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                       lb_conn_close(other_conn);</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%);">+           smlc_subscr_put(smlc_subscr, __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+               if (other_conn)</span><br><span style="color: hsl(120, 100%, 40%);">+                       lb_conn_put(other_conn, __func__);</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%);">+   /* smlc_loc_req has a use count on lb_conn, so its talloc ctx must not be a child of lb_conn. (Otherwise an</span><br><span style="color: hsl(120, 100%, 40%);">+    * lb_conn_put() from smlc_loc_req could cause a free of smlc_loc_req's parent ctx, causing a use after free on</span><br><span style="color: hsl(120, 100%, 40%);">+    * FSM termination.) */</span><br><span style="color: hsl(120, 100%, 40%);">+       smlc_loc_req = smlc_loc_req_alloc(lb_conn->lb_peer);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     *smlc_loc_req = (struct smlc_loc_req){</span><br><span style="color: hsl(120, 100%, 40%);">+                .fi = smlc_loc_req->fi,</span><br><span style="color: hsl(120, 100%, 40%);">+            .lb_conn = lb_conn,</span><br><span style="color: hsl(120, 100%, 40%);">+           .req = *loc_req_pdu,</span><br><span style="color: hsl(120, 100%, 40%);">+  };</span><br><span style="color: hsl(120, 100%, 40%);">+    smlc_loc_req->latest_cell_id = loc_req_pdu->cell_id;</span><br><span style="color: hsl(120, 100%, 40%);">+    lb_conn->smlc_loc_req = smlc_loc_req;</span><br><span style="color: hsl(120, 100%, 40%);">+      lb_conn_get(smlc_loc_req->lb_conn, LB_CONN_USE_SMLC_LOC_REQ);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    LOG_LB_CONN(lb_conn, LOGL_INFO, "Rx Perform Location Request (BSSLAP APDU %s), cell id is %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                loc_req_pdu->apdu_present ?</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_bsslap_msgt_name(loc_req_pdu->apdu.msg_type) : "omitted",</span><br><span style="color: hsl(120, 100%, 40%);">+                   gsm0808_cell_id_name_c(OTC_SELECT, &smlc_loc_req->latest_cell_id));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* state change to start the timeout */</span><br><span style="color: hsl(120, 100%, 40%);">+       smlc_loc_req_fsm_state_chg(smlc_loc_req->fi, SMLC_LOC_REQ_ST_WAIT_TA);</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 int handle_bssmap_le_conn_oriented_info(struct smlc_loc_req *smlc_loc_req,</span><br><span style="color: hsl(120, 100%, 40%);">+                                            const struct bssmap_le_conn_oriented_info *coi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (coi->apdu.msg_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case BSSLAP_MSGT_TA_RESPONSE:</span><br><span style="color: hsl(120, 100%, 40%);">+         return osmo_fsm_inst_dispatch(smlc_loc_req->fi, SMLC_LOC_REQ_EV_RX_TA_RESPONSE,</span><br><span style="color: hsl(120, 100%, 40%);">+                                          (void*)&coi->apdu.ta_response);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        case BSSLAP_MSGT_RESET:</span><br><span style="color: hsl(120, 100%, 40%);">+               return osmo_fsm_inst_dispatch(smlc_loc_req->fi, SMLC_LOC_REQ_EV_RX_BSSLAP_RESET,</span><br><span style="color: hsl(120, 100%, 40%);">+                                         (void*)&coi->apdu.reset);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      case BSSLAP_MSGT_ABORT:</span><br><span style="color: hsl(120, 100%, 40%);">+               smlc_loc_req_fail(LCS_CAUSE_REQUEST_ABORTED, "Aborting Location Request due to BSSLAP Abort");</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%);">+   case BSSLAP_MSGT_REJECT:</span><br><span style="color: hsl(120, 100%, 40%);">+              smlc_loc_req_fail(LCS_CAUSE_REQUEST_ABORTED, "Aborting Location Request due to BSSLAP Reject");</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%);">+   default:</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_ERROR, "rx BSSLAP APDU with unsupported message type %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                              osmo_bsslap_msgt_name(coi->apdu.msg_type));</span><br><span style="color: hsl(120, 100%, 40%);">+               return -ENOTSUP;</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%);">+int smlc_loc_req_rx_bssap_le(struct lb_conn *lb_conn, const struct bssap_le_pdu *bssap_le)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct smlc_loc_req *smlc_loc_req = lb_conn->smlc_loc_req;</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct bssmap_le_pdu *bssmap_le = &bssap_le->bssmap_le;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        LOG_LB_CONN(lb_conn, LOGL_DEBUG, "Rx %s\n", osmo_bssap_le_pdu_to_str_c(OTC_SELECT, bssap_le));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (bssap_le->discr != BSSAP_LE_MSG_DISCR_BSSMAP_LE) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOG_LB_CONN(lb_conn, LOGL_ERROR, "BSSAP-LE discr %d not implemented\n", bssap_le->discr);</span><br><span style="color: hsl(120, 100%, 40%);">+                return -ENOTSUP;</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 (bssmap_le->msg_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   case BSSMAP_LE_MSGT_PERFORM_LOC_REQ:</span><br><span style="color: hsl(120, 100%, 40%);">+          return smlc_loc_req_start(lb_conn, &bssmap_le->perform_loc_req);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case BSSMAP_LE_MSGT_PERFORM_LOC_ABORT:</span><br><span style="color: hsl(120, 100%, 40%);">+                return osmo_fsm_inst_dispatch(smlc_loc_req->fi, SMLC_LOC_REQ_EV_RX_LE_PERFORM_LOCATION_ABORT,</span><br><span style="color: hsl(120, 100%, 40%);">+                                            (void*)&bssmap_le->perform_loc_abort);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO:</span><br><span style="color: hsl(120, 100%, 40%);">+               return handle_bssmap_le_conn_oriented_info(smlc_loc_req, &bssmap_le->conn_oriented_info);</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%);">+              LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_ERROR, "Rx BSSMAP-LE from SMLC with unsupported message type: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                            osmo_bssap_le_pdu_to_str_c(OTC_SELECT, bssap_le));</span><br><span style="color: hsl(120, 100%, 40%);">+            return -ENOTSUP;</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%);">+void smlc_loc_req_reset(struct lb_conn *lb_conn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct smlc_loc_req *smlc_loc_req = lb_conn->smlc_loc_req;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!smlc_loc_req)</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+       smlc_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Aborting Location Request due to RESET on Lb");</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 smlc_loc_req_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 smlc_loc_req *smlc_loc_req = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+      smlc_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Timeout");</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void smlc_loc_req_wait_ta_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%);">+    struct smlc_loc_req *smlc_loc_req = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct bssmap_le_pdu bssmap_le;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Did the original request contain a TA already? */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (smlc_loc_req->req.apdu_present && smlc_loc_req->req.apdu.msg_type == BSSLAP_MSGT_TA_LAYER3) {</span><br><span style="color: hsl(120, 100%, 40%);">+               smlc_loc_req->ta_present = true;</span><br><span style="color: hsl(120, 100%, 40%);">+           smlc_loc_req->ta = smlc_loc_req->req.apdu.ta_layer3.ta;</span><br><span style="color: hsl(120, 100%, 40%);">+         LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_INFO, "TA = %u\n", smlc_loc_req->ta);</span><br><span style="color: hsl(120, 100%, 40%);">+                smlc_loc_req_fsm_state_chg(smlc_loc_req->fi, SMLC_LOC_REQ_ST_GOT_TA);</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%);">+   /* No TA known yet, ask via BSSLAP */</span><br><span style="color: hsl(120, 100%, 40%);">+ bssmap_le = (struct bssmap_le_pdu){</span><br><span style="color: hsl(120, 100%, 40%);">+           .msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,</span><br><span style="color: hsl(120, 100%, 40%);">+                .conn_oriented_info = {</span><br><span style="color: hsl(120, 100%, 40%);">+                       .apdu = {</span><br><span style="color: hsl(120, 100%, 40%);">+                             .msg_type = BSSLAP_MSGT_TA_REQUEST,</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%);">+  lb_conn_send_bssmap_le(smlc_loc_req->lb_conn, &bssmap_le);</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 update_ci(struct gsm0808_cell_id *cell_id, int16_t new_ci)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_cell_global_id cgi = {};</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gsm0808_cell_id ci = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .id_discr = CELL_IDENT_CI,</span><br><span style="color: hsl(120, 100%, 40%);">+            .id.ci = new_ci,</span><br><span style="color: hsl(120, 100%, 40%);">+      };</span><br><span style="color: hsl(120, 100%, 40%);">+    /* Set all values from the cell_id to the cgi */</span><br><span style="color: hsl(120, 100%, 40%);">+      gsm0808_cell_id_to_cgi(&cgi, cell_id);</span><br><span style="color: hsl(120, 100%, 40%);">+    /* Overwrite the CI part */</span><br><span style="color: hsl(120, 100%, 40%);">+   gsm0808_cell_id_to_cgi(&cgi, &ci);</span><br><span style="color: hsl(120, 100%, 40%);">+    /* write back to cell_id, without changing its type */</span><br><span style="color: hsl(120, 100%, 40%);">+        gsm0808_cell_id_from_cgi(cell_id, cell_id->id_discr, &cgi);</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 smlc_loc_req_wait_ta_action(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 smlc_loc_req *smlc_loc_req = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+      const struct bsslap_ta_response *ta_response;</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct bsslap_reset *reset;</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case SMLC_LOC_REQ_EV_RX_TA_RESPONSE:</span><br><span style="color: hsl(120, 100%, 40%);">+          ta_response = data;</span><br><span style="color: hsl(120, 100%, 40%);">+           smlc_loc_req->ta_present = true;</span><br><span style="color: hsl(120, 100%, 40%);">+           smlc_loc_req->ta = ta_response->ta;</span><br><span style="color: hsl(120, 100%, 40%);">+             update_ci(&smlc_loc_req->latest_cell_id, ta_response->cell_id);</span><br><span style="color: hsl(120, 100%, 40%);">+             LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_INFO, "Rx BSSLAP TA Response: cell id is now %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                              gsm0808_cell_id_name_c(OTC_SELECT, &smlc_loc_req->latest_cell_id));</span><br><span style="color: hsl(120, 100%, 40%);">+           smlc_loc_req_fsm_state_chg(smlc_loc_req->fi, SMLC_LOC_REQ_ST_GOT_TA);</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%);">+     case SMLC_LOC_REQ_EV_RX_BSSLAP_RESET:</span><br><span style="color: hsl(120, 100%, 40%);">+         reset = data;</span><br><span style="color: hsl(120, 100%, 40%);">+         smlc_loc_req->ta_present = true;</span><br><span style="color: hsl(120, 100%, 40%);">+           smlc_loc_req->ta = reset->ta;</span><br><span style="color: hsl(120, 100%, 40%);">+           update_ci(&smlc_loc_req->latest_cell_id, reset->cell_id);</span><br><span style="color: hsl(120, 100%, 40%);">+           LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_INFO, "Rx BSSLAP Reset: cell id is now %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                            gsm0808_cell_id_name_c(OTC_SELECT, &smlc_loc_req->latest_cell_id));</span><br><span style="color: hsl(120, 100%, 40%);">+           smlc_loc_req_fsm_state_chg(smlc_loc_req->fi, SMLC_LOC_REQ_ST_GOT_TA);</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%);">+     case SMLC_LOC_REQ_EV_RX_LE_PERFORM_LOCATION_ABORT:</span><br><span style="color: hsl(120, 100%, 40%);">+            LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_INFO, "Rx Perform Location Abort, stopping this request dead\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, NULL);</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%);">+     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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void smlc_loc_req_got_ta_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%);">+     struct smlc_loc_req *smlc_loc_req = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct bssmap_le_pdu bssmap_le;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_gad location;</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 (!smlc_loc_req->ta_present) {</span><br><span style="color: hsl(120, 100%, 40%);">+           smlc_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE,</span><br><span style="color: hsl(120, 100%, 40%);">+                             "Internal error: GOT_TA event, but no TA present");</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%);">+   bssmap_le = (struct bssmap_le_pdu){</span><br><span style="color: hsl(120, 100%, 40%);">+           .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP,</span><br><span style="color: hsl(120, 100%, 40%);">+          .perform_loc_resp = {</span><br><span style="color: hsl(120, 100%, 40%);">+                 .location_estimate_present = 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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = cell_location_from_ta(&location, &smlc_loc_req->latest_cell_id, smlc_loc_req->ta);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc) {</span><br><span style="color: hsl(120, 100%, 40%);">+             smlc_loc_req_fail(LCS_CAUSE_FACILITY_NOTSUPP, "Unable to compose Location Estimate for %s: %s",</span><br><span style="color: hsl(120, 100%, 40%);">+                               gsm0808_cell_id_name_c(OTC_SELECT, &smlc_loc_req->latest_cell_id),</span><br><span style="color: hsl(120, 100%, 40%);">+                             rc == -ENOENT ? "No location information for this cell" : "unknown error");</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_gad_enc(&bssmap_le.perform_loc_resp.location_estimate, &location);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (rc <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             smlc_loc_req_fail(LCS_CAUSE_FACILITY_NOTSUPP, "Unable to encode Location Estimate for %s (rc=%d)",</span><br><span style="color: hsl(120, 100%, 40%);">+                            gsm0808_cell_id_name_c(OTC_SELECT, &smlc_loc_req->latest_cell_id), rc);</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%);">+   LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_INFO, "Returning location estimate to BSC: %s TA=%u --> %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                        gsm0808_cell_id_name_c(OTC_SELECT, &smlc_loc_req->latest_cell_id),</span><br><span style="color: hsl(120, 100%, 40%);">+                     smlc_loc_req->ta, osmo_gad_to_str_c(OTC_SELECT, &location));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (lb_conn_send_bssmap_le(smlc_loc_req->lb_conn, &bssmap_le)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               smlc_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE,</span><br><span style="color: hsl(120, 100%, 40%);">+                             "Unable to encode/send BSSMAP-LE Perform Location Response");</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_fsm_inst_term(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%);">+static void smlc_loc_req_failed_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%);">+  struct smlc_loc_req *smlc_loc_req = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct bssmap_le_pdu bssmap_le = {</span><br><span style="color: hsl(120, 100%, 40%);">+            .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP,</span><br><span style="color: hsl(120, 100%, 40%);">+          .perform_loc_resp = {</span><br><span style="color: hsl(120, 100%, 40%);">+                 .lcs_cause = smlc_loc_req->lcs_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%);">+    int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       rc = lb_conn_send_bssmap_le(smlc_loc_req->lb_conn, &bssmap_le);</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_fsm_inst_term(fi, rc ? OSMO_FSM_TERM_ERROR : 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%);">+void smlc_loc_req_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 smlc_loc_req *smlc_loc_req = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (smlc_loc_req->lb_conn && smlc_loc_req->lb_conn->smlc_loc_req == smlc_loc_req) {</span><br><span style="color: hsl(120, 100%, 40%);">+          smlc_loc_req->lb_conn->smlc_loc_req = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+             lb_conn_put(smlc_loc_req->lb_conn, LB_CONN_USE_SMLC_LOC_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%);">+</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 smlc_loc_req_fsm_states[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+  [SMLC_LOC_REQ_ST_INIT] = {</span><br><span style="color: hsl(120, 100%, 40%);">+            .name = "INIT",</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(SMLC_LOC_REQ_ST_WAIT_TA)</span><br><span style="color: hsl(120, 100%, 40%);">+                  | S(SMLC_LOC_REQ_ST_FAILED)</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%);">+    [SMLC_LOC_REQ_ST_WAIT_TA] = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .name = "WAIT_TA",</span><br><span style="color: hsl(120, 100%, 40%);">+          .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(SMLC_LOC_REQ_EV_RX_TA_RESPONSE)</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(SMLC_LOC_REQ_EV_RX_BSSLAP_RESET)</span><br><span style="color: hsl(120, 100%, 40%);">+                  | S(SMLC_LOC_REQ_EV_RX_LE_PERFORM_LOCATION_ABORT)</span><br><span style="color: hsl(120, 100%, 40%);">+                     ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(SMLC_LOC_REQ_ST_GOT_TA)</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(SMLC_LOC_REQ_ST_FAILED)</span><br><span style="color: hsl(120, 100%, 40%);">+                   ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .onenter = smlc_loc_req_wait_ta_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+              .action = smlc_loc_req_wait_ta_action,</span><br><span style="color: hsl(120, 100%, 40%);">+        },</span><br><span style="color: hsl(120, 100%, 40%);">+    [SMLC_LOC_REQ_ST_GOT_TA] = {</span><br><span style="color: hsl(120, 100%, 40%);">+          .name = "GOT_TA",</span><br><span style="color: hsl(120, 100%, 40%);">+           .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(SMLC_LOC_REQ_ST_FAILED)</span><br><span style="color: hsl(120, 100%, 40%);">+                   ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .onenter = smlc_loc_req_got_ta_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+       },</span><br><span style="color: hsl(120, 100%, 40%);">+    [SMLC_LOC_REQ_ST_FAILED] = {</span><br><span style="color: hsl(120, 100%, 40%);">+          .name = "FAILED",</span><br><span style="color: hsl(120, 100%, 40%);">+           .onenter = smlc_loc_req_failed_onenter,</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 smlc_loc_req_fsm = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "smlc_loc_req",</span><br><span style="color: hsl(120, 100%, 40%);">+     .states = smlc_loc_req_fsm_states,</span><br><span style="color: hsl(120, 100%, 40%);">+    .num_states = ARRAY_SIZE(smlc_loc_req_fsm_states),</span><br><span style="color: hsl(120, 100%, 40%);">+    .log_subsys = DLCS,</span><br><span style="color: hsl(120, 100%, 40%);">+   .event_names = smlc_loc_req_fsm_event_names,</span><br><span style="color: hsl(120, 100%, 40%);">+  .timer_cb = smlc_loc_req_fsm_timer_cb,</span><br><span style="color: hsl(120, 100%, 40%);">+        .cleanup = smlc_loc_req_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 smlc_loc_req_fsm_register(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(osmo_fsm_register(&smlc_loc_req_fsm) == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/osmo-smlc/smlc_main.c b/src/osmo-smlc/smlc_main.c</span><br><span>index 9f32441..105ced3 100644</span><br><span>--- a/src/osmo-smlc/smlc_main.c</span><br><span>+++ b/src/osmo-smlc/smlc_main.c</span><br><span>@@ -31,12 +31,15 @@</span><br><span> #include <osmocom/vty/ports.h></span><br><span> #include <osmocom/vty/logging.h></span><br><span> #include <osmocom/vty/command.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/misc.h></span><br><span> </span><br><span> #include <osmocom/sigtran/xua_msg.h></span><br><span> #include <osmocom/sigtran/sccp_sap.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/debug.h></span><br><span> #include <osmocom/smlc/smlc_data.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/smlc/smlc_sigtran.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/sccp_lb_inst.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/cell_locations.h></span><br><span> </span><br><span> #define _GNU_SOURCE</span><br><span> #include <getopt.h></span><br><span>@@ -47,9 +50,12 @@</span><br><span> #include <time.h></span><br><span> #include <unistd.h></span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> #include "../../config.h"</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#define DEFAULT_M3UA_LOCAL_IP "localhost"</span><br><span style="color: hsl(120, 100%, 40%);">+#define DEFAULT_M3UA_REMOTE_IP "localhost"</span><br><span style="color: hsl(120, 100%, 40%);">+#define SMLC_DEFAULT_PC "0.23.6"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static const char *config_file = "osmo-smlc.cfg";</span><br><span> static int daemonize = 0;</span><br><span> static void *tall_smlc_ctx;</span><br><span>@@ -167,6 +173,26 @@</span><br><span> }</span><br><span> </span><br><span> static const struct log_info_cat smlc_categories[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+   [DSMLC] = {</span><br><span style="color: hsl(120, 100%, 40%);">+           .name = "DSMLC",</span><br><span style="color: hsl(120, 100%, 40%);">+            .description = "Serving Mobile Location Center",</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 style="color: hsl(120, 100%, 40%);">+    [DREF] = {</span><br><span style="color: hsl(120, 100%, 40%);">+            .name = "DREF",</span><br><span style="color: hsl(120, 100%, 40%);">+             .description = "Reference Counting",</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 style="color: hsl(120, 100%, 40%);">+    [DLB] = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .name = "DLB",</span><br><span style="color: hsl(120, 100%, 40%);">+              .description = "Lb interface",</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 style="color: hsl(120, 100%, 40%);">+    [DLCS] = {</span><br><span style="color: hsl(120, 100%, 40%);">+            .name = "DLCS",</span><br><span style="color: hsl(120, 100%, 40%);">+             .description = "Location Services",</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 log_info = {</span><br><span>@@ -177,6 +203,7 @@</span><br><span> int main(int argc, char **argv)</span><br><span> {</span><br><span>     int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       int default_pc;</span><br><span> </span><br><span>  tall_smlc_ctx = talloc_named_const(NULL, 1, "osmo-smlc");</span><br><span>  msgb_talloc_ctx_init(tall_smlc_ctx, 0);</span><br><span>@@ -190,13 +217,14 @@</span><br><span> </span><br><span>  osmo_fsm_set_dealloc_ctx(OTC_SELECT);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       g_smlc = talloc_zero(tall_smlc_ctx, struct smlc_state);</span><br><span style="color: hsl(0, 100%, 40%);">- OSMO_ASSERT(g_smlc);</span><br><span style="color: hsl(120, 100%, 40%);">+  g_smlc = smlc_state_alloc(tall_smlc_ctx);</span><br><span> </span><br><span>        /* This needs to precede handle_options() */</span><br><span>         vty_init(&vty_info);</span><br><span style="color: hsl(0, 100%, 40%);">-        //smlc_vty_init(g_smlc);</span><br><span style="color: hsl(120, 100%, 40%);">+      logging_vty_add_cmds();</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_talloc_vty_add_cmds();</span><br><span>  ctrl_vty_init(tall_smlc_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+ cell_locations_vty_init();</span><br><span> </span><br><span>       /* Initialize SS7 */</span><br><span>         OSMO_ASSERT(osmo_ss7_init() == 0);</span><br><span>@@ -235,9 +263,20 @@</span><br><span>    }</span><br><span>    */</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  if (smlc_sigtran_init() != 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-         LOGP(DLB, LOGL_ERROR, "Failed to initialize sigtran backhaul.\n");</span><br><span style="color: hsl(0, 100%, 40%);">-            exit(1);</span><br><span style="color: hsl(120, 100%, 40%);">+      default_pc = osmo_ss7_pointcode_parse(NULL, SMLC_DEFAULT_PC);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(default_pc);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    g_smlc->sccp_inst = osmo_sccp_simple_client_on_ss7_id(g_smlc, 0, "Lb", default_pc, OSMO_SS7_ASP_PROT_M3UA,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                            0, DEFAULT_M3UA_LOCAL_IP, 0, DEFAULT_M3UA_REMOTE_IP);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!g_smlc->sccp_inst) {</span><br><span style="color: hsl(120, 100%, 40%);">+          fprintf(stderr, "Setting up SCCP failed\n");</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   g_smlc->lb = sccp_lb_init(g_smlc, g_smlc->sccp_inst, OSMO_SCCP_SSN_SMLC_BSSAP_LE, "OsmoSMLC-Lb");</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!g_smlc->lb) {</span><br><span style="color: hsl(120, 100%, 40%);">+         fprintf(stderr, "Setting up Lb receiver failed\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         return 1;</span><br><span>    }</span><br><span> </span><br><span>        signal(SIGINT, &signal_handler);</span><br><span>diff --git a/src/osmo-smlc/smlc_sigtran.c b/src/osmo-smlc/smlc_sigtran.c</span><br><span>deleted file mode 100644</span><br><span>index 902df0c..0000000</span><br><span>--- a/src/osmo-smlc/smlc_sigtran.c</span><br><span>+++ /dev/null</span><br><span>@@ -1,94 +0,0 @@</span><br><span style="color: hsl(0, 100%, 40%);">-/* (C) 2020 by Harald Welte <laforge@gnumonks.org></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%);">- * 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/lienses/>.</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 <errno.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/core/utils.h></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/core/fsm.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/sigtran/osmo_ss7.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/sigtran/sccp_sap.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/gsm/gsm0808.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/smlc/smlc_data.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/smlc/smlc_sigtran.h></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%);">-#define DEFAULT_M3UA_REMOTE_IP     "localhost"</span><br><span style="color: hsl(0, 100%, 40%);">-#define DEFAULT_PC         "0.23.6"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph;</span><br><span style="color: hsl(0, 100%, 40%);">-   //struct osmo_sccp_user *scu = _scu;</span><br><span style="color: hsl(0, 100%, 40%);">-    int rc = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     switch (OSMO_PRIM_HDR(&scu_prim->oph)) {</span><br><span style="color: hsl(0, 100%, 40%);">- case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):</span><br><span style="color: hsl(0, 100%, 40%);">-           /* Handle inbound UNITDATA */</span><br><span style="color: hsl(0, 100%, 40%);">-           DEBUGP(DLB, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));</span><br><span style="color: hsl(0, 100%, 40%);">-             //rc = handle_unitdata_from_bsc(&scu_prim->u.unitdata.calling_addr, oph->msg, scu);</span><br><span style="color: hsl(0, 100%, 40%);">-           break;</span><br><span style="color: hsl(0, 100%, 40%);">-  case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):</span><br><span style="color: hsl(0, 100%, 40%);">-            /* Handle inbound connections */</span><br><span style="color: hsl(0, 100%, 40%);">-                DEBUGP(DLB, "N-CONNECT.ind(X->%u)\n", scu_prim->u.connect.conn_id);</span><br><span style="color: hsl(0, 100%, 40%);">-             break;</span><br><span style="color: hsl(0, 100%, 40%);">-  case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):</span><br><span style="color: hsl(0, 100%, 40%);">-               /* Handle outbound connection confirmation */</span><br><span style="color: hsl(0, 100%, 40%);">-           DEBUGP(DLB, "N-CONNECT.cnf(%u, %s)\n", scu_prim->u.connect.conn_id,</span><br><span style="color: hsl(0, 100%, 40%);">-                        osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));</span><br><span style="color: hsl(0, 100%, 40%);">-           break;</span><br><span style="color: hsl(0, 100%, 40%);">-  case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):</span><br><span style="color: hsl(0, 100%, 40%);">-               /* Handle incoming connection oriented data */</span><br><span style="color: hsl(0, 100%, 40%);">-          DEBUGP(DLB, "N-DATA.ind(%u, %s)\n", scu_prim->u.data.conn_id,</span><br><span style="color: hsl(0, 100%, 40%);">-                      osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-           break;</span><br><span style="color: hsl(0, 100%, 40%);">-  case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):</span><br><span style="color: hsl(0, 100%, 40%);">-         DEBUGP(DLB, "N-DISCONNECT.ind(%u, %s, cause=%i)\n", scu_prim->u.disconnect.conn_id,</span><br><span style="color: hsl(0, 100%, 40%);">-                        osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)),</span><br><span style="color: hsl(0, 100%, 40%);">-                    scu_prim->u.disconnect.cause);</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(DLB, LOGL_ERROR, "Unhandled SIGTRAN operation %s on primitive %u\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                     get_value_string(osmo_prim_op_names, oph->operation), oph->primitive);</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%);">-       msgb_free(oph->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%);">-int smlc_sigtran_init(void)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- struct osmo_sccp_instance *sccp;</span><br><span style="color: hsl(0, 100%, 40%);">-        int default_pc = osmo_ss7_pointcode_parse(NULL, DEFAULT_PC);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(default_pc);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        sccp = osmo_sccp_simple_client_on_ss7_id(g_smlc, 0, "Lb", default_pc, OSMO_SS7_ASP_PROT_M3UA,</span><br><span style="color: hsl(0, 100%, 40%);">-                                          0, NULL, 0, DEFAULT_M3UA_REMOTE_IP);</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%);">-   g_smlc->sccp_user = osmo_sccp_user_bind(sccp, "SMLC", sccp_sap_up, OSMO_SCCP_SSN_SMLC_BSSAP);</span><br><span style="color: hsl(0, 100%, 40%);">-      if (!g_smlc->sccp_user)</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%);">- return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span>diff --git a/src/osmo-smlc/smlc_subscr.c b/src/osmo-smlc/smlc_subscr.c</span><br><span>new file mode 100644</span><br><span>index 0000000..bfbb1e9</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-smlc/smlc_subscr.c</span><br><span>@@ -0,0 +1,125 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* GSM subscriber details for use in SMLC */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2020 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%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.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/smlc/debug.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_subscr.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void smlc_subscr_free(struct smlc_subscr *smlc_subscr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       llist_del(&smlc_subscr->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+        talloc_free(smlc_subscr);</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 smlc_subscr_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct smlc_subscr *smlc_subscr = e->use_count->talloc_object;</span><br><span style="color: hsl(120, 100%, 40%);">+  int32_t total;</span><br><span style="color: hsl(120, 100%, 40%);">+        int level;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!e->use)</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%);">+     total = osmo_use_count_total(&smlc_subscr->use_count);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (total == 0</span><br><span style="color: hsl(120, 100%, 40%);">+            || (total == 1 && old_use_count == 0 && e->count == 1))</span><br><span style="color: hsl(120, 100%, 40%);">+                level = LOGL_INFO;</span><br><span style="color: hsl(120, 100%, 40%);">+    else</span><br><span style="color: hsl(120, 100%, 40%);">+          level = LOGL_DEBUG;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGPSRC(DREF, level, file, line, "%s: %s %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+             smlc_subscr_to_str_c(OTC_SELECT, smlc_subscr),</span><br><span style="color: hsl(120, 100%, 40%);">+                (e->count - old_use_count) > 0? "+" : "-", e->use);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (e->count < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+               return -ERANGE;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (total == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+               smlc_subscr_free(smlc_subscr);</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 struct smlc_subscr *smlc_subscr_alloc()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct smlc_subscr *smlc_subscr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    smlc_subscr = talloc_zero(g_smlc, struct smlc_subscr);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!smlc_subscr)</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%);">+        smlc_subscr->use_count = (struct osmo_use_count){</span><br><span style="color: hsl(120, 100%, 40%);">+          .talloc_object = smlc_subscr,</span><br><span style="color: hsl(120, 100%, 40%);">+         .use_cb = smlc_subscr_use_cb,</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%);">+  llist_add_tail(&smlc_subscr->entry, &g_smlc->subscribers);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return smlc_subscr;</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 smlc_subscr *smlc_subscr_find(const struct osmo_mobile_identity *imsi, const char *use_token)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct smlc_subscr *smlc_subscr;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!imsi)</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%);">+        llist_for_each_entry(smlc_subscr, &g_smlc->subscribers, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+               if (!osmo_mobile_identity_cmp(&smlc_subscr->imsi, imsi)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     smlc_subscr_get(smlc_subscr, use_token);</span><br><span style="color: hsl(120, 100%, 40%);">+                      return smlc_subscr;</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 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%);">+struct smlc_subscr *smlc_subscr_find_or_create(const struct osmo_mobile_identity *imsi, const char *use_token)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct smlc_subscr *smlc_subscr;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!imsi)</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  smlc_subscr = smlc_subscr_find(imsi, use_token);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (smlc_subscr)</span><br><span style="color: hsl(120, 100%, 40%);">+              return smlc_subscr;</span><br><span style="color: hsl(120, 100%, 40%);">+   smlc_subscr = smlc_subscr_alloc();</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!smlc_subscr)</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  smlc_subscr->imsi = *imsi;</span><br><span style="color: hsl(120, 100%, 40%);">+ smlc_subscr_get(smlc_subscr, use_token);</span><br><span style="color: hsl(120, 100%, 40%);">+      return smlc_subscr;</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 smlc_subscr_to_str_buf(char *buf, size_t buf_len, const struct smlc_subscr *smlc_subscr)</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 = buf_len };</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_STRBUF_APPEND(sb, osmo_mobile_identity_to_str_buf, &smlc_subscr->imsi);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_STRBUF_PRINTF(sb, "[");</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_STRBUF_APPEND(sb, osmo_use_count_to_str_buf, &smlc_subscr->use_count);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_STRBUF_PRINTF(sb, "]");</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%);">+char *smlc_subscr_to_str_c(void *ctx, const struct smlc_subscr *smlc_subscr)</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", smlc_subscr_to_str_buf, smlc_subscr)</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/Makefile.am b/tests/Makefile.am</span><br><span>index 0cbf998..9487d3a 100644</span><br><span>--- a/tests/Makefile.am</span><br><span>+++ b/tests/Makefile.am</span><br><span>@@ -1,4 +1,5 @@</span><br><span> SUBDIRS = \</span><br><span style="color: hsl(120, 100%, 40%);">+        smlc_subscr \</span><br><span>        $(NULL)</span><br><span> </span><br><span> # The `:;' works around a Bash 3.2 bug when the output is not writeable.</span><br><span>@@ -25,6 +26,8 @@</span><br><span>      $(TESTSUITE) \</span><br><span>       test_nodes.vty \</span><br><span>     test_nodes.ctrl \</span><br><span style="color: hsl(120, 100%, 40%);">+     cell_locations.vty \</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo-smlc.cfg \</span><br><span>      $(NULL)</span><br><span> </span><br><span> TESTSUITE = $(srcdir)/testsuite</span><br><span>@@ -51,7 +54,7 @@</span><br><span> vty-test:</span><br><span>      osmo_verify_transcript_vty.py -v \</span><br><span>           -n OsmoSMLC -p 4271 \</span><br><span style="color: hsl(0, 100%, 40%);">-           -r "$(top_builddir)/src/osmo-smlc/osmo-smlc -c $(top_srcdir)/doc/examples/osmo-smlc/osmo-smlc.cfg" \</span><br><span style="color: hsl(120, 100%, 40%);">+                -r "$(top_builddir)/src/osmo-smlc/osmo-smlc -c $(top_srcdir)/tests/osmo-smlc.cfg" \</span><br><span>                $(U) $(srcdir)/$(VTY_TEST)</span><br><span> </span><br><span> # To update the CTRL script from current application behavior,</span><br><span>@@ -61,7 +64,7 @@</span><br><span>         -rm -f $(CTRL_TEST_DB)</span><br><span>       osmo_verify_transcript_ctrl.py -v \</span><br><span>          -p 4272 \</span><br><span style="color: hsl(0, 100%, 40%);">-               -r "$(top_builddir)/src/osmo-smlc/osmo-smlc -c $(top_srcdir)/doc/examples/osmo-smlc/osmo-smlc.cfg" \</span><br><span style="color: hsl(120, 100%, 40%);">+                -r "$(top_builddir)/src/osmo-smlc/osmo-smlc -c $(top_srcdir)/tests/osmo-smlc.cfg" \</span><br><span>                $(U) $(srcdir)/*.ctrl</span><br><span>        -rm -f $(CTRL_TEST_DB)</span><br><span>       -rm $(CTRL_TEST_DB)-*</span><br><span>diff --git a/tests/cell_locations.vty b/tests/cell_locations.vty</span><br><span>new file mode 100644</span><br><span>index 0000000..51dc00d</span><br><span>--- /dev/null</span><br><span>+++ b/tests/cell_locations.vty</span><br><span>@@ -0,0 +1,92 @@</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC> enable</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC# show cells</span><br><span style="color: hsl(120, 100%, 40%);">+% No cell locations are configured</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC# configure terminal</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config)# cells?</span><br><span style="color: hsl(120, 100%, 40%);">+  cells  Configure cell locations</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config)# cells</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# list</span><br><span style="color: hsl(120, 100%, 40%);">+...</span><br><span style="color: hsl(120, 100%, 40%);">+  lac-ci <0-65535> <0-65535> lat LATITUDE lon LONGITUDE</span><br><span style="color: hsl(120, 100%, 40%);">+  no lac-ci <0-65535> <0-65535></span><br><span style="color: hsl(120, 100%, 40%);">+  cgi <0-999> <0-999> <0-65535> <0-65535> lat LATITUDE lon LONGITUDE</span><br><span style="color: hsl(120, 100%, 40%);">+  no cgi <0-999> <0-999> <0-65535> <0-65535></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# lac-ci?</span><br><span style="color: hsl(120, 100%, 40%);">+  lac-ci  Cell location by LAC and CI</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# lac-ci ?</span><br><span style="color: hsl(120, 100%, 40%);">+  <0-65535>  LAC</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# lac-ci 23 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  <0-65535>  CI</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# lac-ci 23 42 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  lat  Global latitute coordinate</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# lac-ci 23 42 lat ?</span><br><span style="color: hsl(120, 100%, 40%);">+  LATITUDE  Latitude floating-point number, -90.0 (S) to 90.0 (N)</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# lac-ci 23 42 lat 23.23 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  lon  Global longitude coordinate</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# lac-ci 23 42 lat 23.23 lon ?</span><br><span style="color: hsl(120, 100%, 40%);">+  LONGITUDE  Longitude as floating-point number, -180.0 (W) to 180.0 (E)</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# lac-ci 23 42 lat 23.23 lon 42.42 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  <cr>  </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# cgi?</span><br><span style="color: hsl(120, 100%, 40%);">+  cgi  Cell location by Cell-Global ID</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# cgi ?</span><br><span style="color: hsl(120, 100%, 40%);">+  <0-999>  MCC</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# cgi 001 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  <0-999>  MNC</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# cgi 001 02 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  <0-65535>  LAC</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# cgi 001 02 3 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  <0-65535>  CI</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# cgi 001 02 3 4 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  lat  Global latitute coordinate</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# cgi 001 02 3 4 lat ?</span><br><span style="color: hsl(120, 100%, 40%);">+  LATITUDE  Latitude floating-point number, -90.0 (S) to 90.0 (N)</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# cgi 001 02 3 4 lat 1.1 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  lon  Global longitude coordinate</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# cgi 001 02 3 4 lat 1.1 lon ?</span><br><span style="color: hsl(120, 100%, 40%);">+  LONGITUDE  Longitude as floating-point number, -180.0 (W) to 180.0 (E)</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# cgi 001 02 3 4 lat 1.1 lon 2.2 ?</span><br><span style="color: hsl(120, 100%, 40%);">+  <cr>  </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# lac-ci 23 42 lat 23.23 lon 42.42</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# cgi 001 02 3 4 lat 1.1 lon 2.2</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# do show cells</span><br><span style="color: hsl(120, 100%, 40%);">+cells</span><br><span style="color: hsl(120, 100%, 40%);">+ lac-ci 23 42 lat 23.23 lon 42.42</span><br><span style="color: hsl(120, 100%, 40%);">+ cgi 001 02 3 4 lat 1.1 lon 2.2</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# show running-config</span><br><span style="color: hsl(120, 100%, 40%);">+...</span><br><span style="color: hsl(120, 100%, 40%);">+cells</span><br><span style="color: hsl(120, 100%, 40%);">+ lac-ci 23 42 lat 23.23 lon 42.42</span><br><span style="color: hsl(120, 100%, 40%);">+ cgi 001 02 3 4 lat 1.1 lon 2.2</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%);">+OsmoSMLC(config-cells)# no lac-ci 99 99</span><br><span style="color: hsl(120, 100%, 40%);">+% cannot remove, no such entry</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# no cgi 009 08 7 6</span><br><span style="color: hsl(120, 100%, 40%);">+% cannot remove, no such entry</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# do show cells</span><br><span style="color: hsl(120, 100%, 40%);">+cells</span><br><span style="color: hsl(120, 100%, 40%);">+ lac-ci 23 42 lat 23.23 lon 42.42</span><br><span style="color: hsl(120, 100%, 40%);">+ cgi 001 02 3 4 lat 1.1 lon 2.2</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# lac-ci 23 42 lat 17.17 lon 18.18</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# do show cells</span><br><span style="color: hsl(120, 100%, 40%);">+cells</span><br><span style="color: hsl(120, 100%, 40%);">+ lac-ci 23 42 lat 17.17 lon 18.18</span><br><span style="color: hsl(120, 100%, 40%);">+ cgi 001 02 3 4 lat 1.1 lon 2.2</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# no lac-ci 23 42</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# no cgi 001 02 3 4</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoSMLC(config-cells)# do show cells</span><br><span style="color: hsl(120, 100%, 40%);">+% No cell locations are configured</span><br><span>diff --git a/tests/osmo-smlc.cfg b/tests/osmo-smlc.cfg</span><br><span>new file mode 100644</span><br><span>index 0000000..e69de29</span><br><span>--- /dev/null</span><br><span>+++ b/tests/osmo-smlc.cfg</span><br><span>diff --git a/tests/smlc_subscr/Makefile.am b/tests/smlc_subscr/Makefile.am</span><br><span>new file mode 100644</span><br><span>index 0000000..9ed3b59</span><br><span>--- /dev/null</span><br><span>+++ b/tests/smlc_subscr/Makefile.am</span><br><span>@@ -0,0 +1,39 @@</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%);">+     -I$(top_srcdir)/include \</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%);">+      -Wall \</span><br><span style="color: hsl(120, 100%, 40%);">+       -ggdb3 \</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%);">+        $(COVERAGE_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%);">+     $(COVERAGE_LDFLAGS) \</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%);">+     smlc_subscr_test.ok \</span><br><span style="color: hsl(120, 100%, 40%);">+ smlc_subscr_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%);">+        smlc_subscr_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%);">+smlc_subscr_test_SOURCES = \</span><br><span style="color: hsl(120, 100%, 40%);">+       smlc_subscr_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%);">+smlc_subscr_test_LDADD = \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(top_builddir)/src/osmo-smlc/smlc_data.o \</span><br><span style="color: hsl(120, 100%, 40%);">+   $(top_builddir)/src/osmo-smlc/smlc_subscr.o \</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%);">+update_exp:</span><br><span style="color: hsl(120, 100%, 40%);">+        $(builddir)/smlc_subscr_test >$(srcdir)/smlc_subscr_test.ok 2>$(srcdir)/smlc_subscr_test.err</span><br><span>diff --git a/tests/smlc_subscr/smlc_subscr_test.c b/tests/smlc_subscr/smlc_subscr_test.c</span><br><span>new file mode 100644</span><br><span>index 0000000..92b7293</span><br><span>--- /dev/null</span><br><span>+++ b/tests/smlc_subscr/smlc_subscr_test.c</span><br><span>@@ -0,0 +1,157 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2020 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%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.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/smlc/debug.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/smlc/smlc_subscr.h></span><br><span style="color: hsl(120, 100%, 40%);">+</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/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdio.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdlib.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%);">+struct smlc_state *g_smlc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define VERBOSE_ASSERT(val, expect_op, fmt) \</span><br><span style="color: hsl(120, 100%, 40%);">+  do { \</span><br><span style="color: hsl(120, 100%, 40%);">+                printf(#val " == " fmt "\n", (val)); \</span><br><span style="color: hsl(120, 100%, 40%);">+            OSMO_ASSERT((val) expect_op); \</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%);">+#define USE_FOO "foo"</span><br><span style="color: hsl(120, 100%, 40%);">+#define USE_BAR "bar"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void assert_smlc_subscr(const struct smlc_subscr *smlc_subscr, const struct osmo_mobile_identity *imsi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct smlc_subscr *sfound;</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(smlc_subscr);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(osmo_mobile_identity_cmp(&smlc_subscr->imsi, imsi) == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        sfound = smlc_subscr_find(imsi, __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(sfound == smlc_subscr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ smlc_subscr_put(sfound, __func__);</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 test_smlc_subscr(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct smlc_subscr *s1, *s2, *s3;</span><br><span style="color: hsl(120, 100%, 40%);">+     const struct osmo_mobile_identity imsi1 = { .type = GSM_MI_TYPE_IMSI, .imsi = "1234567890", };</span><br><span style="color: hsl(120, 100%, 40%);">+      const struct osmo_mobile_identity imsi2 = { .type = GSM_MI_TYPE_IMSI, .imsi = "9876543210", };</span><br><span style="color: hsl(120, 100%, 40%);">+      const struct osmo_mobile_identity imsi3 = { .type = GSM_MI_TYPE_IMSI, .imsi = "423423", };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        printf("Test SMLC subscriber allocation and deletion\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Check for emptiness */</span><br><span style="color: hsl(120, 100%, 40%);">+     VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 0, "%d");</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(smlc_subscr_find(&imsi1, "-") == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(smlc_subscr_find(&imsi2, "-") == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(smlc_subscr_find(&imsi3, "-") == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Allocate entry 1 */</span><br><span style="color: hsl(120, 100%, 40%);">+        s1 = smlc_subscr_find_or_create(&imsi1, USE_FOO);</span><br><span style="color: hsl(120, 100%, 40%);">+ VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 1, "%d");</span><br><span style="color: hsl(120, 100%, 40%);">+       assert_smlc_subscr(s1, &imsi1);</span><br><span style="color: hsl(120, 100%, 40%);">+   VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 1, "%d");</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(smlc_subscr_find(&imsi2, "-") == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Allocate entry 2 */</span><br><span style="color: hsl(120, 100%, 40%);">+        s2 = smlc_subscr_find_or_create(&imsi2, USE_BAR);</span><br><span style="color: hsl(120, 100%, 40%);">+ VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 2, "%d");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Allocate entry 3 */</span><br><span style="color: hsl(120, 100%, 40%);">+        s3 = smlc_subscr_find_or_create(&imsi3, USE_FOO);</span><br><span style="color: hsl(120, 100%, 40%);">+ smlc_subscr_get(s3, USE_BAR);</span><br><span style="color: hsl(120, 100%, 40%);">+ VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 3, "%d");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Check entries */</span><br><span style="color: hsl(120, 100%, 40%);">+   assert_smlc_subscr(s1, &imsi1);</span><br><span style="color: hsl(120, 100%, 40%);">+   assert_smlc_subscr(s2, &imsi2);</span><br><span style="color: hsl(120, 100%, 40%);">+   assert_smlc_subscr(s3, &imsi3);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Free entry 1 */</span><br><span style="color: hsl(120, 100%, 40%);">+    smlc_subscr_put(s1, USE_FOO);</span><br><span style="color: hsl(120, 100%, 40%);">+ s1 = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 2, "%d");</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(smlc_subscr_find(&imsi1, "-") == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   assert_smlc_subscr(s2, &imsi2);</span><br><span style="color: hsl(120, 100%, 40%);">+   assert_smlc_subscr(s3, &imsi3);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Free entry 2 */</span><br><span style="color: hsl(120, 100%, 40%);">+    smlc_subscr_put(s2, USE_BAR);</span><br><span style="color: hsl(120, 100%, 40%);">+ s2 = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 1, "%d");</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(smlc_subscr_find(&imsi1, "-") == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(smlc_subscr_find(&imsi2, "-") == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     assert_smlc_subscr(s3, &imsi3);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Remove one use of entry 3 */</span><br><span style="color: hsl(120, 100%, 40%);">+       smlc_subscr_put(s3, USE_BAR);</span><br><span style="color: hsl(120, 100%, 40%);">+ assert_smlc_subscr(s3, &imsi3);</span><br><span style="color: hsl(120, 100%, 40%);">+   VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 1, "%d");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Free entry 3 */</span><br><span style="color: hsl(120, 100%, 40%);">+    smlc_subscr_put(s3, USE_FOO);</span><br><span style="color: hsl(120, 100%, 40%);">+ s3 = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 0, "%d");</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(smlc_subscr_find(&imsi3, "-") == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(llist_empty(&g_smlc->subscribers));</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 log_info_cat log_categories[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+    [DREF] = {</span><br><span style="color: hsl(120, 100%, 40%);">+            .name = "DREF",</span><br><span style="color: hsl(120, 100%, 40%);">+             .description = "Reference Counting",</span><br><span style="color: hsl(120, 100%, 40%);">+                .enabled = 1, .loglevel = LOGL_DEBUG,</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 const struct log_info log_info = {</span><br><span style="color: hsl(120, 100%, 40%);">+   .cat = log_categories,</span><br><span style="color: hsl(120, 100%, 40%);">+        .num_cat = ARRAY_SIZE(log_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()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   void *ctx = talloc_named_const(NULL, 0, "smlc_subscr_test");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_init_logging2(ctx, &log_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%);">+      g_smlc = smlc_state_alloc(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("Testing SMLC subscriber code.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        test_smlc_subscr();</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 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>diff --git a/tests/smlc_subscr/smlc_subscr_test.err b/tests/smlc_subscr/smlc_subscr_test.err</span><br><span>new file mode 100644</span><br><span>index 0000000..8e0d1fa</span><br><span>--- /dev/null</span><br><span>+++ b/tests/smlc_subscr/smlc_subscr_test.err</span><br><span>@@ -0,0 +1,24 @@</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-1234567890[1 (foo)]: + foo</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-1234567890[2 (foo,assert_smlc_subscr)]: + assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-1234567890[1 (foo)]: - assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-9876543210[1 (bar)]: + bar</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-423423[1 (foo)]: + foo</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-423423[2 (foo,bar)]: + bar</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-1234567890[2 (foo,assert_smlc_subscr)]: + assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-1234567890[1 (foo)]: - assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-9876543210[2 (bar,assert_smlc_subscr)]: + assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-9876543210[1 (bar)]: - assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-423423[3 (foo,bar,assert_smlc_subscr)]: + assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-423423[2 (foo,bar)]: - assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-1234567890[0 (-)]: - foo</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-9876543210[2 (bar,assert_smlc_subscr)]: + assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-9876543210[1 (bar)]: - assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-423423[3 (foo,bar,assert_smlc_subscr)]: + assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-423423[2 (foo,bar)]: - assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-9876543210[0 (-)]: - bar</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-423423[3 (foo,bar,assert_smlc_subscr)]: + assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-423423[2 (foo,bar)]: - assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-423423[1 (foo)]: - bar</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-423423[2 (foo,assert_smlc_subscr)]: + assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-423423[1 (foo)]: - assert_smlc_subscr</span><br><span style="color: hsl(120, 100%, 40%);">+DREF IMSI-423423[0 (-)]: - foo</span><br><span>diff --git a/tests/smlc_subscr/smlc_subscr_test.ok b/tests/smlc_subscr/smlc_subscr_test.ok</span><br><span>new file mode 100644</span><br><span>index 0000000..c85007d</span><br><span>--- /dev/null</span><br><span>+++ b/tests/smlc_subscr/smlc_subscr_test.ok</span><br><span>@@ -0,0 +1,12 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Testing SMLC subscriber code.</span><br><span style="color: hsl(120, 100%, 40%);">+Test SMLC subscriber allocation and deletion</span><br><span style="color: hsl(120, 100%, 40%);">+llist_count(&g_smlc->subscribers) == 0</span><br><span style="color: hsl(120, 100%, 40%);">+llist_count(&g_smlc->subscribers) == 1</span><br><span style="color: hsl(120, 100%, 40%);">+llist_count(&g_smlc->subscribers) == 1</span><br><span style="color: hsl(120, 100%, 40%);">+llist_count(&g_smlc->subscribers) == 2</span><br><span style="color: hsl(120, 100%, 40%);">+llist_count(&g_smlc->subscribers) == 3</span><br><span style="color: hsl(120, 100%, 40%);">+llist_count(&g_smlc->subscribers) == 2</span><br><span style="color: hsl(120, 100%, 40%);">+llist_count(&g_smlc->subscribers) == 1</span><br><span style="color: hsl(120, 100%, 40%);">+llist_count(&g_smlc->subscribers) == 1</span><br><span style="color: hsl(120, 100%, 40%);">+llist_count(&g_smlc->subscribers) == 0</span><br><span style="color: hsl(120, 100%, 40%);">+Done</span><br><span>diff --git a/tests/testsuite.at b/tests/testsuite.at</span><br><span>index 09a77c3..0a3b9bb 100644</span><br><span>--- a/tests/testsuite.at</span><br><span>+++ b/tests/testsuite.at</span><br><span>@@ -1,2 +1,9 @@</span><br><span> AT_INIT</span><br><span> AT_BANNER([Regression tests.])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AT_SETUP([smlc_subscr])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_KEYWORDS([smlc_subscr])</span><br><span style="color: hsl(120, 100%, 40%);">+cat $abs_srcdir/smlc_subscr/smlc_subscr_test.ok > expout</span><br><span style="color: hsl(120, 100%, 40%);">+cat $abs_srcdir/smlc_subscr/smlc_subscr_test.err > experr</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CHECK([$abs_top_builddir/tests/smlc_subscr/smlc_subscr_test], [], [expout], [experr])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CLEANUP</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-smlc/+/20470">change 20470</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-smlc/+/20470"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-smlc </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I917ba8fc51a1f1150be77ae01e12a7b16a853052 </div>
<div style="display:none"> Gerrit-Change-Number: 20470 </div>
<div style="display:none"> Gerrit-PatchSet: 7 </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: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-CC: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>