325 lines
10 KiB
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;
|
|
}
|