pespin has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/libosmo-sccp/+/34467?usp=email )


Change subject: WIP: Support for LUDT + LUDTS messages
......................................................................

WIP: Support for LUDT + LUDTS messages

Change-Id: Ic91abfc921f5e4f36045bfa325333112cddd9fa6
---
M include/osmocom/sccp/sccp_types.h
M include/osmocom/sigtran/xua_msg.h
M src/sccp2sua.c
M src/xua_msg.c
M tests/xua/sccp_test_data.c
M tests/xua/sccp_test_data.h
M tests/xua/xua_test.c
M tests/xua/xua_test.ok
8 files changed, 375 insertions(+), 46 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/libosmo-sccp refs/changes/67/34467/1

diff --git a/include/osmocom/sccp/sccp_types.h 
b/include/osmocom/sccp/sccp_types.h
index 277a3b2..e5b3bf2 100644
--- a/include/osmocom/sccp/sccp_types.h
+++ b/include/osmocom/sccp/sccp_types.h
@@ -488,6 +488,64 @@
        uint8_t                 data[0];
 } __attribute__((packed));

+/* Long unitdata (LUDT), ITU-T Q.713 4.20 */
+struct sccp_data_long_unitdata {
+       /* mandatory */
+       uint8_t                 type;
+       uint8_t                 proto_class;
+       uint8_t                 hop_counter;
+
+       /* variable */
+       uint16_t                variable_called;
+       uint16_t                variable_calling;
+       uint16_t                variable_data;
+       /* optional */
+       uint16_t                optional_start;
+
+#if VARIABLE
+       called party address
+       calling party address
+       data
+#endif
+
+#if OPTIONAL
+       segmentation
+       importance
+#endif
+
+       uint8_t                 data[0];
+
+} __attribute__((packed));
+
+/* Long unitdata service (LUDTS), ITU-T Q.713 4.21 */
+struct  sccp_data_long_unitdata_service {
+       /* mandantory */
+       uint8_t                 type;
+       uint8_t                 return_cause;
+       uint8_t                 hop_counter;
+
+
+       /* variable */
+       uint16_t                        variable_called;
+       uint16_t                        variable_calling;
+       uint16_t                        variable_data;
+       /* optional */
+       uint16_t                        optional_start;
+
+#if VARIABLE
+       called party address
+       calling party address
+       data
+#endif
+
+#if OPTIONAL
+       segmentation
+       importancd
+#endif
+
+       uint8_t                 data[0];
+} __attribute__((packed));
+
 struct sccp_data_it {
        /* mandatory */
        uint8_t                 type;
diff --git a/include/osmocom/sigtran/xua_msg.h 
b/include/osmocom/sigtran/xua_msg.h
index 11bce6f..5f877ed 100644
--- a/include/osmocom/sigtran/xua_msg.h
+++ b/include/osmocom/sigtran/xua_msg.h
@@ -94,6 +94,7 @@
 const uint32_t *xua_msg_get_u32p(const struct xua_msg *xua, uint16_t iei, 
uint32_t *out);
 const char *xua_msg_part_get_str(const struct xua_msg_part *part);
 const char *xua_msg_get_str(const struct xua_msg *xua, uint16_t iei);
+int xua_msg_get_len(const struct xua_msg *xua, uint16_t iei);
 void xua_part_add_gt(struct msgb *msg, const struct osmo_sccp_gt *gt);
 int xua_msg_add_sccp_addr(struct xua_msg *xua, uint16_t iei, const struct 
osmo_sccp_addr *addr);

diff --git a/src/sccp2sua.c b/src/sccp2sua.c
index 2f09a8f..3ee79f6 100644
--- a/src/sccp2sua.c
+++ b/src/sccp2sua.c
@@ -338,10 +338,17 @@
 }

 /*! \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, const 
uint8_t *ptr_addr)
+static int sccp_addr_to_sua_ptr(struct xua_msg *xua, uint16_t iei, const 
uint8_t *ptr_addr, bool ptr_addr_is_long)
 {
-       const uint8_t *addr = ptr_addr + *ptr_addr + 1;
-       unsigned int addrlen = *(ptr_addr + *ptr_addr);
+       const uint8_t *addr;
+       unsigned int addrlen;
+
+       if (ptr_addr_is_long) /* +1: Distance from MSB of pointer */
+               addr = ptr_addr + 1 + osmo_load16le(ptr_addr);
+       else
+               addr = ptr_addr + *ptr_addr;
+       addrlen = *addr;
+       addr++;

        return sccp_addr_to_sua(xua, iei, addr, addrlen);
 }
