cnpool: add multiple 'msc' and 'sgsn' cfg (use only the first)

Allow configuring multiple MSCs and SGSNs. Show this in new transcript
test cnpool.vty and in sccp.dot chart.

Still use only the first CN link; actual CN link selection from the pool
follows in I66fba27cfbe6e2b27ee3443718846ecfbbd8a974.

Config examples and VTY tests cfg files will be adjusted to the new cfg
syntax in patch If999b71a8a8237699f6ccfcaa31d1885e66c0518. Only the VTY
write() changes are visible here, to pass all transcript tests.

Related: SYS#6412
Change-Id: I5479eded786ec26062d49403a8be12967f113cdb
This commit is contained in:
Neels Hofmeyr 2023-04-13 02:55:25 +02:00
parent bd5901d3f9
commit 9901316371
14 changed files with 650 additions and 160 deletions

View File

@ -9,13 +9,15 @@ labelloc=t; label="SCCP/SS7 use in OsmoHNBGW"
ss0 -> ss0ab
msc0a [label="2.2.2"]
ss0ab -> msc0a
sgsn0a [label="3.3.3"]
msc1a [label="5.5.5"]
ss0ab -> msc0a,sgsn0a,msc1a
}
sccp0 [label="hnbgw_sccp_user for '0'\n .osmo_ss7_instance 0\n .osmo_sccp_user SSN: RANAP\n PC: ss7 primary = 1.1.1"];
sccp0 [label="hnbgw_sccp_user for '0'\n .osmo_sccp_instance\n .osmo_ss7_user SSN: RANAP"];
ss0 -> sccp0 [dir=back]
msc0 [label="hnbgw_cnlink for IuCS\n .remote_addr -> 2.2.2"]
msc0 [label="hnbgw_cnlink 'msc0'\n .domain = IuCS\n .sccp_addr -> 2.2.2"]
sccp0 -> msc0 [dir=back]
msc0a -> msc0 [dir=back]
@ -23,26 +25,39 @@ labelloc=t; label="SCCP/SS7 use in OsmoHNBGW"
cs1 [label="UE CS conn\n hnbgw_context_map"]
msc0 -> cs0,cs1
sgsn0 [label="hnbgw_cnlink 'sgsn0'\n .domain = IuPS\n .sccp_addr -> 3.3.3"]
sccp0 -> sgsn0 [dir=back]
sgsn0a -> sgsn0 [dir=back]
ps0 [label="UE PS conn\n hnbgw_context_map"]
sgsn0 -> ps0
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
sgsn1a [label="6.6.6"]
ss1ab -> sgsn1a
}
sccp1 [label="hnbgw_sccp_user for '1'\n .osmo_ss7_instance 1\n .osmo_sccp_user SSN: RANAP\n PC: ss7 primary = 4.4.4"];
sccp1 [label="hnbgw_sccp_user for '1'\n .osmo_sccp_instance\n .osmo_ss7_user SSN: RANAP"];
ss1 -> sccp1 [dir=back]
sgsn0 [label="hnbgw_cnlink for IuPS\n .remote_addr -> 3.3.3"]
sccp1 -> sgsn0 [dir=back]
sgsn0a -> sgsn0 [dir=back]
msc1 [label="hnbgw_cnlink 'msc1'\n .domain = IuCS\n .sccp_addr -> 5.5.5"]
sccp0 -> msc1 [dir=back]
msc1a -> msc1 [dir=back]
ps0 [label="UE PS conn\n hnbgw_context_map"]
ps1 [label="UE PS conn\n hnbgw_context_map"]
sgsn0 -> ps0,ps1
cs2 [label="UE CS conn\n hnbgw_context_map"]
msc1 -> cs2
sgsn1 [label="hnbgw_cnlink 'sgsn1'\n .domain = IuPS\n .sccp_addr -> 6.6.6"]
sccp1 -> sgsn1 [dir=back]
sgsn1a -> sgsn1 [dir=back]
ps2 [label="UE PS conn\n hnbgw_context_map"]
sgsn1 -> ps2
subgraph cluster_sccp_inst {
label="global sccp.users (llist)"
@ -50,9 +65,14 @@ labelloc=t; label="SCCP/SS7 use in OsmoHNBGW"
sccp1
}
subgraph cluster_hnbgw {
label="global hnbgw"
subgraph cluster_cnpool_iucs {
label="global cnpool_iucs"
msc0
msc1
}
subgraph cluster_cnpool_iups {
label="global cnpool_iups"
sgsn0
sgsn1
}
}

View File

