neels has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-hnbgw/+/32323 )


Change subject: sccp: allow separate cs7 for IuPS and IuCS
......................................................................

sccp: allow separate cs7 for IuPS and IuCS

Prepare for CN pooling; allow using separate SS7 instances for IuCS and
IuPS.

New struct hnbgw_sccp_inst describes an SCCP instance, one per cs7.
Limit struct hnbgw_cnlink to describing a CN peer, using any SCCP
instance.

Chart sccp.dot shows the changes made.

Related: SYS#6422
Change-Id: Iea1824f1c586723d989c80a909bae16bd2866e08
---
M doc/charts/sccp.dot
M include/osmocom/hnbgw/context_map.h
M include/osmocom/hnbgw/hnbgw.h
M include/osmocom/hnbgw/hnbgw_cn.h
M src/osmo-hnbgw/context_map.c
M src/osmo-hnbgw/context_map_sccp.c
M src/osmo-hnbgw/hnbgw.c
M src/osmo-hnbgw/hnbgw_cn.c
M src/osmo-hnbgw/hnbgw_rua.c
M src/osmo-hnbgw/hnbgw_vty.c
10 files changed, 525 insertions(+), 247 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-hnbgw refs/changes/23/32323/1

diff --git a/doc/charts/sccp.dot b/doc/charts/sccp.dot
index 9c04c35..6b67a99 100644
--- a/doc/charts/sccp.dot
+++ b/doc/charts/sccp.dot
@@ -9,29 +9,50 @@
                ss0 -> ss0ab

                msc0a [label="2.2.2"]
-               sgsn0a [label="3.3.3"]
-               ss0ab -> msc0a,sgsn0a
+               ss0ab -> msc0a
        }