@@ -368,9 +375,10 @@
 /*! \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] var_ptr_is_long Whether the var_ptr field is 2 bytes long 
(network order)
  *  \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, const struct 
xua_msg *xua, uint16_t iei)
+static int sccp_add_var_addr(struct msgb *msg, uint8_t *var_ptr, bool 
var_ptr_is_long, const struct xua_msg *xua, uint16_t iei)
 {
        struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
        uint8_t *lenbyte;
@@ -383,7 +391,10 @@
        /* 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;
+       if (var_ptr_is_long) /* -1: Distance from MSB of pointer */
+               osmo_store16le(lenbyte - var_ptr - 1, var_ptr);
+       else
+               *var_ptr = lenbyte - var_ptr;

        /* then append the encoded SCCP address */
        rc = sua_addr_to_sccp(msg, part);
@@ -426,6 +437,38 @@
        return part->len;
 }

+/*! \brief Add a "SCCP Long 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_long_variable_part(struct msgb *msg, uint8_t *var_ptr, 
const 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, 2);
+       /* update the relative pointer to the length byte.
+        * This is only used in LUDT and LUDTS, so the pointer is always 2 
bytes.
+        * -1: Distance from MSB of pointer */
+       osmo_store16le(lenbyte - var_ptr - 1, 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, LSB first */
+       lenbyte[0] = part->len & 0xff;
+       lenbyte[1] = part->len >> 8;
+
+       return part->len;
+}

 /*! \brief validate that SCCP part with pointer + length doesn't exceed msg 
tail
  *  \param[in] msg Message containing SCCP address
@@ -455,6 +498,45 @@
        return true;
 }

+/*! \brief validate that SCCP part with long pointer (2 bytes) + length 
doesn't exceed msg tail
+ *  \param[in] msg Message containing SCCP address (LUDT or LUDTS)
+ *  \param[in] ptr_addr pointer to byte with relative SCCP long pointer 
(uint16_t, 2 bytes in network order)
+ *  \param[in] len_is_long whether the length field at the starting of the 
value field pointer to by ptr_addr is 2 bytes long.
+ *  \returns true if OK; false if message inconsistent */
+static bool sccp_longptr_part_consistent(const struct msgb *msg, const uint8_t 
*ptr_addr, bool len_is_long)
+{
+       const uint8_t *ptr;
+       uint8_t offs;
+       uint16_t len;
+
+       /* 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;
+       }
+
+       /* +1: Distance from MSB of pointer */
+       ptr = ptr_addr + 1 + osmo_load16le(ptr_addr);
+       if (ptr > msg->tail) {
+               LOGP(DLSUA, LOGL_ERROR, "ptr %p points outside msg boundary 
%p\n", ptr, msg->tail);
+               return false;
+       }
+
+       /* at destination of relative pointer is the length */
+       if (len_is_long) {
+               offs = 2;
+               len = osmo_load16le(ptr);
+       } else {
+               offs = 1;
+               len = *ptr;
+       }
+       if (ptr + offs + len > 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, const 
uint8_t *ptr_addr)
 {
@@ -464,6 +546,22 @@
        return xua_msg_add_data(xua, iei, addrlen, addr);
 }

+/*! \brief convenience wrapper around xua_msg_add_data() for variable 
mandatory data */
+static int sccp_longdata_to_sua_ptr(struct xua_msg *xua, uint16_t iei, const 
uint8_t *ptr_addr)
+{
+       const uint16_t ptr_value = osmo_load16le(ptr_addr);
+       /* +1: Distance from MSB of pointer, +2: size of length field */
+       const uint8_t *addr = ptr_addr + 1 + ptr_value + 2;
+       unsigned int addrlen = 0;
+
+       /* LSB first. +1: Distance from MSB of pointer */
+       addrlen += *(ptr_addr + 1 + ptr_value);
+       addrlen += *(ptr_addr + 1 + ptr_value + 1) << 8;
+
+       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)
@@ -697,22 +795,34 @@
 /*! \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[in] ptr_opt_is_long whether ptr_opt is a long pointer (2 bytes, 
network order)
  *  \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(const struct msgb *msg, const uint8_t 
*ptr_opt, struct xua_msg *xua)
+static struct xua_msg *sccp_to_xua_opt(const struct msgb *msg, const uint8_t 
*ptr_opt, bool ptr_opt_is_long, struct xua_msg *xua)
 {
        const uint8_t *opt_start, *oneopt;
+       uint16_t ptr_opt_value;

        /* some bounds checking */
-       if (ptr_opt < msg->data || ptr_opt > msg->tail)
+       if (ptr_opt < msg->data)
                return NULL;
+       if (ptr_opt + (ptr_opt_is_long ? 1 : 0) > msg->tail)
+               return NULL;
+
+       if (ptr_opt_is_long)
+               ptr_opt_value = osmo_load16le(ptr_opt);
+       else
+               ptr_opt_value = *ptr_opt;

        /* Q.713 section 2.3 "Coding of pointers": pointer value all zeros used
          to indicate that no optional param is present. */
-       if (*ptr_opt == 0)
+       if (ptr_opt_value == 0)
                return xua;

-       opt_start = ptr_opt + *ptr_opt;
+       if (ptr_opt_is_long) /* +1: Distance from MSB of pointer */
+               opt_start = ptr_opt + 1 + ptr_opt_value;
+       else
+               opt_start = ptr_opt + ptr_opt_value;
        if (opt_start > msg->tail)
                return NULL;

@@ -983,7 +1093,7 @@
        return false;
 }

-static int xua_ies_to_sccp_opts(struct msgb *msg, uint8_t *ptr_opt,
+static int xua_ies_to_sccp_opts(struct msgb *msg, uint8_t *ptr_opt, bool 
ptr_opt_is_long,
                                enum sccp_message_types type, const struct 
xua_msg *xua)
 {
        const struct xua_msg_part *part;
@@ -1002,10 +1112,15 @@
        if (any_added) {
                msgb_put_u8(msg, SCCP_PNC_END_OF_OPTIONAL);
                /* store relative pointer to start of optional part */
-               *ptr_opt = old_tail - ptr_opt;
+               if (ptr_opt_is_long) /* -1: Distance from MSB of pointer */
+                       osmo_store16le(old_tail - ptr_opt - 1, ptr_opt);
+               else
+                       *ptr_opt = old_tail - ptr_opt;
        } else {
                /* If nothing was added, simply update the pointer to 0 to 
signal the optional part is ommited. */
                ptr_opt[0] = 0;
+               if (ptr_opt_is_long)
+                       ptr_opt[1] = 0;
        }

        return 0;
@@ -1036,9 +1151,9 @@
        /* Variable Part */
        if (!sccp_ptr_part_consistent(msg, &req->variable_called))
                return NULL;
-       sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &req->variable_called);
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &req->variable_called, 
false);
        /* Optional Part */
-       return sccp_to_xua_opt(msg, &req->optional_start, xua);
+       return sccp_to_xua_opt(msg, &req->optional_start, false, xua);
 }

 /*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
@@ -1055,10 +1170,10 @@
        if (rc < 0)
                return rc;
        /* Variable Part */
