diff options
author | Harald Welte <laforge@osmocom.org> | 2021-02-06 21:39:12 +0100 |
---|---|---|
committer | Harald Welte <laforge@osmocom.org> | 2021-02-08 18:00:56 +0100 |
commit | 6cb841b92bf1457dd11cba02fca25ce74836d2e5 (patch) | |
tree | 8d2000de8e57eaf6c550d2f48af5e7967079c7b1 | |
parent | f5d90c46e15dd79776d9b760ed5854e7ff94cdaa (diff) |
xua: Implement SNM availability/unavailability messaging
M3UA and SUA have one sub-protocol called [S]SNM, through which the
SG informs the ASP about certain destinations (point codes) becoming
available (DAVA) or unavailable (DUNA) in the SS7 network.
This patch adds support for
* generating DAVA/DUAN on a SGP when the AS FSM changes to/from AS-ACTIVE
* receiving DAVA/DUNA on an ASP and informing other "SG role" AS/ASP
* processing DAUD from ASP received by SG, generating relate DAVA/DUNA
responses
Related: OS#2623
Change-Id: Id92be4691b0fd77598a6edb642c028bbd8c5b623
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/m3ua.c | 150 | ||||
-rw-r--r-- | src/sua.c | 154 | ||||
-rw-r--r-- | src/xua_as_fsm.c | 31 | ||||
-rw-r--r-- | src/xua_internal.h | 12 | ||||
-rw-r--r-- | src/xua_snm.c | 211 |
6 files changed, 554 insertions, 6 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 91084de..41d2a8d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,7 +31,7 @@ libosmo_sigtran_la_SOURCES = sccp_sap.c sua.c m3ua.c xua_msg.c sccp_helpers.c \ sccp2sua.c sccp_scrc.c sccp_sclc.c sccp_scoc.c \ sccp_user.c sccp_types.c xua_rkm.c xua_shared.c xua_default_lm_fsm.c \ osmo_ss7.c osmo_ss7_hmrt.c xua_asp_fsm.c xua_as_fsm.c \ - osmo_ss7_vty.c sccp_vty.c ipa.c + xua_snm.c osmo_ss7_vty.c sccp_vty.c ipa.c libosmo_sigtran_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined -export-symbols-regex '^osmo_' libosmo_sigtran_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS) @@ -677,6 +677,8 @@ static int m3ua_rx_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua) return 0; } +static int m3ua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua); + /*! \brief process M3UA message received from socket * \param[in] asp Application Server Process receiving \ref msg * \param[in] msg received message buffer @@ -737,10 +739,7 @@ int m3ua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg) rc = m3ua_rx_rkm(asp, xua); break; case M3UA_MSGC_SNM: - /* FIXME */ - LOGPASP(asp, DLM3UA, LOGL_NOTICE, "Received unsupported M3UA " - "Message Class %u\n", xua->hdr.msg_class); - err = m3ua_gen_error_msg(M3UA_ERR_UNSUPP_MSG_CLASS, msg); + rc = m3ua_rx_snm(asp, xua); break; default: LOGPASP(asp, DLM3UA, LOGL_NOTICE, "Received unknown M3UA " @@ -760,3 +759,146 @@ out: return rc; } + +/*********************************************************************** + * SSNM msg generation + ***********************************************************************/ + +/* 3.4.1 Destination Unavailable (DUNA) */ +static struct xua_msg *m3ua_encode_duna(const uint32_t *rctx, unsigned int num_rctx, + const uint32_t *aff_pc, unsigned int num_aff_pc, + const char *info_string) +{ + struct xua_msg *xua = xua_msg_alloc(); + + xua->hdr = XUA_HDR(M3UA_MSGC_SNM, M3UA_SNM_DUNA); + xua->hdr.version = M3UA_VERSION; + if (rctx) + xua_msg_add_data(xua, M3UA_IEI_ROUTE_CTX, num_rctx * sizeof(*rctx), (const uint8_t *)rctx); + + xua_msg_add_data(xua, M3UA_IEI_AFFECTED_PC, num_aff_pc * sizeof(*aff_pc), (const uint8_t *) aff_pc); + + if (info_string) { + xua_msg_add_data(xua, M3UA_IEI_INFO_STRING, + strlen(info_string)+1, + (const uint8_t *) info_string); + } + return xua; +} + +/* 3.4.2 Destination Available (DAVA) */ +static struct xua_msg *m3ua_encode_dava(const uint32_t *rctx, unsigned int num_rctx, + const uint32_t *aff_pc, unsigned int num_aff_pc, + const char *info_string) +{ + /* encoding is exactly identical to DUNA */ + struct xua_msg *xua = m3ua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, info_string); + if (xua) + xua->hdr.msg_type = M3UA_SNM_DAVA; + return xua; +} + +#if 0 /* not used so far */ +/* 3.4.3 Destination Available (DAUD) */ +static struct xua_msg *m3ua_encode_daud(const uint32_t *rctx, unsigned int num_rctx, + const uint32_t *aff_pc, unsigned int num_aff_pc, + const char *info_string) +{ + /* encoding is exactly identical to DUNA */ + struct xua_msg *xua = m3ua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, info_string); + if (xua) + xua->hdr.msg_type = M3UA_SNM_DAUD; + return xua; +} +#endif + + +/* TODO: 3.4.5 Destination User Part Unavailable (DUPU) */ + +/*! Transmit SSNM DUNA/DAVA message indicating [un]availability of certain point code[s] + * \param[in] asp ASP through which to transmit message. Must be ACTIVE. + * \param[in] rctx array of Routing Contexts in network byte order. + * \param[in] num_rctx number of rctx + * \param[in] aff_pc array of 'Affected Point Code' in network byte order. + * \param[in] num_aff_pc number of aff_pc + * \param[in] info_string optional information string (can be NULL). + * \param[in] available are aff_pc now available (true) or unavailable (false) */ +void m3ua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx, + const uint32_t *aff_pc, unsigned int num_aff_pc, + const char *info_string, bool available) +{ + struct xua_msg *xua; + + if (available) + xua = m3ua_encode_dava(rctx, num_rctx, aff_pc, num_aff_pc, info_string); + else + xua = m3ua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, info_string); + + m3ua_tx_xua_asp(asp, xua); +} + +/* received SNM message on ASP side */ +static int m3ua_rx_snm_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua) +{ + struct osmo_ss7_as *as = NULL; + struct xua_msg_part *rctx_ie = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX); + int rc; + + rc = xua_find_as_for_asp(&as, asp, rctx_ie); + if (rc) + return rc; + + /* report those up the stack so both other ASPs and local SCCP users can be notified */ + switch (xua->hdr.msg_type) { + case M3UA_SNM_DUNA: + xua_snm_rx_duna(asp, as, xua); + break; + case M3UA_SNM_DAVA: + xua_snm_rx_dava(asp, as, xua); + break; + case M3UA_SNM_DUPU: + case M3UA_SNM_SCON: + case M3UA_SNM_DRST: + LOGPASP(asp, DLM3UA, LOGL_NOTICE, "Received unsupported M3UA SNM message type %u\n", + xua->hdr.msg_type); + /* silently ignore those to not confuse the sender */ + break; + default: + return M3UA_ERR_UNSUPP_MSG_TYPE; + } + + return 0; +} + +/* received SNM message on SG side */ +static int m3ua_rx_snm_sg(struct osmo_ss7_asp *asp, struct xua_msg *xua) +{ + switch (xua->hdr.msg_type) { + case M3UA_SNM_DAUD: /* Audit: ASP inquires about availability of Point Codes */ + xua_snm_rx_daud(asp, xua); + break; + default: + return M3UA_ERR_UNSUPP_MSG_TYPE; + } + + return 0; +} + +static int m3ua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua) +{ + /* SNM only permitted in ACTIVE state */ + if (asp->fi->state != XUA_ASP_S_ACTIVE) { + LOGPASP(asp, DLM3UA, LOGL_NOTICE, "Received M3UA SNM while ASP in state %s\n", + osmo_fsm_inst_state_name(asp->fi)); + return M3UA_ERR_UNEXPECTED_MSG; + } + + switch (asp->cfg.role) { + case OSMO_SS7_ASP_ROLE_SG: + return m3ua_rx_snm_sg(asp, xua); + case OSMO_SS7_ASP_ROLE_ASP: + return m3ua_rx_snm_asp(asp, xua); + default: + return M3UA_ERR_UNSUPP_MSG_CLASS; + } +} @@ -1,6 +1,6 @@ /* Minimal implementation of RFC 3868 - SCCP User Adaptation Layer */ -/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org> +/* (C) 2015-2021 by Harald Welte <laforge@gnumonks.org> * All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ @@ -661,6 +661,8 @@ static int sua_rx_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua) return 0; } +static int sua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua); + /*! \brief process SUA message received from socket * \param[in] asp Application Server Process receiving \ref msg * \param[in] msg received message buffer @@ -736,6 +738,8 @@ int sua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg) rc = sua_rx_mgmt(asp, xua); break; case SUA_MSGC_SNM: + rc = sua_rx_snm(asp, xua); + break; case SUA_MSGC_RKM: /* FIXME */ LOGPASP(asp, DLSUA, LOGL_NOTICE, "Received unsupported SUA " @@ -760,3 +764,151 @@ out: return rc; } + +/*********************************************************************** + * SSNM msg generation + ***********************************************************************/ + +/* 3.4.1 Destination Unavailable (DUNA) */ +static struct xua_msg *sua_encode_duna(const uint32_t *rctx, unsigned int num_rctx, + const uint32_t *aff_pc, unsigned int num_aff_pc, + const uint32_t *ssn, const uint32_t *smi, const char *info_string) +{ + struct xua_msg *xua = xua_msg_alloc(); + + xua->hdr = XUA_HDR(SUA_MSGC_SNM, SUA_SNM_DUNA); + xua->hdr.version = SUA_VERSION; + if (rctx) + xua_msg_add_data(xua, SUA_IEI_ROUTE_CTX, num_rctx * 4, (const uint8_t *)rctx); + + xua_msg_add_data(xua, SUA_IEI_AFFECTED_PC, num_aff_pc * 4, (const uint8_t *) aff_pc); + + if (ssn) + xua_msg_add_u32(xua, SUA_IEI_SSN, *ssn); + + if (smi) + xua_msg_add_u32(xua, SUA_IEI_SSN, *smi); + + if (info_string) { + xua_msg_add_data(xua, SUA_IEI_INFO_STRING, + strlen(info_string)+1, + (const uint8_t *) info_string); + } + return xua; +} + +/* 3.4.2 Destination Available (DAVA) */ +static struct xua_msg *sua_encode_dava(const uint32_t *rctx, unsigned int num_rctx, + const uint32_t *aff_pc, unsigned int num_aff_pc, + const uint32_t *ssn, const uint32_t *smi, const char *info_string) +{ + /* encoding is exactly identical to DUNA */ + struct xua_msg *xua = sua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, ssn, smi, info_string); + if (xua) + xua->hdr.msg_type = SUA_SNM_DAVA; + return xua; +} + +#if 0 /* not used so far */ +/* 3.4.3 Destination Available (DAUD) */ +static struct xua_msg *sua_encode_daud(const uint32_t *rctx, unsigned int num_rctx, + const uint32_t *aff_pc, unsigned int num_aff_pc, + const uint32_t *ssn, const uint32_t *smi, const char *info_string) +{ + /* encoding is exactly identical to DUNA */ + struct xua_msg *xua = sua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, ssn, smi, info_string); + if (xua) + xua->hdr.msg_type = SUA_SNM_DAUD; + return xua; +} +#endif + + +/*! Transmit SSNM DUNA/DAVA message indicating [un]availability of certain point code[s] + * \param[in] asp ASP through whihc to transmit message. Must be ACTIVE. + * \param[in] rctx array of Routing Contexts in network byte order. + * \param[in] num_rctx number of rctx + * \param[in] aff_pc array of 'Affected Point Code' in network byte order. + * \param[in] num_aff_pc number of aff_pc + * \param[in] aff_ssn affected SSN (optional) + * \param[in] smi subsystem multiplicity indicator (optional) + * \param[in] info_string optional information strng (can be NULL). + * \param[in] available are aff_pc now available (true) or unavailable (false) */ +void sua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx, + const uint32_t *aff_pc, unsigned int num_aff_pc, const uint32_t *aff_ssn, + const uint32_t *smi, const char *info_string, bool available) +{ + struct xua_msg *xua; + + if (available) + xua = sua_encode_dava(rctx, num_rctx, aff_pc, num_aff_pc, aff_ssn, smi, info_string); + else + xua = sua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, aff_ssn, smi, info_string); + + sua_tx_xua_asp(asp, xua); +} + +/* received SNM message on ASP side */ +static int sua_rx_snm_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua) +{ + struct osmo_ss7_as *as = NULL; + struct xua_msg_part *rctx_ie = xua_msg_find_tag(xua, SUA_IEI_ROUTE_CTX); + int rc; + + rc = xua_find_as_for_asp(&as, asp, rctx_ie); + if (rc) + return rc; + + switch (xua->hdr.msg_type) { + case SUA_SNM_DUNA: + xua_snm_rx_duna(asp, as, xua); + break; + case SUA_SNM_DAVA: + xua_snm_rx_dava(asp, as, xua); + break; + case SUA_SNM_DUPU: + case SUA_SNM_SCON: + case SUA_SNM_DRST: + LOGPASP(asp, DLSUA, LOGL_NOTICE, "Received unsupported SUA SNM message type %u\n", + xua->hdr.msg_type); + /* silently ignore those to not confuse the sender */ + break; + default: + return SUA_ERR_UNSUPP_MSG_TYPE; + } + + return 0; +} + +/* received SNM message on SG side */ +static int sua_rx_snm_sg(struct osmo_ss7_asp *asp, struct xua_msg *xua) +{ + switch (xua->hdr.msg_type) { + case SUA_SNM_DAUD: /* Audit: ASP inquires about availability of Point Codes */ + xua_snm_rx_daud(asp, xua); + break; + default: + return SUA_ERR_UNSUPP_MSG_TYPE; + } + + return 0; +} + +static int sua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua) +{ + /* SNM only permitted in ACTIVE state */ + if (asp->fi->state != XUA_ASP_S_ACTIVE) { + LOGPASP(asp, DLSUA, LOGL_NOTICE, "Received M3UA SNM while ASP in state %s\n", + osmo_fsm_inst_state_name(asp->fi)); + return SUA_ERR_UNEXPECTED_MSG; + } + + switch (asp->cfg.role) { + case OSMO_SS7_ASP_ROLE_SG: + return sua_rx_snm_sg(asp, xua); + case OSMO_SS7_ASP_ROLE_ASP: + return sua_rx_snm_asp(asp, xua); + default: + return SUA_ERR_UNSUPP_MSG_CLASS; + } +} diff --git a/src/xua_as_fsm.c b/src/xua_as_fsm.c index 731504b..7c791cf 100644 --- a/src/xua_as_fsm.c +++ b/src/xua_as_fsm.c @@ -75,6 +75,26 @@ static int as_notify_all_asp(struct osmo_ss7_as *as, struct osmo_xlm_prim_notify return sent; } +/* determine which role (SG/ASP/IPSP) we operate in */ +static int get_local_role(struct osmo_ss7_as *as) +{ + unsigned int i; + + /* this is a bit tricky. "osmo_ss7_as" has no configuation of a role, + * only the ASPs have. As they all must be of the same role, let's simply + * find the first one and return its role */ + for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) { + struct osmo_ss7_asp *asp = as->cfg.asps[i]; + + if (!asp) + continue; + + return asp->cfg.role; + } + /* we don't have any ASPs in this AS? Strange */ + return -1; +} + static struct osmo_ss7_asp *xua_as_select_asp_override(struct osmo_ss7_as *as) { struct osmo_ss7_asp *asp; @@ -318,6 +338,17 @@ static void xua_as_fsm_onenter(struct osmo_fsm_inst *fi, uint32_t old_state) /* TODO: ASP-Id of ASP triggering this state change */ as_notify_all_asp(xafp->as, &npar); + + /* only if we are the SG, we must start broadcasting availability information + * to everyone else */ + if (get_local_role(xafp->as) == OSMO_SS7_ASP_ROLE_SG) { + /* advertise availability of the routing key to others */ + uint32_t aff_pc = htonl(as->cfg.routing_key.pc); + if (old_state != XUA_AS_S_ACTIVE && fi->state == XUA_AS_S_ACTIVE) + xua_snm_pc_available(as, &aff_pc, 1, NULL, true); + else if (old_state == XUA_AS_S_ACTIVE && fi->state != XUA_AS_S_ACTIVE) + xua_snm_pc_available(as, &aff_pc, 1, NULL, false); + } }; static void xua_as_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void *data) diff --git a/src/xua_internal.h b/src/xua_internal.h index 65adfb6..e76fddf 100644 --- a/src/xua_internal.h +++ b/src/xua_internal.h @@ -18,10 +18,19 @@ struct msgb *osmo_sua_to_sccp(struct xua_msg *xua); int sua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg); int sua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua); +void sua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx, + const uint32_t *aff_pc, unsigned int num_aff_pc, const uint32_t *aff_ssn, + const uint32_t *smi, const char *info_string, bool available); struct osmo_mtp_prim *m3ua_to_xfer_ind(struct xua_msg *xua); int m3ua_hmdc_rx_from_l2(struct osmo_ss7_instance *inst, struct xua_msg *xua); int m3ua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua); +void m3ua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx, + const uint32_t *aff_pc, unsigned int num_aff_pc, + const char *info_string, bool available); +void xua_snm_rx_daud(struct osmo_ss7_asp *asp, struct xua_msg *xua); +void xua_snm_rx_duna(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua); +void xua_snm_rx_dava(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua); int m3ua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg); struct msgb *m3ua_msgb_alloc(const char *name); @@ -59,6 +68,9 @@ void xua_asp_send_xlm_prim_simple(struct osmo_ss7_asp *asp, enum osmo_xlm_prim_type prim_type, enum osmo_prim_operation op); +void xua_snm_pc_available(struct osmo_ss7_as *as, const uint32_t *aff_pc, + unsigned int num_aff_pc, const char *info_str, bool available); + extern struct osmo_fsm xua_default_lm_fsm; extern const struct value_string m3ua_rkm_reg_status_vals[]; extern const struct value_string m3ua_rkm_dereg_status_vals[]; diff --git a/src/xua_snm.c b/src/xua_snm.c new file mode 100644 index 0000000..c4dffbb --- /dev/null +++ b/src/xua_snm.c @@ -0,0 +1,211 @@ +/* M3UA/SUA [S]SNM Handling */ + +/* (C) 2021 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 <stdint.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/linuxlist.h> + +#include <osmocom/sigtran/osmo_ss7.h> +#include <osmocom/sigtran/protocol/m3ua.h> +#include <osmocom/sigtran/protocol/sua.h> + +#include "xua_internal.h" + +/* we can share this code between M3UA and SUA as the below conditions are true */ +osmo_static_assert(M3UA_SNM_DUNA == SUA_SNM_DUNA, _sa_duna); +osmo_static_assert(M3UA_SNM_DAVA == SUA_SNM_DAVA, _sa_dava); +osmo_static_assert(M3UA_SNM_DAUD == SUA_SNM_DAUD, _sa_dava); +osmo_static_assert(M3UA_IEI_AFFECTED_PC == SUA_IEI_AFFECTED_PC, _sa_aff_pc); +osmo_static_assert(M3UA_IEI_ROUTE_CTX == SUA_IEI_ROUTE_CTX, _sa_rctx); +osmo_static_assert(M3UA_IEI_INFO_STRING == SUA_IEI_INFO_STRING, _sa_inf_str); + +static const char *format_affected_pcs_c(void *ctx, const struct osmo_ss7_instance *s7i, + const struct xua_msg_part *ie_aff_pc) +{ + const uint32_t *aff_pc = (const uint32_t *) ie_aff_pc->dat; + unsigned int num_aff_pc = ie_aff_pc->len / sizeof(uint32_t); + char *out = talloc_strdup(ctx, ""); + int i; + + for (i = 0; i < num_aff_pc; i++) { + uint32_t _aff_pc = ntohl(aff_pc[i]); + uint32_t pc = _aff_pc & 0xffffff; + uint8_t mask = _aff_pc >> 24; + + /* append point code + mask */ + out = talloc_asprintf_append(out, "%s%s/%u, ", i == 0 ? "" : ", ", + osmo_ss7_pointcode_print(s7i, pc), mask); + } + return out; +} + +/* obtain all routing contexts (in network byte order) that exist within the given ASP */ +static unsigned int get_all_rctx_for_asp(uint32_t *rctx, unsigned int rctx_size, + struct osmo_ss7_asp *asp, struct osmo_ss7_as *excl_as) +{ + unsigned int count = 0; + struct osmo_ss7_as *as; + + llist_for_each_entry(as, &asp->inst->as_list, list) { + if (as == excl_as) + continue; + if (!osmo_ss7_as_has_asp(as, asp)) + continue; + if (as->cfg.routing_key.context == 0) + continue; + if (count >= rctx_size) + break; + rctx[count] = htonl(as->cfg.routing_key.context); + count++; + } + return count; +} + +static void xua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx, + const uint32_t *aff_pc, unsigned int num_aff_pc, + const char *info_str, bool available) +{ + switch (asp->cfg.proto) { + case OSMO_SS7_ASP_PROT_M3UA: + m3ua_tx_snm_available(asp, rctx, num_rctx, aff_pc, num_aff_pc, info_str, available); + break; + case OSMO_SS7_ASP_PROT_SUA: + sua_tx_snm_available(asp, rctx, num_rctx, aff_pc, num_aff_pc, NULL, NULL, info_str, available); + break; + default: + break; + } +} + +/* advertise availability of point codes (with masks) */ +void xua_snm_pc_available(struct osmo_ss7_as *as, const uint32_t *aff_pc, + unsigned int num_aff_pc, const char *info_str, bool available) +{ + struct osmo_ss7_instance *s7i = as->inst; + struct osmo_ss7_asp *asp; + uint32_t rctx[32]; + unsigned int num_rctx; + + llist_for_each_entry(asp, &s7i->asp_list, list) { + /* SSNM is only permitted for ASPs in ACTIVE state */ + if (!osmo_ss7_asp_active(asp)) + continue; + + /* only send DAVA/DUNA if we locally are the SG and the remote is ASP */ + if (asp->cfg.role != OSMO_SS7_ASP_ROLE_SG) + continue; + + num_rctx = get_all_rctx_for_asp(rctx, ARRAY_SIZE(rctx), asp, as); + /* this can happen if the given ASP is only in the AS that reports the change, + * which shall be excluded */ + if (num_rctx == 0) + continue; + xua_tx_snm_available(asp, rctx, num_rctx, aff_pc, num_aff_pc, info_str, available); + } +} + +/* receive DAUD from ASP; pc is 'affected PC' IE with mask in network byte order! */ +void xua_snm_rx_daud(struct osmo_ss7_asp *asp, struct xua_msg *xua) +{ + struct xua_msg_part *ie_aff_pc = xua_msg_find_tag(xua, M3UA_IEI_AFFECTED_PC); + const char *info_str = xua_msg_get_str(xua, M3UA_IEI_INFO_STRING); + struct osmo_ss7_instance *s7i = asp->inst; + unsigned int num_aff_pc; + unsigned int num_rctx; + const uint32_t *aff_pc; + uint32_t rctx[32]; + int log_ss = osmo_ss7_asp_get_log_subsys(asp); + int i; + + OSMO_ASSERT(ie_aff_pc); + aff_pc = (const uint32_t *) ie_aff_pc->dat; + num_aff_pc = ie_aff_pc->len / sizeof(uint32_t); + + num_rctx = get_all_rctx_for_asp(rctx, ARRAY_SIZE(rctx), asp, NULL); + + LOGPASP(asp, log_ss, LOGL_INFO, "Rx DAUD(%s) for %s\n", info_str ? info_str : "", + format_affected_pcs_c(xua, asp->inst, ie_aff_pc)); + + /* iterate over list of point codes, generate DAVA/DUPU */ + for (i = 0; i < num_aff_pc; i++) { + uint32_t _aff_pc = ntohl(aff_pc[i]); + uint32_t pc = _aff_pc & 0xffffff; + uint8_t mask = _aff_pc >> 24; + bool is_available = false; + + if (mask == 0) { + /* one single point code */ + + /* FIXME: don't just check for a route; but also check if the route is "active" */ + if (osmo_ss7_route_lookup(s7i, pc)) + is_available = true; + + xua_tx_snm_available(asp, rctx, num_rctx, &aff_pc[i], 1, "Response to DAUD", + is_available); + } else { + /* TODO: wildcard match */ + LOGPASP(asp, log_ss, LOGL_NOTICE, "DAUD with wildcard match not supported yet\n"); + } + } +} + +/* an incoming xUA DUNA was received from a remote SG */ +void xua_snm_rx_duna(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua) +{ + struct xua_msg_part *ie_aff_pc = xua_msg_find_tag(xua, M3UA_IEI_AFFECTED_PC); + const char *info_str = xua_msg_get_str(xua, M3UA_IEI_INFO_STRING); + /* TODO: should our processing depend on the RCTX included? I somehow don't think so */ + //struct xua_msg_part *ie_rctx = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX); + int log_ss = osmo_ss7_asp_get_log_subsys(asp); + + OSMO_ASSERT(ie_aff_pc); + + if (asp->cfg.role != OSMO_SS7_ASP_ROLE_ASP) + return; + + LOGPASP(asp, log_ss, LOGL_NOTICE, "Rx DUNA(%s) for %s\n", info_str ? info_str : "", + format_affected_pcs_c(xua, asp->inst, ie_aff_pc)); + + xua_snm_pc_available(as, (const uint32_t *)ie_aff_pc->dat, ie_aff_pc->len/4, info_str, false); +} + +/* an incoming xUA DAVA was received from a remote SG */ +void xua_snm_rx_dava(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua) +{ + struct xua_msg_part *ie_aff_pc = xua_msg_find_tag(xua, M3UA_IEI_AFFECTED_PC); + const char *info_str = xua_msg_get_str(xua, M3UA_IEI_INFO_STRING); + /* TODO: should our processing depend on the RCTX included? I somehow don't think so */ + //struct xua_msg_part *ie_rctx = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX); + int log_ss = osmo_ss7_asp_get_log_subsys(asp); + + OSMO_ASSERT(ie_aff_pc); + + if (asp->cfg.role != OSMO_SS7_ASP_ROLE_ASP) + return; + + LOGPASP(asp, log_ss, LOGL_NOTICE, "Rx DAVA(%s) for %s\n", info_str ? info_str : "", + format_affected_pcs_c(xua, asp->inst, ie_aff_pc)); + + xua_snm_pc_available(as, (const uint32_t *)ie_aff_pc->dat, ie_aff_pc->len/4, info_str, true); +} |