pespin has submitted this change. ( 
https://gerrit.osmocom.org/c/osmo-hnbgw/+/40243?usp=email )

Change subject: Move SCCP prim handling to its own hnbgw_sccp.{c,h} file
......................................................................

Move SCCP prim handling to its own hnbgw_sccp.{c,h} file

Similar to what we already have for other protocols; helps properly
splitting logic per layer.

Change-Id: Idb45a25f7ff4bcd38ffd27bf1d3360b9d34149b3
---
M include/osmocom/hnbgw/Makefile.am
M include/osmocom/hnbgw/hnbgw.h
M include/osmocom/hnbgw/hnbgw_cn.h
A include/osmocom/hnbgw/hnbgw_sccp.h
M src/osmo-hnbgw/Makefile.am
M src/osmo-hnbgw/hnbgw_cn.c
A src/osmo-hnbgw/hnbgw_sccp.c
7 files changed, 432 insertions(+), 376 deletions(-)

Approvals:
  laforge: Looks good to me, but someone else must approve
  osmith: Looks good to me, approved
  Jenkins Builder: Verified




diff --git a/include/osmocom/hnbgw/Makefile.am 
b/include/osmocom/hnbgw/Makefile.am
index 03821d5..d3fbb07 100644
--- a/include/osmocom/hnbgw/Makefile.am
+++ b/include/osmocom/hnbgw/Makefile.am
@@ -6,6 +6,7 @@
        hnbgw_pfcp.h \
        hnbgw_ranap.h \
        hnbgw_rua.h \
+       hnbgw_sccp.h \
        kpi.h \
        mgw_fsm.h \
        nft_kpi.h \
diff --git a/include/osmocom/hnbgw/hnbgw.h b/include/osmocom/hnbgw/hnbgw.h
index 23857d5..3995294 100644
--- a/include/osmocom/hnbgw/hnbgw.h
+++ b/include/osmocom/hnbgw/hnbgw.h
@@ -8,7 +8,6 @@
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/core/sockaddr_str.h>
 #include <osmocom/gsm/gsm23003.h>
-#include <osmocom/sigtran/sccp_sap.h>
 #include <osmocom/sigtran/osmo_ss7.h>
 #include <osmocom/ctrl/control_if.h>
 #include <osmocom/ranap/RANAP_CN-DomainIndicator.h>
@@ -20,6 +19,7 @@
 #include <osmocom/mgcp_client/mgcp_client_pool.h>

 #include <osmocom/hnbgw/nft_kpi.h>
+#include <osmocom/hnbgw/hnbgw_sccp.h>

 #define STORE_UPTIME_INTERVAL  10 /* seconds */
 #define HNB_STORE_RAB_DURATIONS_INTERVAL 1 /* seconds */
@@ -202,32 +202,6 @@

 struct hnbgw_context_map;

-/* osmo-hnbgw keeps a single hnbgw_sccp_user per osmo_sccp_instance, for the 
local point-code and SSN == RANAP.
- * This relates the (opaque) osmo_sccp_user to osmo-hnbgw's per-ss7 state. */
-struct hnbgw_sccp_user {
-       /* entry in g_hnbgw->sccp.users */
-       struct llist_head entry;
-
-       /* logging context */
-       char *name;
-
-       /* Which 'cs7 instance' is this for? Below sccp_user is registered at 
the osmo_sccp_instance ss7->sccp. */
-       struct osmo_ss7_instance *ss7;
-
-       /* Local address: cs7 instance's primary PC if present, else the 
default HNBGW PC; with SSN == RANAP. */
-       struct osmo_sccp_addr local_addr;
-
-       /* osmo_sccp API state for above local address on above ss7 instance. */
-       struct osmo_sccp_user *sccp_user;
-
-       /* Fast access to the hnbgw_context_map responsible for a given SCCP 
conn_id of the ss7->sccp instance.
-        * hlist_node: hnbgw_context_map->hnbgw_sccp_user_entry. */
-       DECLARE_HASHTABLE(hnbgw_context_map_by_conn_id, 6);
-};
-
-#define LOG_HSI(HNBGW_SCCP_INST, SUBSYS, LEVEL, FMT, ARGS...) \
-       LOGP(SUBSYS, LEVEL, "(%s) " FMT, (HNBGW_SCCP_INST) ? 
(HNBGW_SCCP_INST)->name : "null", ##ARGS)
-
 /* User provided configuration for struct hnbgw_cnpool. */
 struct hnbgw_cnpool_cfg {
        uint8_t nri_bitlen;
diff --git a/include/osmocom/hnbgw/hnbgw_cn.h b/include/osmocom/hnbgw/hnbgw_cn.h
index a5fa132..78b82b7 100644
--- a/include/osmocom/hnbgw/hnbgw_cn.h
+++ b/include/osmocom/hnbgw/hnbgw_cn.h
@@ -8,8 +8,6 @@
 #include <osmocom/hnbgw/hnbgw.h>

 struct hnbgw_cnlink *cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr);
-struct hnbgw_cnlink *hnbgw_cnlink_find_by_addr(const struct hnbgw_sccp_user 
*hsu,
-                                              const struct osmo_sccp_addr 
*remote_addr);
 struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnbgw_context_map *map);

 void hnbgw_cnpool_start(struct hnbgw_cnpool *cnpool);
diff --git a/include/osmocom/hnbgw/hnbgw_sccp.h 
b/include/osmocom/hnbgw/hnbgw_sccp.h
new file mode 100644
index 0000000..8188b55
--- /dev/null
+++ b/include/osmocom/hnbgw/hnbgw_sccp.h
@@ -0,0 +1,38 @@
+/* SCCP, ITU Q.711 - Q.714 */
+#pragma once
+
+#include <osmocom/core/hashtable.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/prim.h>
+
+#include <osmocom/sigtran/sccp_sap.h>
+
+struct hnbgw_cnlink;
+
+/* osmo-hnbgw keeps a single hnbgw_sccp_user per osmo_sccp_instance, for the 
local point-code and SSN == RANAP.
+ * This relates the (opaque) osmo_sccp_user to osmo-hnbgw's per-ss7 state. */
+struct hnbgw_sccp_user {
+       /* entry in g_hnbgw->sccp.users */
+       struct llist_head entry;
+
+       /* logging context */
+       char *name;
+
+       /* Which 'cs7 instance' is this for? Below sccp_user is registered at 
the osmo_sccp_instance ss7->sccp. */
+       struct osmo_ss7_instance *ss7;
+
+       /* Local address: cs7 instance's primary PC if present, else the 
default HNBGW PC; with SSN == RANAP. */
+       struct osmo_sccp_addr local_addr;
+
+       /* osmo_sccp API state for above local address on above ss7 instance. */
+       struct osmo_sccp_user *sccp_user;
+
+       /* Fast access to the hnbgw_context_map responsible for a given SCCP 
conn_id of the ss7->sccp instance.
+        * hlist_node: hnbgw_context_map->hnbgw_sccp_user_entry. */
+       DECLARE_HASHTABLE(hnbgw_context_map_by_conn_id, 6);
+};
+
+#define LOG_HSI(HNBGW_SCCP_INST, SUBSYS, LEVEL, FMT, ARGS...) \
+       LOGP(SUBSYS, LEVEL, "(%s) " FMT, (HNBGW_SCCP_INST) ? 
(HNBGW_SCCP_INST)->name : "null", ##ARGS)
+
+struct hnbgw_sccp_user *hnbgw_sccp_user_alloc(const struct hnbgw_cnlink 
*cnlink, int ss7_inst_id);
diff --git a/src/osmo-hnbgw/Makefile.am b/src/osmo-hnbgw/Makefile.am
index e108906..df2bb23 100644
--- a/src/osmo-hnbgw/Makefile.am
+++ b/src/osmo-hnbgw/Makefile.am
@@ -37,6 +37,7 @@
        hnbgw_l3.c \
        hnbgw_rua.c \
        hnbgw_ranap.c \
+       hnbgw_sccp.c \
        hnbgw_vty.c \
        context_map.c \
        context_map_rua.c \
diff --git a/src/osmo-hnbgw/hnbgw_cn.c b/src/osmo-hnbgw/hnbgw_cn.c
index 49ae9ae..7b5c575 100644
--- a/src/osmo-hnbgw/hnbgw_cn.c
+++ b/src/osmo-hnbgw/hnbgw_cn.c
@@ -37,7 +37,7 @@
 #include <osmocom/sigtran/sccp_helpers.h>

 #include <osmocom/hnbgw/hnbgw.h>
-#include <osmocom/hnbgw/hnbgw_rua.h>
+#include <osmocom/hnbgw/hnbgw_sccp.h>
 #include <osmocom/hnbgw/hnbgw_ranap.h>
 #include <osmocom/hnbgw/hnbgw_cn.h>
 #include <osmocom/hnbgw/context_map.h>
@@ -46,273 +46,6 @@
  * Incoming primitives from SCCP User SAP
  ***********************************************************************/

-static struct hnbgw_cnlink *cnlink_from_addr(struct hnbgw_sccp_user *hsu, 
const struct osmo_sccp_addr *calling_addr,
-                                            const struct osmo_prim_hdr *oph)
-{
-       struct hnbgw_cnlink *cnlink = NULL;
-       cnlink = hnbgw_cnlink_find_by_addr(hsu, calling_addr);
-       if (!cnlink) {
-               LOG_HSI(hsu, DRANAP, LOGL_ERROR, "Rx from unknown SCCP peer: 
%s: %s\n",
-                       osmo_sccp_inst_addr_name(osmo_ss7_get_sccp(hsu->ss7), 
calling_addr),
-                       osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
-               return NULL;
-       }
-       return cnlink;
-}
-
-static struct hnbgw_context_map *map_from_conn_id(struct hnbgw_sccp_user *hsu, 
uint32_t conn_id,
-                                                 const struct osmo_prim_hdr 
*oph)
-{
-       struct hnbgw_context_map *map;
-       hash_for_each_possible(hsu->hnbgw_context_map_by_conn_id, map, 
hnbgw_sccp_user_entry, conn_id) {
-               if (map->scu_conn_id == conn_id)
-                       return map;
-       }
-       LOGP(DRANAP, LOGL_ERROR, "Rx for unknown SCCP connection ID: %u: %s\n",
-            conn_id, osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
-       return NULL;
-}
-
-static int handle_cn_unitdata(struct hnbgw_sccp_user *hsu,
-                             const struct osmo_scu_unitdata_param *param,
-                             struct osmo_prim_hdr *oph)
-{
-       struct hnbgw_cnlink *cnlink = cnlink_from_addr(hsu, 
&param->calling_addr, oph);
-       if (!cnlink)
-               return -ENOENT;
-
-       if (param->called_addr.ssn != OSMO_SCCP_SSN_RANAP) {
-               LOGP(DCN, LOGL_NOTICE, "N-UNITDATA.ind for unknown SSN %u\n",
-                       param->called_addr.ssn);
-               return -1;
-       }
-
-       return hnbgw_ranap_rx_udt_dl(cnlink, param, msgb_l2(oph->msg), 
msgb_l2len(oph->msg));
-}
-
-static int handle_cn_conn_conf(struct hnbgw_sccp_user *hsu,
-                              const struct osmo_scu_connect_param *param,
-                              struct osmo_prim_hdr *oph)
-{
-       struct hnbgw_context_map *map;
-
-       map = map_from_conn_id(hsu, param->conn_id, oph);
-       if (!map || !map->cnlink)
-               return -ENOENT;
-
-       LOGP(DCN, LOGL_DEBUG, "handle_cn_conn_conf() conn_id=%d, addrs: 
called=%s calling=%s responding=%s\n",
-            param->conn_id,
-            cnlink_sccp_addr_to_str(map->cnlink, &param->called_addr),
-            cnlink_sccp_addr_to_str(map->cnlink, &param->calling_addr),
-            cnlink_sccp_addr_to_str(map->cnlink, &param->responding_addr));
-
-       map_sccp_dispatch(map, MAP_SCCP_EV_RX_CONNECTION_CONFIRM, oph->msg);
-       return 0;
-}
-
-static int handle_cn_data_ind(struct hnbgw_sccp_user *hsu,
-                             const struct osmo_scu_data_param *param,
-                             struct osmo_prim_hdr *oph)
-{
-       struct hnbgw_context_map *map;
-
-       map = map_from_conn_id(hsu, param->conn_id, oph);
-       if (!map || !map->cnlink)
-               return -ENOENT;
-
-       return map_sccp_dispatch(map, MAP_SCCP_EV_RX_DATA_INDICATION, oph->msg);
-}
-
-static int handle_cn_disc_ind(struct hnbgw_sccp_user *hsu,
-                             const struct osmo_scu_disconn_param *param,
-                             struct osmo_prim_hdr *oph)
-{
-       struct hnbgw_context_map *map;
-       char cause_buf[128];
-
-       map = map_from_conn_id(hsu, param->conn_id, oph);
-       if (!map || !map->cnlink)
-               return -ENOENT;
-
-       LOGP(DCN, LOGL_DEBUG, "handle_cn_disc_ind() conn_id=%u 
responding_addr=%s cause=%s\n",
-            param->conn_id,
-            cnlink_sccp_addr_to_str(map->cnlink, &param->responding_addr),
-            osmo_sua_sccp_cause_name(param->cause, cause_buf, 
sizeof(cause_buf)));
-
-       return map_sccp_dispatch(map, MAP_SCCP_EV_RX_RELEASED, oph->msg);
-}
-
-static struct hnbgw_cnlink *_cnlink_find_by_remote_pc(struct hnbgw_cnpool 
*cnpool, struct osmo_ss7_instance *cs7, uint32_t pc)
-{
-       struct hnbgw_cnlink *cnlink;
-       llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
-               if (!cnlink->hnbgw_sccp_user)
-                       continue;
-               if (cnlink->hnbgw_sccp_user->ss7 != cs7)
-                       continue;
-               if ((cnlink->remote_addr.presence & OSMO_SCCP_ADDR_T_PC) == 0)
-                       continue;
-               if (cnlink->remote_addr.pc != pc)
-                       continue;
-               return cnlink;
-       }
-       return NULL;
-}
-
-/* Find a cnlink by its remote sigtran point code on a given cs7 instance. */
-static struct hnbgw_cnlink *cnlink_find_by_remote_pc(struct osmo_ss7_instance 
*cs7, uint32_t pc)
-{
-       struct hnbgw_cnlink *cnlink;
-       cnlink = _cnlink_find_by_remote_pc(&g_hnbgw->sccp.cnpool_iucs, cs7, pc);
-       if (!cnlink)
-               cnlink = _cnlink_find_by_remote_pc(&g_hnbgw->sccp.cnpool_iups, 
cs7, pc);
-       return cnlink;
-}
-
-static void handle_pcstate_ind(struct hnbgw_sccp_user *hsu, const struct 
osmo_scu_pcstate_param *pcst)
-{
-       struct hnbgw_cnlink *cnlink;
-       bool connected;
-       bool disconnected;
-       struct osmo_ss7_instance *cs7 = hsu->ss7;
-
-       LOGP(DCN, LOGL_DEBUG, "N-PCSTATE ind: affected_pc=%u sp_status=%s 
remote_sccp_status=%s\n",
-            pcst->affected_pc, osmo_sccp_sp_status_name(pcst->sp_status),
-            osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
-
-       /* If we don't care about that point-code, ignore PCSTATE. */
-       cnlink = cnlink_find_by_remote_pc(cs7, pcst->affected_pc);
-       if (!cnlink)
-               return;
-
-       /* See if this marks the point code to have become available, or to 
have been lost.
-        *
-        * I want to detect two events:
-        * - connection event (both indicators say PC is reachable).
-        * - disconnection event (at least one indicator says the PC is not 
reachable).
-        *
-        * There are two separate incoming indicators with various possible 
values -- the incoming events can be:
-        *
-        * - neither connection nor disconnection indicated -- just indicating 
congestion
-        *   connected == false, disconnected == false --> do nothing.
-        * - both incoming values indicate that we are connected
-        *   --> trigger connected
-        * - both indicate we are disconnected
-        *   --> trigger disconnected
-        * - one value indicates 'connected', the other indicates 'disconnected'
-        *   --> trigger disconnected
-        *
-        * Congestion could imply that we're connected, but it does not 
indicate that a PC's reachability changed, so no need to
-        * trigger on that.
-        */
-       connected = false;
-       disconnected = false;
-
-       switch (pcst->sp_status) {
-       case OSMO_SCCP_SP_S_ACCESSIBLE:
-               connected = true;
-               break;
-       case OSMO_SCCP_SP_S_INACCESSIBLE:
-               disconnected = true;
-               break;
-       default:
-       case OSMO_SCCP_SP_S_CONGESTED:
-               /* Neither connecting nor disconnecting */
-               break;
-       }
-
-       switch (pcst->remote_sccp_status) {
-       case OSMO_SCCP_REM_SCCP_S_AVAILABLE:
-               if (!disconnected)
-                       connected = true;
-               break;
-       case OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN:
-       case OSMO_SCCP_REM_SCCP_S_UNEQUIPPED:
-       case OSMO_SCCP_REM_SCCP_S_INACCESSIBLE:
-               disconnected = true;
-               connected = false;
-               break;
-       default:
-       case OSMO_SCCP_REM_SCCP_S_CONGESTED:
-               /* Neither connecting nor disconnecting */
-               break;
-       }
-
-       if (disconnected && cnlink_is_conn_ready(cnlink)) {
-               LOG_CNLINK(cnlink, DCN, LOGL_NOTICE,
-                          "now unreachable: N-PCSTATE ind: pc=%u sp_status=%s 
remote_sccp_status=%s\n",
-                          pcst->affected_pc,
-                          osmo_sccp_sp_status_name(pcst->sp_status),
-                          
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
-               /* A previously usable cnlink has disconnected. Kick it back to 
DISC state. */
-               cnlink_set_disconnected(cnlink);
-       } else if (connected && !cnlink_is_conn_ready(cnlink)) {
-               LOG_CNLINK(cnlink, DCN, LOGL_NOTICE,
-                          "now available: N-PCSTATE ind: pc=%u sp_status=%s 
remote_sccp_status=%s\n",
-                          pcst->affected_pc,
-                          osmo_sccp_sp_status_name(pcst->sp_status),
-                          
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
-               /* A previously unusable cnlink has become reachable. Trigger 
immediate RANAP RESET -- we would resend a
-                * RESET either way, but we might as well do it now to speed up 
connecting. */
-               cnlink_resend_reset(cnlink);
-       }
-}
-
-/* Entry point for primitives coming up from SCCP User SAP.
- * Ownership of oph->msg is transferred to us. */
-static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx)
-{
-       struct osmo_sccp_user *scu = ctx;
-       struct hnbgw_sccp_user *hsu;
-       struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
-       int rc = 0;
-
-       LOGP(DCN, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph));
-
-       if (!scu) {
-               LOGP(DCN, LOGL_ERROR,
-                    "sccp_sap_up(): NULL osmo_sccp_user, cannot send prim (sap 
%u prim %u op %d)\n",
-                    oph->sap, oph->primitive, oph->operation);
-               return -1;
-       }
-
-       hsu = osmo_sccp_user_get_priv(scu);
-       if (!hsu) {
-               LOGP(DCN, LOGL_ERROR,
-                    "sccp_sap_up(): NULL hnbgw_sccp_user, cannot send prim 
(sap %u prim %u op %d)\n",
-                    oph->sap, oph->primitive, oph->operation);
-               return -1;
-       }
-
-       talloc_steal(OTC_SELECT, oph->msg);
-
-       switch (OSMO_PRIM_HDR(oph)) {
-       case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
-               rc = handle_cn_unitdata(hsu, &prim->u.unitdata, oph);
-               break;
-       case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
-               rc = handle_cn_conn_conf(hsu, &prim->u.connect, oph);
-               break;
-       case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
-               rc = handle_cn_data_ind(hsu, &prim->u.data, oph);
-               break;
-       case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
-               rc = handle_cn_disc_ind(hsu, &prim->u.disconnect, oph);
-               break;
-       case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION):
-               handle_pcstate_ind(hsu, &prim->u.pcstate);
-               break;
-
-       default:
-               LOGP(DCN, LOGL_ERROR,
-                       "Received unknown prim %u from SCCP USER SAP\n",
-                       OSMO_PRIM_HDR(oph));
-               break;
-       }
-
-       return rc;
-}
-
 static bool addr_has_pc_and_ssn(const struct osmo_sccp_addr *addr)
 {
        if (!(addr->presence & OSMO_SCCP_ADDR_T_SSN))
@@ -423,61 +156,6 @@
                   cnlink->name, cnlink->use.remote_addr_name ? : "(default 
remote point-code)");
 }

-static struct hnbgw_sccp_user *hnbgw_sccp_user_alloc(const struct hnbgw_cnlink 
*cnlink, int ss7_id)
-{
-       struct osmo_ss7_instance *ss7 = NULL;
-       struct osmo_sccp_instance *sccp;
-       struct osmo_sccp_user *sccp_user;
-       uint32_t local_pc;
-       struct hnbgw_sccp_user *hsu;
-
-       sccp = osmo_sccp_simple_client_on_ss7_id(g_hnbgw,
-                                                ss7_id,
-                                                cnlink->name,
-                                                DEFAULT_PC_HNBGW,
-                                                OSMO_SS7_ASP_PROT_M3UA,
-                                                0,
-                                                "localhost",
-                                                -1,
-                                                "localhost");
-       if (!sccp) {
-               LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Failed to configure SCCP 
on 'cs7 instance %u'\n",
-                          ss7_id);
-               return NULL;
-       }
-       ss7 = osmo_sccp_get_ss7(sccp);
-       LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "created SCCP instance on cs7 
instance %u\n", osmo_ss7_instance_get_id(ss7));
-
-       /* Bind the SCCP user, using the cs7 instance's default point-code if 
one is configured, or osmo-hnbgw's default
-        * local PC. */
-       local_pc = osmo_ss7_instance_get_primary_pc(ss7);
-       if (!osmo_ss7_pc_is_valid(local_pc))
-               local_pc = DEFAULT_PC_HNBGW;
-
-       LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "binding OsmoHNBGW user to cs7 
instance %u, local PC %u = %s\n",
-                  osmo_ss7_instance_get_id(ss7), local_pc, 
osmo_ss7_pointcode_print(ss7, local_pc));
-
-       sccp_user = osmo_sccp_user_bind_pc(sccp, "OsmoHNBGW", sccp_sap_up, 
OSMO_SCCP_SSN_RANAP, local_pc);
-       if (!sccp_user) {
-               LOGP(DCN, LOGL_ERROR, "Failed to init SCCP User\n");
-               return NULL;
-       }
-
-       hsu = talloc_zero(cnlink, struct hnbgw_sccp_user);
-       *hsu = (struct hnbgw_sccp_user){
-               .name = talloc_asprintf(hsu, "cs7-%u.sccp", 
osmo_ss7_instance_get_id(ss7)),
-               .ss7 = ss7,
-               .sccp_user = sccp_user,
-       };
-       osmo_sccp_make_addr_pc_ssn(&hsu->local_addr, local_pc, 
OSMO_SCCP_SSN_RANAP);
-       hash_init(hsu->hnbgw_context_map_by_conn_id);
-       osmo_sccp_user_set_priv(sccp_user, hsu);
-
-       llist_add_tail(&hsu->entry, &g_hnbgw->sccp.users);
-
-       return hsu;
-}
-
 /* If not present yet, set up all of osmo_ss7_instance, osmo_sccp_instance and 
hnbgw_sccp_user for the given cnlink.
  * The cs7 instance nr to use is determined by cnlink->remote_addr_name, or 
cs7 instance 0 if that is not present.
  * Set cnlink->hnbgw_sccp_user to the new SCCP instance. Return 0 on success, 
negative on error. */
@@ -581,30 +259,6 @@
        return cnlink_alloc(cnpool, nr);
 }