-       sccp_add_var_addr(msg, &req->variable_called, xua, SUA_IEI_DEST_ADDR);
+       sccp_add_var_addr(msg, &req->variable_called, false, xua, 
SUA_IEI_DEST_ADDR);

        /* Optional Part */
-       return xua_ies_to_sccp_opts(msg, &req->optional_start, req->type, xua);
+       return xua_ies_to_sccp_opts(msg, &req->optional_start, false, 
req->type, xua);
 }

 /*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
@@ -1071,7 +1186,7 @@
        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);
+       return sccp_to_xua_opt(msg, &cnf->optional_start, false, xua);
 }

 /*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
@@ -1091,7 +1206,7 @@
        if (rc < 0)
                return rc;
        /* Optional Part */
-       return xua_ies_to_sccp_opts(msg, &cnf->optional_start, cnf->type, xua);
+       return xua_ies_to_sccp_opts(msg, &cnf->optional_start, false, 
cnf->type, xua);
 }

 /*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
@@ -1103,7 +1218,7 @@
        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);
+       return sccp_to_xua_opt(msg, &ref->optional_start, false, xua);
 }

 /*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
@@ -1120,7 +1235,7 @@
                return rc;
        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);
+       return xua_ies_to_sccp_opts(msg, &ref->optional_start, false, 
ref->type, xua);
 }

 /*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
@@ -1133,7 +1248,7 @@
        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);
+       return sccp_to_xua_opt(msg, &rlsd->optional_start, false, xua);
 }

 static int sua_to_sccp_rlsd(struct msgb *msg, const struct xua_msg *xua)
@@ -1153,7 +1268,7 @@
        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);
+       return xua_ies_to_sccp_opts(msg, &rlsd->optional_start, false, 
rlsd->type, xua);
 }

 /*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
@@ -1227,10 +1342,10 @@
        /* Variable Part */
        if (!sccp_ptr_part_consistent(msg, &udt->variable_called))
                return NULL;
