This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
Harald Welte gerrit-no-reply at lists.osmocom.orgHarald Welte has submitted this change and it was merged. Change subject: Add SCCP <-> SUA message transcoding routines ...................................................................... Add SCCP <-> SUA message transcoding routines Change-Id: I8151a9b08a0b0ca97b9c73105ad4548512ce3be8 --- M src/Makefile.am A src/sccp2sua.c 2 files changed, 1,266 insertions(+), 0 deletions(-) Approvals: Harald Welte: Looks good to me, approved Jenkins Builder: Verified diff --git a/src/Makefile.am b/src/Makefile.am index ec3fe99..4455127 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,6 +27,7 @@ LIBVERSION=0:0:0 libosmo_sigtran_la_SOURCES = sccp_sap.c sua.c m3ua.c xua_msg.c sccp_helpers.c \ + sccp2sua.c \ osmo_ss7.c osmo_ss7_hmrt.c xua_asp_fsm.c xua_as_fsm.c libosmo_sigtran_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined -export-symbols-regex '^osmo_' libosmo_sigtran_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS) diff --git a/src/sccp2sua.c b/src/sccp2sua.c new file mode 100644 index 0000000..070a8cb --- /dev/null +++ b/src/sccp2sua.c @@ -0,0 +1,1265 @@ +/* SCCP <-> SUA transcoding routines */ + +/* (C) 2017 by Harald Welte <laforge at gnumonks.org> + * All Rights Reserved + * + * based on my 2011 Erlang implementation osmo_ss7/src/sua_sccp_conv.erl + * + * References: ITU-T Q.713 and IETF RFC 3868 + * + * 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 <sccp/sccp.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/logging.h> +#include <osmocom/sigtran/sccp_sap.h> +#include <osmocom/sigtran/sccp_helpers.h> +#include <osmocom/sigtran/protocol/sua.h> +#include <osmocom/sigtran/xua_msg.h> + +#include "xua_internal.h" +#include "sccp_internal.h" + +/* libosmocore candidates */ + +static void msgb_put_u24be(struct msgb *msg, uint32_t val) +{ + msgb_put_u8(msg, (val >> 16) & 0xff); + msgb_put_u8(msg, (val >> 8) & 0xff); + msgb_put_u8(msg, val & 0xff); +} + +static void msgb_put_u16le(struct msgb *msg, uint16_t val) +{ + msgb_put_u8(msg, val & 0xff); + msgb_put_u8(msg, (val >> 8) & 0xff); +} + +/*! \brief load a 24bit value as big-endian */ +static uint32_t load_24be(const void *ptr) +{ + const uint8_t *data = ptr; + return (data[0] << 16) | (data[1] << 8) | data[2]; +} + + + +/*! \brief Parse ISUP style address of BCD digets + * \param[out] out_digits user-allocated buffer for ASCII digits + * \param[in] in BCD-encoded digits + * \param[in] in_num_bytes Size of \ref in in bytes + * \param[in] odd Odd (true) or even (false) number of digits + * \returns number of digits generated + * */ +int osmo_isup_party_parse(char *out_digits, const uint8_t *in, + unsigned int in_num_bytes, bool odd) +{ + char *out = out_digits; + unsigned int i; + + for (i = 0; i < in_num_bytes; i++) { + *out_digits++ = osmo_bcd2char(in[i] & 0x0F); + if (i+1 == in_num_bytes && odd) + break; + *out_digits++ = osmo_bcd2char(in[i] >> 4); + } + *out_digits = '\0'; + return (out_digits - out); +} + +/*! \brief Encode an ISUP style address of BCD digits + * \param[out] msg Message to which the encoded address is appended + * \param[in] in_digits NUL-terminated ASCII string of digits + * \returns number of octets used for encoding \ref in_digits */ +int osmo_isup_party_encode(struct msgb *msg, const char *in_digits) +{ + unsigned int num_digits = strlen(in_digits); + unsigned int i, num_octets = num_digits/2; + const char *cur_digit = in_digits; + uint8_t *cur; + + if (num_digits & 1) + num_octets++; + + cur = msgb_put(msg, num_octets); + + for (i = 0; i < num_octets; i++) { + cur[i] = osmo_char2bcd(*cur_digit++); + if (cur_digit - in_digits < num_digits) + cur[i] |= osmo_char2bcd(*cur_digit++) << 4; + } + return num_octets; +} + +/*! \brief Parse wire-encoded SCCP address into omso_sccp_addr + * \param[out] out user-allocated output data structure + * \param[in] addr wire-encoded SCCP address + * \param[in] addrlen Size of \ref addr in bytes + * \returns 0 in case of success, negative on error + * According to Q.713/3.4 and RFC3868/3.10.2 */ +int osmo_sccp_addr_parse(struct osmo_sccp_addr *out, + const uint8_t *addr, unsigned int addrlen) +{ + struct sccp_called_party_address *sca; + uint8_t *cur; + uint8_t encoding; + bool odd; + int rc; + + memset(out, 0, sizeof(*out)); + + sca = (struct sccp_called_party_address *) addr; + cur = sca->data; + + if (sca->routing_indicator) + out->ri = OSMO_SCCP_RI_SSN_PC; + else + out->ri = OSMO_SCCP_RI_GT; + + if (sca->point_code_indicator) { + out->presence |= OSMO_SCCP_ADDR_T_PC; + out->pc = ((cur[1] << 8) & 0x3f) | cur[0]; + cur += 2; + } + + if (sca->ssn_indicator) { + out->presence |= OSMO_SCCP_ADDR_T_SSN; + out->ssn = *cur; + cur += 1; + } + + switch (sca->global_title_indicator) { + case SCCP_TITLE_IND_NONE: + out->gt.gti = OSMO_SCCP_GTI_NO_GT; + return 0; + case SCCP_TITLE_IND_NATURE_ONLY: + out->presence |= OSMO_SCCP_ADDR_T_GT; + out->gt.gti = OSMO_SCCP_GTI_NAI_ONLY; + out->gt.nai = *cur & 0x7f; + if (*cur++ & 0x80) + odd = true; + else + odd = false; + break; + case SCCP_TITLE_IND_TRANSLATION_ONLY: + out->presence |= OSMO_SCCP_ADDR_T_GT; + out->gt.gti = OSMO_SCCP_GTI_TT_ONLY; + out->gt.tt = *cur++; + /* abort, for national use only */ + return -EINVAL; + case SCCP_TITLE_IND_TRANS_NUM_ENC: + out->presence |= OSMO_SCCP_ADDR_T_GT; + out->gt.gti = OSMO_SCCP_GTI_TT_NPL_ENC; + out->gt.tt = *cur++; + out->gt.npi = *cur >> 4; + switch (*cur++ & 0xF) { + case 1: + odd = true; + break; + case 2: + odd = false; + break; + default: + return -1; + } + break; + case SCCP_TITLE_IND_TRANS_NUM_ENC_NATURE: + out->presence |= OSMO_SCCP_ADDR_T_GT; + out->gt.gti = OSMO_SCCP_GTI_TT_NPL_ENC_NAI; + out->gt.tt = *cur++; + out->gt.npi = *cur >> 4; + encoding = *cur++ & 0xF; + switch (encoding) { + case 1: + odd = true; + break; + case 2: + odd = false; + break; + default: + LOGP(DLSUA, LOGL_ERROR, "Unknown GT encoding 0x%x\n", + encoding); + return -EINVAL; + } + out->gt.nai = *cur++ & 0x7f; + break; + default: + LOGP(DLSUA, LOGL_ERROR, "Unknown GTI %u in SCCP message\n", + sca->global_title_indicator); + return -EINVAL; + } + rc = osmo_isup_party_parse(out->gt.digits, cur, (addr+addrlen-cur), odd); + if (rc < 0) + return rc; + + return 0; +} + +/*! \brief encode a SCCP address from parsed format to wire format + * \param[out] msg message buffer to which address is to be appended + * \param[in] in data structure describing SCCP address + * \returns number of bytes written to \ref msg */ +int osmo_sccp_addr_encode(struct msgb *msg, const struct osmo_sccp_addr *in) +{ + struct sccp_called_party_address *sca; + bool odd; + + sca = (struct sccp_called_party_address *) msgb_put(msg, sizeof(*sca)); + switch (in->ri) { + case OSMO_SCCP_RI_SSN_PC: + sca->routing_indicator = 1; + break; + case OSMO_SCCP_RI_GT: + sca->routing_indicator = 0; + break; + default: + LOGP(DLSUA, LOGL_ERROR, "Unknown CCP Routing Indicator %u" + "requested\n", in->ri); + return -EINVAL; + } + + if (in->presence & OSMO_SCCP_ADDR_T_PC) { + sca->point_code_indicator = 1; + msgb_put_u16le(msg, in->pc & 0x3ff); + } + + if (in->presence & OSMO_SCCP_ADDR_T_SSN) { + sca->ssn_indicator = 1; + msgb_put_u8(msg, in->ssn); + } + + if (!(in->presence & OSMO_SCCP_ADDR_T_GT)) { + sca->global_title_indicator = SCCP_TITLE_IND_NONE; + goto out; + } + + odd = strlen(in->gt.digits) & 1; + switch (in->gt.gti) { + case OSMO_SCCP_GTI_NO_GT: + sca->global_title_indicator = SCCP_TITLE_IND_NONE; + goto out; + case OSMO_SCCP_GTI_NAI_ONLY: + sca->global_title_indicator = SCCP_TITLE_IND_NATURE_ONLY; + msgb_put_u8(msg, (odd << 7) | (in->gt.nai & 0x7f)); + break; + case OSMO_SCCP_GTI_TT_ONLY: + sca->global_title_indicator = SCCP_TITLE_IND_TRANSLATION_ONLY; + msgb_put_u8(msg, in->gt.tt); + /* abort, for national use only */ + LOGP(DLSUA, LOGL_ERROR, "Unsupported Translation Type %u" + "requested\n", in->gt.gti); + return -EINVAL; + case OSMO_SCCP_GTI_TT_NPL_ENC: + sca->global_title_indicator = SCCP_TITLE_IND_TRANS_NUM_ENC; + msgb_put_u8(msg, in->gt.tt); + msgb_put_u8(msg, (in->gt.npi << 4) | (odd ? 1 : 2)); + break; + case OSMO_SCCP_GTI_TT_NPL_ENC_NAI: + sca->global_title_indicator = SCCP_TITLE_IND_TRANS_NUM_ENC_NATURE; + msgb_put_u8(msg, in->gt.tt); + msgb_put_u8(msg, (in->gt.npi << 4) | (odd ? 1 : 2)); + msgb_put_u8(msg, in->gt.nai & 0x7f); + break; + } + osmo_isup_party_encode(msg, in->gt.digits); + +out: + /* return number of bytes written */ + return msg->tail - (uint8_t *)sca; +} + +/*! \brief convert SCCP address to SUA address + * \param xua user-provided xUA message to which address shall be added + * \param[in] iei SUA Information Element Identifier for address + * \param[in] addr SCCP wire format binary address + * \param[in] addrlen Size of \ref addr in bytes + * \returns 0 in case of success; negative on error */ +static int sccp_addr_to_sua(struct xua_msg *xua, uint16_t iei, const uint8_t *addr, + unsigned int addrlen) +{ + struct osmo_sccp_addr osa; + int rc; + + /* First decode the address from SCCP wire format to + * osmo_sccp_addr */ + rc = osmo_sccp_addr_parse(&osa, addr, addrlen); + if (rc < 0) + return rc; + + LOGP(DLSUA, LOGL_DEBUG, "Parsed Addr: %s\n", osmo_sccp_addr_dump(&osa)); + + /* Then re-encode it as SUA address */ + return xua_msg_add_sccp_addr(xua, iei, &osa); +} + +/*! \brief convenience wrapper around sccp_addr_to_sua() for variable mandatory addresses */ +static int sccp_addr_to_sua_ptr(struct xua_msg *xua, uint16_t iei, struct msgb *msg, uint8_t *ptr_addr) +{ + uint8_t *addr = ptr_addr + *ptr_addr + 1; + unsigned int addrlen = *(ptr_addr + *ptr_addr); + + return sccp_addr_to_sua(xua, iei, addr, addrlen); +} + +/*! \brief convert SUA address to SCCP address + * \param msg user-provided message buffer to which address shall be * appended + * \param[in] part SUA wire format binary address + * \returns 0 in case of success; negative on error */ +static int sua_addr_to_sccp(struct msgb *msg, struct xua_msg_part *part) +{ + struct osmo_sccp_addr osa; + int rc; + + /* First decode the address from SUA wire format to + * osmo_sccp_addr */ + rc = sua_addr_parse_part(&osa, part); + if (rc < 0) + return rc; + + /* Then re-encode it as SCCP address */ + return osmo_sccp_addr_encode(msg, &osa); +} + +/*! \brief Add a "SCCP Variable Mandatory Part" (Address format) to the given msgb + * \param msg Message buffer to which part shall be added + * \param[out] var_ptr pointer to relative pointer in SCCP header + * \param[in] xua xUA message from which to use address + * \param[in] iei xUA information element identifier of address */ +static int sccp_add_var_addr(struct msgb *msg, uint8_t *var_ptr, struct xua_msg *xua, uint16_t iei) +{ + struct xua_msg_part *part = xua_msg_find_tag(xua, iei); + uint8_t *lenbyte; + int rc; + if (!part) { + LOGP(DLSUA, LOGL_ERROR, "Cannot find IEI %u in SUA message\n", iei); + return -ENODEV; + } + + /* first allocate one byte for the length */ + lenbyte = msgb_put(msg, 1); + /* update the relative pointer to the length byte */ + *var_ptr = lenbyte - var_ptr; + + /* then append the encoded SCCP address */ + rc = sua_addr_to_sccp(msg, part); + if (rc < 0) + return rc; + + /* store the encoded length of the address */ + *lenbyte = rc; + + return rc; +} + +/*! \brief Add a "SCCP Variable Mandatory Part" to the given msgb + * \param msg Message buffer to which part shall be added + * \param[out] var_ptr pointer to relative pointer in SCCP header + * \param[in] xua xUA message from which to use source data + * \param[in] iei xUA information element identifier of source data */ +static int sccp_add_variable_part(struct msgb *msg, uint8_t *var_ptr, struct xua_msg *xua, uint16_t iei) +{ + struct xua_msg_part *part = xua_msg_find_tag(xua, iei); + uint8_t *lenbyte; + uint8_t *cur; + if (!part) { + LOGP(DLSUA, LOGL_ERROR, "Cannot find IEI %u in SUA message\n", iei); + return -ENODEV; + } + + /* first allocate one byte for the length */ + lenbyte = msgb_put(msg, 1); + /* update the relative pointer to the length byte */ + *var_ptr = lenbyte - var_ptr; + + /* then append the encoded SCCP address */ + cur = msgb_put(msg, part->len); + memcpy(cur, part->dat, part->len); + + /* store the encoded length of the address */ + *lenbyte = part->len; + + return part->len; +} + + +/*! \brief validate that SCCP part with pointer + length doesn't exceed msg tail + * \param[in] msg Message containing SCCP address + * \param[in] ptr_addr pointer to byte with relative SCCP pointer + * \returns true if OK; false if message inconsistent */ +static bool sccp_ptr_part_consistent(struct msgb *msg, uint8_t *ptr_addr) +{ + uint8_t *ptr; + + /* check the address of the relative pointer is within msg */ + if (ptr_addr < msg->data || ptr_addr > msg->tail) { + LOGP(DLSUA, LOGL_ERROR, "ptr_addr outside msg boundary\n"); + return false; + } + + ptr = ptr_addr + *ptr_addr; + if (ptr > msg->tail) { + LOGP(DLSUA, LOGL_ERROR, "ptr points outside msg boundary\n"); + return false; + } + + /* at destination of relative pointer is the length */ + if (ptr + 1 + *ptr > msg->tail) { + LOGP(DLSUA, LOGL_ERROR, "ptr + len points outside msg boundary\n"); + return false; + } + return true; +} + +/*! \brief convenience wrapper around xua_msg_add_data() for variable mandatory data */ +static int sccp_data_to_sua_ptr(struct xua_msg *xua, uint16_t iei, struct msgb *msg, uint8_t *ptr_addr) +{ + uint8_t *addr = ptr_addr + *ptr_addr + 1; + unsigned int addrlen = *(ptr_addr + *ptr_addr); + + return xua_msg_add_data(xua, iei, addrlen, addr); +} + +/*! \brief Convert a given SCCP option to SUA and add it to given xua_msg + * \param xua caller-provided xUA message to which option is to be added + * \param[in] sccp_opt_type SCCP option type (PNC) + * \param[in] opt_len size of \ref opt in bytes + * \param[in] opt pointer to wire-format encoded SCCP option data + * \returns 0 in case of success; negative on error */ +static int xua_msg_add_sccp_opt(struct xua_msg *xua, uint8_t sccp_opt_type, + uint16_t opt_len, uint8_t *opt) +{ + switch (sccp_opt_type) { + case SCCP_PNC_DESTINATION_LOCAL_REFERENCE: + if (opt_len != 3) + return -EINVAL; + xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(opt)); + break; + case SCCP_PNC_SOURCE_LOCAL_REFERENCE: + if (opt_len != 3) + return -EINVAL; + xua_msg_add_u32(xua, SUA_IEI_SRC_REF, load_24be(opt)); + break; + case SCCP_PNC_CALLED_PARTY_ADDRESS: + if (opt_len < 3) + return -EINVAL; + sccp_addr_to_sua(xua, SUA_IEI_DEST_ADDR, opt, opt_len); + break; + case SCCP_PNC_CALLING_PARTY_ADDRESS: + if (opt_len < 3) + return -EINVAL; + sccp_addr_to_sua(xua, SUA_IEI_SRC_ADDR, opt, opt_len); + break; + case SCCP_PNC_PROTOCOL_CLASS: + if (opt_len < 1) + return -EINVAL; + xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, *opt); + break; + case SCCP_PNC_CREDIT: + if (opt_len != 1) + return -EINVAL; + xua_msg_add_u32(xua, SUA_IEI_IMPORTANCE, *opt & 0x7); + break; + case SCCP_PNC_RELEASE_CAUSE: + if (opt_len != 1) + return -EINVAL; + xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RELEASE | *opt); + break; + case SCCP_PNC_RETURN_CAUSE: + if (opt_len != 1) + return -EINVAL; + xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RETURN | *opt); + break; + case SCCP_PNC_RESET_CAUSE: + if (opt_len != 1) + return -EINVAL; + xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RESET | *opt); + break; + case SCCP_PNC_ERROR_CAUSE: + if (opt_len != 1) + return -EINVAL; + xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_ERROR | *opt); + break; + case SCCP_PNC_REFUSAL_CAUSE: + if (opt_len != 1) + return -EINVAL; + xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_REFUSAL | *opt); + break; + case SCCP_PNC_DATA: + xua_msg_add_data(xua, SUA_IEI_DATA, opt_len, opt); + break; + case SCCP_PNC_HOP_COUNTER: + if (opt_len != 1) + return -EINVAL; + xua_msg_add_u32(xua, SUA_IEI_S7_HOP_CTR, *opt); + break; + case SCCP_PNC_IMPORTANCE: + if (opt_len != 1) + return -EINVAL; + xua_msg_add_u32(xua, SUA_IEI_IMPORTANCE, *opt & 0x7); + break; + case SCCP_PNC_LONG_DATA: + xua_msg_add_data(xua, SUA_IEI_DATA, opt_len, opt); + break; + case SCCP_PNC_SEGMENTATION: + case SCCP_PNC_SEGMENTING: + case SCCP_PNC_RECEIVE_SEQ_NUMBER: + /* only in class 3 */ + case SCCP_PNC_SEQUENCING: + /* only in class 3 */ + default: + LOGP(DLSUA, LOGL_ERROR, "Unsupported SCCP option type %u\n", + sccp_opt_type); + return -1; + } + return 0; +} + +/*! \brief append a SCCP option header to the given message + * \param msg Message to which header is to be appended + * \param[in] pnc PNC of the option header + * \param[in] len length of the option, excluding the header */ +static void msgb_put_sccp_opt_hdr(struct msgb *msg, uint8_t pnc, uint8_t len) +{ + msgb_put_u8(msg, pnc); + msgb_put_u8(msg, len); +} + +/*! \brief append a SCCP option to the given message + * \param msg Message to which option is to be appended + * \param[in] pnc PNC of the option header + * \param[in] len length of the option, excluding the header + * \param[in] data actual data to be appended */ +static void msgb_put_sccp_opt(struct msgb *msg, uint8_t pnc, uint8_t len, const uint8_t *data) +{ + uint8_t *cur; + + msgb_put_sccp_opt_hdr(msg, pnc, len); + cur = msgb_put(msg, len); + memcpy(cur, data, len); +} + +/*! \brief Convert a given SUA option/IE to SCCP and add it to given * msgb + * \param msg caller-provided message buffer to which option is to be appended + * \param[in] opt xUA option/IE (messge part) to be converted+added + * \returns 0 in case of success; negative on error */ +static int sccp_msg_add_sua_opt(struct msgb *msg, struct xua_msg_part *opt) +{ + uint32_t tmp32; + uint8_t pnc, *lenptr; + int rc; + + switch (opt->tag) { + case SUA_IEI_DEST_REF: + msgb_put_sccp_opt_hdr(msg, SCCP_PNC_DESTINATION_LOCAL_REFERENCE, 3); + msgb_put_u24be(msg, xua_msg_part_get_u32(opt)); + break; + case SUA_IEI_SRC_REF: + msgb_put_sccp_opt_hdr(msg, SCCP_PNC_SOURCE_LOCAL_REFERENCE, 3); + msgb_put_u24be(msg, xua_msg_part_get_u32(opt)); + break; + case SUA_IEI_DEST_ADDR: + msgb_put_u8(msg, SCCP_PNC_CALLED_PARTY_ADDRESS); + lenptr = msgb_put(msg, 1); + rc = sua_addr_to_sccp(msg, opt); + if (rc < 0) + return rc; + *lenptr = rc; + break; + case SUA_IEI_SRC_ADDR: + msgb_put_u8(msg, SCCP_PNC_CALLING_PARTY_ADDRESS); + lenptr = msgb_put(msg, 1); + rc = sua_addr_to_sccp(msg, opt); + if (rc < 0) + return rc; + *lenptr = rc; + break; + case SUA_IEI_PROTO_CLASS: + msgb_put_sccp_opt_hdr(msg, SCCP_PNC_PROTOCOL_CLASS, 1); + msgb_put_u8(msg, xua_msg_part_get_u32(opt)); + break; + case SUA_IEI_CREDIT: + msgb_put_sccp_opt_hdr(msg, SCCP_PNC_CREDIT, 1); + msgb_put_u8(msg, xua_msg_part_get_u32(opt) & 0x7); + break; + case SUA_IEI_CAUSE: + tmp32 = xua_msg_part_get_u32(opt); + switch (tmp32 & SUA_CAUSE_T_MASK) { + case SUA_CAUSE_T_RETURN: + pnc = SCCP_PNC_RETURN_CAUSE; + break; + case SUA_CAUSE_T_REFUSAL: + pnc = SCCP_PNC_REFUSAL_CAUSE; + break; + case SUA_CAUSE_T_RELEASE: + pnc = SCCP_PNC_RELEASE_CAUSE; + break; + case SUA_CAUSE_T_RESET: + pnc = SCCP_PNC_RESET_CAUSE; + break; + case SUA_CAUSE_T_ERROR: + pnc = SCCP_PNC_ERROR_CAUSE; + break; + default: + LOGP(DLSUA, LOGL_ERROR, "Unknown SUA Cause Class 0x%04x\n", tmp32); + return -EINVAL; + } + msgb_put_sccp_opt_hdr(msg, pnc, 1); + msgb_put_u8(msg, tmp32 & 0xff); + break; + case SUA_IEI_DATA: + msgb_put_sccp_opt(msg, SCCP_PNC_DATA, opt->len, opt->dat); + break; + case SUA_IEI_S7_HOP_CTR: + msgb_put_sccp_opt_hdr(msg, SCCP_PNC_HOP_COUNTER, 1); + msgb_put_u8(msg, xua_msg_part_get_u32(opt)); + break; + case SUA_IEI_IMPORTANCE: + msgb_put_sccp_opt_hdr(msg, SCCP_PNC_IMPORTANCE, 1); + msgb_put_u8(msg, xua_msg_part_get_u32(opt) & 0x7); + break; + case SUA_IEI_ROUTE_CTX: + break; + case SUA_IEI_SEQ_CTRL: + /* TODO */ + break; + default: + LOGP(DLSUA, LOGL_ERROR, "Unknown SUA IEI 0x%04x\n", opt->tag); + return -1; + } + return 0; +} + +/*! \brief convert SCCP optional part to list of SUA options + * \param[in] msg Message buffer holding SCCP message + * \param[in] ptr_opt address of relative pointer to optional part + * \param xua caller-provided xUA message to which options are added + * \returns \ref xua in case of success, NULL on error (xua not freed!) */ +static struct xua_msg *sccp_to_xua_opt(struct msgb *msg, uint8_t *ptr_opt, struct xua_msg *xua) +{ + uint8_t *opt_start, *oneopt; + + /* some bounds checking */ + if (ptr_opt < msg->data || ptr_opt > msg->tail) + return NULL; + opt_start = ptr_opt + *ptr_opt; + if (opt_start > msg->tail) + return NULL; + + oneopt = opt_start; + + while (oneopt < msg->tail) { + uint8_t opt_type = oneopt[0]; + + if (opt_type == SCCP_PNC_END_OF_OPTIONAL) + return xua; + + if (opt_type == SCCP_PNC_LONG_DATA) { + uint16_t opt_len16; + /* two byte length field */ + if (oneopt + 2 > msg->tail) + return NULL; + opt_len16 = oneopt[1] << 8 | oneopt[2]; + if (oneopt + 3 + opt_len16 > msg->tail) + return NULL; + xua_msg_add_sccp_opt(xua, opt_type, opt_len16, oneopt+3); + oneopt += 3 + opt_len16; + } else { + uint8_t opt_len; + /* one byte length field */ + if (oneopt + 1 > msg->tail) + return NULL; + + opt_len = oneopt[1]; + if (oneopt + 2 + opt_len > msg->tail) + return NULL; + xua_msg_add_sccp_opt(xua, opt_type, opt_len, oneopt+2); + oneopt += 2 + opt_len; + } + } + return NULL; +} + +#define MAX_IES 6 +#define NUM_SCCP_MSGT (SCCP_MSG_TYPE_LUDTS+1) + +/* This table indicates which information elements are mandatory and not + * optional in SCCP, per message type */ +static const uint16_t sccp_mandatory[NUM_SCCP_MSGT][MAX_IES] = { + /* Table 3/Q.713 */ + [SCCP_MSG_TYPE_CR] = { + SUA_IEI_SRC_REF, SUA_IEI_PROTO_CLASS, SUA_IEI_DEST_ADDR , 0 + }, + /* Table 4/Q.713 */ + [SCCP_MSG_TYPE_CC] = { + SUA_IEI_DEST_REF, SUA_IEI_SRC_REF, SUA_IEI_PROTO_CLASS, 0 + }, + /* Table 5/Q.713 */ + [SCCP_MSG_TYPE_CREF] = { + SUA_IEI_DEST_REF, SUA_IEI_CAUSE, 0 + }, + /* Table 6/Q.713 */ + [SCCP_MSG_TYPE_RLSD] = { + SUA_IEI_DEST_REF, SUA_IEI_SRC_REF, SUA_IEI_CAUSE, 0 + }, + /* Table 7/Q.713 */ + [SCCP_MSG_TYPE_RLC] = { + SUA_IEI_DEST_REF, SUA_IEI_SRC_REF, 0 + }, + /* Table 8/Q.713 */ + [SCCP_MSG_TYPE_DT1] = { + SUA_IEI_DEST_REF, SUA_IEI_SEGMENTATION, 0 + }, + /* Table 9/Q.713 */ + [SCCP_MSG_TYPE_DT2] = { + SUA_IEI_DEST_REF, SUA_IEI_SEGMENTATION, 0 + }, + /* Table 10/Q.713 */ + [SCCP_MSG_TYPE_AK] = { + SUA_IEI_DEST_REF, SUA_IEI_RX_SEQ_NR, 0 + }, + /* Table 11/Q.713 */ + [SCCP_MSG_TYPE_UDT] = { + SUA_IEI_PROTO_CLASS, SUA_IEI_DEST_ADDR, + SUA_IEI_SRC_ADDR, SUA_IEI_DATA, 0 + }, + /* Table 12/Q.713 */ + [SCCP_MSG_TYPE_UDTS] = { + SUA_IEI_CAUSE, SUA_IEI_DEST_ADDR, SUA_IEI_SRC_ADDR, SUA_IEI_DATA, 0 + }, + /* Table 13/Q.713 */ + [SCCP_MSG_TYPE_ED] = { + SUA_IEI_DEST_REF, 0 + }, + /* Table 14/Q.713 */ + [SCCP_MSG_TYPE_EA] = { + SUA_IEI_DEST_REF, 0 + }, + /* Table 15/Q.713 */ + [SCCP_MSG_TYPE_RSR] = { + SUA_IEI_DEST_REF, SUA_IEI_SRC_REF, SUA_IEI_CAUSE, 0 + }, + /* Table 16/Q.713 */ + [SCCP_MSG_TYPE_RSC] = { + SUA_IEI_DEST_REF, SUA_IEI_SRC_REF, 0 + }, + /* Table 17/Q.713 */ + [SCCP_MSG_TYPE_ERR] = { + SUA_IEI_DEST_REF, SUA_IEI_CAUSE, 0 + }, + /* Table 18/Q.713 */ + [SCCP_MSG_TYPE_IT] = { + SUA_IEI_DEST_REF, SUA_IEI_SRC_REF, SUA_IEI_PROTO_CLASS, + SUA_IEI_SEGMENTATION, SUA_IEI_CREDIT, 0 + }, + /* Table 19/Q.713 */ + [SCCP_MSG_TYPE_XUDT] = { + SUA_IEI_PROTO_CLASS, SUA_IEI_S7_HOP_CTR, + SUA_IEI_DEST_ADDR, SUA_IEI_SRC_ADDR, SUA_IEI_DATA, 0 + }, + /* Table 20/Q.713 */ + [SCCP_MSG_TYPE_XUDTS] = { + SUA_IEI_CAUSE, SUA_IEI_S7_HOP_CTR, SUA_IEI_DEST_ADDR, + SUA_IEI_SRC_ADDR, SUA_IEI_DATA, 0 + }, + /* Table 21/Q.713 */ + [SCCP_MSG_TYPE_LUDT] = { + SUA_IEI_PROTO_CLASS, SUA_IEI_S7_HOP_CTR, + SUA_IEI_DEST_ADDR, SUA_IEI_SRC_ADDR, SUA_IEI_DATA, 0 + }, + /* Table 22/Q.713 */ + [SCCP_MSG_TYPE_LUDTS] = { + SUA_IEI_CAUSE, SUA_IEI_S7_HOP_CTR, SUA_IEI_DEST_ADDR, + SUA_IEI_SRC_ADDR, SUA_IEI_DATA, 0 + }, +}; + +static bool sccp_is_mandatory(enum sccp_message_types type, const struct xua_msg_part *part) +{ + unsigned int i; + + if (type > ARRAY_SIZE(sccp_mandatory)) + return false; + + for (i = 0; i < MAX_IES; i++) { + uint16_t val = sccp_mandatory[type][i]; + if (val == 0) { + /* end of list, don't iterate further */ + return false; + } + if (val == part->tag) { + /* found in list, it's mandatory */ + return true; + } + } + /* not mandatory */ + return false; +} + +static int xua_ies_to_sccp_opts(struct msgb *msg, uint8_t *ptr_opt, + enum sccp_message_types type, struct xua_msg *xua) +{ + struct xua_msg_part *part; + + /* store relative pointer to start of optional part */ + *ptr_opt = msg->tail - ptr_opt; + + llist_for_each_entry(part, &xua->headers, entry) { + /* make sure we don't add a SCCP option for information + * that is already present in mandatory fixed or + * mandatory variable parts of the header */ + if (!sccp_is_mandatory(type, part)) + sccp_msg_add_sua_opt(msg, part); + } + msgb_put_u8(msg, SCCP_PNC_END_OF_OPTIONAL); + + return 0; +} + +/* store a 'local reference' as big-eidian 24bit value at local_ref */ +static void store_local_ref(struct sccp_source_reference *local_ref, struct xua_msg *xua, uint16_t iei) +{ + uint32_t tmp32 = xua_msg_get_u32(xua, iei); + local_ref->octet1 = (tmp32 >> 16) & 0xff; + local_ref->octet2 = (tmp32 >> 8) & 0xff; + local_ref->octet3 = tmp32 & 0xff; +} + +static struct xua_msg *sccp_to_xua_cr(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_connection_request *req = (struct sccp_connection_request *)msg->l2h; + + /* Fixed Part */ + xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, req->proto_class); + xua_msg_add_u32(xua, SUA_IEI_SRC_REF, load_24be(&req->source_local_reference)); + /* Variable Part */ + if (!sccp_ptr_part_consistent(msg, &req->variable_called)) + return NULL; + sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, msg, &req->variable_called); + /* Optional Part */ + return sccp_to_xua_opt(msg, &req->optional_start, xua); +} + +static int sua_to_sccp_cr(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_connection_request *req; + req = (struct sccp_connection_request *) msgb_put(msg, sizeof(*req)); + + /* Fixed Part */ + req->type = SCCP_MSG_TYPE_CR; + req->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS); + store_local_ref(&req->source_local_reference, xua, SUA_IEI_SRC_REF); + /* Variable Part */ + sccp_add_var_addr(msg, &req->variable_called, xua, SUA_IEI_DEST_ADDR); + + /* Optional Part */ + return xua_ies_to_sccp_opts(msg, &req->optional_start, req->type, xua); +} + +static struct xua_msg *sccp_to_xua_cc(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_connection_confirm *cnf = (struct sccp_connection_confirm *)msg->l2h; + + /* Fixed Part */ + xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, cnf->proto_class); + xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&cnf->destination_local_reference)); + xua_msg_add_u32(xua, SUA_IEI_SRC_REF, load_24be(&cnf->source_local_reference)); + /* Optional Part */ + return sccp_to_xua_opt(msg, &cnf->optional_start, xua); +} + +static int sua_to_sccp_cc(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_connection_confirm *cnf; + cnf = (struct sccp_connection_confirm *) msgb_put(msg, sizeof(*cnf)); + + /* Fixed Part */ + cnf->type = SCCP_MSG_TYPE_CC; + cnf->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS); + store_local_ref(&cnf->destination_local_reference, xua, SUA_IEI_DEST_REF); + store_local_ref(&cnf->source_local_reference, xua, SUA_IEI_SRC_REF); + /* Optional Part */ + return xua_ies_to_sccp_opts(msg, &cnf->optional_start, cnf->type, xua); +} + +static struct xua_msg *sccp_to_xua_cref(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_connection_refused *ref = (struct sccp_connection_refused *)msg->l2h; + + /* Fixed Part */ + xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&ref->destination_local_reference)); + xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_REFUSAL | ref->cause); + /* Optional Part */ + return sccp_to_xua_opt(msg, &ref->optional_start, xua); +} + +static int sua_to_sccp_cref(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_connection_refused *ref; + ref = (struct sccp_connection_refused *) msgb_put(msg, sizeof(*ref)); + + /* Fixed Part */ + ref->type = SCCP_MSG_TYPE_CREF; + store_local_ref(&ref->destination_local_reference, xua, SUA_IEI_DEST_REF); + ref->cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff; + /* Optional Part */ + return xua_ies_to_sccp_opts(msg, &ref->optional_start, ref->type, xua); +} + +static struct xua_msg *sccp_to_xua_rlsd(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_connection_released *rlsd = (struct sccp_connection_released *)msg->l2h; + + /* Fixed Part */ + xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&rlsd->destination_local_reference)); + xua_msg_add_u32(xua, SUA_IEI_SRC_REF, load_24be(&rlsd->source_local_reference)); + xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RELEASE | rlsd->release_cause); + /* Optional Part */ + return sccp_to_xua_opt(msg, &rlsd->optional_start, xua); +} + +static int sua_to_sccp_rlsd(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_connection_released *rlsd; + rlsd =(struct sccp_connection_released *) msgb_put(msg, sizeof(*rlsd)); + + /* Fixed Part */ + rlsd->type = SCCP_MSG_TYPE_RLSD; + store_local_ref(&rlsd->destination_local_reference, xua, SUA_IEI_DEST_REF); + store_local_ref(&rlsd->source_local_reference, xua, SUA_IEI_SRC_REF); + rlsd->release_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff; + + /* Optional Part */ + return xua_ies_to_sccp_opts(msg, &rlsd->optional_start, rlsd->type, xua); +} + +static struct xua_msg *sccp_to_xua_rlc(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_connection_release_complete *rlc; + rlc = (struct sccp_connection_release_complete *) msg->l2h; + + /* Fixed Part */ + xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&rlc->destination_local_reference)); + xua_msg_add_u32(xua, SUA_IEI_SRC_REF, load_24be(&rlc->source_local_reference)); + return xua; +} + +static int sua_to_sccp_rlc(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_connection_release_complete *rlc; + rlc = (struct sccp_connection_release_complete *) msgb_put(msg, sizeof(*rlc)); + + /* Fixed Part */ + rlc->type = SCCP_MSG_TYPE_RLC; + store_local_ref(&rlc->destination_local_reference, xua, SUA_IEI_DEST_REF); + store_local_ref(&rlc->source_local_reference, xua, SUA_IEI_SRC_REF); + return 0; +} + +static struct xua_msg *sccp_to_xua_dt1(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_data_form1 *dt1 = (struct sccp_data_form1 *) msg->l2h; + + /* Fixed Part */ + xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&dt1->destination_local_reference)); + xua_msg_add_u32(xua, SUA_IEI_SEGMENTATION, dt1->segmenting); + /* Variable Part */ + if (!sccp_ptr_part_consistent(msg, &dt1->variable_start)) + return NULL; + sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, msg, &dt1->variable_start); + return xua; +} + +static int sua_to_sccp_dt1(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_data_form1 *dt1; + dt1 = (struct sccp_data_form1 *) msgb_put(msg, sizeof(*dt1)); + + /* Fixed Part */ + dt1->type = SCCP_MSG_TYPE_DT1; + store_local_ref(&dt1->destination_local_reference, xua, SUA_IEI_DEST_REF); + dt1->segmenting = xua_msg_get_u32(xua, SUA_IEI_SEGMENTATION); + /* Variable Part */ + sccp_add_variable_part(msg, &dt1->variable_start, xua, SUA_IEI_DATA); + return 0; +} + +static struct xua_msg *sccp_to_xua_udt(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msg->l2h; + + /* Fixed Part */ + xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, udt->proto_class); + /* Variable Part */ + if (!sccp_ptr_part_consistent(msg, &udt->variable_called)) + return NULL; + sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, msg, &udt->variable_called); + if (!sccp_ptr_part_consistent(msg, &udt->variable_calling)) + return NULL; + sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, msg, &udt->variable_calling); + if (!sccp_ptr_part_consistent(msg, &udt->variable_data)) + return NULL; + sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, msg, &udt->variable_data); + return xua; + +} + +static int sua_to_sccp_udt(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_data_unitdata *udt; + udt = (struct sccp_data_unitdata *) msgb_put(msg, sizeof(*udt)); + + /* Fixed Part */ + udt->type = SCCP_MSG_TYPE_UDT; + udt->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS); + /* Variable Part */ + sccp_add_var_addr(msg, &udt->variable_called, xua, SUA_IEI_DEST_ADDR); + sccp_add_var_addr(msg, &udt->variable_calling, xua, SUA_IEI_SRC_ADDR); + sccp_add_variable_part(msg, &udt->variable_data, xua, SUA_IEI_DATA); + return 0; +} + +static struct xua_msg *sccp_to_xua_udts(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_data_unitdata_service *udts; + udts =(struct sccp_data_unitdata_service *)msg->l2h; + + /* Fixed Part */ + xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RETURN | udts->return_cause); + /* Variable Part */ + if (!sccp_ptr_part_consistent(msg, &udts->variable_called)) + return NULL; + sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, msg, &udts->variable_called); + if (!sccp_ptr_part_consistent(msg, &udts->variable_calling)) + return NULL; + sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, msg, &udts->variable_calling); + if (!sccp_ptr_part_consistent(msg, &udts->variable_data)) + return NULL; + sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, msg, &udts->variable_data); + return xua; + +} + +static int sua_to_sccp_udts(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_data_unitdata_service *udts; + udts = (struct sccp_data_unitdata_service *) msgb_put(msg, sizeof(*udts)); + + /* Fixed Part */ + udts->type = SCCP_MSG_TYPE_UDTS; + udts->return_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff; + /* Variable Part */ + sccp_add_var_addr(msg, &udts->variable_called, xua, SUA_IEI_DEST_ADDR); + sccp_add_var_addr(msg, &udts->variable_calling, xua, SUA_IEI_SRC_ADDR); + sccp_add_variable_part(msg, &udts->variable_data, xua, SUA_IEI_DATA); + return 0; +} + +static struct xua_msg *sccp_to_xua_it(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_data_it *it = (struct sccp_data_it *)msg->l2h; + + /* Fixed Part */ + xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, it->proto_class); + xua_msg_add_u32(xua, SUA_IEI_SRC_REF, load_24be(&it->source_local_reference)); + xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&it->destination_local_reference)); + if ((it->proto_class & 0xF) == 3) { + //xua_msg_add_u32(xua, SUA_IEI_SEQUENCING, it->sequencing); + xua_msg_add_u32(xua, SUA_IEI_CREDIT, it->credit); + } + return xua; +} + +static int sua_to_sccp_it(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_data_it *it; + it = (struct sccp_data_it *) msgb_put(msg, sizeof(*it)); + + /* Fixed Part */ + it->type = SCCP_MSG_TYPE_IT; + it->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS); + store_local_ref(&it->destination_local_reference, xua, SUA_IEI_DEST_REF); + store_local_ref(&it->source_local_reference, xua, SUA_IEI_SRC_REF); + if ((it->proto_class & 0xF) == 3) { + //it->sequencing + it->credit = xua_msg_get_u32(xua, SUA_IEI_CREDIT); + } + + return 0; +} + +static struct xua_msg *sccp_to_xua_err(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_proto_err *err = (struct sccp_proto_err *)msg->l2h; + + /* Fixed Part */ + xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&err->destination_local_reference)); + xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_ERROR | err->error_cause); + return xua; +} + +static int sua_to_sccp_err(struct msgb *msg, struct xua_msg *xua) +{ + struct sccp_proto_err *err; + err = (struct sccp_proto_err *) msgb_put(msg, sizeof(*err)); + + /* Fixed Part */ + err->type = SCCP_MSG_TYPE_ERR; + store_local_ref(&err->destination_local_reference, xua, SUA_IEI_DEST_REF); + err->error_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff; + return 0; +} + +/*! \brief convert SCCP message to a SUA message + * \param[in] msg message buffer holding SCCP message at l2h + * \returns callee-allocated xUA message on success; NULL on error */ +struct xua_msg *osmo_sccp_to_xua(struct msgb *msg) +{ + struct xua_msg *xua; + + if (msgb_l2len(msg) < 1) { + LOGP(DLSUA, LOGL_ERROR, "Short SCCP Message, cannot transcode\n"); + return NULL; + } + + xua = xua_msg_alloc(); + if (!xua) + return NULL; + + switch (msg->l2h[0]) { + case SCCP_MSG_TYPE_CR: + xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CORE); + return sccp_to_xua_cr(msg, xua); + case SCCP_MSG_TYPE_CC: + xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_COAK); + return sccp_to_xua_cc(msg, xua); + case SCCP_MSG_TYPE_CREF: + xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_COREF); + return sccp_to_xua_cref(msg, xua); + case SCCP_MSG_TYPE_RLSD: + xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_RELRE); + return sccp_to_xua_rlsd(msg, xua); + case SCCP_MSG_TYPE_RLC: + xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_RELCO); + return sccp_to_xua_rlc(msg, xua); + case SCCP_MSG_TYPE_DT1: + xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CODT); + return sccp_to_xua_dt1(msg, xua); + case SCCP_MSG_TYPE_UDT: + xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT); + return sccp_to_xua_udt(msg, xua); + case SCCP_MSG_TYPE_UDTS: + xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDR); + return sccp_to_xua_udts(msg, xua); + case SCCP_MSG_TYPE_IT: + xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_COIT); + return sccp_to_xua_it(msg, xua); + case SCCP_MSG_TYPE_ERR: + xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_COERR); + return sccp_to_xua_err(msg, xua); + /* Unsupported Message Types */ + case SCCP_MSG_TYPE_DT2: + case SCCP_MSG_TYPE_AK: + case SCCP_MSG_TYPE_ED: + case SCCP_MSG_TYPE_EA: + case SCCP_MSG_TYPE_RSR: + case SCCP_MSG_TYPE_RSC: + case SCCP_MSG_TYPE_XUDT: + case SCCP_MSG_TYPE_XUDTS: + case SCCP_MSG_TYPE_LUDT: + case SCCP_MSG_TYPE_LUDTS: + default: + LOGP(DLSUA, LOGL_ERROR, "Unsupported SCCP message type %u\n", + msg->l2h[0]); + xua_msg_free(xua); + return NULL; + } + + return NULL; +} + +/*! \brief convert parsed SUA message to SCCP message + * \param[in] xua parsed SUA message to be converted + * \returns callee-allocated msgb containing encoded SCCP message */ +struct msgb *osmo_sua_to_sccp(struct xua_msg *xua) +{ + struct msgb *msg = sccp_msgb_alloc("SCCP from SUA"); + int rc; + + switch (xua->hdr.msg_class) { + case SUA_MSGC_CL: + switch (xua->hdr.msg_type) { + case SUA_CL_CLDT: + rc = sua_to_sccp_udt(msg, xua); + break; + case SUA_CL_CLDR: + rc = sua_to_sccp_udts(msg, xua); + break; + default: + LOGP(DLSUA, LOGL_ERROR, "Unsupported SUA message %s\n", + xua_hdr_dump(xua, &xua_dialect_sua)); + goto out_err; + } + break; + case SUA_MSGC_CO: + switch (xua->hdr.msg_type) { + case SUA_CO_CORE: + rc = sua_to_sccp_cr(msg, xua); + break; + case SUA_CO_COAK: + rc = sua_to_sccp_cc(msg, xua); + break; + case SUA_CO_COREF: + rc = sua_to_sccp_cref(msg, xua); + break; + case SUA_CO_RELRE: + rc = sua_to_sccp_rlsd(msg, xua); + break; + case SUA_CO_RELCO: + rc = sua_to_sccp_rlc(msg, xua); + break; + case SUA_CO_CODT: + rc = sua_to_sccp_dt1(msg, xua); + break; + case SUA_CO_COIT: + rc = sua_to_sccp_it(msg, xua); + break; + case SUA_CO_COERR: + rc = sua_to_sccp_err(msg, xua); + break; + default: + LOGP(DLSUA, LOGL_ERROR, "Unsupported SUA message %s\n", + xua_hdr_dump(xua, &xua_dialect_sua)); + goto out_err; + } + break; + default: + LOGP(DLSUA, LOGL_ERROR, "Unsupported SUA message class %s\n", + xua_hdr_dump(xua, &xua_dialect_sua)); + goto out_err; + } + + if (rc < 0) + goto out_err; + + return msg; + +out_err: + msgb_free(msg); + return NULL; +} -- To view, visit https://gerrit.osmocom.org/2213 To unsubscribe, visit https://gerrit.osmocom.org/settings Gerrit-MessageType: merged Gerrit-Change-Id: I8151a9b08a0b0ca97b9c73105ad4548512ce3be8 Gerrit-PatchSet: 6 Gerrit-Project: libosmo-sccp Gerrit-Branch: master Gerrit-Owner: Harald Welte <laforge at gnumonks.org> Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org> Gerrit-Reviewer: Jenkins Builder