From dc923e49cd3b623dab05f6016a3e935d7c652cb3 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 13 Feb 2017 03:47:00 +0100 Subject: WIP: SCCP <-> SUA transcoding routines Using related code, SCCP can be transcoded to SUA and vice-versa. This way the common code such as the state machines for connection oriented SCCP work with one format (SUA) only, and the conversion from/to SCCP is done when SCCP is used as protocol. Change-Id: I7a77e7d418f2427e9e379867a78a3f1b9ad718cb --- src/Makefile.am | 2 +- src/sccp2sua.c | 937 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 938 insertions(+), 1 deletion(-) create mode 100644 src/sccp2sua.c diff --git a/src/Makefile.am b/src/Makefile.am index 26482a0..babd595 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,6 +24,6 @@ lib_LTLIBRARIES = libosmo-sigtran.la # documentation before making any modification LIBVERSION=0:0:0 -libosmo_sigtran_la_SOURCES = sccp_sap.c sua.c xua_msg.c sccp_helpers.c +libosmo_sigtran_la_SOURCES = sccp_sap.c sua.c xua_msg.c sccp_helpers.c sccp2sua.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..5ca74bb --- /dev/null +++ b/src/sccp2sua.c @@ -0,0 +1,937 @@ +/* SCCP <-> SUA transcoding routines */ + +/* (C) 2017 by Harald Welte + * 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 Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +/* 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(void *ptr) +{ + 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); +} + +int osmo_isup_party_encode(struct msgb *msg, const char *in_digits) +{ + unsigned int num_digits = strlen(in_digits); + + /* FIXME */ +} + +/*! \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; + bool odd; + int rc; + + 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; + switch (*cur++ & 0xF) { + case 1: + odd = true; + break; + case 2: + odd = false; + break; + default: + return -1; + } + out->gt.nai = *cur++ & 0x7f; + break; + default: + return -EINVAL; + } + rc = osmo_isup_party_parse(&out->gt.digits, cur, (addr+addrlen-cur), odd); + if (rc < 0) + return rc; + out->gt.nr_digits = 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: + return -EINVAL; + } + + if (in->presence & OSMO_SCCP_ADDR_T_PC) { + sca->point_code_indicator = 1; + msgb_put_u24be(msg, in->pc); + } + + if (in->presence & OSMO_SCCP_ADDR_T_SSN) { + sca->ssn_indicator = 1; + msgb_put_u16le(msg, in->pc & 0x3ff); + } + + if (!(in->presence & OSMO_SCCP_ADDR_T_GT)) { + sca->global_title_indicator = SCCP_TITLE_IND_NONE; + return 0; + } + + odd = in->gt.nr_digits & 1; + switch (in->gt.gti) { + case OSMO_SCCP_GTI_NO_GT: + sca->global_title_indicator = SCCP_TITLE_IND_NONE; + return 0; + 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 */ + 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_isu_party_encode(msg, in->gt.digits); + + /* 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 */ + memset(&osa, 0, sizeof(osa)); + rc = osmo_sccp_addr_parse(&osa, addr, addrlen); + if (rc < 0) + return rc; + + /* 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 */ + memset(&osa, 0, sizeof(osa)); + rc = smo_sua_addr_parse(&osa, part); + if (rc < 0) + return rc; + + /* Then re-encode it as SCCP address */ + return osmo_sccp_addr_encode(msg, &osa); +} + +/*! \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) { + fprintf(stderr, "ptr_addr outside msg boundary\n"); + return false; + } + + ptr = ptr_addr + *ptr_addr; + if (ptr > msg->tail) { + fprintf(stderr, "ptr points outside msg boundary\n"); + return false; + } + + /* at destination of relative pointer is the length */ + if (ptr + 1 + *ptr > msg->tail) { + fprintf(stderr, "ptr + len points outside msg boundary\n"); + return false; + } + return true; +} + +/*! \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, const 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 & 0xF); + /* FIXME */ + 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: + /* FIXME: print error */ + return -1; + } +} + +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); +} + +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) & 0xF); + 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: + 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; + default: + /* FIXME: print error */ + 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; +} + +static int xua_ies_to_sccp_opts(struct msgb *msg, struct xua_msg *xua) +{ + struct xua_msg_part *part; + + llist_for_each_entry(part, &xua->headers, entry) { + sccp_msg_add_sua_opt(msg, part); + } + msgb_put_u8(msg, SCCP_PNC_END_OF_OPTIONAL); + + return 0; +} + +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 & 0xF); + 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->proto_class = + req->source_local_ref + /* Variable Part */ + req->variable_called; + + /* Optional Part */ + return xua_ies_to_sccp_opts(msg, 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 & 0xF); + 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->proto_class; + cnf->destination_local_reference; + cnf->source_local_reference; + /* Optional Part */ + return xua_ies_to_sccp_opts(msg, 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->destination_local_reference; + ref->cause; + /* Optional Part */ + return xua_ies_to_sccp_opts(msg, 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->destination_local_reference + rlsd->sourcea_local_reference + rlsd->release_cause + + /* Optional Part */ + return xua_ies_to_sccp_opts(msg, 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 */ +} + +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; + 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 */ + /* Variable Part */ +} + +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 & 0xF); + /* Variable Part */ + if (!sccp_ptr_part_consistent(msg, &udt->variable_called)) + return NULL; + if (!sccp_ptr_part_consistent(msg, &udt->variable_calling)) + return NULL; + if (!sccp_ptr_part_consistent(msg, &udt->variable_data)) + return NULL; + sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, msg, &udt->variable_called); + sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, msg, &udt->variable_calling); + 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 */ + /* Variable Part */ +} + +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, &udt->variable_called)) + return NULL; + if (!sccp_ptr_part_consistent(msg, &udt->variable_calling)) + return NULL; + if (!sccp_ptr_part_consistent(msg, &udt->variable_data)) + return NULL; + sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, msg, &udt->variable_called); + sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, msg, &udt->variable_calling); + 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 */ + /* Variable Part */ +} + +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 & 0xF); + 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 */ +} + +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 */ +} + +/*! \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) + 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: + xua_msg_free(xua); + return NULL; + } + + return NULL; +} + +struct msgb *osmo_sua_to_sccp(struct xua_msg *xua) +{ + struct msgb *msg = msgb_alloc(2048, "SCCP from SUA"); + + switch (xua->hdr.msg_class) { + case SUA_MSGC_CL: + switch (xua->hdr.msg_type) { + case SUA_CL_CLDT: + return sua_to_sccp_udt(msg, xua); + default: + goto out_err; + } + break; + case SUA_MSGC_CO: + switch (xua->hdr.msg_type) { + case SUA_CO_CORE: + return sua_to_sccp_cr(msg, xua); + case SUA_CO_COAK: + return sua_to_sccp_cc(msg, xua); + case SUA_CO_COREF: + return sua_to_sccp_cref(msg, xua); + case SUA_CO_RELRE: + return sua_to_sccp_rlsd(msg, xua); + case SUA_CO_RELCO: + return sua_to_sccp_rlc(msg, xua); + case SUA_CO_CODT: + return sua_to_sccp_dt1(msg, xua); + case SUA_CO_COIT: + return sua_to_sccp_it(msg, xua); + case SUA_CO_COERR: + return sua_to_sccp_err(msg, xua); + default: + goto out_err; + } + break; + default: + goto out_err; + } + +out_err: + msgb_free(msg); + return NULL; +} -- cgit v1.2.3