-       sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &udt->variable_called);
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &udt->variable_called, 
false);
        if (!sccp_ptr_part_consistent(msg, &udt->variable_calling))
                return NULL;
-       sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &udt->variable_calling);
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &udt->variable_calling, 
false);
        if (!sccp_ptr_part_consistent(msg, &udt->variable_data))
                return NULL;
        sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &udt->variable_data);
@@ -1239,11 +1354,18 @@
 }

 static int sua_to_sccp_xudt(struct msgb *msg, const struct xua_msg *xua);
+static int sua_to_sccp_ludt(struct msgb *msg, const struct xua_msg *xua);

 static int sua_to_sccp_udt(struct msgb *msg, const struct xua_msg *xua)
 {
        struct sccp_data_unitdata *udt;

+       /* Use LUDT if length exceeds 255 (single byte length field) */
+       /* TODO: start using LUDT sooner if called/calling party contain GT or 
if
+        * segmentation and/or importance present, see Q.715 Section 8.3.2 */
+       if (xua_msg_get_len(xua, SUA_IEI_DATA) > 255)
+               return sua_to_sccp_ludt(msg, xua);
+
        /* Use XUDT if we have a hop counter on the SUA side */
        if (xua_msg_find_tag(xua, SUA_IEI_S7_HOP_CTR))
                return sua_to_sccp_xudt(msg, xua);
@@ -1254,8 +1376,8 @@
        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_var_addr(msg, &udt->variable_called, false, xua, 
SUA_IEI_DEST_ADDR);
+       sccp_add_var_addr(msg, &udt->variable_calling, false, xua, 
SUA_IEI_SRC_ADDR);
        sccp_add_variable_part(msg, &udt->variable_data, xua, SUA_IEI_DATA);
        return 0;
 }
@@ -1271,21 +1393,28 @@
        /* Variable Part */
        if (!sccp_ptr_part_consistent(msg, &xudt->variable_called))
                return NULL;
-       sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &xudt->variable_called);
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &xudt->variable_called, 
false);
        if (!sccp_ptr_part_consistent(msg, &xudt->variable_calling))
                return NULL;
-       sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &xudt->variable_calling);
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &xudt->variable_calling, 
false);
        if (!sccp_ptr_part_consistent(msg, &xudt->variable_data))
                return NULL;
        sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &xudt->variable_data);
        /* Optional Part */