@ -71,6 +71,8 @@ struct umts_cell_id {
uint32_t cid; /*!< Cell ID */
};
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 {
@ -97,19 +99,62 @@ struct hnbgw_sccp_user {
#define LOG_HSI(HNBGW_SCCP_INST, SUBSYS, LEVEL, FMT, ARGS...) \
LOGP(SUBSYS, LEVEL, "(%s) " FMT, (HNBGW_SCCP_INST) ? (HNBGW_SCCP_INST)->name : "null", ##ARGS)
/* A CN peer, like MSC or SGSN. */
/* User provided configuration for struct hnbgw_cnpool. */
struct hnbgw_cnpool_cfg {
/* FUTURE: This will be added here shortly:
* - global NRI config: bitlen and NULL-NRI.
*/
};
/* User provided configuration for struct hnbgw_cnlink. */
struct hnbgw_cnlink_cfg {
/* cs7 address book entry to indicate both the remote point-code of the peer, as well as which cs7 instance to
* use. */
char *remote_addr_name;
/* FUTURE: This will be added here shortly:
* - per peer NRI config: NRI ranges assigned to this peer.
*/
};
/* Collection of CN peers to distribute UE connections across. MSCs for DOMAIN_CS, SGSNs for DOMAIN_PS. */
struct hnbgw_cnpool {
RANAP_CN_DomainIndicator_t domain;
/* CN pool string used in VTY config and logging, "iucs" or "iups". */
const char *pool_name;
/* CN peer string used in VTY config and logging, "msc" or "sgsn". */
const char *peer_name;
/* What we use as the remote MSC/SGSN point-code if the user does not configure any address. */
uint32_t default_remote_pc;
struct hnbgw_cnpool_cfg vty;
struct hnbgw_cnpool_cfg use;
/* List of struct hnbgw_cnlink */
struct llist_head cnlinks;
/* FUTURE: This will be added here shortly:
* - round robin state for new conns
*/
};
/* A CN peer, like 'msc 0' or 'sgsn 23' */
struct hnbgw_cnlink {
struct llist_head entry;
/* backpointer to CS or PS CN pool. */
struct hnbgw_cnpool *pool;
int nr;
struct hnbgw_cnlink_cfg vty;
struct hnbgw_cnlink_cfg use;
/* To print in logging/VTY */
char *name;
/* IuCS or IuPS? */
RANAP_CN_DomainIndicator_t domain;
/* 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. */
/* Copy of the address book entry use.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
@ -123,14 +168,16 @@ struct hnbgw_cnlink {
#define LOG_CNLINK(CNLINK, SUBSYS, LEVEL, FMT, ARGS...) \
LOGP(SUBSYS, LEVEL, "(%s) " FMT, (CNLINK) ? (CNLINK)->name : "null", ##ARGS)
struct hnbgw_cnlink *cnlink_get_nr(struct hnbgw_cnpool *cnpool, int nr, bool create_if_missing);
static inline bool cnlink_is_cs(const struct hnbgw_cnlink *cnlink)
{
return cnlink && cnlink->domain == DOMAIN_CS;
return cnlink && cnlink->pool->domain == DOMAIN_CS;
}
static inline bool cnlink_is_ps(const struct hnbgw_cnlink *cnlink)
{
return cnlink && cnlink->domain == DOMAIN_PS;
return cnlink && cnlink->pool->domain == DOMAIN_PS;
}
static inline struct osmo_sccp_instance *cnlink_sccp(const struct hnbgw_cnlink *cnlink)
@ -186,8 +233,6 @@ struct hnbgw {
/*! The UDP port where we receive multiplexed CS user
* plane traffic from HNBs */
uint16_t iuh_cs_mux_port;
const char *iucs_remote_addr_name;
const char *iups_remote_addr_name;
uint16_t rnc_id;
bool hnbap_allow_tmsi;
/*! print hnb-id (true) or MCC-MNC-LAC-RAC-SAC (false) in logs */
@ -213,9 +258,11 @@ struct hnbgw {
struct {
/* List of hnbgw_sccp_user */
struct llist_head users;
/* FUTURE: cnlink_iucs, cnlink_iups will be replaced with llist cnpool. */
struct hnbgw_cnlink *cnlink_iucs;
struct hnbgw_cnlink *cnlink_iups;
/* Pool of core network peers: MSCs for IuCS */
struct hnbgw_cnpool cnpool_iucs;
/* Pool of core network peers: SGSNs for IuPS */
struct hnbgw_cnpool cnpool_iups;
} sccp;
/* MGW pool, also includes the single MGCP client as fallback if no
* pool is configured. */

View File

@ -2,12 +2,14 @@
#include <osmocom/hnbgw/hnbgw.h>
struct hnbgw_cnlink *hnbgw_cnlink_alloc(const char *remote_addr_name, RANAP_CN_DomainIndicator_t domain);
const struct osmo_sccp_addr *hnbgw_cn_get_remote_addr(bool is_ps);
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(bool is_ps);
struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnbgw_context_map *map);
void hnbgw_cnpool_start(struct hnbgw_cnpool *cnpool);
void hnbgw_cnpool_apply_cfg(struct hnbgw_cnpool *cnpool);
void hnbgw_cnpool_cnlinks_start_or_restart(struct hnbgw_cnpool *cnpool);
int hnbgw_cnlink_start_or_restart(struct hnbgw_cnlink *cnlink);
char *cnlink_sccp_addr_to_str(struct hnbgw_cnlink *cnlink, const struct osmo_sccp_addr *addr);

View File

@ -10,5 +10,7 @@ enum osmo_iuh_vty_node {
MGCP_NODE,
MGW_NODE,
PFCP_NODE,
MSC_NODE,
SGSN_NODE,
};

View File

@ -137,7 +137,7 @@ static int tx_sccp_cr(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
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->is_ps);
prim->u.connect.called_addr = map->cnlink->remote_addr;
prim->u.connect.calling_addr = map->cnlink->hnbgw_sccp_user->local_addr;
prim->u.connect.sccp_class = 2;
prim->u.connect.conn_id = map->scu_conn_id;

View File

@ -60,6 +60,32 @@ void g_hnbgw_alloc(void *ctx)
#if ENABLE_PFCP
g_hnbgw->config.pfcp.remote_port = OSMO_PFCP_PORT;
#endif
g_hnbgw->sccp.cnpool_iucs = (struct hnbgw_cnpool){
.domain = DOMAIN_CS,
.pool_name = "iucs",
.peer_name = "msc",
.default_remote_pc = DEFAULT_PC_MSC,
.vty = {
/* FUTURE: This will be added here shortly:
* - defaults for global NRI config: bitlen and NULL-NRI.
*/
},
};
INIT_LLIST_HEAD(&g_hnbgw->sccp.cnpool_iucs.cnlinks);
g_hnbgw->sccp.cnpool_iups = (struct hnbgw_cnpool){
.domain = DOMAIN_PS,
.pool_name = "iups",
.peer_name = "sgsn",
.default_remote_pc = DEFAULT_PC_SGSN,
.vty = {
/* FUTURE: This will be added here shortly:
* - defaults for global NRI config: bitlen and NULL-NRI.
*/
},
};
INIT_LLIST_HEAD(&g_hnbgw->sccp.cnpool_iups.cnlinks);
}
static struct hnb_context *hnb_context_by_id(uint32_t cid)

View File

@ -60,7 +60,7 @@ static int transmit_rst(struct hnbgw_cnlink *cnlink)
cnlink_is_cs(cnlink) ? "IuCS" : "IuPS",
osmo_sccp_inst_addr_name(cnlink->hnbgw_sccp_user->sccp, &cnlink->remote_addr));
msg = ranap_new_msg_reset(cnlink->domain, &cause);
msg = ranap_new_msg_reset(cnlink->pool->domain, &cause);
return osmo_sccp_tx_unitdata_msg(cnlink->hnbgw_sccp_user->sccp_user,
&cnlink->local_addr,
@ -84,7 +84,7 @@ static int transmit_reset_ack(struct hnbgw_cnlink *cnlink)
cnlink_sccp_addr_to_str(cnlink, &cnlink->hnbgw_sccp_user->local_addr),
cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr));
msg = ranap_new_msg_reset_ack(cnlink->domain, NULL);
msg = ranap_new_msg_reset_ack(cnlink->pool->domain, NULL);
return osmo_sccp_tx_unitdata_msg(cnlink->hnbgw_sccp_user->sccp_user,
&cnlink->hnbgw_sccp_user->local_addr,
@ -422,35 +422,83 @@ static int resolve_addr_name(struct osmo_sccp_addr *dest, struct osmo_ss7_instan
{
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,
osmo_sccp_addr_name(*ss7, dest));
if (label)
LOGP(DMAIN, LOGL_INFO, "%s remote addr not configured, using default: %s\n", label,
osmo_sccp_addr_name(*ss7, dest));
return 0;
}
*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);
if (label)
LOGP(DMAIN, LOGL_ERROR, "%s remote addr: no such SCCP address book entry: '%s'\n",
label, addr_name);
return -1;
}
osmo_sccp_addr_set_ssn(dest, OSMO_SCCP_SSN_RANAP);
if (!addr_has_pc_and_ssn(dest)) {
LOGP(DMAIN, LOGL_ERROR, "Invalid/incomplete %s remote-addr: %s\n",
label, osmo_sccp_addr_name(*ss7, dest));
if (label)
LOGP(DMAIN, LOGL_ERROR, "Invalid/incomplete %s remote-addr: %s\n",
label, osmo_sccp_addr_name(*ss7, dest));
return -1;
}
LOGP(DRANAP, LOGL_NOTICE, "Remote %s SCCP addr: %s\n",
label, osmo_sccp_addr_name(*ss7, dest));
if (label)
LOGP(DRANAP, LOGL_NOTICE, "Remote %s SCCP addr: %s\n",
label, osmo_sccp_addr_name(*ss7, dest));
return 0;
}
void hnbgw_cnpool_apply_cfg(struct hnbgw_cnpool *cnpool)
{
cnpool->use = cnpool->vty;
}
static void hnbgw_cnlink_cfg_copy(struct hnbgw_cnlink *cnlink)
{
osmo_talloc_replace_string(cnlink, &cnlink->use.remote_addr_name, cnlink->vty.remote_addr_name);
}
static bool hnbgw_cnlink_sccp_cfg_changed(struct hnbgw_cnlink *cnlink)
{
bool changed = false;
if (cnlink->vty.remote_addr_name && cnlink->use.remote_addr_name) {
struct osmo_ss7_instance *ss7;
struct osmo_sccp_addr remote_addr = {};
/* Instead of comparing whether the address book entry names are different, actually resolve the
* resulting SCCP address, and only restart the cnlink if the resulting address changed. */
resolve_addr_name(&remote_addr, &ss7, cnlink->vty.remote_addr_name, NULL, DEFAULT_PC_HNBGW);
if (osmo_sccp_addr_cmp(&remote_addr, &cnlink->remote_addr, OSMO_SCCP_ADDR_T_PC | OSMO_SCCP_ADDR_T_SSN))
changed = true;
} else if (cnlink->vty.remote_addr_name != cnlink->use.remote_addr_name) {
/* One of them is NULL, the other is not. */
changed = true;
}
/* if more cnlink configuration is added in the future, it needs to be compared here. */
return changed;
}
static void hnbgw_cnlink_drop_sccp(struct hnbgw_cnlink *cnlink)
{
struct hnbgw_context_map *map, *map2;
llist_for_each_entry_safe(map, map2, &cnlink->map_list, hnbgw_cnlink_entry) {
map_sccp_dispatch(map, MAP_SCCP_EV_USER_ABORT, NULL);
}
cnlink->hnbgw_sccp_user = NULL;
}
/* 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. */
int cnlink_ensure_sccp(struct hnbgw_cnlink *cnlink)
int hnbgw_cnlink_start_or_restart(struct hnbgw_cnlink *cnlink)
{
struct osmo_ss7_instance *ss7 = NULL;
struct osmo_sccp_instance *sccp;
@ -460,39 +508,36 @@ int cnlink_ensure_sccp(struct hnbgw_cnlink *cnlink)
/* If a hnbgw_sccp_user has already been set up, use that. */
if (cnlink->hnbgw_sccp_user) {
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "SCCP instance already set up, using %s\n",
cnlink->hnbgw_sccp_user->name);
return 0;
if (hnbgw_cnlink_sccp_cfg_changed(cnlink)) {
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "config changed, restarting SCCP\n");
hnbgw_cnlink_drop_sccp(cnlink);
} else {
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "SCCP instance already set up, using %s\n",
cnlink->hnbgw_sccp_user->name);
return 0;
}
} else {
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "no SCCP instance selected yet\n");
}
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "no SCCP instance selected yet\n");
/* Copy the current configuration: cnlink->use = cnlink->vty */
hnbgw_cnlink_cfg_copy(cnlink);
/* 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) {
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "resolving 'remote-addr %s'\n", cnlink->remote_addr_name);
if (resolve_addr_name(&cnlink->remote_addr, &ss7, cnlink->remote_addr_name, cnlink->name,
if (cnlink->use.remote_addr_name) {
if (resolve_addr_name(&cnlink->remote_addr, &ss7, cnlink->use.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);
cnlink->use.remote_addr_name);
return -ENOENT;
}
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "remote-addr is '%s', using cs7 instance %u\n",
cnlink->remote_addr_name, ss7->cfg.id);
cnlink->use.remote_addr_name, ss7->cfg.id);
} else {
/* If no address is configured, use the default remote CN address, according to legacy behavior. */
uint32_t remote_pc;
switch (cnlink->domain) {
case DOMAIN_CS:
remote_pc = DEFAULT_PC_MSC;
break;
case DOMAIN_PS:
remote_pc = DEFAULT_PC_SGSN;
break;
default:
return -EINVAL;
}
osmo_sccp_make_addr_pc_ssn(&cnlink->remote_addr, remote_pc, OSMO_SCCP_SSN_RANAP);
osmo_sccp_make_addr_pc_ssn(&cnlink->remote_addr, cnlink->pool->default_remote_pc, OSMO_SCCP_SSN_RANAP);
}
/* If no 'cs7 instance' has been selected by the address, see if there already is a cs7 0 we can use by default.
@ -563,47 +608,54 @@ int cnlink_ensure_sccp(struct hnbgw_cnlink *cnlink)
return 0;
}
struct hnbgw_cnlink *hnbgw_cnlink_alloc(const char *remote_addr_name, RANAP_CN_DomainIndicator_t domain)
void hnbgw_cnpool_cnlinks_start_or_restart(struct hnbgw_cnpool *cnpool)
{
struct hnbgw_cnlink *cnlink;
hnbgw_cnpool_apply_cfg(cnpool);
llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
hnbgw_cnlink_start_or_restart(cnlink);
}
}
void hnbgw_cnpool_start(struct hnbgw_cnpool *cnpool)
{
/* Legacy compat: when there is no 'msc N' at all in the config file, set up 'msc 0' with default values (or
* 'sgsn' depending on cnpool). */
if (llist_empty(&cnpool->cnlinks))
cnlink_get_nr(cnpool, 0, true);
hnbgw_cnpool_cnlinks_start_or_restart(cnpool);
}
static struct hnbgw_cnlink *cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr)
{
struct hnbgw_cnlink *cnlink;
cnlink = talloc_zero(g_hnbgw, struct hnbgw_cnlink);
*cnlink = (struct hnbgw_cnlink){
.name = (domain == DOMAIN_CS ? "msc-0" : "sgsn-0"),
.domain = domain,
.remote_addr_name = talloc_strdup(cnlink, remote_addr_name),
.name = talloc_asprintf(cnlink, "%s-%d", cnpool->peer_name, nr),
.pool = cnpool,
.nr = nr,
.vty = {
/* VTY config defaults for the new 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(!g_hnbgw->sccp.cnlink_iucs);
g_hnbgw->sccp.cnlink_iucs = cnlink;
break;
case DOMAIN_PS:
OSMO_ASSERT(!g_hnbgw->sccp.cnlink_iups);
g_hnbgw->sccp.cnlink_iups = cnlink;
break;
default:
OSMO_ASSERT(false);
}
llist_add_tail(&cnlink->entry, &cnpool->cnlinks);
return cnlink;
}
const struct osmo_sccp_addr *hnbgw_cn_get_remote_addr(bool is_ps)
struct hnbgw_cnlink *cnlink_get_nr(struct hnbgw_cnpool *cnpool, int nr, bool create_if_missing)
{
struct hnbgw_cnlink *cnlink = is_ps ? g_hnbgw->sccp.cnlink_iups : g_hnbgw->sccp.cnlink_iucs;
if (!cnlink)
struct hnbgw_cnlink *cnlink;
llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
if (cnlink->nr == nr)
return cnlink;
}
if (!create_if_missing)
return NULL;
return &cnlink->remote_addr;
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)
@ -614,24 +666,37 @@ static bool cnlink_matches(const struct hnbgw_cnlink *cnlink, const struct hnbgw
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)
{
/* FUTURE: loop over llist g_hnb_gw->sccp.cnpool */
if (cnlink_matches(g_hnbgw->sccp.cnlink_iucs, hsu, remote_addr))
return g_hnbgw->sccp.cnlink_iucs;
if (cnlink_matches(g_hnbgw->sccp.cnlink_iups, hsu, remote_addr))
return g_hnbgw->sccp.cnlink_iups;
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;
}
struct hnbgw_cnlink *hnbgw_cnlink_select(bool is_ps)
struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnbgw_context_map *map)
{
struct hnbgw_cnpool *cnpool = map->is_ps ? &g_hnbgw->sccp.cnpool_iups : &g_hnbgw->sccp.cnpool_iucs;
struct hnbgw_cnlink *cnlink;
/* 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 g_hnbgw->sccp.cnlink_iups;
return g_hnbgw->sccp.cnlink_iucs;
llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
if (!cnlink->hnbgw_sccp_user || !cnlink->hnbgw_sccp_user->sccp_user)
continue;
LOG_MAP(map, DCN, LOGL_INFO, "Selected %s / %s\n",
cnlink->name,
cnlink->hnbgw_sccp_user->name);
return cnlink;
}
return NULL;
}
char *cnlink_sccp_addr_to_str(struct hnbgw_cnlink *cnlink, const struct osmo_sccp_addr *addr)

View File

@ -74,6 +74,7 @@ DEFUN(cfg_hnbgw_iucs, cfg_hnbgw_iucs_cmd,
"iucs", "Configure IuCS options")
{
vty->node = IUCS_NODE;
vty->index = &g_hnbgw->sccp.cnpool_iucs;
return CMD_SUCCESS;
}
@ -87,6 +88,7 @@ DEFUN(cfg_hnbgw_iups, cfg_hnbgw_iups_cmd,
"iups", "Configure IuPS options")
{
vty->node = IUPS_NODE;
vty->index = &g_hnbgw->sccp.cnpool_iups;
return CMD_SUCCESS;
}
@ -138,8 +140,8 @@ static void _show_cnlink(struct vty *vty, struct hnbgw_cnlink *cnlink)
vty_out(vty, "%s <->",
osmo_sccp_user_name(cnlink->hnbgw_sccp_user->sccp_user));
vty_out(vty, " %s%s%s%s",
cnlink->remote_addr_name ? : "",
cnlink->remote_addr_name ? "=" : "",
cnlink->use.remote_addr_name ? : "",
cnlink->use.remote_addr_name ? "=" : "",
cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr),
VTY_NEWLINE);
@ -150,10 +152,13 @@ static void _show_cnlink(struct vty *vty, struct hnbgw_cnlink *cnlink)
DEFUN(show_cnlink, show_cnlink_cmd, "show cnlink",
SHOW_STR "Display information on core network link\n")
{
struct hnbgw_cnlink *cnlink;
vty_out(vty, "IuCS: ");
_show_cnlink(vty, g_hnbgw->sccp.cnlink_iucs);
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iucs.cnlinks, entry)
_show_cnlink(vty, cnlink);
vty_out(vty, "IuPS: ");
_show_cnlink(vty, g_hnbgw->sccp.cnlink_iups);
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iups.cnlinks, entry)
_show_cnlink(vty, cnlink);
return CMD_SUCCESS;
}
@ -341,23 +346,130 @@ DEFUN_DEPRECATED(cfg_hnbgw_max_sccp_cr_payload_len, cfg_hnbgw_max_sccp_cr_payloa
return CMD_WARNING;
}
DEFUN(cfg_hnbgw_iucs_remote_addr,
cfg_hnbgw_iucs_remote_addr_cmd,
"remote-addr NAME",
"SCCP address to send IuCS to (MSC)\n"
"SCCP address book entry name (see 'cs7-instance')\n")
/* Legacy from when there was only one IuCS and one IuPS peer. Instead, there are now 'msc 123' / 'sgsn 123' sub nodes.
* To yield legacy behavior, set the first cnlink config in this pool ('msc 0' / 'sgsn 0'). */
DEFUN_DEPRECATED(cfg_hnbgw_cnpool_remote_addr,
cfg_hnbgw_cnpool_remote_addr_cmd,
"remote-addr NAME",
"Deprecated command: same as '{msc,sgsn} 0' / 'remote-addr NAME'\n-\n")
{
g_hnbgw->config.iucs_remote_addr_name = talloc_strdup(g_hnbgw, argv[0]);
const char *logmsg;
struct hnbgw_cnpool *cnpool = vty->index;
struct hnbgw_cnlink *cnlink = cnlink_get_nr(cnpool, 0, true);
OSMO_ASSERT(cnlink);
cnlink->vty.remote_addr_name = talloc_strdup(cnlink, argv[0]);
logmsg = talloc_asprintf(OTC_SELECT,
"Deprecated: instead of hnbgw/%s/remote-addr,"
" use '%s 0'/remote-addr",
cnpool->pool_name,
cnpool->peer_name);
vty_out(vty, "%% %s%s", logmsg, VTY_NEWLINE);
LOGP(DLGLOBAL, LOGL_ERROR, "config: %s\n", logmsg);
return CMD_SUCCESS;
}
DEFUN(cfg_hnbgw_iups_remote_addr,
cfg_hnbgw_iups_remote_addr_cmd,
"remote-addr NAME",
"SCCP address to send IuPS to (SGSN)\n"
"SCCP address book entry name (see 'cs7-instance')\n")
#define CNLINK_NR_RANGE "<0-1000>"
static struct cmd_node msc_node = {
MSC_NODE,
"%s(config-msc)# ",
1,
};
static struct cmd_node sgsn_node = {
SGSN_NODE,
"%s(config-sgsn)# ",
1,
};
/* Commands that are common for 'msc 0' and 'sgsn 0' */
static int cnlink_nr(struct vty *vty, struct hnbgw_cnpool *cnpool, int argc, const char **argv)
{
g_hnbgw->config.iups_remote_addr_name = talloc_strdup(g_hnbgw, argv[0]);
int nr = atoi(argv[0]);
struct hnbgw_cnlink *cnlink = cnlink_get_nr(cnpool, nr, true);
OSMO_ASSERT(cnlink);
switch (cnpool->domain) {
case DOMAIN_CS:
vty->node = MSC_NODE;
break;
case DOMAIN_PS:
vty->node = SGSN_NODE;
break;
default:
OSMO_ASSERT(false);
}
vty->index = cnlink;
return CMD_SUCCESS;
}
/* 'msc 0' */
DEFUN(cfg_msc_nr, cfg_msc_nr_cmd,
"msc " CNLINK_NR_RANGE,
"Configure an IuCS link to an MSC\n"
"MSC nr\n")
{
return cnlink_nr(vty, &g_hnbgw->sccp.cnpool_iucs, argc, argv);
}
/* 'sgsn 0' */
DEFUN(cfg_sgsn_nr, cfg_sgsn_nr_cmd,
"sgsn " CNLINK_NR_RANGE,
"Configure an IuPS link to an SGSN\n"
"SGSN nr\n")
{
return cnlink_nr(vty, &g_hnbgw->sccp.cnpool_iups, argc, argv);
}
/* 'msc 0' / 'remote-addr my-msc' and
* 'sgsn 0' / 'remote-addr my-sgsn'
*/
DEFUN(cfg_cnlink_remote_addr,
cfg_cnlink_remote_addr_cmd,
"remote-addr NAME",
"SCCP address to send RANAP/SCCP to\n"
"SCCP address book entry name (see 'cs7 instance' / 'sccp-address')\n")
{
struct hnbgw_cnlink *cnlink = vty->index;
cnlink->vty.remote_addr_name = talloc_strdup(cnlink, argv[0]);
return CMD_SUCCESS;
}
#define APPLY_STR "Immediately use configuration modified via telnet VTY, and restart components as needed.\n"
#define SCCP_RESTART_STR \
" If 'remote-addr' changed, related SCCP links will be restarted, possibly dropping active UE contexts."
#define IMPLICIT_ON_STARTUP_STR \
" This is run implicitly on program startup, only useful to apply changes made later via telnet VTY."
DEFUN(cfg_cnlink_apply_sccp, cfg_cnlink_apply_sccp_cmd,
"apply sccp",
APPLY_STR
"For telnet VTY: apply SCCP and NRI config changes made to this CN link in the running osmo-hnbgw process."
SCCP_RESTART_STR IMPLICIT_ON_STARTUP_STR "\n")
{
struct hnbgw_cnlink *cnlink = vty->index;
hnbgw_cnlink_start_or_restart(cnlink);
return CMD_SUCCESS;
}
DEFUN(cfg_config_apply_sccp, cfg_config_apply_sccp_cmd,
"apply sccp",
APPLY_STR
"For telnet VTY: apply all SCCP and NRI config changes made to any CN pools and CN links in the running"
" osmo-hnbgw process."
SCCP_RESTART_STR IMPLICIT_ON_STARTUP_STR "\n")
{
struct hnbgw_cnpool *cnpool;
cnpool = &g_hnbgw->sccp.cnpool_iucs;
hnbgw_cnpool_apply_cfg(cnpool);
hnbgw_cnpool_cnlinks_start_or_restart(cnpool);
cnpool = &g_hnbgw->sccp.cnpool_iups;
hnbgw_cnpool_apply_cfg(cnpool);
hnbgw_cnpool_cnlinks_start_or_restart(cnpool);
return CMD_SUCCESS;
}
@ -440,27 +552,31 @@ static int config_write_hnbgw_iuh(struct vty *vty)
return CMD_SUCCESS;
}
static int config_write_hnbgw_iucs(struct vty *vty)
/* hnbgw
* msc 0 } this part
* sgsn 0 }
*/
static void _config_write_cnlink(struct vty *vty, struct hnbgw_cnpool *cnpool)
{
if (!g_hnbgw->config.iucs_remote_addr_name)
return CMD_SUCCESS;
struct hnbgw_cnlink *cnlink;
vty_out(vty, " iucs%s", VTY_NEWLINE);
vty_out(vty, " remote-addr %s%s", g_hnbgw->config.iucs_remote_addr_name,
VTY_NEWLINE);
llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
vty_out(vty, "%s %d%s", cnpool->peer_name, cnlink->nr, VTY_NEWLINE);
if (cnlink->vty.remote_addr_name)
vty_out(vty, " remote-addr %s%s", cnlink->vty.remote_addr_name, VTY_NEWLINE);
/* FUTURE: NRI config */
}
}
static int config_write_msc(struct vty *vty)
{
_config_write_cnlink(vty, &g_hnbgw->sccp.cnpool_iucs);
return CMD_SUCCESS;
}
static int config_write_hnbgw_iups(struct vty *vty)
static int config_write_sgsn(struct vty *vty)
{
if (!g_hnbgw->config.iups_remote_addr_name)
return CMD_SUCCESS;
vty_out(vty, " iups%s", VTY_NEWLINE);
vty_out(vty, " remote-addr %s%s", g_hnbgw->config.iups_remote_addr_name,
VTY_NEWLINE);
_config_write_cnlink(vty, &g_hnbgw->sccp.cnpool_iups);
return CMD_SUCCESS;
}
@ -477,6 +593,12 @@ static int config_write_hnbgw_pfcp(struct vty *vty)
}
#endif
static void install_cnlink_elements(int node)
{
install_element(node, &cfg_cnlink_remote_addr_cmd);
install_element(node, &cfg_cnlink_apply_sccp_cmd);
}
void hnbgw_vty_init(void)
{
install_element(CONFIG_NODE, &cfg_hnbgw_cmd);
@ -494,14 +616,14 @@ void hnbgw_vty_init(void)
install_element(IUH_NODE, &cfg_hnbgw_iuh_hnbap_allow_tmsi_cmd);
install_element(HNBGW_NODE, &cfg_hnbgw_iucs_cmd);
install_node(&iucs_node, config_write_hnbgw_iucs);
install_element(IUCS_NODE, &cfg_hnbgw_iucs_remote_addr_cmd);
install_node(&iucs_node, NULL);
install_element(HNBGW_NODE, &cfg_hnbgw_iups_cmd);
install_node(&iups_node, config_write_hnbgw_iups);
install_node(&iups_node, NULL);
install_element(IUPS_NODE, &cfg_hnbgw_iups_remote_addr_cmd);
/* deprecated: 'remote-addr' outside of 'msc 123' redirects to 'msc 0' / same for 'sgsn' */
install_element(IUCS_NODE, &cfg_hnbgw_cnpool_remote_addr_cmd);
install_element(IUPS_NODE, &cfg_hnbgw_cnpool_remote_addr_cmd);
install_element_ve(&show_cnlink_cmd);
install_element_ve(&show_hnb_cmd);
@ -524,5 +646,16 @@ void hnbgw_vty_init(void)
install_element(PFCP_NODE, &cfg_pfcp_remote_addr_cmd);
#endif
install_element(CONFIG_NODE, &cfg_msc_nr_cmd);
install_node(&msc_node, config_write_msc);
install_cnlink_elements(MSC_NODE);
install_element(CONFIG_NODE, &cfg_sgsn_nr_cmd);
install_node(&sgsn_node, config_write_sgsn);
install_cnlink_elements(SGSN_NODE);
/* global 'apply sccp' command. There are two more on MSC_NODE and SGSN_NODE from install_cnlink_elements(). */
install_element(CONFIG_NODE, &cfg_config_apply_sccp_cmd);
osmo_tdef_vty_groups_init(HNBGW_NODE, hnbgw_tdef_group);
}

