602 lines
20 KiB
C
602 lines
20 KiB
C
/* xUA Routing Key Management (RKM) as per RFC 4666 */
|
|
/* (C) 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 <string.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include <osmocom/core/logging.h>
|
|
#include <osmocom/core/linuxlist.h>
|
|
|
|
#include <osmocom/sigtran/xua_msg.h>
|
|
#include <osmocom/sigtran/osmo_ss7.h>
|
|
#include <osmocom/sigtran/protocol/m3ua.h>
|
|
|
|
#include "xua_internal.h"
|
|
#include "xua_as_fsm.h"
|
|
#include "xua_asp_fsm.h"
|
|
|
|
const struct value_string m3ua_rkm_reg_status_vals[] = {
|
|
{ M3UA_RKM_REG_SUCCESS, "SUCCESS" },
|
|
{ M3UA_RKM_REG_ERR_UNKNOWN, "Unknown Error" },
|
|
{ M3UA_RKM_REG_ERR_INVAL_DPC, "Invalid Destination Pointcode" },
|
|
{ M3UA_RKM_REG_ERR_INVAL_NET_APPEAR, "Invalid Network Appearance" },
|
|
{ M3UA_RKM_REG_ERR_INVAL_RKEY, "Invalid Routing Key" },
|
|
{ M3UA_RKM_REG_ERR_PERM_DENIED, "Permission Denied" },
|
|
{ M3UA_RKM_REG_ERR_CANT_SUPP_UNQ_RT, "Cannot Support Unique Routing" },
|
|
{ M3UA_RKM_REG_ERR_RKEY_NOT_PROVD, "Routing Key Not Provided" },
|
|
{ M3UA_RKM_REG_ERR_INSUFF_RESRC, "Insufficient Resources" },
|
|
{ M3UA_RKM_REG_ERR_UNSUPP_RK_PARAM, "Unsupported Routing Key Parameter" },
|
|
{ M3UA_RKM_REG_ERR_UNSUPP_TRAF_MODE, "Unsupported Traffic Mode Type" },
|
|
{ M3UA_RKM_REG_ERR_RKEY_CHG_REFUSED, "Routing Key Change Refused" },
|
|
{ M3UA_RKM_REG_ERR_RKEY_ALRDY_REGD, "Routing Key Already Registered" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
const struct value_string m3ua_rkm_dereg_status_vals[] = {
|
|
{ M3UA_RKM_DEREG_SUCCESS, "SUCCSS" },
|
|
{ M3UA_RKM_DEREG_ERR_UNKNOWN, "Unknown Error" },
|
|
{ M3UA_RKM_DEREG_ERR_INVAL_RCTX, "Invalid Routing Context" },
|
|
{ M3UA_RKM_DEREG_ERR_PERM_DENIED, "Permission Denied" },
|
|
{ M3UA_RKM_DEREG_ERR_NOT_REGD, "Error: Not Registered" },
|
|
{ M3UA_RKM_DEREG_ERR_ASP_ACTIVE, "Error: ASP Active" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
/* push a M3UA header to the front of the given message */
|
|
static void msgb_push_m3ua_hdr(struct msgb *msg, uint8_t msg_class, uint8_t msg_type)
|
|
{
|
|
struct xua_common_hdr *hdr;
|
|
|
|
msg->l2h = msgb_push(msg, sizeof(*hdr));
|
|
hdr = (struct xua_common_hdr *) msg->l2h;
|
|
|
|
hdr->version = M3UA_VERSION;
|
|
hdr->spare = 0;
|
|
hdr->msg_class = msg_class;
|
|
hdr->msg_type = msg_type;
|
|
hdr->msg_length = htonl(msgb_l2len(msg));
|
|
}
|
|
|
|
/* SG: append a single registration result to given msgb */
|
|
static int msgb_append_reg_res(struct msgb *msg, uint32_t local_rk_id,
|
|
uint32_t status, uint32_t rctx)
|
|
{
|
|
uint8_t *old_tail = msg->tail;
|
|
|
|
/* One individual Registration Result according to Chapter 3.6.2 */
|
|
msgb_put_u16(msg, M3UA_IEI_REG_RESULT); /* outer IEI */
|
|
msgb_put_u16(msg, 24 + 4); /* outer length */
|
|
/* nested IEIs */
|
|
msgb_t16l16vp_put_u32(msg, M3UA_IEI_LOC_RKEY_ID, local_rk_id);
|
|
msgb_t16l16vp_put_u32(msg, M3UA_IEI_REG_STATUS, status);
|
|
msgb_t16l16vp_put_u32(msg, M3UA_IEI_ROUTE_CTX, rctx);
|
|
|
|
return msg->tail - old_tail;
|
|
}
|
|
|
|
/* SG: append a single de-registration result to given msgb */
|
|
static int msgb_append_dereg_res(struct msgb *msg,
|
|
uint32_t status, uint32_t rctx)
|
|
{
|
|
uint8_t *old_tail = msg->tail;
|
|
|
|
/* One individual De-Registration Result according to Chapter 3.6.4 */
|
|
msgb_put_u16(msg, M3UA_IEI_DEREG_RESULT); /* outer IEI */
|
|
msgb_put_u16(msg, 16 + 4); /* outer length */
|
|
/* nested IEIs */
|
|
msgb_t16l16vp_put_u32(msg, M3UA_IEI_ROUTE_CTX, rctx);
|
|
msgb_t16l16vp_put_u32(msg, M3UA_IEI_DEREG_STATUS, status);
|
|
|
|
return msg->tail - old_tail;
|
|
}
|
|
|
|
/* ASP: send a RKM Registration Request message for a single routing key */
|
|
static void xua_rkm_send_reg_req(struct osmo_ss7_asp *asp,
|
|
const struct osmo_ss7_routing_key *rkey,
|
|
enum osmo_ss7_as_traffic_mode traf_mode)
|
|
{
|
|
struct msgb *msg = m3ua_msgb_alloc(__func__);
|
|
int tmod = osmo_ss7_tmode_to_xua(traf_mode);
|
|
|
|
/* One individual Registration Request according to Chapter 3.6.1 */
|
|
msgb_put_u16(msg, M3UA_IEI_ROUT_KEY); /* outer IEI */
|
|
msgb_put_u16(msg, 32 + 4); /* outer length */
|
|
/* nested IEIs */
|
|
msgb_t16l16vp_put_u32(msg, M3UA_IEI_LOC_RKEY_ID, rkey->l_rk_id);
|
|
msgb_t16l16vp_put_u32(msg, M3UA_IEI_ROUTE_CTX, rkey->context);
|
|
msgb_t16l16vp_put_u32(msg, M3UA_IEI_TRAF_MODE_TYP, tmod);
|
|
msgb_t16l16vp_put_u32(msg, M3UA_IEI_DEST_PC, rkey->pc);
|
|
|
|
msgb_push_m3ua_hdr(msg, M3UA_MSGC_RKM, M3UA_RKM_REG_REQ);
|
|
|
|
osmo_ss7_asp_send(asp, msg);
|
|
}
|
|
|
|
/* ASP: send a RKM De-Registration Request message for a single routing context */
|
|
static void xua_rkm_send_dereg_req(struct osmo_ss7_asp *asp, uint32_t route_ctx)
|
|
{
|
|
struct msgb *msg = m3ua_msgb_alloc(__func__);
|
|
|
|
/* One individual De-Registration Request according to Chapter 3.6.3 */
|
|
msgb_t16l16vp_put_u32(msg, M3UA_IEI_ROUTE_CTX, route_ctx);
|
|
|
|
msgb_push_m3ua_hdr(msg, M3UA_MSGC_RKM, M3UA_RKM_DEREG_REQ);
|
|
|
|
osmo_ss7_asp_send(asp, msg);
|
|
}
|
|
|
|
/* maximum number of newly-assigned Application Servers in one dynamic
|
|
* RKM REG request */
|
|
#define MAX_NEW_AS 16
|
|
|
|
/* SG: handle a single registration request IE (nested IEs in 'innner' */
|
|
static int handle_rkey_reg(struct osmo_ss7_asp *asp, struct xua_msg *inner,
|
|
struct msgb *resp, struct osmo_ss7_as **newly_assigned_as,
|
|
unsigned int max_nas_idx, unsigned int *nas_idx)
|
|
{
|
|
uint32_t rk_id, rctx, _tmode, dpc;
|
|
enum osmo_ss7_as_traffic_mode tmode;
|
|
struct osmo_ss7_as *as;
|
|
struct osmo_ss7_route *rt;
|
|
char namebuf[32];
|
|
|
|
/* mandatory local routing key ID */
|
|
rk_id = xua_msg_get_u32(inner, M3UA_IEI_LOC_RKEY_ID);
|
|
/* ASP may already include a routing context value here */
|
|
rctx = xua_msg_get_u32(inner, M3UA_IEI_ROUTE_CTX);
|
|
|
|
/* traffic mode type (0 = undefined) */
|
|
_tmode = xua_msg_get_u32(inner, M3UA_IEI_TRAF_MODE_TYP);
|
|
if (xua_msg_find_tag(inner, M3UA_IEI_TRAF_MODE_TYP) && _tmode != M3UA_TMOD_OVERRIDE &&
|
|
_tmode != M3UA_TMOD_LOADSHARE && _tmode != M3UA_TMOD_BCAST) {
|
|
LOGPASP(asp, DLSS7, LOGL_NOTICE, "RKM: Invalid Traffic Mode %u\n", _tmode);
|
|
msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_UNSUPP_TRAF_MODE, 0);
|
|
return -1;
|
|
}
|
|
|
|
/* destination point code (mandatory) */
|
|
dpc = xua_msg_get_u32(inner, M3UA_IEI_DEST_PC);
|
|
|
|
/* We don't support routing keys with the following criteria, so
|
|
* we have to reject those */
|
|
/* TODO: network appearance (optional) */
|
|
/* TODO: service indicators (optional) */
|
|
/* TODO: originating point code list (optional) */
|
|
if (xua_msg_find_tag(inner, M3UA_IEI_NET_APPEAR) ||
|
|
xua_msg_find_tag(inner, M3UA_IEI_SVC_IND) ||
|
|
xua_msg_find_tag(inner, M3UA_IEI_ORIG_PC)) {
|
|
LOGPASP(asp, DLSS7, LOGL_NOTICE, "RKM: Unsupported Routing Key\n");
|
|
msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_UNSUPP_RK_PARAM, 0);
|
|
return -1;
|
|
}
|
|
|
|
/* if the ASP did not include a routing context number, allocate
|
|
* one locally (will be part of response) */
|
|
if (!rctx)
|
|
rctx = osmo_ss7_find_free_rctx(asp->inst);
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_INFO, "RKM: Registering routing key %u for DPC %s\n",
|
|
rctx, osmo_ss7_pointcode_print(asp->inst, dpc));
|
|
|
|
/* We have two cases here:
|
|
* a) pre-configured routing context on both ASP and SG: We will
|
|
* find the AS based on the RCTX send by the client, check if
|
|
* the routing key matches, associated AS with ASP and return
|
|
* success.
|
|
* b) no routing context set on ASP, no pre-existing AS
|
|
* definition on SG. We have to create the AS, set the RK,
|
|
* allocate the RCTX and return that RCTX to the client. This
|
|
* is a slightly non-standard interpretation of M3UA RKM
|
|
* which requires the SG to not have a-priori-knowledge of
|
|
* all AS/RK in situations where the ASP are trusted.
|
|
*/
|
|
|
|
/* check if there is already an AS for this routing key */
|
|
as = osmo_ss7_as_find_by_rctx(asp->inst, rctx);
|
|
if (as) {
|
|
LOGPASP(asp, DLSS7, LOGL_NOTICE, "RKM: Found existing AS for RCTX %u\n", rctx);
|
|
if (as->cfg.routing_key.pc != dpc) {
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "RKM: DPC doesn't match, rejecting AS (%u != %u)\n",
|
|
as->cfg.routing_key.pc, dpc);
|
|
msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_INVAL_RKEY, 0);
|
|
return -1;
|
|
}
|
|
if (_tmode) { /* if the peer has specified a traffic mode at all */
|
|
tmode = osmo_ss7_tmode_from_xua(_tmode);
|
|
if (!as->cfg.mode_set_by_peer && !as->cfg.mode_set_by_vty) {
|
|
as->cfg.mode = tmode;
|
|
LOGPAS(as, DLSS7, LOGL_INFO,
|
|
"RKM: Traffic mode set dynamically by peer to %s\n",
|
|
osmo_ss7_as_traffic_mode_name(as->cfg.mode));
|
|
/* verify if existing AS has same traffic-mode as new request (if any) */
|
|
} else if (!osmo_ss7_as_tmode_compatible_xua(as, _tmode)) {
|
|
LOGPASP(asp, DLSS7, LOGL_NOTICE, "RKM: Non-matching Traffic Mode %s\n",
|
|
osmo_ss7_as_traffic_mode_name(tmode));
|
|
msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_UNSUPP_TRAF_MODE, 0);
|
|
return -1;
|
|
}
|
|
as->cfg.mode_set_by_peer = true;
|
|
}
|
|
} else if (asp->inst->cfg.permit_dyn_rkm_alloc) {
|
|
/* Create an AS for this routing key */
|
|
snprintf(namebuf, sizeof(namebuf), "as-rkm-%u", rctx);
|
|
as = osmo_ss7_as_find_or_create(asp->inst, namebuf, OSMO_SS7_ASP_PROT_M3UA);
|
|
if (!as) {
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "RKM: Cannot create AS %s\n", namebuf);
|
|
msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_INSUFF_RESRC, 0);
|
|
return -1;
|
|
}
|
|
|
|
as->cfg.description = talloc_strdup(as, "Auto-generated by RKM");
|
|
as->rkm_dyn_allocated = true;
|
|
if (!as->cfg.mode_set_by_vty && _tmode) {
|
|
as->cfg.mode = osmo_ss7_tmode_from_xua(_tmode);
|
|
as->cfg.mode_set_by_peer = true;
|
|
}
|
|
/* fill routing key */
|
|
as->cfg.routing_key.pc = dpc;
|
|
as->cfg.routing_key.context = rctx;
|
|
|
|
/* add route for that routing key */
|
|
rt = osmo_ss7_route_create(as->inst->rtable_system, dpc, 0xFFFFFF, namebuf);
|
|
if (!rt) {
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "RKM: Cannot insert route for DPC %s / as %s\n",
|
|
osmo_ss7_pointcode_print(asp->inst, dpc), namebuf);
|
|
osmo_ss7_as_destroy(as);
|
|
msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_CANT_SUPP_UNQ_RT, 0);
|
|
return -1;
|
|
}
|
|
|
|
/* append to list of newly assigned as */
|
|
if (*nas_idx >= max_nas_idx) {
|
|
osmo_ss7_route_destroy(rt);
|
|
osmo_ss7_as_destroy(as);
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "RKM: not enough room for newly assigned AS (max %u AS)\n",
|
|
max_nas_idx+1);
|
|
msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_INSUFF_RESRC, 0);
|
|
return -1;
|
|
}
|
|
newly_assigned_as[(*nas_idx)++] = as;
|
|
} else {
|
|
/* not permitted to create dynamic RKM entries */
|
|
LOGPASP(asp, DLSS7, LOGL_NOTICE, "RKM: RCTX %u not found in configuration, and "
|
|
"dynamic RKM allocation not permitted; permission denied\n", rctx);
|
|
msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_ERR_PERM_DENIED, 0);
|
|
return -1;
|
|
}
|
|
|
|
/* Success: Add just-create AS to connected ASP + report success */
|
|
osmo_ss7_as_add_asp(as, asp->cfg.name);
|
|
msgb_append_reg_res(resp, rk_id, M3UA_RKM_REG_SUCCESS, rctx);
|
|
return 0;
|
|
}
|
|
|
|
/* SG: receive a registration request from ASP */
|
|
static int m3ua_rx_rkm_reg_req(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
|
{
|
|
struct xua_msg_part *part;
|
|
struct msgb *resp = m3ua_msgb_alloc(__func__);
|
|
struct osmo_ss7_as *newly_assigned_as[MAX_NEW_AS];
|
|
unsigned int i, nas_idx = 0;
|
|
|
|
memset(newly_assigned_as, 0, sizeof(newly_assigned_as));
|
|
|
|
/* iterate over all routing key IEs in message */
|
|
llist_for_each_entry(part, &xua->headers, entry) {
|
|
struct xua_msg *inner;
|
|
|
|
if (part->tag != M3UA_IEI_ROUT_KEY)
|
|
continue;
|
|
|
|
inner = xua_from_nested(part);
|
|
if (!inner) {
|
|
LOGPASP(asp, DLSS7, LOGL_NOTICE, "RKM: Unable to parse "
|
|
"nested IE for Routing Key\n");
|
|
continue;
|
|
}
|
|
/* handle single registration and append result to
|
|
* 'resp' */
|
|
handle_rkey_reg(asp, inner, resp, newly_assigned_as,
|
|
ARRAY_SIZE(newly_assigned_as), &nas_idx);
|
|
|
|
xua_msg_free(inner);
|
|
}
|
|
/* now first send the RKM REG Response */
|
|
msgb_push_m3ua_hdr(resp, M3UA_MSGC_RKM, M3UA_RKM_REG_RSP);
|
|
osmo_ss7_asp_send(asp, resp);
|
|
|
|
/* and *after* the RKM REG Response inform the newly assigned
|
|
* ASs about the fact that there's an INACTIVE ASP for them,
|
|
* which will cause them to send NOTIFY to the client */
|
|
for (i = 0; i < ARRAY_SIZE(newly_assigned_as); i++) {
|
|
struct osmo_ss7_as *as = newly_assigned_as[i];
|
|
if (!as)
|
|
continue;
|
|
/* Notify AS that it has an INACTIVE ASP */
|
|
osmo_fsm_inst_dispatch(as->fi, XUA_ASPAS_ASP_INACTIVE_IND, asp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* SG: handle a single routing key de-registration IE */
|
|
static int handle_rkey_dereg(struct osmo_ss7_asp *asp, uint32_t rctx,
|
|
struct msgb *resp)
|
|
{
|
|
struct osmo_ss7_instance *inst = asp->inst;
|
|
struct osmo_ss7_as *as;
|
|
struct osmo_ss7_route *rt;
|
|
|
|
as = osmo_ss7_as_find_by_rctx(inst, rctx);
|
|
if (!as) {
|
|
msgb_append_dereg_res(resp, M3UA_RKM_DEREG_ERR_INVAL_RCTX, 0);
|
|
return -1;
|
|
}
|
|
|
|
/* Reject if not dynamically allocated (OS#4239) */
|
|
if (!as->rkm_dyn_allocated) {
|
|
msgb_append_dereg_res(resp, M3UA_RKM_DEREG_ERR_NOT_REGD, 0);
|
|
return -1;
|
|
}
|
|
|
|
/* Reject if ASP is not even part of AS */
|
|
if (!osmo_ss7_as_has_asp(as, asp)) {
|
|
msgb_append_dereg_res(resp, M3UA_RKM_DEREG_ERR_INVAL_RCTX, 0);
|
|
return -1;
|
|
}
|
|
|
|
/* Reject if ASP is still active */
|
|
if (asp->fi->state == XUA_ASP_S_ACTIVE) {
|
|
msgb_append_dereg_res(resp, M3UA_RKM_DEREG_ERR_ASP_ACTIVE, 0);
|
|
return -1;
|
|
}
|
|
|
|
rt = osmo_ss7_route_find_dpc(inst->rtable_system, as->cfg.routing_key.pc);
|
|
if (!rt) {
|
|
msgb_append_dereg_res(resp, M3UA_RKM_DEREG_ERR_UNKNOWN, 0);
|
|
return -1;
|
|
}
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_INFO, "RKM: De-Registering rctx %u for DPC %s\n",
|
|
rctx, osmo_ss7_pointcode_print(inst, as->cfg.routing_key.pc));
|
|
|
|
/* remove ASP from AS */
|
|
osmo_ss7_as_del_asp(as, asp->cfg.name);
|
|
/* FIXME: Rather than spoofing teh ASP-DOWN.ind to the AS here,
|
|
* we should refuse RKM DEREG if the ASP is still ACTIVE */
|
|
osmo_fsm_inst_dispatch(as->fi, XUA_ASPAS_ASP_DOWN_IND, asp);
|
|
|
|
/* if we were dynamically allocated, release the associated
|
|
* route and destroy the AS */
|
|
if (as->rkm_dyn_allocated) {
|
|
/* remove route + AS definition */
|
|
osmo_ss7_route_destroy(rt);
|
|
osmo_ss7_as_destroy(as);
|
|
}
|
|
/* report success */
|
|
msgb_append_dereg_res(resp, M3UA_RKM_DEREG_SUCCESS, rctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* SG: receive a De-Registration request from ASP */
|
|
static int m3ua_rx_rkm_dereg_req(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
|
{
|
|
struct xua_msg_part *part = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX);
|
|
struct msgb *resp = m3ua_msgb_alloc(__func__);
|
|
uint32_t *rctx;
|
|
|
|
if (!part)
|
|
return -1;
|
|
|
|
for (rctx = (uint32_t *)part->dat; (uint8_t *)rctx < part->dat + part->len; rctx++)
|
|
handle_rkey_dereg(asp, ntohl(*rctx), resp);
|
|
|
|
msgb_push_m3ua_hdr(resp, M3UA_MSGC_RKM, M3UA_RKM_DEREG_RSP);
|
|
osmo_ss7_asp_send(asp, resp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ASP: handle a single registration response IE (nested IEs in 'inner') */
|
|
static int handle_rkey_reg_resp(struct osmo_ss7_asp *asp, struct xua_msg *inner)
|
|
{
|
|
struct osmo_xlm_prim *oxp;
|
|
|
|
if (!xua_msg_find_tag(inner, M3UA_IEI_LOC_RKEY_ID) ||
|
|
!xua_msg_find_tag(inner, M3UA_IEI_REG_STATUS) ||
|
|
!xua_msg_find_tag(inner, M3UA_IEI_ROUTE_CTX)) {
|
|
LOGPASP(asp, DLSS7, LOGL_NOTICE, "Missing Inner IE in REG RESP\n");
|
|
/* FIXME: ERROR to peer */
|
|
return -1;
|
|
}
|
|
|
|
oxp = xua_xlm_prim_alloc(OSMO_XLM_PRIM_M_RK_REG, PRIM_OP_CONFIRM);
|
|
if (!oxp)
|
|
return -1;
|
|
|
|
oxp->u.rk_reg.key.l_rk_id = xua_msg_get_u32(inner, M3UA_IEI_LOC_RKEY_ID);
|
|
oxp->u.rk_reg.key.context = xua_msg_get_u32(inner, M3UA_IEI_ROUTE_CTX);
|
|
oxp->u.rk_reg.status = xua_msg_get_u32(inner, M3UA_IEI_REG_STATUS);
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_INFO, "Received RKM REG RES rctx=%u status=%s\n",
|
|
oxp->u.rk_reg.key.context,
|
|
get_value_string(m3ua_rkm_reg_status_vals, oxp->u.rk_reg.status));
|
|
|
|
/* Send primitive to LM */
|
|
xua_asp_send_xlm_prim(asp, oxp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ASP: receive a registration response (ASP role) */
|
|
static int m3ua_rx_rkm_reg_rsp(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
|
{
|
|
struct xua_msg_part *part;
|
|
struct xua_msg *inner = NULL;
|
|
|
|
llist_for_each_entry(part, &xua->headers, entry) {
|
|
/* skip other IEs and/or short REG_RES IEs */
|
|
if (part->tag != M3UA_IEI_REG_RESULT || part->len < 24)
|
|
continue;
|
|
|
|
/* we leave the above loop at the first valid
|
|
* registration result (we only support one AS per ASP
|
|
* for now) */
|
|
inner = xua_from_nested(part);
|
|
if (!inner)
|
|
continue;
|
|
|
|
handle_rkey_reg_resp(asp, inner);
|
|
xua_msg_free(inner);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ASP: handle a single De-Registration response IE (nested IEs in 'inner' */
|
|
static int handle_rkey_dereg_resp(struct osmo_ss7_asp *asp, struct xua_msg *inner)
|
|
{
|
|
struct osmo_xlm_prim *oxp;
|
|
|
|
if (!xua_msg_find_tag(inner, M3UA_IEI_DEREG_STATUS) ||
|
|
!xua_msg_find_tag(inner, M3UA_IEI_ROUTE_CTX)) {
|
|
LOGPASP(asp, DLSS7, LOGL_NOTICE, "Missing Inner IE in DEREG RESP\n");
|
|
/* FIXME: ERROR to peer */
|
|
return -1;
|
|
}
|
|
|
|
oxp = xua_xlm_prim_alloc(OSMO_XLM_PRIM_M_RK_DEREG, PRIM_OP_CONFIRM);
|
|
if (!oxp)
|
|
return -1;
|
|
|
|
oxp->u.rk_dereg.route_ctx = xua_msg_get_u32(inner, M3UA_IEI_ROUTE_CTX);
|
|
oxp->u.rk_dereg.status = xua_msg_get_u32(inner, M3UA_IEI_DEREG_STATUS);
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_INFO, "Received RKM DEREG RES rctx=%u status=%s\n",
|
|
oxp->u.rk_reg.key.context,
|
|
get_value_string(m3ua_rkm_dereg_status_vals, oxp->u.rk_dereg.status));
|
|
|
|
/* Send primitive to LM */
|
|
xua_asp_send_xlm_prim(asp, oxp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ASP: receive a De-Registration response */
|
|
static int m3ua_rx_rkm_dereg_rsp(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
|
{
|
|
struct xua_msg_part *part;
|
|
struct xua_msg *inner = NULL;
|
|
|
|
llist_for_each_entry(part, &xua->headers, entry) {
|
|
/* skip other IEs and/or short REG_RES IEs */
|
|
if (part->tag != M3UA_IEI_DEREG_RESULT || part->len < 16)
|
|
continue;
|
|
|
|
/* we leave the above loop at the first valid
|
|
* registration result (we only support one AS per ASP
|
|
* for now) */
|
|
inner = xua_from_nested(part);
|
|
if (!inner)
|
|
continue;
|
|
|
|
handle_rkey_dereg_resp(asp, inner);
|
|
xua_msg_free(inner);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* process an incoming RKM message in xua format */
|
|
int m3ua_rx_rkm(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
|
{
|
|
int rc;
|
|
|
|
switch (xua->hdr.msg_type) {
|
|
/* SG Side */
|
|
case M3UA_RKM_REG_REQ:
|
|
/* TOOD: ensure we are role SG */
|
|
rc = m3ua_rx_rkm_reg_req(asp, xua);
|
|
break;
|
|
case M3UA_RKM_DEREG_REQ:
|
|
/* TOOD: ensure we are role SG */
|
|
rc = m3ua_rx_rkm_dereg_req(asp, xua);
|
|
break;
|
|
/* ASP Side */
|
|
case M3UA_RKM_REG_RSP:
|
|
/* TOOD: ensure we are role ASP */
|
|
rc = m3ua_rx_rkm_reg_rsp(asp, xua);
|
|
break;
|
|
case M3UA_RKM_DEREG_RSP:
|
|
/* TOOD: ensure we are role ASP */
|
|
rc = m3ua_rx_rkm_dereg_rsp(asp, xua);
|
|
break;
|
|
default:
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "Received unknown RKM msg_type %u\n",
|
|
xua->hdr.msg_type);
|
|
rc = -1;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* process a primitive from the xUA Layer Manager (LM) */
|
|
int osmo_xlm_sap_down(struct osmo_ss7_asp *asp, struct osmo_prim_hdr *oph)
|
|
{
|
|
struct osmo_xlm_prim *prim = (struct osmo_xlm_prim *) oph;
|
|
|
|
LOGPASP(asp, DLSS7, LOGL_DEBUG, "Received XUA Layer Manager Primitive: %s)\n",
|
|
osmo_xlm_prim_name(&prim->oph));
|
|
|
|
switch (OSMO_PRIM_HDR(&prim->oph)) {
|
|
case OSMO_PRIM(OSMO_XLM_PRIM_M_RK_REG, PRIM_OP_REQUEST):
|
|
/* Layer Manager asks us to send a Routing Key Reg Request */
|
|
xua_rkm_send_reg_req(asp, &prim->u.rk_reg.key, prim->u.rk_reg.traf_mode);
|
|
break;
|
|
case OSMO_PRIM(OSMO_XLM_PRIM_M_RK_DEREG, PRIM_OP_REQUEST):
|
|
/* Layer Manager asks us to send a Routing Key De-Reg Request */
|
|
xua_rkm_send_dereg_req(asp, prim->u.rk_dereg.route_ctx);
|
|
break;
|
|
default:
|
|
LOGPASP(asp, DLSS7, LOGL_ERROR, "Unknown XUA Layer Manager Primitive: %s\n",
|
|
osmo_xlm_prim_name(&prim->oph));
|
|
break;
|
|
}
|
|
|
|
msgb_free(prim->oph.msg);
|
|
return 0;
|
|
}
|
|
|
|
/* clean-up any dynamically created ASs + routes */
|
|
void xua_rkm_cleanup_dyn_as_for_asp(struct osmo_ss7_asp *asp)
|
|
{
|
|
struct osmo_ss7_instance *inst = asp->inst;
|
|
struct osmo_ss7_as *as, *as2;
|
|
|
|
llist_for_each_entry_safe(as, as2, &inst->as_list, list) {
|
|
if (!osmo_ss7_as_has_asp(as, asp))
|
|
continue;
|
|
/* FIXME: check if there are no other ASPs! */
|
|
if (!as->rkm_dyn_allocated)
|
|
continue;
|
|
|
|
osmo_ss7_as_destroy(as);
|
|
}
|
|
}
|