-       return sccp_to_xua_opt(msg, &xudt->optional_start, xua);
+       return sccp_to_xua_opt(msg, &xudt->optional_start, false, xua);

 }

 static int sua_to_sccp_xudt(struct msgb *msg, const struct xua_msg *xua)
 {
        struct sccp_data_ext_unitdata *xudt;
+
+       /* Use LUDT if length exceeds 255 (single byte length field) */
+       /* TODO: start using LUDTS sooner if called/calling party contain GT or 
if
+        * segmentation and/or importance present, see Q.715 Section 8.3.2 */
+       if (xua_msg_get_len(xua, SUA_IEI_DATA) > 254)
+               return sua_to_sccp_ludt(msg, xua);
+
        xudt = (struct sccp_data_ext_unitdata *) msgb_put(msg, sizeof(*xudt));

        /* Fixed Part */
@@ -1293,11 +1422,50 @@
        xudt->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
        xudt->hop_counter = xua_msg_get_u32(xua, SUA_IEI_S7_HOP_CTR);
        /* Variable Part */
-       sccp_add_var_addr(msg, &xudt->variable_called, xua, SUA_IEI_DEST_ADDR);
-       sccp_add_var_addr(msg, &xudt->variable_calling, xua, SUA_IEI_SRC_ADDR);
+       sccp_add_var_addr(msg, &xudt->variable_called, false, xua, 
SUA_IEI_DEST_ADDR);
+       sccp_add_var_addr(msg, &xudt->variable_calling, false, xua, 
SUA_IEI_SRC_ADDR);
        sccp_add_variable_part(msg, &xudt->variable_data, xua, SUA_IEI_DATA);
        /* Optional Part */
-       return xua_ies_to_sccp_opts(msg, &xudt->optional_start, xudt->type, 
xua);
+       return xua_ies_to_sccp_opts(msg, &xudt->optional_start, false, 
xudt->type, xua);
+}
+
+/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
+static struct xua_msg *sccp_to_xua_ludt(struct msgb *msg, struct xua_msg *xua)
+{
+       struct sccp_data_long_unitdata *ludt = (struct sccp_data_long_unitdata 
*)msg->l2h;
+
+       /* Fixed Part */
+       xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, ludt->proto_class);
+       xua_msg_add_u32(xua, SUA_IEI_S7_HOP_CTR, ludt->hop_counter);
+       /* Variable Part */
+       if (!sccp_longptr_part_consistent(msg, (uint8_t 
*)&ludt->variable_called, false))
+               return NULL;
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, (uint8_t 
*)&ludt->variable_called, true);
+       if (!sccp_longptr_part_consistent(msg, (uint8_t 
*)&ludt->variable_calling, false))
+               return NULL;
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, (uint8_t 
*)&ludt->variable_calling, true);
+       if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludt->variable_data, 
true))
+               return NULL;
+       sccp_longdata_to_sua_ptr(xua, SUA_IEI_DATA, (uint8_t 
*)&ludt->variable_data);
+       /* Optional Part */
+       return sccp_to_xua_opt(msg, (uint8_t *)&ludt->optional_start, true, 
xua);
+}
+
+static int sua_to_sccp_ludt(struct msgb *msg, const struct xua_msg *xua)
+{
+       struct sccp_data_long_unitdata *ludt;
+       ludt = (struct sccp_data_long_unitdata *) msgb_put(msg, sizeof(*ludt));
+
+       /* Fixed Part */
+       ludt->type = SCCP_MSG_TYPE_LUDT;
+       ludt->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
+       ludt->hop_counter = xua_msg_get_u32(xua, SUA_IEI_S7_HOP_CTR);
+       /* Variable Part */
+       sccp_add_var_addr(msg, (uint8_t *)&ludt->variable_called, true, xua, 
SUA_IEI_DEST_ADDR);
+       sccp_add_var_addr(msg, (uint8_t *)&ludt->variable_calling, true, xua, 
SUA_IEI_SRC_ADDR);
+       sccp_add_long_variable_part(msg, (uint8_t *)&ludt->variable_data, xua, 
SUA_IEI_DATA);
+       /* Optional Part */
+       return xua_ies_to_sccp_opts(msg, (uint8_t *)&ludt->optional_start, 
true, ludt->type, xua);
 }

 /*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
@@ -1311,10 +1479,10 @@
        /* Variable Part */
        if (!sccp_ptr_part_consistent(msg, &udts->variable_called))
                return NULL;
-       sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &udts->variable_called);
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &udts->variable_called, 
false);
        if (!sccp_ptr_part_consistent(msg, &udts->variable_calling))
                return NULL;
-       sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &udts->variable_calling);
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &udts->variable_calling, 
false);
        if (!sccp_ptr_part_consistent(msg, &udts->variable_data))
                return NULL;
        sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &udts->variable_data);
@@ -1323,11 +1491,18 @@
 }

 static int sua_to_sccp_xudts(struct msgb *msg, const struct xua_msg *xua);
+static int sua_to_sccp_ludts(struct msgb *msg, const struct xua_msg *xua);

 static int sua_to_sccp_udts(struct msgb *msg, const struct xua_msg *xua)
 {
        struct sccp_data_unitdata_service *udts;

+       /* Use LUDTS if length exceeds 255 (single byte length field) */
+       /* TODO: start using LUDTS sooner if called/calling party contain GT,
+        * see Q.715 Section 8.3.2 */
+       if (xua_msg_get_len(xua, SUA_IEI_DATA) > 255)
+               return sua_to_sccp_ludts(msg, xua);
+
        /* Use XUDTS if we have a hop counter */
        if (xua_msg_find_tag(xua, SUA_IEI_S7_HOP_CTR))
                return sua_to_sccp_xudts(msg, xua);
@@ -1338,8 +1513,8 @@
        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_var_addr(msg, &udts->variable_called, false, xua, 
SUA_IEI_DEST_ADDR);
+       sccp_add_var_addr(msg, &udts->variable_calling, false, xua, 
SUA_IEI_SRC_ADDR);
        sccp_add_variable_part(msg, &udts->variable_data, xua, SUA_IEI_DATA);
        return 0;
 }