-static bool cnlink_matches(const struct hnbgw_cnlink *cnlink, const struct 
hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *remote_addr)
-{
-       if (cnlink->hnbgw_sccp_user != hsu)
-               return false;
-       if (osmo_sccp_addr_cmp(&cnlink->remote_addr, remote_addr, 
OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC))
-               return false;
-       return true;
-}
-
-struct hnbgw_cnlink *hnbgw_cnlink_find_by_addr(const struct hnbgw_sccp_user 
*hsu,
-                                              const struct osmo_sccp_addr 
*remote_addr)
-{
-       struct hnbgw_cnlink *cnlink;
-       llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iucs.cnlinks, entry) 
{
-               if (cnlink_matches(cnlink, hsu, remote_addr))
-                       return cnlink;
-       }
-       llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iups.cnlinks, entry) 
{
-               if (cnlink_matches(cnlink, hsu, remote_addr))
-                       return cnlink;
-       }
-       return NULL;
-}
-
 static bool is_cnlink_usable(struct hnbgw_cnlink *cnlink, bool is_emerg)
 {
        if (is_emerg && !cnlink->allow_emerg)
diff --git a/src/osmo-hnbgw/hnbgw_sccp.c b/src/osmo-hnbgw/hnbgw_sccp.c
new file mode 100644
index 0000000..8570f51
--- /dev/null
+++ b/src/osmo-hnbgw/hnbgw_sccp.c
@@ -0,0 +1,390 @@
+/* hnb-gw specific code for SCCP, ITU Q.711 - Q.714 */
+
+/* (C) 2015 by Harald Welte <lafo...@gnumonks.org>
+ * (C) 2025 by sysmocom s.f.m.c. GmbH <i...@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/netif/stream.h>
+
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/sigtran/sccp_helpers.h>
+#include <osmocom/sigtran/protocol/sua.h>
+
+#include <osmocom/hnbgw/hnbgw_cn.h>
+#include <osmocom/hnbgw/context_map.h>
+#include <osmocom/hnbgw/hnbgw_ranap.h>
+
+/***********************************************************************
+ * Incoming primitives from SCCP User SAP
+ ***********************************************************************/
+
+static bool cnlink_matches(const struct hnbgw_cnlink *cnlink, const struct 
hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *remote_addr)
+{
+       if (cnlink->hnbgw_sccp_user != hsu)
+               return false;
+       if (osmo_sccp_addr_cmp(&cnlink->remote_addr, remote_addr, 
OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC))
+               return false;
+       return true;
+}
+
+static struct hnbgw_cnlink *hnbgw_cnlink_find_by_addr(const struct 
hnbgw_sccp_user *hsu,
+                                              const struct osmo_sccp_addr 
*remote_addr)
+{
+       struct hnbgw_cnlink *cnlink;
+       llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iucs.cnlinks, entry) 
{
+               if (cnlink_matches(cnlink, hsu, remote_addr))
+                       return cnlink;
+       }
+       llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iups.cnlinks, entry) 
{
+               if (cnlink_matches(cnlink, hsu, remote_addr))
+                       return cnlink;
+       }
+       return NULL;
+}
+
+static struct hnbgw_cnlink *cnlink_from_addr(struct hnbgw_sccp_user *hsu, 
const struct osmo_sccp_addr *calling_addr,
+                                            const struct osmo_prim_hdr *oph)
+{
+       struct hnbgw_cnlink *cnlink = NULL;
+       cnlink = hnbgw_cnlink_find_by_addr(hsu, calling_addr);
+       if (!cnlink) {
+               LOG_HSI(hsu, DRANAP, LOGL_ERROR, "Rx from unknown SCCP peer: 
%s: %s\n",
+                       osmo_sccp_inst_addr_name(osmo_ss7_get_sccp(hsu->ss7), 
calling_addr),
+                       osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
+               return NULL;
+       }
+       return cnlink;
+}
+
+static struct hnbgw_context_map *map_from_conn_id(struct hnbgw_sccp_user *hsu, 
uint32_t conn_id,
+                                                 const struct osmo_prim_hdr 
*oph)
+{
+       struct hnbgw_context_map *map;
+       hash_for_each_possible(hsu->hnbgw_context_map_by_conn_id, map, 
hnbgw_sccp_user_entry, conn_id) {
+               if (map->scu_conn_id == conn_id)
+                       return map;
+       }
+       LOGP(DRANAP, LOGL_ERROR, "Rx for unknown SCCP connection ID: %u: %s\n",
+            conn_id, osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
+       return NULL;
+}
+
+static int handle_cn_unitdata(struct hnbgw_sccp_user *hsu,
+                             const struct osmo_scu_unitdata_param *param,
+                             struct osmo_prim_hdr *oph)
+{
+       struct hnbgw_cnlink *cnlink = cnlink_from_addr(hsu, 
&param->calling_addr, oph);
+       if (!cnlink)
+               return -ENOENT;
+
+       if (param->called_addr.ssn != OSMO_SCCP_SSN_RANAP) {
+               LOGP(DCN, LOGL_NOTICE, "N-UNITDATA.ind for unknown SSN %u\n",
+                       param->called_addr.ssn);
+               return -1;
+       }
+
+       return hnbgw_ranap_rx_udt_dl(cnlink, param, msgb_l2(oph->msg), 
msgb_l2len(oph->msg));
+}
+
+static int handle_cn_conn_conf(struct hnbgw_sccp_user *hsu,
+                              const struct osmo_scu_connect_param *param,
+                              struct osmo_prim_hdr *oph)
+{
+       struct hnbgw_context_map *map;
+
+       map = map_from_conn_id(hsu, param->conn_id, oph);
+       if (!map || !map->cnlink)
+               return -ENOENT;
+
+       LOGP(DCN, LOGL_DEBUG, "handle_cn_conn_conf() conn_id=%d, addrs: 
called=%s calling=%s responding=%s\n",
+            param->conn_id,
+            cnlink_sccp_addr_to_str(map->cnlink, &param->called_addr),
+            cnlink_sccp_addr_to_str(map->cnlink, &param->calling_addr),
+            cnlink_sccp_addr_to_str(map->cnlink, &param->responding_addr));
+
+       map_sccp_dispatch(map, MAP_SCCP_EV_RX_CONNECTION_CONFIRM, oph->msg);
+       return 0;
+}
+
+static int handle_cn_data_ind(struct hnbgw_sccp_user *hsu,
+                             const struct osmo_scu_data_param *param,
+                             struct osmo_prim_hdr *oph)
+{
+       struct hnbgw_context_map *map;
+
+       map = map_from_conn_id(hsu, param->conn_id, oph);
+       if (!map || !map->cnlink)
+               return -ENOENT;
+
+       return map_sccp_dispatch(map, MAP_SCCP_EV_RX_DATA_INDICATION, oph->msg);
+}
+
+static int handle_cn_disc_ind(struct hnbgw_sccp_user *hsu,
+                             const struct osmo_scu_disconn_param *param,
+                             struct osmo_prim_hdr *oph)
+{
+       struct hnbgw_context_map *map;
+       char cause_buf[128];
+
+       map = map_from_conn_id(hsu, param->conn_id, oph);
+       if (!map || !map->cnlink)
+               return -ENOENT;
+
+       LOGP(DCN, LOGL_DEBUG, "handle_cn_disc_ind() conn_id=%u 
responding_addr=%s cause=%s\n",
+            param->conn_id,
+            cnlink_sccp_addr_to_str(map->cnlink, &param->responding_addr),
+            osmo_sua_sccp_cause_name(param->cause, cause_buf, 
sizeof(cause_buf)));
+
+       return map_sccp_dispatch(map, MAP_SCCP_EV_RX_RELEASED, oph->msg);
+}
+
+static struct hnbgw_cnlink *_cnlink_find_by_remote_pc(struct hnbgw_cnpool 
*cnpool, struct osmo_ss7_instance *cs7, uint32_t pc)
+{
+       struct hnbgw_cnlink *cnlink;
+       llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
+               if (!cnlink->hnbgw_sccp_user)
+                       continue;
+               if (cnlink->hnbgw_sccp_user->ss7 != cs7)
+                       continue;
+               if ((cnlink->remote_addr.presence & OSMO_SCCP_ADDR_T_PC) == 0)
+                       continue;
+               if (cnlink->remote_addr.pc != pc)
+                       continue;
+               return cnlink;
+       }
+       return NULL;
+}
+
+/* Find a cnlink by its remote sigtran point code on a given cs7 instance. */
+static struct hnbgw_cnlink *cnlink_find_by_remote_pc(struct osmo_ss7_instance 
*cs7, uint32_t pc)
+{
+       struct hnbgw_cnlink *cnlink;
+       cnlink = _cnlink_find_by_remote_pc(&g_hnbgw->sccp.cnpool_iucs, cs7, pc);
+       if (!cnlink)
+               cnlink = _cnlink_find_by_remote_pc(&g_hnbgw->sccp.cnpool_iups, 
cs7, pc);
+       return cnlink;
+}
+
+static void handle_pcstate_ind(struct hnbgw_sccp_user *hsu, const struct 
osmo_scu_pcstate_param *pcst)
+{
+       struct hnbgw_cnlink *cnlink;
+       bool connected;
+       bool disconnected;
+       struct osmo_ss7_instance *cs7 = hsu->ss7;
+
+       LOGP(DCN, LOGL_DEBUG, "N-PCSTATE ind: affected_pc=%u sp_status=%s 
remote_sccp_status=%s\n",
+            pcst->affected_pc, osmo_sccp_sp_status_name(pcst->sp_status),
+            osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
+
+       /* If we don't care about that point-code, ignore PCSTATE. */
+       cnlink = cnlink_find_by_remote_pc(cs7, pcst->affected_pc);
+       if (!cnlink)
+               return;
+
+       /* See if this marks the point code to have become available, or to 
have been lost.
+        *
+        * I want to detect two events:
+        * - connection event (both indicators say PC is reachable).
+        * - disconnection event (at least one indicator says the PC is not 
reachable).
+        *
+        * There are two separate incoming indicators with various possible 
values -- the incoming events can be:
+        *
+        * - neither connection nor disconnection indicated -- just indicating 
congestion
+        *   connected == false, disconnected == false --> do nothing.
+        * - both incoming values indicate that we are connected
+        *   --> trigger connected
+        * - both indicate we are disconnected
+        *   --> trigger disconnected
+        * - one value indicates 'connected', the other indicates 'disconnected'
+        *   --> trigger disconnected
+        *
+        * Congestion could imply that we're connected, but it does not 
indicate that a PC's reachability changed, so no need to
+        * trigger on that.
+        */
+       connected = false;
+       disconnected = false;
+
+       switch (pcst->sp_status) {
+       case OSMO_SCCP_SP_S_ACCESSIBLE:
+               connected = true;
+               break;
+       case OSMO_SCCP_SP_S_INACCESSIBLE:
+               disconnected = true;
+               break;
+       default:
+       case OSMO_SCCP_SP_S_CONGESTED:
+               /* Neither connecting nor disconnecting */
+               break;
+       }
+
+       switch (pcst->remote_sccp_status) {
+       case OSMO_SCCP_REM_SCCP_S_AVAILABLE:
+               if (!disconnected)
+                       connected = true;
+               break;
+       case OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN:
+       case OSMO_SCCP_REM_SCCP_S_UNEQUIPPED:
+       case OSMO_SCCP_REM_SCCP_S_INACCESSIBLE:
+               disconnected = true;
+               connected = false;
+               break;
+       default:
+       case OSMO_SCCP_REM_SCCP_S_CONGESTED:
+               /* Neither connecting nor disconnecting */
+               break;
+       }
+
+       if (disconnected && cnlink_is_conn_ready(cnlink)) {
+               LOG_CNLINK(cnlink, DCN, LOGL_NOTICE,
+                          "now unreachable: N-PCSTATE ind: pc=%u sp_status=%s 
remote_sccp_status=%s\n",
+                          pcst->affected_pc,
+                          osmo_sccp_sp_status_name(pcst->sp_status),
+                          
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
+               /* A previously usable cnlink has disconnected. Kick it back to 
DISC state. */
+               cnlink_set_disconnected(cnlink);
+       } else if (connected && !cnlink_is_conn_ready(cnlink)) {
+               LOG_CNLINK(cnlink, DCN, LOGL_NOTICE,
+                          "now available: N-PCSTATE ind: pc=%u sp_status=%s 
remote_sccp_status=%s\n",
+                          pcst->affected_pc,
+                          osmo_sccp_sp_status_name(pcst->sp_status),
+                          
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
+               /* A previously unusable cnlink has become reachable. Trigger 
immediate RANAP RESET -- we would resend a
+                * RESET either way, but we might as well do it now to speed up 
connecting. */
+               cnlink_resend_reset(cnlink);
+       }
+}
+
+/* Entry point for primitives coming up from SCCP User SAP.
+ * Ownership of oph->msg is transferred to us. */
+static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx)
+{
+       struct osmo_sccp_user *scu = ctx;
+       struct hnbgw_sccp_user *hsu;
+       struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
+       int rc = 0;
+
+       LOGP(DCN, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph));
+
+       if (!scu) {
+               LOGP(DCN, LOGL_ERROR,
+                    "sccp_sap_up(): NULL osmo_sccp_user, cannot send prim (sap 
%u prim %u op %d)\n",
+                    oph->sap, oph->primitive, oph->operation);
+               return -1;
+       }
+
+       hsu = osmo_sccp_user_get_priv(scu);
+       if (!hsu) {
+               LOGP(DCN, LOGL_ERROR,
+                    "sccp_sap_up(): NULL hnbgw_sccp_user, cannot send prim 
(sap %u prim %u op %d)\n",
+                    oph->sap, oph->primitive, oph->operation);
+               return -1;
+       }
+
+       talloc_steal(OTC_SELECT, oph->msg);
+
+       switch (OSMO_PRIM_HDR(oph)) {
+       case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
+               rc = handle_cn_unitdata(hsu, &prim->u.unitdata, oph);
+               break;
+       case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
+               rc = handle_cn_conn_conf(hsu, &prim->u.connect, oph);
+               break;
+       case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
+               rc = handle_cn_data_ind(hsu, &prim->u.data, oph);
+               break;
+       case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
+               rc = handle_cn_disc_ind(hsu, &prim->u.disconnect, oph);
+               break;
+       case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION):
+               handle_pcstate_ind(hsu, &prim->u.pcstate);
+               break;
+
+       default:
+               LOGP(DCN, LOGL_ERROR,
+                       "Received unknown prim %u from SCCP USER SAP\n",
+                       OSMO_PRIM_HDR(oph));
+               break;
+       }
+
+       return rc;
+}
+
+
+struct hnbgw_sccp_user *hnbgw_sccp_user_alloc(const struct hnbgw_cnlink 
*cnlink, int ss7_id)
+{
+       struct osmo_ss7_instance *ss7 = NULL;
+       struct osmo_sccp_instance *sccp;
+       struct osmo_sccp_user *sccp_user;
+       uint32_t local_pc;
+       struct hnbgw_sccp_user *hsu;
+
+       sccp = osmo_sccp_simple_client_on_ss7_id(g_hnbgw,
+                                                ss7_id,
+                                                cnlink->name,
+                                                DEFAULT_PC_HNBGW,
+                                                OSMO_SS7_ASP_PROT_M3UA,
+                                                0,
+                                                "localhost",
+                                                -1,
+                                                "localhost");
+       if (!sccp) {
+               LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Failed to configure SCCP 
on 'cs7 instance %u'\n",
+                          ss7_id);
+               return NULL;
+       }
+       ss7 = osmo_sccp_get_ss7(sccp);
+       LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "created SCCP instance on cs7 
instance %u\n", osmo_ss7_instance_get_id(ss7));
+
+       /* Bind the SCCP user, using the cs7 instance's default point-code if 
one is configured, or osmo-hnbgw's default
+        * local PC. */
+       local_pc = osmo_ss7_instance_get_primary_pc(ss7);
+       if (!osmo_ss7_pc_is_valid(local_pc))
+               local_pc = DEFAULT_PC_HNBGW;
+
+       LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "binding OsmoHNBGW user to cs7 
instance %u, local PC %u = %s\n",
+                  osmo_ss7_instance_get_id(ss7), local_pc, 
osmo_ss7_pointcode_print(ss7, local_pc));
+
+       sccp_user = osmo_sccp_user_bind_pc(sccp, "OsmoHNBGW", sccp_sap_up, 
OSMO_SCCP_SSN_RANAP, local_pc);
+       if (!sccp_user) {
+               LOGP(DCN, LOGL_ERROR, "Failed to init SCCP User\n");
+               return NULL;
+       }
+
+       hsu = talloc_zero(cnlink, struct hnbgw_sccp_user);
+       *hsu = (struct hnbgw_sccp_user){
+               .name = talloc_asprintf(hsu, "cs7-%u.sccp", 
osmo_ss7_instance_get_id(ss7)),
+               .ss7 = ss7,
+               .sccp_user = sccp_user,
+       };
+       osmo_sccp_make_addr_pc_ssn(&hsu->local_addr, local_pc, 
OSMO_SCCP_SSN_RANAP);
+       hash_init(hsu->hnbgw_context_map_by_conn_id);
+       osmo_sccp_user_set_priv(sccp_user, hsu);
+
+       llist_add_tail(&hsu->entry, &g_hnbgw->sccp.users);
+
+       return hsu;
+}
+

--
To view, visit https://gerrit.osmocom.org/c/osmo-hnbgw/+/40243?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: merged
Gerrit-Project: osmo-hnbgw
Gerrit-Branch: master
Gerrit-Change-Id: Idb45a25f7ff4bcd38ffd27bf1d3360b9d34149b3
Gerrit-Change-Number: 40243
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <pes...@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <lafo...@osmocom.org>
Gerrit-Reviewer: osmith <osm...@sysmocom.de>
Gerrit-Reviewer: pespin <pes...@sysmocom.de>

Reply via email to