-       iucs_addr [label=".iucs_addr -> 2.2.2"]
-       iups_addr [label=".iups_addr -> 3.3.3"]
-       msc0a -> iucs_addr [dir=back]
-       sgsn0a -> iups_addr [dir=back]
-       cnlink [label="hnbgw_cnlink (singleton)\n .osmo_sccp_instance\n 
.osmo_ss7_user SSN: RANAP"];
-       ss0 -> cnlink [dir=back]
+       sccp0 [label="hnbgw_sccp_inst for '0'\n .osmo_sccp_instance\n 
.osmo_ss7_user SSN: RANAP"];
+       ss0 -> sccp0 [dir=back]
+
+       msc0 [label="hnbgw_cnlink for IuCS\n .sccp_addr -> 2.2.2"]
+       sccp0 -> msc0 [dir=back]
+       msc0a -> msc0 [dir=back]

        cs0 [label="UE CS conn\n hnbgw_context_map"]
        cs1 [label="UE CS conn\n hnbgw_context_map"]
+       msc0 -> cs0,cs1
+
+       subgraph cluster_ss1 {
+               label=""
+               ss1 [label="cs7 instance 1\n local pc: 4.4.4"]
+               ss1ab [label="address book"]
+               ss1 -> ss1ab
+
+               sgsn0a [label="3.3.3"]
+               ss1ab -> sgsn0a
+       }
+
+       sccp1 [label="hnbgw_sccp_inst for '1'\n .osmo_sccp_instance\n 
.osmo_ss7_user SSN: RANAP"];
+       ss1 -> sccp1 [dir=back]
+
+       sgsn0 [label="hnbgw_cnlink for IuPS\n .sccp_addr -> 3.3.3"]
+       sccp1 -> sgsn0 [dir=back]
+       sgsn0a -> sgsn0 [dir=back]
+
        ps0 [label="UE PS conn\n hnbgw_context_map"]
        ps1 [label="UE PS conn\n hnbgw_context_map"]
-       cnlink -> cs0,cs1,ps0,ps1
-       iucs_addr -> cs0,cs1 [style=dotted,dir=none]
-       iups_addr -> ps0,ps1 [style=dotted,dir=none]
+       sgsn0 -> ps0,ps1
+
+       subgraph cluster_sccp_inst {
+               label="global sccp_instances (llist)"
+               sccp0
+               sccp1
+       }

        subgraph cluster_hnbgw {
                label="global hnb_gw"
-               cnlink
-               iucs_addr
-               iups_addr
+               msc0
+               sgsn0
        }
 }
diff --git a/include/osmocom/hnbgw/context_map.h 
b/include/osmocom/hnbgw/context_map.h
index b64358a..4e2c02c 100644
--- a/include/osmocom/hnbgw/context_map.h
+++ b/include/osmocom/hnbgw/context_map.h
@@ -72,11 +72,14 @@

 struct hnbgw_context_map {
        /* entry in the per-CN list of mappings */
-       struct llist_head cn_list;
+       struct llist_head hnbgw_cnlink_entry;
        /* entry in the per-HNB list of mappings. If hnb_ctx == NULL, then this 
llist entry has been llist_del()eted and
         * must not be used. */
        struct llist_head hnb_list;

+       /* entry in the per-SCCP-conn-id hashtable */
+       struct hlist_node hnbgw_sccp_inst_entry;
+
        /* Backpointer to global hnb_gw. */
        struct hnb_gw *gw;

@@ -89,7 +92,7 @@
        struct osmo_fsm_inst *rua_fi;

        /* Pointer to CN, to transceive SCCP. */
-       struct hnbgw_cnlink *cn_link;
+       struct hnbgw_cnlink *cnlink;
        /* SCCP User SAP connection ID used in SCCP messages to/from the 
cn_link. */
        uint32_t scu_conn_id;
        /* FSM handling the SCCP state for scu_conn_id. */
@@ -134,10 +137,8 @@
 enum hnbgw_context_map_state map_rua_get_state(struct hnbgw_context_map *map);
 enum hnbgw_context_map_state map_sccp_get_state(struct hnbgw_context_map *map);

-struct hnbgw_context_map *
-context_map_alloc_by_hnb(struct hnb_context *hnb, uint32_t rua_ctx_id,
-                        bool is_ps,
-                        struct hnbgw_cnlink *cn_if_new);
+struct hnbgw_context_map *context_map_find_or_create_by_rua_ctx_id(struct 
hnb_context *hnb, uint32_t rua_ctx_id,
+                                                                  bool is_ps);

 void map_rua_fsm_alloc(struct hnbgw_context_map *map);
 void map_sccp_fsm_alloc(struct hnbgw_context_map *map);
diff --git a/include/osmocom/hnbgw/hnbgw.h b/include/osmocom/hnbgw/hnbgw.h
index 1bf140a..e6e53c3 100644
--- a/include/osmocom/hnbgw/hnbgw.h
+++ b/include/osmocom/hnbgw/hnbgw.h
@@ -2,11 +2,14 @@

 #include <osmocom/core/select.h>
 #include <osmocom/core/linuxlist.h>
+#include <osmocom/core/hashtable.h>
 #include <osmocom/core/write_queue.h>
 #include <osmocom/core/timer.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>
+
 #define DEBUG
 #include <osmocom/core/logging.h>

@@ -26,6 +29,9 @@
 #define LOGHNB(HNB_CTX, ss, lvl, fmt, args ...) \
        LOGP(ss, lvl, "(%s) " fmt, hnb_context_name(HNB_CTX), ## args)

+#define DOMAIN_CS RANAP_CN_DomainIndicator_cs_domain
+#define DOMAIN_PS RANAP_CN_DomainIndicator_ps_domain
+
 enum hnb_ctrl_node {
        CTRL_NODE_HNB = _LAST_CTRL_NODE,
        _LAST_CTRL_NODE_HNB
@@ -37,6 +43,10 @@
 #define HNBGW_IUCS_REMOTE_IP_DEFAULT "127.0.0.1"
 #define HNBGW_IUPS_REMOTE_IP_DEFAULT "127.0.0.1"

+#define DEFAULT_PC_HNBGW ((23 << 3) + 5)
+#define DEFAULT_PC_MSC ((23 << 3) + 1)
+#define DEFAULT_PC_SGSN ((23 << 3) + 4)
+
 /* 25.467 Section 7.1 */
 #define IUH_DEFAULT_SCTP_PORT  29169
 #define RNA_DEFAULT_SCTP_PORT  25471
@@ -60,6 +70,25 @@

 struct hnb_gw;

+struct hnbgw_sccp_inst {
+       struct llist_head entry;
+
+       struct hnb_gw *gw;
+
+       char *name;
+
+       /* There is one osmo_sccp_instance per cs7_instance.
+        * Below osmo_sccp_instance is running on this cs7 instance: */
+       uint32_t cs7_instance;
+       struct osmo_sccp_instance *sccp;
+       /* for SSN = RANAP */
+       struct osmo_sccp_user *sccp_user;
+
+       uint32_t next_conn_id;
+
+       DECLARE_HASHTABLE(hnbgw_context_map_by_conn_id, 6);
+};
+
 enum hnbgw_cnlink_state {
        /* we have just been initialized or were disconnected */
        CNLINK_S_NULL,
@@ -73,21 +102,63 @@
        CNLINK_S_EST_ACTIVE,
 };

+/* A CN peer, like MSC or SGSN. */
 struct hnbgw_cnlink {
-       struct llist_head list;
-       enum hnbgw_cnlink_state state;
+       /* backpointer to global hnb_gw */
        struct hnb_gw *gw;
+
+       /* To print in logging/VTY */
+       char *name;
+
+       /* IuCS or IuPS? */
+       RANAP_CN_DomainIndicator_t domain;
+
+       /* FUTURE: In principle, there may be different local point-codes for 
separate CN links on the same SCCP
+        * instance. So far, each hnbgw_cnlink->local_addr just contains SSN = 
RANAP, so that the cs7 instance fills in
+        * its primary point code. */
+       struct osmo_sccp_addr local_addr;
+
+       /* cs7 address book entry to indicate both the remote point-code of the 
peer, as well as which cs7 instance to
+        * use. */
+       const char *remote_addr_name;
+
+       /* Copy of the address pointed at by remote_addr_name. */
+       struct osmo_sccp_addr remote_addr;
+
+       /* The SCCP instance for the cs7 instance indicated by 
remote_addr_name. (Multiple hnbgw_cnlinks may use the
+        * same hnbgw_sccp_inst -- there is exactly one hnbgw_sccp_inst per 
configured cs7 instance.) */
+       struct hnbgw_sccp_inst *hnbgw_sccp_inst;
+
+       enum hnbgw_cnlink_state state;
        /* timer for re-transmitting the RANAP Reset */
        struct osmo_timer_list T_RafC;
-       /* reference to the SCCP User SAP by which we communicate */
-       struct osmo_sccp_instance *sccp;
-       struct osmo_sccp_user *sccp_user;
-       uint32_t next_conn_id;

        /* linked list of hnbgw_context_map */
        struct llist_head map_list;
 };

+#define LOG_CNLINK(CNLINK, SUBSYS, LEVEL, FMT, ARGS...) \
+       LOGP(SUBSYS, LEVEL, "(%s) " FMT, (CNLINK) ? (CNLINK)->name : "null", 
##ARGS)
+
+static inline bool cnlink_is_cs(const struct hnbgw_cnlink *cnlink)
+{
+       return cnlink && cnlink->domain == DOMAIN_CS;
+}
+
+static inline bool cnlink_is_ps(const struct hnbgw_cnlink *cnlink)
+{
+       return cnlink && cnlink->domain == DOMAIN_PS;
+}
+
+static inline struct osmo_sccp_instance *cnlink_sccp(const struct hnbgw_cnlink 
*cnlink)
+{
+       if (!cnlink)
+               return NULL;
+       if (!cnlink->hnbgw_sccp_inst)
+               return NULL;
+       return cnlink->hnbgw_sccp_inst->sccp;
+}
+
 /* The lifecycle of the hnb_context object is the same as its conn */
 struct hnb_context {
        /*! Entry in HNB-global list of HNB */
@@ -157,11 +228,10 @@
        struct ctrl_handle *ctrl;
        /* currently active CN links for CS and PS */
        struct {
-               struct osmo_sccp_instance *client;
-               struct hnbgw_cnlink *cnlink;
-               struct osmo_sccp_addr local_addr;
-               struct osmo_sccp_addr iucs_remote_addr;
-               struct osmo_sccp_addr iups_remote_addr;
+               /* List of hnbgw_sccp_inst */
+               struct llist_head instances;
+               struct hnbgw_cnlink *cnlink_iucs;
+               struct hnbgw_cnlink *cnlink_iups;
        } sccp;
        /* MGW pool, also includes the single MGCP client as fallback if no
         * pool is configured. */
@@ -205,3 +275,5 @@
 }

 struct msgb *hnbgw_ranap_msg_alloc(const char *name);
+
+struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnb_gw *gw, bool is_ps);
diff --git a/include/osmocom/hnbgw/hnbgw_cn.h b/include/osmocom/hnbgw/hnbgw_cn.h
index 0df2716..036fdc7 100644
--- a/include/osmocom/hnbgw/hnbgw_cn.h
+++ b/include/osmocom/hnbgw/hnbgw_cn.h
@@ -2,6 +2,10 @@

 #include <osmocom/hnbgw/hnbgw.h>

-int hnbgw_cnlink_init(struct hnb_gw *gw, const char *stp_host, uint16_t 
stp_port, const char *local_ip);
+struct hnbgw_cnlink *hnbgw_cnlink_alloc(struct hnb_gw *gw, const char 
*remote_addr_name,
+                                       RANAP_CN_DomainIndicator_t domain);

 const struct osmo_sccp_addr *hnbgw_cn_get_remote_addr(struct hnb_gw *gw, bool 
is_ps);
+
+struct hnbgw_cnlink *hnbgw_cnlink_find_by_addr(const struct hnbgw_sccp_inst 
*hsi,
+                                              const struct osmo_sccp_addr 
*remote_addr);
diff --git a/src/osmo-hnbgw/context_map.c b/src/osmo-hnbgw/context_map.c
index 31f635d..06272d4 100644
--- a/src/osmo-hnbgw/context_map.c
+++ b/src/osmo-hnbgw/context_map.c
@@ -53,11 +53,11 @@
 }

 /* is a given SCCP USER SAP Connection ID in use for a given CN link? */
-static int cn_id_in_use(struct hnbgw_cnlink *cn, uint32_t id)
+static int sccp_id_in_use(struct hnbgw_sccp_inst *hsi, uint32_t id)
 {
        struct hnbgw_context_map *map;

-       llist_for_each_entry(map, &cn->map_list, cn_list) {
+       hash_for_each_possible(hsi->hnbgw_context_map_by_conn_id, map, 
hnbgw_sccp_inst_entry, id) {
                if (map->scu_conn_id == id)
                        return 1;
        }
@@ -65,11 +65,14 @@
 }

 /* try to allocate a new SCCP User SAP Connection ID */
-static int alloc_cn_conn_id(struct hnbgw_cnlink *cn, uint32_t *id_out)
+static int sccp_alloc_conn_id(struct hnbgw_sccp_inst *hsi, uint32_t *id_out)
 {
        uint32_t i;
        uint32_t id;

+       if (!hsi)
+               return -ENOENT;
+
        /* SUA: RFC3868 sec 3.10.4:
         *    The source reference number is a 4 octet long integer.
         *    This is allocated by the source SUA instance.
@@ -82,28 +85,28 @@
         */

        for (i = 0; i < 0x00ffffff; i++) {
-               id = cn->next_conn_id++;
-               if (cn->next_conn_id == 0x00ffffff)
-                       cn->next_conn_id = 0;
-               if (!cn_id_in_use(cn, id)) {
+               id = hsi->next_conn_id++;
+               if (hsi->next_conn_id == 0x00ffffff)
+                       hsi->next_conn_id = 0;
+               if (!sccp_id_in_use(hsi, id)) {
                        *id_out = id;
-                       return 1;
+                       return 0;
                }
        }
-       return -1;
+       return -EADDRNOTAVAIL;
 }

 /* Map from a HNB + ContextID to the SCCP-side Connection ID */
-struct hnbgw_context_map *
-context_map_alloc_by_hnb(struct hnb_context *hnb, uint32_t rua_ctx_id,
-                        bool is_ps,
-                        struct hnbgw_cnlink *cn_if_new)
+struct hnbgw_context_map *context_map_find_or_create_by_rua_ctx_id(struct 
hnb_context *hnb, uint32_t rua_ctx_id,
+                                                                  bool is_ps)
 {
        struct hnbgw_context_map *map;
        uint32_t new_scu_conn_id;
+       struct hnbgw_cnlink *cnlink;
+       struct hnbgw_sccp_inst *hsi;

        llist_for_each_entry(map, &hnb->map_list, hnb_list) {
-               if (map->cn_link != cn_if_new)
+               if (map->is_ps != is_ps)
                        continue;

                /* Matching on RUA context id -- only match for RUA context 
that has not been disconnected yet. If an
@@ -112,24 +115,39 @@
                if (!map_rua_is_active(map))
                        continue;

-               if (map->rua_ctx_id == rua_ctx_id
-                   && map->is_ps == is_ps) {
-                       return map;
-               }
+               if (map->rua_ctx_id != rua_ctx_id)
+                       continue;
+
+               /* Already exists */
+               return map;
        }

-       if (alloc_cn_conn_id(cn_if_new, &new_scu_conn_id) < 0) {
-               LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unable to allocate CN 
connection ID\n");
+       /* Does not exist yet, create a new hnbgw_context_map. */
+
+       /* From the RANAP/RUA input, determine which cnlink to use */
+       cnlink = hnbgw_cnlink_select(hnb->gw, is_ps);
+       if (!cnlink) {
+               LOGHNB(hnb, DMAIN, LOGL_ERROR, "Failed to select CN link\n");
                return NULL;
        }

-       LOGHNB(hnb, DMAIN, LOGL_INFO, "Creating new Mapping RUA CTX %p/%u <-> 
SCU Conn ID %p/%u\n",
-               hnb, rua_ctx_id, cn_if_new, new_scu_conn_id);
+       /* Allocate new SCCP conn id on the SCCP instance the cnlink is on. */
+       hsi = cnlink->hnbgw_sccp_inst;
+       if (!hsi) {
+               LOGHNB(hnb, DMAIN, LOGL_ERROR, "Cannot allocate context map: No 
SCCP instance for CN link %s\n",
+                      cnlink->name);
+               return NULL;
+       }
+
+       if (sccp_alloc_conn_id(hsi, &new_scu_conn_id)) {
+               LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unable to allocate SCCP conn ID 
on %s\n", hsi->name);
+               return NULL;
+       }

        /* allocate a new map entry. */
        map = talloc_zero(hnb, struct hnbgw_context_map);
        map->gw = hnb->gw;
-       map->cn_link = cn_if_new;
+       map->cnlink = cnlink;
        map->hnb_ctx = hnb;
        map->rua_ctx_id = rua_ctx_id;
        map->is_ps = is_ps;
@@ -140,9 +158,12 @@
        map_rua_fsm_alloc(map);
        map_sccp_fsm_alloc(map);

-       /* put it into both lists */
        llist_add_tail(&map->hnb_list, &hnb->map_list);
-       llist_add_tail(&map->cn_list, &cn_if_new->map_list);
+       llist_add_tail(&map->hnbgw_cnlink_entry, &cnlink->map_list);
+       hash_add(hsi->hnbgw_context_map_by_conn_id, 
&map->hnbgw_sccp_inst_entry, new_scu_conn_id);
+
+       LOG_MAP(map, DMAIN, LOGL_INFO, "Creating new Mapping RUA CTX %u <-> SCU 
Conn ID %s/%u\n",
+               rua_ctx_id, hsi->name, new_scu_conn_id);

        return map;
 }
@@ -180,7 +201,9 @@
 {
        struct hnbgw_context_map *map;

-       llist_for_each_entry(map, &cn->map_list, cn_list) {
+       /* TODO: use hashlist 
cnlink->hnbgw_sccp_inst->hnbgw_context_map_by_conn_id */
+
+       llist_for_each_entry(map, &cn->map_list, hnbgw_cnlink_entry) {
                /* Matching on SCCP conn id -- only match for SCCP conn that 
has not been disconnected yet. If an
                 * inactive context map for an scu_conn_id is still around, we 
may have two entries for the same
                 * scu_conn_id around at the same time. That should only stay 
until its RUA side is done releasing. */
@@ -240,8 +263,10 @@
        hnbgw_gtpmap_release(map);
 #endif

-       if (map->cn_link)
-               llist_del(&map->cn_list);
+       if (map->cnlink) {
+               llist_del(&map->hnbgw_cnlink_entry);
+               hash_del(&map->hnbgw_sccp_inst_entry);
+       }
        if (map->hnb_ctx)
                llist_del(&map->hnb_list);

diff --git a/src/osmo-hnbgw/context_map_sccp.c 
b/src/osmo-hnbgw/context_map_sccp.c
index 65939f3..eab0029 100644
--- a/src/osmo-hnbgw/context_map_sccp.c
+++ b/src/osmo-hnbgw/context_map_sccp.c
@@ -123,6 +123,11 @@
        struct osmo_scu_prim *prim;
        int rc;

+       if (!map->cnlink || !map->cnlink->hnbgw_sccp_inst) {
+               LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP Connection 
Request: no CN link\n");
+               return -1;
+       }
+
        if (!ranap_msg) {
                /* prepare a msgb to send an empty N-Connect prim (but this 
should never happen in practice) */
                ranap_msg = hnbgw_ranap_msg_alloc("SCCP-CR-empty");
@@ -131,13 +136,13 @@
        prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim));
        osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_CONNECT, 
PRIM_OP_REQUEST, ranap_msg);
        prim->u.connect.called_addr = *hnbgw_cn_get_remote_addr(map->gw, 
map->is_ps);
-       prim->u.connect.calling_addr = map->gw->sccp.local_addr;
+       prim->u.connect.calling_addr = map->cnlink->local_addr;
        prim->u.connect.sccp_class = 2;
        prim->u.connect.conn_id = map->scu_conn_id;

-       rc = osmo_sccp_user_sap_down_nofree(map->cn_link->sccp_user, 
&prim->oph);
+       rc = 
osmo_sccp_user_sap_down_nofree(map->cnlink->hnbgw_sccp_inst->sccp_user, 
&prim->oph);
        if (rc)
-               LOGPFSML(fi, LOGL_ERROR, "Failed to forward SCCP Connectoin 
Request to CN\n");
+               LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP Connection 
Request to CN\n");
        return rc;
 }

@@ -150,20 +155,31 @@
        if (!msg_has_l2_data(ranap_msg))
                return 0;

+       if (!map->cnlink || !map->cnlink->hnbgw_sccp_inst) {
+               LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP Data Form 1: no 
CN link\n");
+               return -1;
+       }
+
        prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim));
        osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, 
PRIM_OP_REQUEST, ranap_msg);
        prim->u.data.conn_id = map->scu_conn_id;

-       rc = osmo_sccp_user_sap_down_nofree(map->cn_link->sccp_user, 
&prim->oph);
+       rc = 
osmo_sccp_user_sap_down_nofree(map->cnlink->hnbgw_sccp_inst->sccp_user, 
&prim->oph);
        if (rc)
-               LOGPFSML(fi, LOGL_ERROR, "Failed to forward SCCP Data Form 1 to 
CN\n");
+               LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP Data Form 1 to 
CN\n");
        return rc;
 }

 static int tx_sccp_rlsd(struct osmo_fsm_inst *fi)
 {
        struct hnbgw_context_map *map = fi->priv;
-       return osmo_sccp_tx_disconn(map->cn_link->sccp_user, map->scu_conn_id, 
NULL, 0);
+
+       if (!map->cnlink || !map->cnlink->hnbgw_sccp_inst) {
+               LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP RLSD: no CN 
link\n");
+               return -1;
+       }
+
+       return osmo_sccp_tx_disconn(map->cnlink->hnbgw_sccp_inst->sccp_user, 
map->scu_conn_id, NULL, 0);
 }

 static int destruct_ranap_ran_rx_co_ies(ranap_message *ranap_message_p)
@@ -455,7 +471,7 @@
                /* send SCCP RLSD. libosmo-sigtran/sccp_scoc.c will do the SCCP 
connection cleanup.
                 * (It will repeatedly send SCCP RLSD until the peer responded 
with SCCP RLC, or until the
                 * sccp_connection->t_int timer expires, and the 
sccp_connection is freed.) */
-               if (map->cn_link && map->cn_link->sccp_user)
+               if (map->cnlink)
                        tx_sccp_rlsd(fi);
                map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
                return 0;
diff --git a/src/osmo-hnbgw/hnbgw.c b/src/osmo-hnbgw/hnbgw.c
index bd45dda..e4a2302 100644
--- a/src/osmo-hnbgw/hnbgw.c
+++ b/src/osmo-hnbgw/hnbgw.c
@@ -97,6 +97,7 @@
        gw->next_ue_ctx_id = 23;
        INIT_LLIST_HEAD(&gw->hnb_list);
        INIT_LLIST_HEAD(&gw->ue_list);
+       INIT_LLIST_HEAD(&gw->sccp.instances);

        gw->mgw_pool = mgcp_client_pool_alloc(gw);
        gw->config.mgcp_client = talloc_zero(tall_hnb_ctx, struct 
mgcp_client_conf);
@@ -806,11 +807,13 @@

        ranap_set_log_area(DRANAP);

-       rc = hnbgw_cnlink_init(g_hnb_gw, "localhost", M3UA_PORT, "localhost");
-       if (rc < 0) {
+       if (!hnbgw_cnlink_alloc(g_hnb_gw, 
g_hnb_gw->config.iucs_remote_addr_name, DOMAIN_CS)
+           || !hnbgw_cnlink_alloc(g_hnb_gw, 
g_hnb_gw->config.iups_remote_addr_name, DOMAIN_PS)) {
                LOGP(DMAIN, LOGL_ERROR, "Failed to initialize SCCP link to 
CN\n");
                exit(1);
        }
+       OSMO_ASSERT(g_hnb_gw->sccp.cnlink_iucs);
+       OSMO_ASSERT(g_hnb_gw->sccp.cnlink_iups);

        LOGP(DHNBAP, LOGL_NOTICE, "Using RNC-Id %u\n", g_hnb_gw->config.rnc_id);

@@ -871,3 +874,12 @@
        ranap_msg->l2h = ranap_msg->data;
        return ranap_msg;
 }
+
+struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnb_gw *gw, bool is_ps)
+{
+       /* FUTURE: soon we will pick one of many configurable CN peers from a 
pool. There will be more input arguments
+        * (MI, or TMSI, or NRI decoded from RANAP) and this function will do 
round robin for new subscribers. */
+       if (is_ps)
+               return gw->sccp.cnlink_iups;
+       return gw->sccp.cnlink_iucs;
+}
diff --git a/src/osmo-hnbgw/hnbgw_cn.c b/src/osmo-hnbgw/hnbgw_cn.c
index e2dccf9..e358c11 100644
--- a/src/osmo-hnbgw/hnbgw_cn.c
+++ b/src/osmo-hnbgw/hnbgw_cn.c
@@ -33,6 +33,7 @@

 #include <osmocom/hnbgw/hnbgw.h>
 #include <osmocom/hnbgw/hnbgw_rua.h>
+#include <osmocom/hnbgw/hnbgw_cn.h>
 #include <osmocom/ranap/ranap_ies_defs.h>
 #include <osmocom/ranap/ranap_msg_factory.h>
 #include <osmocom/hnbgw/context_map.h>
@@ -43,8 +44,7 @@
 
 void hnbgw_cnlink_change_state(struct hnbgw_cnlink *cnlink, enum 
hnbgw_cnlink_state state);

-static int transmit_rst(struct hnb_gw *gw, RANAP_CN_DomainIndicator_t domain,
-                       struct osmo_sccp_addr *remote_addr)
+static int transmit_rst(struct hnbgw_cnlink *cnlink)
 {
        struct msgb *msg;
        RANAP_Cause_t cause = {
@@ -52,43 +52,57 @@
                .choice. transmissionNetwork = 
RANAP_CauseTransmissionNetwork_signalling_transport_resource_failure,
        };

-       LOGP(DRANAP, LOGL_NOTICE, "Tx RESET to %s %s\n",
-            domain == RANAP_CN_DomainIndicator_cs_domain ? "IuCS" : "IuPS",
-            osmo_sccp_inst_addr_name(gw->sccp.cnlink->sccp, remote_addr));
+       if (!cnlink)
+               return -1;

-       msg = ranap_new_msg_reset(domain, &cause);
+       if (!cnlink->hnbgw_sccp_inst) {
+               LOG_CNLINK(cnlink, DRANAP, LOGL_ERROR, "cannot send RANAP 
RESET: no CN link\n");
+               return -1;
+       }

-       return osmo_sccp_tx_unitdata_msg(gw->sccp.cnlink->sccp_user,
-                                        &gw->sccp.local_addr,
-                                        remote_addr,
+       LOG_CNLINK(cnlink, DRANAP, LOGL_NOTICE, "Tx RANAP RESET to %s %s\n",
+                  cnlink_is_cs(cnlink) ? "IuCS" : "IuPS",
+                  osmo_sccp_inst_addr_name(cnlink->hnbgw_sccp_inst->sccp, 
&cnlink->remote_addr));
+
+       msg = ranap_new_msg_reset(cnlink->domain, &cause);
+
+       return osmo_sccp_tx_unitdata_msg(cnlink->hnbgw_sccp_inst->sccp_user,
+                                        &cnlink->local_addr,
+                                        &cnlink->remote_addr,
                                         msg);
 }

-static int transmit_reset_ack(struct hnb_gw *gw, RANAP_CN_DomainIndicator_t 
domain,
-                             const struct osmo_sccp_addr *remote_addr)
+static int transmit_reset_ack(struct hnbgw_cnlink *cnlink)
 {
        struct msgb *msg;
+       struct osmo_sccp_instance *sccp = cnlink_sccp(cnlink);

-       LOGP(DRANAP, LOGL_NOTICE, "Tx RESET ACK to %s %s\n",
-            domain == RANAP_CN_DomainIndicator_cs_domain ? "IuCS" : "IuPS",
-            osmo_sccp_inst_addr_name(gw->sccp.cnlink->sccp, remote_addr));
+       if (!sccp) {
+               LOG_CNLINK(cnlink, DRANAP, LOGL_ERROR, "cannot send RANAP RESET 
ACK: no CN link\n");
+               return -1;
+       }

-       msg = ranap_new_msg_reset_ack(domain, NULL);
+       LOG_CNLINK(cnlink, DRANAP, LOGL_NOTICE, "Tx RANAP RESET ACK %s %s --> 
%s\n",
+                  cnlink_is_cs(cnlink) ? "IuCS" : "IuPS",
+                  osmo_sccp_inst_addr_to_str_c(OTC_SELECT, 
cnlink->hnbgw_sccp_inst->sccp, &cnlink->local_addr),
+                  osmo_sccp_inst_addr_to_str_c(OTC_SELECT, 
cnlink->hnbgw_sccp_inst->sccp, &cnlink->remote_addr));

-       return osmo_sccp_tx_unitdata_msg(gw->sccp.cnlink->sccp_user,
-                                        &gw->sccp.local_addr,
-                                        remote_addr,
+       msg = ranap_new_msg_reset_ack(cnlink->domain, NULL);
+
+       return osmo_sccp_tx_unitdata_msg(cnlink->hnbgw_sccp_inst->sccp_user,
+                                        &cnlink->local_addr,
+                                        &cnlink->remote_addr,
                                         msg);
 }

 /* Timer callback once T_RafC expires */
 static void cnlink_trafc_cb(void *data)
 {
-       struct hnb_gw *gw = data;
+       struct hnbgw_cnlink *cnlink = data;

-       transmit_rst(gw, RANAP_CN_DomainIndicator_cs_domain, 
&gw->sccp.iucs_remote_addr);
-       transmit_rst(gw, RANAP_CN_DomainIndicator_ps_domain, 
&gw->sccp.iups_remote_addr);
-       hnbgw_cnlink_change_state(gw->sccp.cnlink, 
CNLINK_S_EST_RST_TX_WAIT_ACK);
+       /* FUTURE: there will be a list of cnlinks and we will do 
llist_for_each_entry(gw->sccp.cnlinks) here. */
+       transmit_rst(cnlink);
+       hnbgw_cnlink_change_state(cnlink, CNLINK_S_EST_RST_TX_WAIT_ACK);
        /* The spec states that we should abandon after a configurable
         * number of times.  We decide to simply continue trying */
 }
@@ -101,7 +115,7 @@
        case CNLINK_S_EST_PEND:
                break;
        case CNLINK_S_EST_CONF:
-               cnlink_trafc_cb(cnlink->gw);
+               cnlink_trafc_cb(cnlink);
                break;
        case CNLINK_S_EST_RST_TX_WAIT_ACK:
                osmo_timer_schedule(&cnlink->T_RafC, 5, 0);
@@ -128,16 +142,16 @@
        domain = ies.cN_DomainIndicator;
        ranap_free_reseties(&ies);

-       LOGP(DRANAP, LOGL_NOTICE, "Rx RESET from %s %s, returning ACK\n",
-            domain == RANAP_CN_DomainIndicator_cs_domain ? "IuCS" : "IuPS",
-            osmo_sccp_inst_addr_name(cnlink->sccp, &unitdata->calling_addr));
+       LOG_CNLINK(cnlink, DRANAP, LOGL_NOTICE, "Rx RESET from %s %s, returning 
ACK\n",
+                  domain == DOMAIN_CS ? "IuCS" : "IuPS",
+                  osmo_sccp_inst_addr_name(cnlink_sccp(cnlink), 
&unitdata->calling_addr));

        /* FIXME: actually reset connections, if any */

-       if (transmit_reset_ack(cnlink->gw, domain, &unitdata->calling_addr))
+       if (transmit_reset_ack(cnlink))
                LOGP(DRANAP, LOGL_ERROR, "Error: cannot send RESET ACK to %s 
%s\n",
-                    domain == RANAP_CN_DomainIndicator_cs_domain ? "IuCS" : 
"IuPS",
-                    osmo_sccp_inst_addr_name(cnlink->sccp, 
&unitdata->calling_addr));
+                    domain == DOMAIN_CS ? "IuCS" : "IuPS",
+                    osmo_sccp_inst_addr_name(cnlink_sccp(cnlink), 
&unitdata->calling_addr));

        return rc;
 }
@@ -280,102 +294,97 @@
        return rc;
 }

-static bool pc_and_ssn_match(const struct osmo_sccp_addr *a, const struct 
osmo_sccp_addr *b)
+static struct hnbgw_cnlink *cnlink_from_addr(struct hnbgw_sccp_inst *hsi, 
const struct osmo_sccp_addr *calling_addr,
+                                            const struct osmo_prim_hdr *oph)
 {
-       return (a == b)
-              || ((a->pc == b->pc)
-                  && (a->ssn == b->ssn));
+       struct hnbgw_cnlink *cnlink = NULL;
+       cnlink = hnbgw_cnlink_find_by_addr(hsi, calling_addr);
+       if (!cnlink) {
+               LOGP(DRANAP, LOGL_ERROR, "Rx from unknown SCCP peer: %s: %s\n",
+                    osmo_sccp_inst_addr_name(hsi->sccp, calling_addr),
+                    osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
+               return NULL;
+       }
+       return cnlink;
 }

-static int classify_cn_remote_addr(const struct hnb_gw *gw,
-                                  const struct osmo_sccp_addr *cn_remote_addr,
-                                  bool *is_ps)
+static struct hnbgw_context_map *map_from_conn_id(struct hnbgw_sccp_inst *hsi, 
uint32_t conn_id,
+                                                 const struct osmo_prim_hdr 
*oph)
 {
-       if (pc_and_ssn_match(cn_remote_addr, &gw->sccp.iucs_remote_addr)) {
-               if (is_ps)
-                       *is_ps = false;
-               return 0;
-       }
-       if (pc_and_ssn_match(cn_remote_addr, &gw->sccp.iups_remote_addr)) {
-               if (is_ps)
-                       *is_ps = true;
-               return 0;
-       }
-       LOGP(DMAIN, LOGL_ERROR, "Unexpected remote address, matches neither CS 
nor PS address: %s\n",
-            osmo_sccp_addr_dump(cn_remote_addr));
-       return -1;
+       struct hnbgw_context_map *map;
+       hash_for_each_possible(hsi->hnbgw_context_map_by_conn_id, map, 
hnbgw_sccp_inst_entry, 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_cnlink *cnlink,
+static int handle_cn_unitdata(struct hnbgw_sccp_inst *hsi,
                              const struct osmo_scu_unitdata_param *param,
                              struct osmo_prim_hdr *oph)
 {
+       struct hnbgw_cnlink *cnlink = cnlink_from_addr(hsi, 
&param->calling_addr, oph);
+       if (!cnlink)
+               return -ENOENT;
+
        if (param->called_addr.ssn != OSMO_SCCP_SSN_RANAP) {
                LOGP(DMAIN, LOGL_NOTICE, "N-UNITDATA.ind for unknown SSN %u\n",
                        param->called_addr.ssn);
                return -1;
        }

-       if (classify_cn_remote_addr(cnlink->gw, &param->calling_addr, NULL) < 0)
-               return -1;
-
        return handle_cn_ranap(cnlink, param, msgb_l2(oph->msg), 
msgb_l2len(oph->msg));
 }

-static int handle_cn_conn_conf(struct hnbgw_cnlink *cnlink,
+static int handle_cn_conn_conf(struct hnbgw_sccp_inst *hsi,
                               const struct osmo_scu_connect_param *param,
                               struct osmo_prim_hdr *oph)
 {
-       struct osmo_ss7_instance *ss7 = 
osmo_sccp_get_ss7(cnlink->gw->sccp.client);
        struct hnbgw_context_map *map;
+       struct osmo_sccp_instance *sccp;
+
+       map = map_from_conn_id(hsi, param->conn_id, oph);
+       if (!map || !map->cnlink)
+               return -ENOENT;
+
+       sccp = cnlink_sccp(map->cnlink);

        LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() conn_id=%d, addrs: 
called=%s calling=%s responding=%s\n",
             param->conn_id,
-            osmo_sccp_addr_to_str_c(OTC_SELECT, ss7, &param->called_addr),
-            osmo_sccp_addr_to_str_c(OTC_SELECT, ss7, &param->calling_addr),
-            osmo_sccp_addr_to_str_c(OTC_SELECT, ss7, &param->responding_addr));
-
-       map = context_map_by_cn(cnlink, param->conn_id);
-       if (!map) {
-               /* We have no such SCCP connection. Ignore. */
-               return 0;
-       }
+            osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sccp, 
&param->called_addr),
+            osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sccp, 
&param->calling_addr),
+            osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sccp, 
&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_cnlink *cnlink,
+static int handle_cn_data_ind(struct hnbgw_sccp_inst *hsi,
                              const struct osmo_scu_data_param *param,
                              struct osmo_prim_hdr *oph)
 {
        struct hnbgw_context_map *map;

-       map = context_map_by_cn(cnlink, param->conn_id);
-       if (!map) {
-               /* We have no such SCCP connection. Ignore. */
-               return 0;
-       }
+       map = map_from_conn_id(hsi, 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_cnlink *cnlink,
+static int handle_cn_disc_ind(struct hnbgw_sccp_inst *hsi,
                              const struct osmo_scu_disconn_param *param,
                              struct osmo_prim_hdr *oph)
 {
        struct hnbgw_context_map *map;

-       LOGP(DMAIN, LOGL_DEBUG, "handle_cn_disc_ind() conn_id=%d 
originator=%d\n",
-            param->conn_id, param->originator);
-       LOGP(DMAIN, LOGL_DEBUG, "handle_cn_disc_ind() responding_addr=%s\n",
-            inet_ntoa(param->responding_addr.ip.v4));
+       map = map_from_conn_id(hsi, param->conn_id, oph);
+       if (!map || !map->cnlink)
+               return -ENOENT;

-       map = context_map_by_cn(cnlink, param->conn_id);
-       if (!map) {
-               /* We have no connection. Ignore. */
-               return 0;
-       }
+       LOGP(DMAIN, LOGL_DEBUG, "handle_cn_disc_ind() conn_id=%u 
responding_addr=%s\n",
+            param->conn_id,
+            osmo_sccp_inst_addr_to_str_c(OTC_SELECT, cnlink_sccp(map->cnlink), 
&param->responding_addr));

        return map_sccp_dispatch(map, MAP_SCCP_EV_RX_RELEASED, oph->msg);
 }
@@ -384,7 +393,7 @@
 static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx)
 {
        struct osmo_sccp_user *scu = ctx;
-       struct hnbgw_cnlink *cnlink;
+       struct hnbgw_sccp_inst *hsi;
        struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
        int rc = 0;

@@ -397,10 +406,10 @@
                return -1;
        }

-       cnlink = osmo_sccp_user_get_priv(scu);
-       if (!cnlink) {
+       hsi = osmo_sccp_user_get_priv(scu);
+       if (!hsi) {
                LOGP(DMAIN, LOGL_ERROR,
-                    "sccp_sap_up(): NULL hnbgw_cnlink, cannot send prim (sap 
%u prim %u op %d)\n",
+                    "sccp_sap_up(): NULL hnbgw_sccp_inst, cannot send prim 
(sap %u prim %u op %d)\n",
                     oph->sap, oph->primitive, oph->operation);
                return -1;
        }
@@ -409,16 +418,16 @@

        switch (OSMO_PRIM_HDR(oph)) {
        case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
-               rc = handle_cn_unitdata(cnlink, &prim->u.unitdata, oph);
+               rc = handle_cn_unitdata(hsi, &prim->u.unitdata, oph);
                break;
        case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
-               rc = handle_cn_conn_conf(cnlink, &prim->u.connect, oph);
+               rc = handle_cn_conn_conf(hsi, &prim->u.connect, oph);
                break;
        case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
-               rc = handle_cn_data_ind(cnlink, &prim->u.data, oph);
+               rc = handle_cn_data_ind(hsi, &prim->u.data, oph);
                break;
        case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
-               rc = handle_cn_disc_ind(cnlink, &prim->u.disconnect, oph);
+               rc = handle_cn_disc_ind(hsi, &prim->u.disconnect, oph);
                break;
        case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION):
                LOGP(DMAIN, LOGL_DEBUG, "Ignoring prim %s from SCCP USER SAP\n",
@@ -447,8 +456,6 @@
                             const char *addr_name, const char *label,
                             uint32_t default_pc)
 {
-       struct osmo_ss7_instance *ss7_tmp;
-
        if (!addr_name) {
                osmo_sccp_make_addr_pc_ssn(dest, default_pc, 
OSMO_SCCP_SSN_RANAP);
                LOGP(DMAIN, LOGL_INFO, "%s remote addr not configured, using 
default: %s\n", label,
@@ -456,21 +463,13 @@
                return 0;
        }

-       ss7_tmp = osmo_sccp_addr_by_name(dest, addr_name);
-       if (!ss7_tmp) {
+       *ss7 = osmo_sccp_addr_by_name(dest, addr_name);
+       if (!*ss7) {
                LOGP(DMAIN, LOGL_ERROR, "%s remote addr: no such SCCP address 
book entry: '%s'\n",
-                       label, addr_name);
+                    label, addr_name);
                return -1;
        }

-       if (*ss7 && (*ss7 != ss7_tmp)) {
-               LOGP(DMAIN, LOGL_ERROR, "IuCS and IuPS cannot be served from 
separate CS7 instances,"
-                    " cs7 instance %d != %d\n", (*ss7)->cfg.id, 
ss7_tmp->cfg.id);
-               return -1;
-       }
-
-       *ss7 = ss7_tmp;
-
        osmo_sccp_addr_set_ssn(dest, OSMO_SCCP_SSN_RANAP);

        if (!addr_has_pc_and_ssn(dest)) {
@@ -484,78 +483,173 @@
        return 0;
 }

-int hnbgw_cnlink_init(struct hnb_gw *gw, const char *stp_host, uint16_t 
stp_port, const char *local_ip)
+void cnlink_set_sccp_inst(struct hnbgw_cnlink *cnlink, struct hnbgw_sccp_inst 
*hsi)
 {
-       struct hnbgw_cnlink *cnlink;
-       struct osmo_ss7_instance *ss7;
        uint32_t local_pc;

-       OSMO_ASSERT(!gw->sccp.client);
-       OSMO_ASSERT(!gw->sccp.cnlink);
+       cnlink->hnbgw_sccp_inst = hsi;

-       ss7 = NULL;
-       if (resolve_addr_name(&gw->sccp.iucs_remote_addr, &ss7,
-                             gw->config.iucs_remote_addr_name, "IuCS", (23 << 
3) + 1))
-               return -1;
-       if (resolve_addr_name(&gw->sccp.iups_remote_addr, &ss7,
-                             gw->config.iups_remote_addr_name, "IuPS", (23 << 
3) + 4))
-               return -1;
-
-       if (!ss7) {
-               LOGP(DRANAP, LOGL_NOTICE, "No cs7 instance configured for IuCS 
nor IuPS,"
-                    " creating default instance\n");
-               ss7 = osmo_ss7_instance_find_or_create(gw, 0);
-               if (!ss7)
-                       return -1;
-               ss7->cfg.primary_pc = (23 << 3) + 5;
+       if ((cnlink->local_addr.presence & OSMO_SCCP_ADDR_T_PC)
+           && osmo_ss7_pc_is_valid(cnlink->local_addr.pc)) {
+               local_pc = cnlink->local_addr.pc;
+       } else {
+               struct osmo_ss7_instance *ss7 = osmo_sccp_get_ss7(hsi->sccp);
+               OSMO_ASSERT(ss7);
+               local_pc = ss7->cfg.primary_pc;
        }

-       if (!osmo_ss7_pc_is_valid(ss7->cfg.primary_pc)) {
-               LOGP(DMAIN, LOGL_ERROR, "IuCS/IuPS uplink cannot be setup: CS7 
instance %d has no point-code set\n",
-                    ss7->cfg.id);
-               return -1;
-       }
-       local_pc = ss7->cfg.primary_pc;
+       osmo_sccp_make_addr_pc_ssn(&cnlink->local_addr, local_pc, 
OSMO_SCCP_SSN_RANAP);
+}

-       osmo_sccp_make_addr_pc_ssn(&gw->sccp.local_addr, local_pc, 
OSMO_SCCP_SSN_RANAP);
-       LOGP(DRANAP, LOGL_NOTICE, "Local SCCP addr: %s\n", 
osmo_sccp_addr_name(ss7, &gw->sccp.local_addr));
+/* If not present yet, set up all of osmo_ss7_instance, osmo_sccp_instance and 
hnbgw_sccp_inst 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_inst to the new SCCP instance. Return 0 on success, 
negative on error. */
+int cnlink_ensure_sccp(struct hnbgw_cnlink *cnlink)
+{
+       struct hnb_gw *gw = cnlink->gw;
+       struct osmo_ss7_instance *ss7 = NULL;
+       struct osmo_sccp_instance *sccp;
+       struct osmo_sccp_user *sccp_user;
+       uint32_t local_pc;
+       struct hnbgw_sccp_inst *hsi;

-       gw->sccp.client = osmo_sccp_simple_client_on_ss7_id(gw, ss7->cfg.id, 
"OsmoHNBGW",
-                                                           local_pc, 
OSMO_SS7_ASP_PROT_M3UA,
-                                                           0, local_ip, 
stp_port, stp_host);
-       if (!gw->sccp.client) {
-               LOGP(DMAIN, LOGL_ERROR, "Failed to init SCCP Client\n");
-               return -1;
+       /* If a hnbgw_sccp_inst has already been set up, use that. */
+       if (cnlink->hnbgw_sccp_inst)
+               return 0;
+
+       /* Figure out which cs7 instance to use. If cnlink->remote_addr_name is 
set, it points to an address book entry
+        * in a specific cs7 instance. If it is not set, leave ss7 == NULL to 
use cs7 instance 0. */
+       if (cnlink->remote_addr_name) {
+               if (resolve_addr_name(&cnlink->remote_addr, &ss7, 
cnlink->remote_addr_name, cnlink->name,
+                                     DEFAULT_PC_HNBGW)) {
+                       LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "cannot initialize 
SCCP: there is no SCCP address named '%s'\n",
+                                  cnlink->remote_addr_name);
+                       return -ENOENT;
+               }
+
+               LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "using cs7 instance %u\n", 
ss7->cfg.id);
+
+               /* Has another cnlink already set up an SCCP instance for this 
ss7? */
+               llist_for_each_entry(hsi, &gw->sccp.instances, entry) {
+                       if (hsi->cs7_instance != ss7->cfg.id)
+                               continue;
+                       cnlink_set_sccp_inst(cnlink, hsi);
+                       return 0;
+               }
+               /* else cnlink->hnbgw_sccp_inst stays NULL and is set up below. 
*/
+
+               /* All SCCP instances should originate from this function. So 
if there is no hnbgw_sccp_inst for the cs7
+                * instance, then the cs7 instance should not have an SCCP 
instance yet. */
+               OSMO_ASSERT(!ss7->sccp);
        }

-       cnlink = talloc_zero(gw, struct hnbgw_cnlink);
-       cnlink->gw = gw;
-       INIT_LLIST_HEAD(&cnlink->map_list);
-       cnlink->T_RafC.cb = cnlink_trafc_cb;
-       cnlink->T_RafC.data = gw;
-       cnlink->next_conn_id = 1000;
+       /* No SCCP instance yet for this ss7. Create it. */
+       sccp = osmo_sccp_simple_client_on_ss7_id(gw, ss7 ? ss7->cfg.id : 0, 
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 'cs7 
instance %u'\n", ss7->cfg.id);
+               return -1;
+       }
+       ss7 = osmo_sccp_get_ss7(sccp);

-       cnlink->sccp_user = osmo_sccp_user_bind_pc(gw->sccp.client, 
"OsmoHNBGW", sccp_sap_up,
-                                                  OSMO_SCCP_SSN_RANAP, 
gw->sccp.local_addr.pc);
-       if (!cnlink->sccp_user) {
+       /* If the cnlink provides a local point-code, use that. */
+       if ((cnlink->local_addr.presence & OSMO_SCCP_ADDR_T_PC)
+           && osmo_ss7_pc_is_valid(cnlink->local_addr.pc))
+               local_pc = cnlink->local_addr.pc;
+       else if (osmo_ss7_pc_is_valid(ss7->cfg.primary_pc))
+               local_pc = ss7->cfg.primary_pc;
+       else
+               local_pc = DEFAULT_PC_HNBGW;
+
+       sccp_user = osmo_sccp_user_bind_pc(sccp, "OsmoHNBGW", sccp_sap_up, 
OSMO_SCCP_SSN_RANAP, local_pc);
+       if (!sccp_user) {
                LOGP(DMAIN, LOGL_ERROR, "Failed to init SCCP User\n");
                return -1;
        }

-       LOGP(DRANAP, LOGL_NOTICE, "Remote SCCP addr: IuCS: %s\n",
-            osmo_sccp_addr_name(ss7, &gw->sccp.iucs_remote_addr));
-       LOGP(DRANAP, LOGL_NOTICE, "Remote SCCP addr: IuPS: %s\n",
-            osmo_sccp_addr_name(ss7, &gw->sccp.iups_remote_addr));
+       hsi = talloc(cnlink, struct hnbgw_sccp_inst);
+       *hsi = (struct hnbgw_sccp_inst){
+               .gw = gw,
+               .name = talloc_asprintf(hsi, "cs7-%u.sccp", ss7->cfg.id),
+               .cs7_instance = ss7->cfg.id,
+               .sccp = sccp,
+               .sccp_user = sccp_user,
+               .next_conn_id = 1,
+       };
+       hash_init(hsi->hnbgw_context_map_by_conn_id);

-       /* In sccp_sap_up() we expect the cnlink in the user's priv. */
-       osmo_sccp_user_set_priv(cnlink->sccp_user, cnlink);
+       osmo_sccp_user_set_priv(sccp_user, hsi);

-       gw->sccp.cnlink = cnlink;
+       llist_add_tail(&hsi->entry, &gw->sccp.instances);

+       cnlink_set_sccp_inst(cnlink, hsi);
        return 0;
 }

+struct hnbgw_cnlink *hnbgw_cnlink_alloc(struct hnb_gw *gw, const char 
*remote_addr_name,
+                                       RANAP_CN_DomainIndicator_t domain)
+{
+       struct hnbgw_cnlink *cnlink;
+
+       cnlink = talloc(gw, struct hnbgw_cnlink);
+       *cnlink = (struct hnbgw_cnlink){
+               .gw = gw,
+               .name = talloc_strdup(cnlink, remote_addr_name),
+               .domain = domain,
+               .remote_addr_name = talloc_strdup(cnlink, remote_addr_name),
+               .T_RafC = {
+                       .cb = cnlink_trafc_cb,
+                       .data = cnlink,
+               },
+       };
+
+       INIT_LLIST_HEAD(&cnlink->map_list);
+
+       if (cnlink_ensure_sccp(cnlink)) {
+               /* error logging already in cnlink_ensure_sccp() */
+               talloc_free(cnlink);
+               return NULL;
+       }
+
+       switch (domain) {
+       case DOMAIN_CS:
+               OSMO_ASSERT(!gw->sccp.cnlink_iucs);
+               gw->sccp.cnlink_iucs = cnlink;
+               break;
+       case DOMAIN_PS:
+               OSMO_ASSERT(!gw->sccp.cnlink_iups);
+               gw->sccp.cnlink_iups = cnlink;
+               break;
+       default:
+               OSMO_ASSERT(false);
+       }
+
+       return cnlink;
+}
+
 const struct osmo_sccp_addr *hnbgw_cn_get_remote_addr(struct hnb_gw *gw, bool 
is_ps)
 {
-       return is_ps ? &gw->sccp.iups_remote_addr : &gw->sccp.iucs_remote_addr;
+       struct hnbgw_cnlink *cnlink = is_ps ? gw->sccp.cnlink_iups : 
gw->sccp.cnlink_iucs;
+       if (!cnlink)
+               return NULL;
+       return &cnlink->remote_addr;
+}
+
+static bool cnlink_matches(const struct hnbgw_cnlink *cnlink, const struct 
hnbgw_sccp_inst *hsi, const struct osmo_sccp_addr *remote_addr)
+{
+       if (cnlink->hnbgw_sccp_inst != hsi)
+               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_inst 
*hsi,
+                                              const struct osmo_sccp_addr 
*remote_addr)
+{
+       /* FUTURE: loop over llist g_hnb_gw->sccp.cnlinks */
+       if (cnlink_matches(hsi->gw->sccp.cnlink_iucs, hsi, remote_addr))
+               return hsi->gw->sccp.cnlink_iucs;
+       if (cnlink_matches(hsi->gw->sccp.cnlink_iups, hsi, remote_addr))
+               return hsi->gw->sccp.cnlink_iups;
+       return NULL;
 }
diff --git a/src/osmo-hnbgw/hnbgw_rua.c b/src/osmo-hnbgw/hnbgw_rua.c
index 83410b9..f0dc124 100644
--- a/src/osmo-hnbgw/hnbgw_rua.c
+++ b/src/osmo-hnbgw/hnbgw_rua.c
@@ -200,7 +200,6 @@
 {
        struct msgb *ranap_msg = NULL;
        struct hnbgw_context_map *map = NULL;
-       struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink;
        bool is_ps;

        switch (cN_DomainIndicator) {
@@ -215,11 +214,6 @@
                return -1;
        }

-       if (!cn) {
-               LOGHNB(hnb, DRUA, LOGL_NOTICE, "CN=NULL, discarding message\n");
-               return 0;
-       }
-
        /* If there is RANAP data, include it in the msgb. In RUA there is 
always data in practice, but theoretically it
         * could be an empty Connect or Disconnect. */
        if (data && len) {
@@ -230,8 +224,13 @@
                memcpy(ranap_msg->l2h, data, len);
        }

-       map = context_map_alloc_by_hnb(hnb, context_id, is_ps, cn);
-       OSMO_ASSERT(map);
+       map = context_map_find_or_create_by_rua_ctx_id(hnb, context_id, is_ps);
+       if (!map) {
+               LOGHNB(hnb, DRUA, LOGL_ERROR,
+                      "Failed to create context map for %s: rx RUA %s with %u 
bytes RANAP data\n",
+                      is_ps ? "IuPS" : "IuCS", 
rua_procedure_code_name(rua_procedure), data ? len : 0);
+               return -1;
+       }

        LOG_MAP(map, DRUA, LOGL_DEBUG, "rx RUA %s with %u bytes RANAP data\n",
                rua_procedure_code_name(rua_procedure), data ? len : 0);
diff --git a/src/osmo-hnbgw/hnbgw_vty.c b/src/osmo-hnbgw/hnbgw_vty.c
index 8d2366f..1be9ce0 100644
--- a/src/osmo-hnbgw/hnbgw_vty.c
+++ b/src/osmo-hnbgw/hnbgw_vty.c
@@ -134,36 +134,51 @@
        return vty->node;
 }

-DEFUN(show_cnlink, show_cnlink_cmd, "show cnlink",
-      SHOW_STR "Display information on core network link\n")
+static void _show_cnlink(struct vty *vty, struct hnbgw_cnlink *cnlink)
 {
        struct osmo_ss7_route *rt;
-       struct osmo_ss7_instance *ss7 = 
osmo_sccp_get_ss7(g_hnb_gw->sccp.client);
+       struct osmo_ss7_instance *ss7;
+
+       if (!cnlink) {
+               vty_out(vty, "NULL%s", VTY_NEWLINE);
+               return;
+       }
+
+       if (!cnlink->hnbgw_sccp_inst) {
+               vty_out(vty, "no SCCP instance%s", VTY_NEWLINE);
+               return;
+       }
+
+       if (!cnlink->hnbgw_sccp_inst->sccp_user) {
+               vty_out(vty, "no SCCP user%s", VTY_NEWLINE);
+               return;
+       }
+
+       ss7 = osmo_sccp_get_ss7(cnlink->hnbgw_sccp_inst->sccp);
 #define GUARD(STR) \
        STR ? STR : "", \
        STR ? ":" : ""

-       vty_out(vty, "IuCS: %s <->",
-               osmo_sccp_user_name(g_hnb_gw->sccp.cnlink->sccp_user));
+       vty_out(vty, "%s <->",
+               osmo_sccp_user_name(cnlink->hnbgw_sccp_inst->sccp_user));
        vty_out(vty, " %s%s%s%s",
-               GUARD(g_hnb_gw->config.iucs_remote_addr_name),
-               osmo_sccp_inst_addr_name(g_hnb_gw->sccp.client, 
&g_hnb_gw->sccp.iucs_remote_addr),
+               GUARD(cnlink->remote_addr_name),
+               osmo_sccp_inst_addr_name(cnlink->hnbgw_sccp_inst->sccp, 
&cnlink->remote_addr),
                VTY_NEWLINE);

-       rt = osmo_ss7_route_lookup(ss7, g_hnb_gw->sccp.iucs_remote_addr.pc);
-       vty_out(vty, "      SS7 route: %s%s", osmo_ss7_route_name(rt, true), 
VTY_NEWLINE);
-
-       vty_out(vty, "IuPS: %s <->",
-               osmo_sccp_user_name(g_hnb_gw->sccp.cnlink->sccp_user));
-       vty_out(vty, " %s%s%s%s",
-               GUARD(g_hnb_gw->config.iups_remote_addr_name),
-               osmo_sccp_inst_addr_name(g_hnb_gw->sccp.client, 
&g_hnb_gw->sccp.iups_remote_addr),
-               VTY_NEWLINE);
-
-       rt = osmo_ss7_route_lookup(ss7, g_hnb_gw->sccp.iups_remote_addr.pc);
+       rt = osmo_ss7_route_lookup(ss7, cnlink->remote_addr.pc);
        vty_out(vty, "      SS7 route: %s%s", osmo_ss7_route_name(rt, true), 
VTY_NEWLINE);

 #undef GUARD
+}
+
+DEFUN(show_cnlink, show_cnlink_cmd, "show cnlink",
+      SHOW_STR "Display information on core network link\n")
+{
+       vty_out(vty, "IuCS: ");
+       _show_cnlink(vty, g_hnb_gw->sccp.cnlink_iucs);
+       vty_out(vty, "IuPS: ");
+       _show_cnlink(vty, g_hnb_gw->sccp.cnlink_iups);
        return CMD_SUCCESS;
 }


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

Gerrit-Project: osmo-hnbgw
Gerrit-Branch: master
Gerrit-Change-Id: Iea1824f1c586723d989c80a909bae16bd2866e08
Gerrit-Change-Number: 32323
Gerrit-PatchSet: 1
Gerrit-Owner: neels <[email protected]>
Gerrit-MessageType: newchange

Reply via email to