libosmo-sccp/src/osmo_ss7_hmrt.c

325 lines
10 KiB
C

/***********************************************************************
* MTP Level 3 - Signalling message handling (SMH) Figure 23/Q.704
***********************************************************************/
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#include <osmocom/sigtran/mtp_sap.h>
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/protocol/m3ua.h>
#include "xua_internal.h"
#include "ss7_internal.h"
/* convert from M3UA message to MTP-TRANSFER.ind osmo_mtp_prim */
struct osmo_mtp_prim *m3ua_to_xfer_ind(struct xua_msg *xua)
{
struct osmo_mtp_prim *prim;
struct osmo_mtp_transfer_param *param;
struct xua_msg_part *data_ie = xua_msg_find_tag(xua, M3UA_IEI_PROT_DATA);
struct m3ua_data_hdr *data_hdr;
struct msgb *upmsg = m3ua_msgb_alloc("M3UA MTP-TRANSFER.ind");
if (!data_ie || data_ie->len < sizeof(*data_hdr)) {
/* FIXME: ERROR message */
msgb_free(upmsg);
return NULL;
}
data_hdr = (struct m3ua_data_hdr *) data_ie->dat;
/* fill primitive */
prim = (struct osmo_mtp_prim *) msgb_put(upmsg, sizeof(*prim));
param = &prim->u.transfer;
osmo_prim_init(&prim->oph, MTP_SAP_USER,
OSMO_MTP_PRIM_TRANSFER,
PRIM_OP_INDICATION, upmsg);
m3ua_dh_to_xfer_param(param, data_hdr);
/* copy data */
upmsg->l2h = msgb_put(upmsg, data_ie->len - sizeof(*data_hdr));
memcpy(upmsg->l2h, data_ie->dat+sizeof(*data_hdr), data_ie->len - sizeof(*data_hdr));
return prim;
}
/* convert from MTP-TRANSFER.req to osmo_mtp_prim */
static struct xua_msg *mtp_prim_to_m3ua(struct osmo_mtp_prim *prim)
{
struct msgb *msg = prim->oph.msg;
struct osmo_mtp_transfer_param *param = &prim->u.transfer;
struct m3ua_data_hdr data_hdr;
mtp_xfer_param_to_m3ua_dh(&data_hdr, param);
return m3ua_xfer_from_data(&data_hdr, msgb_l2(msg), msgb_l2len(msg));
}
/* delivery given XUA message to given SS7 user */
static int deliver_to_mtp_user(const struct osmo_ss7_user *osu,
struct xua_msg *xua)
{
struct osmo_mtp_prim *prim;
/* Create MTP-TRANSFER.ind and feed to user */
prim = m3ua_to_xfer_ind(xua);
if (!prim)
return -1;
prim->u.transfer = xua->mtp;
return osu->prim_cb(&prim->oph, (void *) osu->priv);
}
/* HMDC -> HMDT: Message for distribution; Figure 25/Q.704 */
/* This means it is a message we received from remote/L2, and it is to
* be routed to a local user part */
static int hmdt_message_for_distribution(struct osmo_ss7_instance *inst, struct xua_msg *xua)
{
struct m3ua_data_hdr *mdh;
const struct osmo_ss7_user *osu;
uint32_t service_ind;
switch (xua->hdr.msg_class) {
case M3UA_MSGC_XFER:
switch (xua->hdr.msg_type) {
case M3UA_XFER_DATA:
mdh = data_hdr_from_m3ua(xua);
service_ind = mdh->si & 0xf;
break;
default:
LOGP(DLSS7, LOGL_ERROR, "Unknown M3UA XFER Message "
"Type %u\n", xua->hdr.msg_type);
return -1;
}
break;
case M3UA_MSGC_SNM:
/* FIXME */
/* FIXME: SI = Signalling Network Management -> SRM/SLM/STM */
/* FIXME: SI = Signalling Network Testing and Maintenance -> SLTC */
default:
/* Discard Message */
LOGP(DLSS7, LOGL_ERROR, "Unknown M3UA Message Class %u\n",
xua->hdr.msg_class);
return -1;
}
/* Check for local SSN registered for this DPC/SSN */
osu = inst->user[service_ind];
if (osu) {
return deliver_to_mtp_user(osu, xua);
} else {
LOGP(DLSS7, LOGL_NOTICE, "No MTP-User for SI %u\n", service_ind);
/* Discard Message */
/* FIXME: User Part Unavailable HMDT -> HMRT */
return -1;
}
}
/*! Return human readable representation of the route, in a static buffer.
* This uses both osmo_ss7_pointcode_print() and osmo_ss7_pointcode_print2(), so pairing
* osmo_ss7_route_name() with osmo_ss7_pointcode_print() in the same printf statement is likely to
* conflict.
* \param[in] rt The route information to print, or NULL.
* \param[in] list_asps If true, append info for all ASPs for the route's AS.
* \returns A string constant or static buffer. */
const char *osmo_ss7_route_name(struct osmo_ss7_route *rt, bool list_asps)
{
static char buf[256];
char *pos = buf;
struct osmo_ss7_instance *inst;
size_t l;
if (!rt)
return "no route";
inst = rt->rtable->inst;
#define APPEND(fmt, args ...) \
do { \
l = snprintf(pos, sizeof(buf) - (pos - buf), fmt, ## args); \
pos += l; \
if (pos - buf >= sizeof(buf) ) \
goto out; \
} while (0)
APPEND("pc=%u=%s mask=0x%x=%s",
rt->cfg.pc, osmo_ss7_pointcode_print(inst, rt->cfg.pc),
rt->cfg.mask, osmo_ss7_pointcode_print2(inst, rt->cfg.mask));
if (rt->dest.as) {
struct osmo_ss7_as *as = rt->dest.as;
int i;
APPEND(" via AS %s proto=%s", as->cfg.name, osmo_ss7_asp_protocol_name(as->cfg.proto));
if (list_asps) {
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
struct osmo_ss7_asp *asp = as->cfg.asps[i];
if (!asp)
continue;
APPEND(" ASP");
if (asp->cfg.name)
APPEND(" %s", asp->cfg.name);
if (asp->sock_name)
APPEND(" %s", asp->sock_name);
}
}
} else if (rt->dest.linkset)
APPEND(" via linkset %s", rt->dest.linkset->cfg.name);
else
APPEND(" has no route set");
#undef APPEND
out:
buf[sizeof(buf)-1] = '\0';
return buf;
}
/* HMDC->HMRT Msg For Routing; Figure 26/Q.704 */
/* local message was receive d from L4, SRM, SLM, STM or SLTC, or
* remote message received from L2 and HMDC determined msg for routing */
static int hmrt_message_for_routing(struct osmo_ss7_instance *inst,
struct xua_msg *xua)
{
uint32_t dpc = xua->mtp.dpc;
struct osmo_ss7_route *rt;
/* find route for DPC */
/* FIXME: unify with gen_mtp_transfer_req_xua() */
rt = osmo_ss7_route_lookup(inst, dpc);
if (rt) {
/* FIXME: DPC SP restart? */
/* FIXME: DPC Congested? */
/* FIXME: Select link based on SLS */
/* FIXME: Transmit over respective Link */
if (rt->dest.as) {
struct osmo_ss7_as *as = rt->dest.as;
if (log_check_level(DLSS7, LOGL_DEBUG)) {
/* osmo_ss7_route_name() calls osmo_ss7_pointcode_print() and
* osmo_ss7_pointcode_print2(), guard against its static buffer being
* overwritten. */
const char *rt_name = osmo_ss7_route_name(rt, false);
DEBUGP(DLSS7, "Found route for dpc=%u=%s: %s\n",
dpc, osmo_ss7_pointcode_print(inst, dpc), rt_name);
}
if (osmo_ss7_as_down(as)) {
LOGP(DLSS7, LOGL_ERROR, "Unable to route HMRT message: the AS %s is down\n",
as->cfg.name);
return -ENETDOWN;
}
rate_ctr_inc2(as->ctrg, SS7_AS_CTR_TX_MSU_TOTAL);
switch (as->cfg.proto) {
case OSMO_SS7_ASP_PROT_M3UA:
DEBUGP(DLSS7, "rt->dest.as proto is M3UA for dpc=%u=%s\n",
dpc, osmo_ss7_pointcode_print(inst, dpc));
return m3ua_tx_xua_as(as,xua);
case OSMO_SS7_ASP_PROT_IPA:
return ipa_tx_xua_as(as, xua);
default:
LOGP(DLSS7, LOGL_ERROR, "MTP message "
"for ASP of unknown protocol %u\n",
as->cfg.proto);
break;
}
} else if (rt->dest.linkset) {
if (log_check_level(DLSS7, LOGL_ERROR)) {
/* osmo_ss7_route_name() calls osmo_ss7_pointcode_print() and
* osmo_ss7_pointcode_print2(), guard against its static buffer being
* overwritten. */
const char *rt_name = osmo_ss7_route_name(rt, false);
LOGP(DLSS7, LOGL_ERROR,
"Found route for dpc=%u=%s: %s,"
" but MTP-TRANSFER.req unsupported for linkset.\n",
dpc, osmo_ss7_pointcode_print(inst, dpc), rt_name);
}
} else
OSMO_ASSERT(0);
} else {
LOGP(DLSS7, LOGL_ERROR, "MTP-TRANSFER.req for DPC %u: "
"no route!\n", dpc);
/* DPC unknown HMRT -> MGMT */
/* Message Received for inaccesible SP HMRT ->RTPC */
/* Discard Message */
}
return -1;
}
/* HMDC: Received Message L2 -> L3; Figure 24/Q.704 */
/* This means a message was received from L2 and we have to decide if it
* is for the local stack (HMDT) or for routng (HMRT) */
int m3ua_hmdc_rx_from_l2(struct osmo_ss7_instance *inst, struct xua_msg *xua)
{
uint32_t dpc = xua->mtp.dpc;
if (osmo_ss7_pc_is_local(inst, dpc)) {
DEBUGP(DLSS7, "%s(): found dpc=%u=%s as local\n", __func__,
dpc, osmo_ss7_pointcode_print(inst, dpc));
return hmdt_message_for_distribution(inst, xua);
} else {
DEBUGP(DLSS7, "%s(): dpc=%u=%s not local, message is for routing\n", __func__,
dpc, osmo_ss7_pointcode_print(inst, dpc));
return hmrt_message_for_routing(inst, xua);
}
}
/* MTP-User requests to send a MTP-TRANSFER.req via the stack */
int osmo_ss7_user_mtp_xfer_req(struct osmo_ss7_instance *inst,
struct osmo_mtp_prim *omp)
{
struct xua_msg *xua;
int rc;
OSMO_ASSERT(omp->oph.sap == MTP_SAP_USER);
switch (OSMO_PRIM_HDR(&omp->oph)) {
case OSMO_PRIM(OSMO_MTP_PRIM_TRANSFER, PRIM_OP_REQUEST):
xua = mtp_prim_to_m3ua(omp);
xua->mtp = omp->u.transfer;
/* normally we would call hmrt_message_for_routing()
* here, if we were to follow the state diagrams of the
* ITU-T Q.70x specifications. However, what if a local
* MTP user sends a MTP-TRANSFER.req to a local SSN?
* This wouldn't work as per the spec, but I believe it
* is a very useful feature (aka "loopback device" in
* IPv4). So we call m3ua_hmdc_rx_from_l2() just like
* the MTP-TRANSFER had been received from L2. */
rc = m3ua_hmdc_rx_from_l2(inst, xua);
xua_msg_free(xua);
break;
default:
LOGP(DLSS7, LOGL_ERROR, "Ignoring unknown primitive %u:%u\n",
omp->oph.primitive, omp->oph.operation);
rc = -1;
}
msgb_free(omp->oph.msg);
return rc;
}