/*********************************************************************** * MTP Level 3 - Transfer prohibited control (RTPC), ITU Q.704 Figure 44 ***********************************************************************/ /* (C) 2025 by sysmocom s.f.m.c. GmbH * 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 . * */ #include #include #include #include "mtp3_hmdt.h" #include "mtp3_rtpc.h" #include "ss7_as.h" #include "ss7_internal.h" #include "ss7_route.h" #include "ss7_route_table.h" #include "xua_internal.h" #include "xua_msg.h" /* Generate a DUNA message to be sent back to originator. */ static struct xua_msg *gen_duna_ret_msg(struct osmo_ss7_instance *inst, const struct xua_msg *orig_xua) { struct xua_msg *xua; struct xua_msg_part *rctx_ie; unsigned int num_rctx = 0; uint32_t rctx = 0; uint32_t aff_pc = htonl(orig_xua->mtp.dpc); if ((rctx_ie = xua_msg_find_tag(orig_xua, M3UA_IEI_ROUTE_CTX))) { rctx = xua_msg_part_get_u32(rctx_ie); num_rctx = 1; } xua = m3ua_encode_duna(&rctx, num_rctx, &aff_pc, 1, "transfer prohibited (inaccessible SP)"); OSMO_ASSERT(xua); xua->mtp = orig_xua->mtp; xua->mtp.opc = orig_xua->mtp.dpc; xua->mtp.dpc = orig_xua->mtp.opc; return xua; } /* Figure 44/Q.704 (sheet 1 of 3), "Message received for inaccessible SP HMRT -> RTPC" */ int mtp3_rtpc_rx_msg_for_inaccessible_sp(struct osmo_ss7_instance *inst, const struct xua_msg *orig_xua) { struct xua_msg *xua; char buf_orig_opc[MAX_PC_STR_LEN]; char buf_orig_dpc[MAX_PC_STR_LEN]; struct osmo_ss7_route_label rtlabel; struct osmo_ss7_route *rt; /* Start T8 */ if (ss7_instance_t8_inaccessible_sp_running(inst, orig_xua->mtp.dpc)) { /* T8 is running for this SP, inhibit Tx of transfer prohibited */ LOGSS7(inst, LOGL_DEBUG, "Tx TFP (DUNA) inaccessible SP %u=%s to concerned SP %u=%s: inhibit due to T8\n", orig_xua->mtp.dpc, osmo_ss7_pointcode_print_buf(buf_orig_dpc, sizeof(buf_orig_dpc), inst, orig_xua->mtp.dpc), orig_xua->mtp.opc, osmo_ss7_pointcode_print_buf(buf_orig_opc, sizeof(buf_orig_opc), inst, orig_xua->mtp.opc)); return 0; } ss7_instance_t8_inaccessible_sp_start(inst, orig_xua->mtp.dpc); /* "transfer prohibited RTPC -> HMRT", "To concerned SP or STP". * See also Q.704 13.2 Transfer-prohibited. */ /* Note: There's no explicit mention of MTP3 TFP equivalent in RFC4666 (M3UA) specs, * but section 1.4.3.2 explicitly mentions: "TFP ... MUST NOT be encapsulated as * Data message Payload Data and sent either from SG to ASP or from ASP to * SG. The SG MUST terminate these messages and generate M3UA messages, * as appropriate." * Best match for it is DUNA, so DUNA we send. */ if (osmo_ss7_pc_is_local(inst, orig_xua->mtp.opc)) { xua = gen_duna_ret_msg(inst, orig_xua); return mtp3_hmdt_message_for_distribution(inst, xua); } /* We should only be sending DUNA to M3UA peers, hence why we don't * simply call mtp3_hmrt_message_for_routing() here. */ rtlabel = (struct osmo_ss7_route_label){ .opc = orig_xua->mtp.dpc, .dpc = orig_xua->mtp.opc, .sls = orig_xua->mtp.sls, }; rt = ss7_instance_lookup_route(inst, &rtlabel); if (!rt) { LOGSS7(inst, LOGL_NOTICE, "Tx TFP (DUNA) inaccessible SP %u=%s to concerned SP %u=%s: no route!\n", orig_xua->mtp.dpc, osmo_ss7_pointcode_print_buf(buf_orig_dpc, sizeof(buf_orig_dpc), inst, orig_xua->mtp.dpc), orig_xua->mtp.opc, osmo_ss7_pointcode_print_buf(buf_orig_opc, sizeof(buf_orig_opc), inst, orig_xua->mtp.opc)); return 0; } if (!rt->dest.as) { LOGSS7(inst, LOGL_ERROR, "Tx TFP (DUNA) inaccessible SP %u=%s to concerned SP %u=%s: unsupported for linkset!\n", orig_xua->mtp.dpc, osmo_ss7_pointcode_print_buf(buf_orig_dpc, sizeof(buf_orig_dpc), inst, orig_xua->mtp.dpc), orig_xua->mtp.opc, osmo_ss7_pointcode_print_buf(buf_orig_opc, sizeof(buf_orig_opc), inst, orig_xua->mtp.opc)); return 0; } switch (rt->dest.as->cfg.proto) { case OSMO_SS7_ASP_PROT_M3UA: LOGSS7(inst, LOGL_INFO, "Message received for inaccessible SP %u=%s. Tx TFP (DUNA) to concerned SP %u=%s\n", orig_xua->mtp.dpc, osmo_ss7_pointcode_print_buf(buf_orig_dpc, sizeof(buf_orig_dpc), inst, orig_xua->mtp.dpc), orig_xua->mtp.opc, osmo_ss7_pointcode_print_buf(buf_orig_opc, sizeof(buf_orig_opc), inst, orig_xua->mtp.opc)); xua = gen_duna_ret_msg(inst, orig_xua); return m3ua_tx_xua_as(rt->dest.as, xua); case OSMO_SS7_ASP_PROT_IPA: /* FIXME: No DUNA in IPA, maybe send SUA CLDR (SCCP UDTS) instead? */ LOGSS7(inst, LOGL_INFO, "Message received for inaccessible SP %u=%s, " "but concerned SP %u=%s is IPA-based and doesn't support TFP (DUNA)\n", orig_xua->mtp.dpc, osmo_ss7_pointcode_print_buf(buf_orig_dpc, sizeof(buf_orig_dpc), inst, orig_xua->mtp.dpc), orig_xua->mtp.opc, osmo_ss7_pointcode_print_buf(buf_orig_opc, sizeof(buf_orig_opc), inst, orig_xua->mtp.opc)); return 0; default: LOGSS7(inst, LOGL_ERROR, "DUNA message for ASP of unknown protocol %u\n", rt->dest.as->cfg.proto); return 0; } return 0; }