@@ -1354,17 +1529,17 @@
        xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RETURN | 
xudts->return_cause);
        xua_msg_add_u32(xua, SUA_IEI_S7_HOP_CTR, xudts->hop_counter);
        /* Variable Part */
-       if (!sccp_ptr_part_consistent(msg, &xudts->variable_called))
+       if (!sccp_ptr_part_consistent(msg, (uint8_t *)&xudts->variable_called))
                return NULL;
-       sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &xudts->variable_called);
-       if (!sccp_ptr_part_consistent(msg, &xudts->variable_calling))
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &xudts->variable_called, 
false);
+       if (!sccp_ptr_part_consistent(msg, (uint8_t *)&xudts->variable_calling))
                return NULL;
-       sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &xudts->variable_calling);
-       if (!sccp_ptr_part_consistent(msg, &xudts->variable_data))
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &xudts->variable_calling, 
false);
+       if (!sccp_ptr_part_consistent(msg, (uint8_t *)&xudts->variable_data))
                return NULL;
        sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &xudts->variable_data);
        /* Optional Part */
-       return sccp_to_xua_opt(msg, &xudts->optional_start, xua);
+       return sccp_to_xua_opt(msg, &xudts->optional_start, false, xua);
 }

 static int sua_to_sccp_xudts(struct msgb *msg, const struct xua_msg *xua)
@@ -1377,11 +1552,51 @@
        xudts->return_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
        xudts->hop_counter = xua_msg_get_u32(xua, SUA_IEI_S7_HOP_CTR);
        /* Variable Part */
-       sccp_add_var_addr(msg, &xudts->variable_called, xua, SUA_IEI_DEST_ADDR);
-       sccp_add_var_addr(msg, &xudts->variable_calling, xua, SUA_IEI_SRC_ADDR);
+       sccp_add_var_addr(msg, &xudts->variable_called, false, xua, 
SUA_IEI_DEST_ADDR);
+       sccp_add_var_addr(msg, &xudts->variable_calling, false, xua, 
SUA_IEI_SRC_ADDR);
        sccp_add_variable_part(msg, &xudts->variable_data, xua, SUA_IEI_DATA);
        /* Optional Part */
-       return xua_ies_to_sccp_opts(msg, &xudts->optional_start, xudts->type, 
xua);
+       return xua_ies_to_sccp_opts(msg, &xudts->optional_start, false, 
xudts->type, xua);
+}
+
+/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
+static struct xua_msg *sccp_to_xua_ludts(const struct msgb *msg, struct 
xua_msg *xua)
+{
+       const struct sccp_data_long_unitdata_service *ludts;
+       ludts = (const struct sccp_data_long_unitdata_service *)msg->l2h;
+
+       /* Fixed Part */
+       xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RETURN | 
ludts->return_cause);
+       xua_msg_add_u32(xua, SUA_IEI_S7_HOP_CTR, ludts->hop_counter);
+       /* Variable Part */
+       if (!sccp_longptr_part_consistent(msg, (uint8_t 
*)&ludts->variable_called, false))
+               return NULL;
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, (uint8_t 
*)&ludts->variable_called, true);
+       if (!sccp_longptr_part_consistent(msg, (uint8_t 
*)&ludts->variable_calling, false))
+               return NULL;
+       sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, (uint8_t 
*)&ludts->variable_calling, true);
+       if (!sccp_longptr_part_consistent(msg, (uint8_t 
*)&ludts->variable_data, true))
+               return NULL;
+       sccp_longdata_to_sua_ptr(xua, SUA_IEI_DATA, (uint8_t 
*)&ludts->variable_data);
+       /* Optional Part */
+       return sccp_to_xua_opt(msg, (uint8_t *)&ludts->optional_start, true, 
xua);
+}
+
+static int sua_to_sccp_ludts(struct msgb *msg, const struct xua_msg *xua)
+{
+       struct sccp_data_long_unitdata_service *ludts;
+       ludts = (struct sccp_data_long_unitdata_service *) msgb_put(msg, 
sizeof(*ludts));
+
+       /* Fixed Part */
+       ludts->type = SCCP_MSG_TYPE_LUDTS;
+       ludts->return_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
+       ludts->hop_counter = xua_msg_get_u32(xua, SUA_IEI_S7_HOP_CTR);
+       /* Variable Part */
+       sccp_add_var_addr(msg, (uint8_t *)&ludts->variable_called, true, xua, 
SUA_IEI_DEST_ADDR);
+       sccp_add_var_addr(msg, (uint8_t *)&ludts->variable_calling, true, xua, 
SUA_IEI_SRC_ADDR);
+       sccp_add_long_variable_part(msg, (uint8_t *)&ludts->variable_data, xua, 
SUA_IEI_DATA);
+       /* Optional Part */
+       return xua_ies_to_sccp_opts(msg, (uint8_t *)&ludts->optional_start, 
true, ludts->type, xua);
 }

 /*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
@@ -1526,6 +1741,16 @@
                if (!sccp_to_xua_xudts(msg, xua))
                        goto malformed;
                return xua;
+       case SCCP_MSG_TYPE_LUDT:
+               xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT);
+               if (!sccp_to_xua_ludt(msg, xua))
+                       goto malformed;
+               return xua;
+       case SCCP_MSG_TYPE_LUDTS:
+               xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDR);
+               if (!sccp_to_xua_ludts(msg, xua))
+                       goto malformed;
+               return xua;
        /* Unsupported Message Types */
        case SCCP_MSG_TYPE_DT2:
        case SCCP_MSG_TYPE_AK:
@@ -1533,8 +1758,6 @@
        case SCCP_MSG_TYPE_EA:
        case SCCP_MSG_TYPE_RSR:
        case SCCP_MSG_TYPE_RSC:
-       case SCCP_MSG_TYPE_LUDT:
-       case SCCP_MSG_TYPE_LUDTS:
                LOGP(DLSUA, LOGL_ERROR, "Unsupported SCCP message %s\n",
                        osmo_sccp_msg_type_name(msg->l2h[0]));
                xua_msg_free(xua);
diff --git a/src/xua_msg.c b/src/xua_msg.c
index 443b73e..5671e1a 100644
--- a/src/xua_msg.c
+++ b/src/xua_msg.c
@@ -330,6 +330,14 @@
        return xua_msg_part_get_str(part);
 }

+int xua_msg_get_len(const struct xua_msg *xua, uint16_t iei)
+{
+       struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
+       if (!part)
+               return -1;
+       return part->len;
+}
+
 void xua_part_add_gt(struct msgb *msg, const struct osmo_sccp_gt *gt)
 {
        uint16_t *len_ptr;
diff --git a/tests/xua/sccp_test_data.c b/tests/xua/sccp_test_data.c
index c7c8f27..758b6fc 100644
--- a/tests/xua/sccp_test_data.c
+++ b/tests/xua/sccp_test_data.c
@@ -100,3 +100,12 @@
        0x01, 0x41, 0x84, 0x01, 0x04, 0x30, 0x03, 0x82,
        0x01, 0x18, 0x00, 0x00, 0x00, 0x00
 };
+
+/* SCCP LUDT message containing a RANAP RESET */
+const uint8_t ludt_ranap_reset[40] = {
+       0x13, 0x00, 0x0f, 0x07, 0x00, 0x0a, 0x00, 0x0d,
+       0x00, 0x00, 0x00, 0x04, 0x43, 0xc4, 0x04, 0x8e,
+       0x04, 0x43, 0xe9, 0x03, 0x8e, 0x11, 0x00, 0x00,
+       0x09, 0x00, 0x0d, 0x00, 0x00, 0x02, 0x00, 0x04,
+       0x40, 0x01, 0x42, 0x00, 0x03, 0x00, 0x01, 0x00
+};
diff --git a/tests/xua/sccp_test_data.h b/tests/xua/sccp_test_data.h
index 3d70549..3fb2e0b 100644
--- a/tests/xua/sccp_test_data.h
+++ b/tests/xua/sccp_test_data.h
@@ -12,3 +12,4 @@
 extern const uint8_t bssmap_released[14];
 extern const uint8_t bssmap_release_complete[7];
 extern const uint8_t tcap_global_title[183];