View File

@ -259,14 +259,6 @@ int main(int argc, char **argv)
ranap_set_log_area(DRANAP);
if (!hnbgw_cnlink_alloc(g_hnbgw->config.iucs_remote_addr_name, DOMAIN_CS)
|| !hnbgw_cnlink_alloc(g_hnbgw->config.iups_remote_addr_name, DOMAIN_PS)) {
LOGP(DMAIN, LOGL_ERROR, "Failed to initialize SCCP link to CN\n");
exit(1);
}
OSMO_ASSERT(g_hnbgw->sccp.cnlink_iucs);
OSMO_ASSERT(g_hnbgw->sccp.cnlink_iups);
LOGP(DHNBAP, LOGL_NOTICE, "Using RNC-Id %u\n", g_hnbgw->config.rnc_id);
OSMO_ASSERT(g_hnbgw->config.iuh_local_ip);
@ -300,6 +292,9 @@ int main(int argc, char **argv)
hnbgw_pfcp_init();
#endif
hnbgw_cnpool_start(&g_hnbgw->sccp.cnpool_iucs);
hnbgw_cnpool_start(&g_hnbgw->sccp.cnpool_iups);
if (hnbgw_cmdline_config.daemonize) {
rc = osmo_daemonize();
if (rc < 0) {

194
tests/cnpool.vty Normal file
View File

@ -0,0 +1,194 @@
OsmoHNBGW> enable
OsmoHNBGW# configure terminal
OsmoHNBGW(config)# ### cnpool doc strings
OsmoHNBGW(config)# hnbgw
OsmoHNBGW(config-hnbgw)# list
...
iucs
iups
...
OsmoHNBGW(config-hnbgw)# iucs?
iucs Configure IuCS options
OsmoHNBGW(config-hnbgw)# iups?
iups Configure IuPS options
OsmoHNBGW(config-hnbgw)# exit
OsmoHNBGW(config)# msc?
msc Configure an IuCS link to an MSC
OsmoHNBGW(config)# msc ?
<0-1000> MSC nr
OsmoHNBGW(config)# sgsn?
sgsn Configure an IuPS link to an SGSN
OsmoHNBGW(config)# sgsn ?
<0-1000> SGSN nr
OsmoHNBGW(config)# ### The config file has no 'msc' or 'sgsn', so defaults have been set up
OsmoHNBGW(config)# show running-config
...
msc 0
sgsn 0
...
OsmoHNBGW(config)# msc 1
OsmoHNBGW(config-msc)# list
...
remote-addr NAME
apply sccp
...
OsmoHNBGW(config-msc)# remote-addr?
remote-addr SCCP address to send RANAP/SCCP to
OsmoHNBGW(config-msc)# remote-addr ?
NAME SCCP address book entry name (see 'cs7 instance' / 'sccp-address')
OsmoHNBGW(config-msc)# apply?
apply Immediately use configuration modified via telnet VTY, and restart components as needed.
OsmoHNBGW(config-msc)# apply ?
sccp For telnet VTY: apply SCCP and NRI config changes made to this CN link in the running osmo-hnbgw process. If 'remote-addr' changed, related SCCP links will be restarted, possibly dropping active UE contexts. This is run implicitly on program startup, only useful to apply changes made later via telnet VTY.
OsmoHNBGW(config-msc)# exit
OsmoHNBGW(config)# sgsn 1
OsmoHNBGW(config-sgsn)# list
...
remote-addr NAME
apply sccp
...
OsmoHNBGW(config-sgsn)# remote-addr?
remote-addr SCCP address to send RANAP/SCCP to
OsmoHNBGW(config-sgsn)# remote-addr ?
NAME SCCP address book entry name (see 'cs7 instance' / 'sccp-address')
OsmoHNBGW(config-sgsn)# apply?
apply Immediately use configuration modified via telnet VTY, and restart components as needed.
OsmoHNBGW(config-sgsn)# apply ?
sccp For telnet VTY: apply SCCP and NRI config changes made to this CN link in the running osmo-hnbgw process. If 'remote-addr' changed, related SCCP links will be restarted, possibly dropping active UE contexts. This is run implicitly on program startup, only useful to apply changes made later via telnet VTY.
OsmoHNBGW(config-sgsn)# exit
OsmoHNBGW(config)# ### Just by entering the nodes above, 'msc 1' and 'sgsn 1' now exist
OsmoHNBGW(config)# show running-config
...
msc 0
msc 1
sgsn 0
sgsn 1
...
OsmoHNBGW(config)# ### Add {msc,sgsn}x{2,3}
OsmoHNBGW(config)# msc 2
OsmoHNBGW(config-msc)# remote-addr addr-msc2
OsmoHNBGW(config-msc)# exit
OsmoHNBGW(config)# msc 3
OsmoHNBGW(config-msc)# remote-addr addr-msc3
OsmoHNBGW(config-msc)# exit
OsmoHNBGW(config)# sgsn 2
OsmoHNBGW(config-sgsn)# remote-addr addr-sgsn2
OsmoHNBGW(config-sgsn)# exit
OsmoHNBGW(config)# sgsn 3
OsmoHNBGW(config-sgsn)# remote-addr addr-sgsn3
OsmoHNBGW(config-sgsn)# exit
OsmoHNBGW(config)# show running-config
...
msc 0
msc 1
msc 2
remote-addr addr-msc2
msc 3
remote-addr addr-msc3
sgsn 0
sgsn 1
sgsn 2
remote-addr addr-sgsn2
sgsn 3
remote-addr addr-sgsn3
...
OsmoHNBGW(config)# ### Re-entering existing nodes works (does not create new ones)
OsmoHNBGW(config)# msc 3
OsmoHNBGW(config-msc)# remote-addr addr-msc4
OsmoHNBGW(config-msc)# exit
OsmoHNBGW(config)# sgsn 2
OsmoHNBGW(config-sgsn)# remote-addr addr-sgsn4
OsmoHNBGW(config-sgsn)# exit
OsmoHNBGW(config)# show running-config
...
msc 0
msc 1
msc 2
remote-addr addr-msc2
msc 3
remote-addr addr-msc4
sgsn 0
sgsn 1
sgsn 2
remote-addr addr-sgsn4
sgsn 3
remote-addr addr-sgsn3
...
OsmoHNBGW(config)# ### Legacy configuration {'iups','iucs'} / 'remote-addr' redirects to {'msc 0','sgsn 0'}:
OsmoHNBGW(config)# hnbgw
OsmoHNBGW(config-hnbgw)# iucs
OsmoHNBGW(config-hnbgw-iucs)# remote-addr?
% There is no matched command.
OsmoHNBGW(config-hnbgw-iucs)# remote-addr ?
% There is no matched command.
OsmoHNBGW(config-hnbgw-iucs)# remote-addr addr-msc0
% Deprecated: instead of hnbgw/iucs/remote-addr, use 'msc 0'/remote-addr
OsmoHNBGW(config-hnbgw-iucs)# exit
OsmoHNBGW(config-hnbgw)# iups
OsmoHNBGW(config-hnbgw-iups)# remote-addr?
% There is no matched command.
OsmoHNBGW(config-hnbgw-iups)# remote-addr ?
% There is no matched command.
OsmoHNBGW(config-hnbgw-iups)# remote-addr addr-sgsn0
% Deprecated: instead of hnbgw/iups/remote-addr, use 'sgsn 0'/remote-addr
OsmoHNBGW(config-hnbgw-iups)# exit
OsmoHNBGW(config-hnbgw)# show running-config
...
hnbgw
...
msc 0
remote-addr addr-msc0
msc 1
msc 2
remote-addr addr-msc2
msc 3
remote-addr addr-msc4
sgsn 0
remote-addr addr-sgsn0
sgsn 1
sgsn 2
remote-addr addr-sgsn4
sgsn 3
remote-addr addr-sgsn3
...
OsmoHNBGW(config-hnbgw)# ### placement of the new 'msc' and 'sgsn' nodes must be below 'pfcp' and 'mgw' nodes
OsmoHNBGW(config-hnbgw)# show running-config
...
hnbgw
... !msc
iuh
... !msc
mgw 0
...
msc 0
...

View File

@ -39,8 +39,9 @@ hnbgw
rnc-id 23
log-prefix hnb-id
iuh
iucs
remote-addr my-msc
iups
remote-addr my-sgsn
...
msc 0
remote-addr my-msc
sgsn 0
remote-addr my-sgsn
...

View File

@ -32,6 +32,8 @@ hnbgw
rnc-id 23
log-prefix hnb-id
iuh
iucs
remote-addr my-msc
...
msc 0
remote-addr my-msc
sgsn 0
... !remote-addr

View File

@ -32,6 +32,8 @@ hnbgw
rnc-id 23
log-prefix hnb-id
iuh
iups
remote-addr my-sgsn
...
msc 0
sgsn 0
remote-addr my-sgsn
...

View File

@ -50,8 +50,9 @@ hnbgw
rnc-id 23
log-prefix hnb-id
iuh
iucs
remote-addr my-msc
iups
remote-addr my-sgsn
...
msc 0
remote-addr my-msc
sgsn 0
remote-addr my-sgsn
...