mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw.git
synced 2025-11-02 21:13:20 +00:00
There's no behavior change since the enum item has value 0. Change-Id: I9de79db8e16cf425919b678d837b46f24ffe9bbe
567 lines
18 KiB
C
567 lines
18 KiB
C
/* hnb-gw specific code for SCCP, ITU Q.711 - Q.714 */
|
|
|
|
/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
|
|
* (C) 2025 by sysmocom s.f.m.c. GmbH <info@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/sccp/sccp_types.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_HSU(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, ¶m->calling_addr, oph);
|
|
if (!cnlink)
|
|
return -ENOENT;
|
|
|
|
CNLINK_CTR_INC(cnlink, CNLINK_CTR_SCCP_N_UNITDATA_IND);
|
|
|
|
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 void handle_notice_ind(struct hnbgw_sccp_user *hsu,
|
|
const struct osmo_scu_notice_param *param,
|
|
const struct osmo_prim_hdr *oph)
|
|
{
|
|
struct hnbgw_cnlink *cnlink;
|
|
|
|
cnlink = cnlink_from_addr(hsu, ¶m->calling_addr, oph);
|
|
if (!cnlink) {
|
|
LOGP(DCN, LOGL_DEBUG, "(calling_addr=%s) N-NOTICE.ind cause=%u='%s' importance=%u didn't match any cnlink, ignoring\n",
|
|
osmo_sccp_addr_dump(¶m->calling_addr),
|
|
param->cause, osmo_sccp_return_cause_name(param->cause),
|
|
param->importance);
|
|
return;
|
|
}
|
|
|
|
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "N-NOTICE.ind cause=%u='%s' importance=%u\n",
|
|
param->cause, osmo_sccp_return_cause_name(param->cause),
|
|
param->importance);
|
|
CNLINK_CTR_INC(cnlink, CNLINK_CTR_SCCP_N_NOTICE_IND);
|
|
switch (param->cause) {
|
|
case SCCP_RETURN_CAUSE_SUBSYSTEM_CONGESTION:
|
|
case SCCP_RETURN_CAUSE_NETWORK_CONGESTION:
|
|
/* Transient failures (hopefully), keep going. */
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Messages are not arriving to destination of cnlink. Kick it back to DISC state. */
|
|
cnlink_set_disconnected(cnlink);
|
|
}
|
|
|
|
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,
|
|
hnbgw_cnlink_sccp_addr_to_str(map->cnlink, ¶m->called_addr),
|
|
hnbgw_cnlink_sccp_addr_to_str(map->cnlink, ¶m->calling_addr),
|
|
hnbgw_cnlink_sccp_addr_to_str(map->cnlink, ¶m->responding_addr));
|
|
|
|
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_N_CONNECT_CNF);
|
|
|
|
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,
|
|
hnbgw_cnlink_sccp_addr_to_str(map->cnlink, ¶m->responding_addr),
|
|
osmo_sua_sccp_cause_name(param->cause, cause_buf, sizeof(cause_buf)));
|
|
|
|
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_N_DISCONNECT_IND);
|
|
|
|
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=%s sp_status=%s remote_sccp_status=%s\n",
|
|
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, 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;
|
|
|
|
CNLINK_CTR_INC(cnlink, CNLINK_CTR_SCCP_N_PCSTATE_IND);
|
|
|
|
/* 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=%s sp_status=%s remote_sccp_status=%s\n",
|
|
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, 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=%s sp_status=%s remote_sccp_status=%s\n",
|
|
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, 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_NOTICE, PRIM_OP_INDICATION):
|
|
handle_notice_ind(hsu, &prim->u.notice, 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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Submit primitives to SCCP User SAP
|
|
***********************************************************************/
|
|
|
|
int hnbgw_sccp_user_tx_unitdata_req(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *called_addr, struct msgb *ranap_msg)
|
|
{
|
|
if (!hsu) {
|
|
LOGP(DCN, LOGL_ERROR, "Failed to send SCCP N-UNITDATA.req: no SCCP User\n");
|
|
return -1;
|
|
}
|
|
OSMO_ASSERT(called_addr);
|
|
return osmo_sccp_tx_unitdata_msg(hsu->sccp_user,
|
|
&hsu->local_addr,
|
|
called_addr,
|
|
ranap_msg);
|
|
}
|
|
|
|
int hnbgw_sccp_user_tx_connect_req(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *called_addr, uint32_t scu_conn_id, struct msgb *ranap_msg)
|
|
{
|
|
struct osmo_scu_prim *prim;
|
|
int rc;
|
|
|
|
if (!hsu) {
|
|
LOGP(DCN, LOGL_ERROR, "Failed to send SCCP N-CONNECT.req(%u): no SCCP User\n", scu_conn_id);
|
|
return -1;
|
|
}
|
|
|
|
OSMO_ASSERT(called_addr);
|
|
|
|
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 = *called_addr;
|
|
prim->u.connect.calling_addr = hsu->local_addr;
|
|
prim->u.connect.sccp_class = 2;
|
|
prim->u.connect.conn_id = scu_conn_id;
|
|
|
|
rc = osmo_sccp_user_sap_down_nofree(hsu->sccp_user, &prim->oph);
|
|
if (rc)
|
|
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to send SCCP Connection Request to CN\n");
|
|
return rc;
|
|
}
|
|
|
|
int hnbgw_sccp_user_tx_data_req(struct hnbgw_sccp_user *hsu, uint32_t scu_conn_id, struct msgb *ranap_msg)
|
|
{
|
|
struct osmo_scu_prim *prim;
|
|
int rc;
|
|
|
|
if (!hsu) {
|
|
LOGP(DCN, LOGL_ERROR, "Failed to send SCCP N-DATA.req(%u): no SCCP User\n", scu_conn_id);
|
|
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 = scu_conn_id;
|
|
|
|
rc = osmo_sccp_user_sap_down_nofree(hsu->sccp_user, &prim->oph);
|
|
if (rc)
|
|
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to send SCCP N-DATA.req(%u)\n", scu_conn_id);
|
|
return rc;
|
|
}
|
|
|
|
int hnbgw_sccp_user_tx_disconnect_req(struct hnbgw_sccp_user *hsu, uint32_t scu_conn_id)
|
|
{
|
|
int rc;
|
|
|
|
if (!hsu) {
|
|
LOGP(DCN, LOGL_ERROR, "Failed to send SCCP N-DISCONNECT.req(%u): no SCCP User\n", scu_conn_id);
|
|
return -1;
|
|
}
|
|
|
|
rc = osmo_sccp_tx_disconn(hsu->sccp_user, scu_conn_id, NULL,
|
|
SCCP_RELEASE_CAUSE_END_USER_ORIGINATED);
|
|
if (rc)
|
|
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to send SCCP N-DISCONNECT.req(%u)\n", scu_conn_id);
|
|
return rc;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* struct hnbgw_sccp_user lifecycle:
|
|
***********************************************************************/
|
|
|
|
static int hnbgw_sccp_user_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
|
|
{
|
|
struct hnbgw_sccp_user *hsu = e->use_count->talloc_object;
|
|
int32_t total;
|
|
int level;
|
|
|
|
if (!e->use)
|
|
return -EINVAL;
|
|
|
|
total = osmo_use_count_total(&hsu->use_count);
|
|
|
|
if (total == 0
|
|
|| (total == 1 && old_use_count == 0 && e->count == 1))
|
|
level = LOGL_INFO;
|
|
else
|
|
level = LOGL_DEBUG;
|
|
|
|
LOGPSRC(DCN, level, file, line,
|
|
"%s: %s %s: now used by %s\n",
|
|
hsu->name,
|
|
(e->count - old_use_count) > 0 ? "+" : "-",
|
|
e->use,
|
|
osmo_use_count_to_str_c(OTC_SELECT, &hsu->use_count));
|
|
|
|
if (e->count < 0)
|
|
return -ERANGE;
|
|
|
|
if (total == 0)
|
|
talloc_free(hsu);
|
|
return 0;
|
|
}
|
|
|
|
static int hnbgw_sccp_user_talloc_destructor(struct hnbgw_sccp_user *hsu)
|
|
{
|
|
if (hsu->sccp_user) {
|
|
osmo_sccp_user_unbind(hsu->sccp_user);
|
|
hsu->sccp_user = NULL;
|
|
}
|
|
llist_del(&hsu->entry);
|
|
return 0;
|
|
}
|
|
|
|
struct hnbgw_sccp_user *hnbgw_sccp_user_alloc(int ss7_id)
|
|
{
|
|
struct osmo_sccp_instance *sccp;
|
|
uint32_t local_pc;
|
|
struct hnbgw_sccp_user *hsu;
|
|
|
|
hsu = talloc_zero(g_hnbgw, struct hnbgw_sccp_user);
|
|
OSMO_ASSERT(hsu);
|
|
*hsu = (struct hnbgw_sccp_user){
|
|
.name = talloc_asprintf(hsu, "cs7-%u-sccp-OsmoHNBGW", ss7_id),
|
|
.use_count = {
|
|
.talloc_object = hsu,
|
|
.use_cb = hnbgw_sccp_user_use_cb,
|
|
},
|
|
};
|
|
hash_init(hsu->hnbgw_context_map_by_conn_id);
|
|
llist_add_tail(&hsu->entry, &g_hnbgw->sccp.users);
|
|
talloc_set_destructor(hsu, hnbgw_sccp_user_talloc_destructor);
|
|
|
|
sccp = osmo_sccp_simple_client_on_ss7_id(g_hnbgw,
|
|
ss7_id,
|
|
hsu->name,
|
|
DEFAULT_PC_HNBGW,
|
|
OSMO_SS7_ASP_PROT_M3UA,
|
|
0,
|
|
"localhost",
|
|
-1,
|
|
"localhost");
|
|
if (!sccp) {
|
|
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to configure SCCP on 'cs7 instance %u'\n",
|
|
ss7_id);
|
|
goto free_hsu_ret;
|
|
}
|
|
hsu->ss7 = osmo_sccp_get_ss7(sccp);
|
|
LOG_HSU(hsu, DCN, LOGL_NOTICE, "created SCCP instance on cs7 instance %u\n", osmo_ss7_instance_get_id(hsu->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(hsu->ss7);
|
|
if (!osmo_ss7_pc_is_valid(local_pc))
|
|
local_pc = DEFAULT_PC_HNBGW;
|
|
|
|
LOG_HSU(hsu, DCN, LOGL_DEBUG, "binding OsmoHNBGW user to cs7 instance %u, local PC %u = %s\n",
|
|
osmo_ss7_instance_get_id(hsu->ss7), local_pc, osmo_ss7_pointcode_print(hsu->ss7, local_pc));
|
|
|
|
char *sccp_user_name = talloc_asprintf(hsu, "%s-RANAP", hsu->name);
|
|
hsu->sccp_user = osmo_sccp_user_bind_pc(sccp, sccp_user_name, sccp_sap_up, OSMO_SCCP_SSN_RANAP, local_pc);
|
|
talloc_free(sccp_user_name);
|
|
if (!hsu->sccp_user) {
|
|
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to init SCCP User\n");
|
|
goto free_hsu_ret;
|
|
}
|
|
|
|
osmo_sccp_make_addr_pc_ssn(&hsu->local_addr, local_pc, OSMO_SCCP_SSN_RANAP);
|
|
osmo_sccp_user_set_priv(hsu->sccp_user, hsu);
|
|
|
|
return hsu;
|
|
|
|
free_hsu_ret:
|
|
talloc_free(hsu);
|
|
return NULL;
|
|
}
|
|
|