+extern const uint8_t ludt_ranap_reset[40];
diff --git a/tests/xua/xua_test.c b/tests/xua/xua_test.c
index 628d860..f883099 100644
--- a/tests/xua/xua_test.c
+++ b/tests/xua/xua_test.c
@@ -501,6 +501,18 @@
                        },
                },
        },
+{
+               .name = "LUDT-RANAP_RELEASE",
+               .sccp = PANDSIZ(ludt_ranap_reset),
+               .sua = {
+                       .hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
+                       .parts = {
+                               PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
+                               PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap),
+                               PARTARR(SUA_IEI_SRC_ADDR, sua_addr_ssn_bssmap),
+                       },
+               },
+       },
 };

 static void test_sccp2sua_case(const struct sccp2sua_testcase *tcase)
diff --git a/tests/xua/xua_test.ok b/tests/xua/xua_test.ok
index cb6aab1..a8aa830 100644
--- a/tests/xua/xua_test.ok
+++ b/tests/xua/xua_test.ok
@@ -92,6 +92,14 @@
 Decoded SUA: HDR=(CL:CLDT,V=0,LEN=0), PART(T=Protocol Class,L=4,D=00000081), 
PART(T=Destination 
Address,L=32,D=0001000580010014000000040a00010453840900170000008003000800000007),
 PART(T=Source 
Address,L=32,D=0001000580010014000000040c00010444872000206500008003000800000006),
 
PART(T=Data,L=154,D=6581974804260001984904510103df6c8188a181850201440201073080a780a08004012b30803012830110840107850791445776671697860120300682011884010400000000a306040142840105a306040151840105a306040131840105a309040112840105820102a309040111840105810101a306040114840100a30b0401418401043003830110a30b040141840104300382011800000000)
 Re-Encoding decoded SUA to SCCP
 SCCP Output: [L2]> 09 81 03 0d 18 0a 12 07 00 12 04 53 84 09 00 17 0b 12 06 00 
12 04 44 87 20 00 20 65 9a 65 81 97 48 04 26 00 01 98 49 04 51 01 03 df 6c 81 
88 a1 81 85 02 01 44 02 01 07 30 80 a7 80 a0 80 04 01 2b 30 80 30 12 83 01 10 
84 01 07 85 07 91 44 57 76 67 16 97 86 01 20 30 06 82 01 18 84 01 04 00 00 00 
00 a3 06 04 01 42 84 01 05 a3 06 04 01 51 84 01 05 a3 06 04 01 31 84 01 05 a3 
09 04 01 12 84 01 05 82 01 02 a3 09 04 01 11 84 01 05 81 01 01 a3 06 04 01 14 
84 01 00 a3 0b 04 01 41 84 01 04 30 03 83 01 10 a3 0b 04 01 41 84 01 04 30 03 
82 01 18 00 00 00 00
+
+=> LUDT-RANAP_RELEASE
+SCCP Input: [L2]> 13 00 0f 07 00 0a 00 0d 00 00 00 04 43 c4 04 8e 04 43 e9 03 
8e 11 00 00 09 00 0d 00 00 02 00 04 40 01 42 00 03 00 01 00
+Transcoding message SCCP -> XUA
+Decoded SUA: HDR=(CL:CLDT,V=0,LEN=0), PART(T=Protocol Class,L=4,D=00000000), 
PART(T=SS7 Hop Counter,L=4,D=0000000f), PART(T=Destination 
Address,L=20,D=0002000380020008000004c4800300080000008e), PART(T=Source 
Address,L=20,D=0002000380020008000003e9800300080000008e), 
PART(T=Data,L=17,D=0009000d00000200044001420003000100)
+Re-Encoding decoded SUA to SCCP
+SCCP Output: [L2]> 11 00 0f 04 08 0c 00 04 43 c4 04 8e 04 43 e9 03 8e 11 00 09 
00 0d 00 00 02 00 04 40 01 42 00 03 00 01 00
+Input != re-encoded output!
 Parsing M3UA Message
 Parsing Nested M3UA Routing Key IE
 Testing SCCP Address Encode/Decode

--
To view, visit https://gerrit.osmocom.org/c/libosmo-sccp/+/34467?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: libosmo-sccp
Gerrit-Branch: master
Gerrit-Change-Id: Ic91abfc921f5e4f36045bfa325333112cddd9fa6
Gerrit-Change-Number: 34467
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pes...@sysmocom.de>
Gerrit-CC: laforge <lafo...@osmocom.org>
Gerrit-MessageType: newchange

Reply via email to