fixeria has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/libosmo-gprs/+/29482 )


Change subject: llc: implement Exchange Identification (XID) codec
......................................................................

llc: implement Exchange Identification (XID) codec

Change-Id: I16b3f7ab82c489144098aad0dccc4c67d8382c67
---
M include/osmocom/gprs/llc/llc.h
M src/llc/llc_xid.c
M tests/llc/pdu_codec_test.c
M tests/llc/pdu_codec_test.ok
4 files changed, 346 insertions(+), 0 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/libosmo-gprs refs/changes/82/29482/1

diff --git a/include/osmocom/gprs/llc/llc.h b/include/osmocom/gprs/llc/llc.h
index 0e4531e..a7cd521 100644
--- a/include/osmocom/gprs/llc/llc.h
+++ b/include/osmocom/gprs/llc/llc.h
@@ -95,6 +95,17 @@
        return get_value_string(osmo_gprs_llc_xid_type_names, val);
 }

+struct osmo_gprs_llc_xid_field {
+       enum osmo_gprs_llc_xid_type type;
+       /* Fixed-length */
+       uint32_t val;
+       /* Variable-length */
+       struct {
+               const uint8_t *val;
+               uint8_t val_len;
+       } var;
+};
+
 /* Section 4.5.2 Logical Link States + Annex C.2 */
 enum osmo_gprs_llc_lle_state {
        OSMO_GPRS_LLC_LLES_UNASSIGNED   = 1,    /* No TLLI yet */
@@ -158,4 +169,12 @@

 uint32_t osmo_gprs_llc_fcs(const uint8_t *data, size_t len);

+bool osmo_gprs_llc_xid_field_is_valid(const struct osmo_gprs_llc_xid_field 
*field);
+int osmo_gprs_llc_xid_decode(struct osmo_gprs_llc_xid_field *fields,
+                            unsigned int max_fields,
+                            const uint8_t *data, size_t data_len);
+int osmo_gprs_llc_xid_encode(struct msgb *msg,
+                            const struct osmo_gprs_llc_xid_field *fields,
+                            unsigned int num_fields);
+
 void osmo_gprs_llc_set_log_cat(int cat);
diff --git a/src/llc/llc_xid.c b/src/llc/llc_xid.c
index fb84137..ef42e4d 100644
--- a/src/llc/llc_xid.c
+++ b/src/llc/llc_xid.c
@@ -20,10 +20,17 @@
  *
  */

+#include <errno.h>
+#include <stdint.h>
+
+#include <osmocom/core/msgb.h>
 #include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>

 #include <osmocom/gprs/llc/llc.h>

+extern int g_log_cat;
+
 const struct value_string osmo_gprs_llc_xid_type_names[] = {
        { OSMO_GPRS_LLC_XID_T_VERSION,          "LLC-Version" },
        { OSMO_GPRS_LLC_XID_T_IOV_UI,           "IOV-UI" },
@@ -43,3 +50,199 @@
        { OSMO_GPRS_LLC_XID_T_MAC_IOV_UI,       "MAC-IOV-UI" },
        { 0, NULL }
 };
+
+/* Table 6: LLC layer parameter negotiation */
+static const struct {
+       bool var_len;
+       uint8_t len;
+       unsigned int min;
+       unsigned int max;
+} gprs_llc_xid_desc[] = {
+       [OSMO_GPRS_LLC_XID_T_VERSION] = { .len = 1, .min = 0, .max = 15 },
+       [OSMO_GPRS_LLC_XID_T_IOV_UI] = { .len = 4, .min = 0, .max = UINT32_MAX 
},
+       [OSMO_GPRS_LLC_XID_T_IOV_I] = { .len = 4, .min = 0, .max = UINT32_MAX },
+       [OSMO_GPRS_LLC_XID_T_T200] = { .len = 2, .min = 0, .max = 4095 },
+       [OSMO_GPRS_LLC_XID_T_N200] = { .len = 1, .min = 1, .max = 15 },
+       [OSMO_GPRS_LLC_XID_T_N201_U] = { .len = 2, .min = 140, .max = 1520 },
+       [OSMO_GPRS_LLC_XID_T_N201_I] = { .len = 2, .min = 140, .max = 1520 },
+       [OSMO_GPRS_LLC_XID_T_mD] = { .len = 2, .min = 9, .max = 24320 },
+       [OSMO_GPRS_LLC_XID_T_mU] = { .len = 2, .min = 9, .max = 24320 },
+       [OSMO_GPRS_LLC_XID_T_kD] = { .len = 1, .min = 1, .max = 255 },
+       [OSMO_GPRS_LLC_XID_T_kU] = { .len = 1, .min = 1, .max = 255 },
+       [OSMO_GPRS_LLC_XID_T_L3_PAR] = { .var_len = true },
+       [OSMO_GPRS_LLC_XID_T_RESET] = { .len = 0 },
+       [OSMO_GPRS_LLC_XID_T_IIOV_UI] = { .len = 4, .min = 0, .max = UINT32_MAX 
},
+       [OSMO_GPRS_LLC_XID_T_IIOV_UI_CNT] = { .len = 1, .min = 1, .max = 255 },
+       [OSMO_GPRS_LLC_XID_T_MAC_IOV_UI] = { .len = 4, .min = 0, .max = 
UINT32_MAX },
+};
+
+bool osmo_gprs_llc_xid_field_is_valid(const struct osmo_gprs_llc_xid_field 
*field)
+{
+       if (field->type >= ARRAY_SIZE(gprs_llc_xid_desc)) {
+               LOGP(g_log_cat, LOGL_ERROR,
+                    "Unknown XID field type 0x%02x\n", field->type);
+               return false;
+       }
+
+       if (gprs_llc_xid_desc[field->type].var_len)
+               return true;
+
+       if (field->val < gprs_llc_xid_desc[field->type].min) {
+               LOGP(g_log_cat, LOGL_ERROR,
+                    "XID field %s value=%u < min=%u\n",
+                    osmo_gprs_llc_xid_type_name(field->type),
+                    field->val, gprs_llc_xid_desc[field->type].min);
+               return false;
+       }
+
+       if (field->val > gprs_llc_xid_desc[field->type].max) {
+               LOGP(g_log_cat, LOGL_ERROR,
+                    "XID field %s value=%u > max=%u\n",
+                    osmo_gprs_llc_xid_type_name(field->type),
+                    field->val, gprs_llc_xid_desc[field->type].max);
+               return false;
+       }
+
+       return true;
+}
+
+int osmo_gprs_llc_xid_encode(struct msgb *msg,
+                            const struct osmo_gprs_llc_xid_field *fields,
+                            unsigned int num_fields)
+{
+       for (unsigned int i = 0; i < num_fields; i++) {
+               const struct osmo_gprs_llc_xid_field *field = &fields[i];
+               uint8_t *hdr, len;
+
+               if (!osmo_gprs_llc_xid_field_is_valid(field))
+                       return -EINVAL;
+
+               /* XID field type */
+               hdr = msgb_put(msg, 1);
+               hdr[0] = (field->type & 0x1f) << 2;
+
+               /* XID field length */
+               if (gprs_llc_xid_desc[field->type].var_len)
+                       len = field->var.val_len;
+               else
+                       len = gprs_llc_xid_desc[field->type].len;
+
+               if (len == 0)
+                       continue;
+               if (len < 4) {
+                       hdr[0] |= len;
+               } else {
+                       msgb_put(msg, 1);
+                       hdr[0] |= (1 << 7); /* XL=1 */
+                       hdr[0] |= (len >> 6) & 0x03;
+                       hdr[1]  = (len << 2) & 0xff;
+               }
+
+               /* XID field value (variable length) */
+               if (gprs_llc_xid_desc[field->type].var_len) {
+                       memcpy(msgb_put(msg, len), field->var.val, len);
+               } else {
+                       switch (len) {
+                       case 1:
+                               msgb_put_u8(msg, field->val);
+                               break;
+                       case 2:
+                               osmo_store16be(field->val, msgb_put(msg, 2));
+                               break;
+                       case 4:
+                               osmo_store32be(field->val, msgb_put(msg, 4));
+                               break;
+                       default:
+                               /* Shall not happen */
+                               OSMO_ASSERT(0);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int osmo_gprs_llc_xid_decode(struct osmo_gprs_llc_xid_field *fields,
+                            unsigned int max_fields,
+                            const uint8_t *data, size_t data_len)
+{
+       const uint8_t *ptr = &data[0];
+       unsigned int num_fields = 0;
+
+#define check_len(len, text) \
+       do { \
+               if (data_len < (len)) { \
+                       LOGP(g_log_cat, LOGL_ERROR, "Failed to parse XID: 
%s\n", text); \
+                       return -EINVAL; \
+               } \
+       } while (0)
+
+       while (data_len > 0) {
+               struct osmo_gprs_llc_xid_field *field = &fields[num_fields++];
+               uint8_t len;
+
+               if (num_fields > max_fields) {
+                       LOGP(g_log_cat, LOGL_ERROR,
+                            "Failed to parse XID: too many fields\n");
+                       return -ENOMEM;
+               }
+
+               check_len(1, "short read at XID header");
+               data_len -= 1;
+
+               /* XID field type */
+               field->type = (*ptr >> 2) & 0x1f;
+               if (field->type >= ARRAY_SIZE(gprs_llc_xid_desc)) {
+                       LOGP(g_log_cat, LOGL_ERROR,
+                            "Failed to parse XID: unknown type 0x%02x\n", 
field->type);
+                       return -EINVAL;
+               }
+
+               /* XID field length */
+               if (*ptr & (1 << 7)) {
+                       check_len(1, "short read");
+                       data_len -= 1;
+                       len  = (*(ptr++) & 0x07) << 6;
+                       len |= (*(ptr++) >> 2);
+               } else {
+                       len = *(ptr++) & 0x03;
+               }
+
+               if (len == 0)
+                       continue;
+
+               check_len(len, "short read at XID payload");
+               data_len -= len;
+
+               /* XID field value (variable length) */
+               if (gprs_llc_xid_desc[field->type].var_len) {
+                       field->var.val_len = len;
+                       field->var.val = ptr;
+               } else {
+                       switch (len) {
+                       case 1:
+                               field->val = *ptr;
+                               break;
+                       case 2:
+                               field->val = osmo_load16be(ptr);
+                               break;
+                       case 4:
+                               field->val = osmo_load32be(ptr);
+                               break;
+                       default:
+                               LOGP(g_log_cat, LOGL_ERROR,
+                                    "Failed to parse XID: weird field 
length=%u\n", len);
+                               return -EINVAL;
+                       }
+               }
+
+               if (!osmo_gprs_llc_xid_field_is_valid(field))
+                       return -EINVAL;
+
+               ptr += len;
+       }
+
+#undef check_len
+
+       return num_fields;
+}
diff --git a/tests/llc/pdu_codec_test.c b/tests/llc/pdu_codec_test.c
index ed4e8bd..0577205 100644
--- a/tests/llc/pdu_codec_test.c
+++ b/tests/llc/pdu_codec_test.c
@@ -77,6 +77,105 @@
        printf("\n");
 }

+static void print_xid_fields(const struct osmo_gprs_llc_xid_field *fields,
+                            unsigned int num_fields)
+{
+       for (unsigned int i = 0; i < num_fields; i++) {
+               const struct osmo_gprs_llc_xid_field *field = &fields[i];
+
+               printf("  field[%02d]: type=0x%02x (%s)",
+                      i, field->type, 
osmo_gprs_llc_xid_type_name(field->type));
+               if (field->type == OSMO_GPRS_LLC_XID_T_L3_PAR) {
+                       printf(" data[] (len=%u): %s\n", field->var.val_len,
+                              osmo_hexdump_nospc(field->var.val, 
field->var.val_len));
+               } else {
+                       printf(" val=%u\n", field->val);
+               }
+       }
+}
+
+static void test_xid_dec_enc(void)
+{
+       static const char *testData[] = {
+               /* TODO: more test vectors */
+               "01001601f41a05df",
+       };
+
+       struct msgb *msg = msgb_alloc(1024, "LLC-XID");
+       OSMO_ASSERT(msg != NULL);
+
+       for (unsigned int i = 0; i < ARRAY_SIZE(testData); i++) {
+               struct osmo_gprs_llc_xid_field fields[16] = { 0 };
+               uint8_t xid[256];
+               size_t xid_len;
+               int rc;
+
+               printf("%s(): decoding testData[%u] = %s\n", __func__, i, 
testData[i]);
+
+               rc = osmo_hexparse(testData[i], &xid[0], sizeof(xid));
+               xid_len = strlen(testData[i]) / 2;
+               OSMO_ASSERT(rc == xid_len);
+
+               rc = osmo_gprs_llc_xid_decode(fields, ARRAY_SIZE(fields),
+                                             &xid[0], xid_len);
+               printf("  osmo_gprs_llc_xid_decode() returns %d\n", rc);
+
+               if (rc > 0)
+                       print_xid_fields(&fields[0], rc);
+               else
+                       continue;
+
+               printf("%s(): encoding decoded testData[%u]\n", __func__, i);
+
+               msgb_reset(msg);
+               rc = osmo_gprs_llc_xid_encode(msg, fields, rc);
+               printf("  osmo_gprs_llc_xid_encode() returns %d\n", rc);
+               printf("  osmo_gprs_llc_xid_encode(): %s\n", 
osmo_hexdump_nospc(msg->data, msg->len));
+               printf("  memcmp() returns %d\n", memcmp(&xid, msg->data, 
xid_len));
+       }
+
+       msgb_free(msg);
+       printf("\n");
+}
+
+static void test_xid_enc_dec(void)
+{
+       struct osmo_gprs_llc_xid_field dec_fields[16] = { 0 };
+       int rc;
+
+       const uint8_t l3_params[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+       const struct osmo_gprs_llc_xid_field test_fields[] = {
+               { .type = OSMO_GPRS_LLC_XID_T_VERSION,  .val = 0 },
+               { .type = OSMO_GPRS_LLC_XID_T_T200,     .val = 4095 },
+               { .type = OSMO_GPRS_LLC_XID_T_N200,     .val = 10 },
+               { .type = OSMO_GPRS_LLC_XID_T_IOV_I,    .val = 0x42424242 },
+               { .type = OSMO_GPRS_LLC_XID_T_IOV_UI,   .val = 0xdeadbeef },
+               { .type = OSMO_GPRS_LLC_XID_T_L3_PAR,
+                 .var = { .val_len = sizeof(l3_params), .val = &l3_params[0] } 
},
+               { .type = OSMO_GPRS_LLC_XID_T_RESET },
+       };
+
+       struct msgb *msg = msgb_alloc(1024, "LLC-XID");
+       OSMO_ASSERT(msg != NULL);
+
+       printf("%s(): encoding hand-crafted testData\n", __func__);
+
+       rc = osmo_gprs_llc_xid_encode(msg, &test_fields[0], 
ARRAY_SIZE(test_fields));
+       printf("  osmo_gprs_llc_xid_encode() returns %d\n", rc);
+       printf("  osmo_gprs_llc_xid_encode(): %s\n", 
osmo_hexdump_nospc(msg->data, msg->len));
+
+       printf("%s(): decoding encoded hand-crafted testData\n", __func__);
+
+       rc = osmo_gprs_llc_xid_decode(&dec_fields[0], ARRAY_SIZE(dec_fields),
+                                     msg->data, msg->len);
+       printf("  osmo_gprs_llc_xid_decode() returns %d\n", rc);
+       if (rc > 0)
+               print_xid_fields(&dec_fields[0], rc);
+
+       msgb_free(msg);
+       printf("\n");
+}
+
 static const struct log_info_cat test_log_categories[] = { };
 static const struct log_info test_log_info = {
        .cat = test_log_categories,
@@ -97,6 +196,8 @@
        log_set_use_color(osmo_stderr_target, 0);

        test_pdu_dec_enc();
+       test_xid_dec_enc();
+       test_xid_enc_dec();

        talloc_free(tall_ctx);
 }
diff --git a/tests/llc/pdu_codec_test.ok b/tests/llc/pdu_codec_test.ok
index 691e983..d374f19 100644
--- a/tests/llc/pdu_codec_test.ok
+++ b/tests/llc/pdu_codec_test.ok
@@ -38,3 +38,26 @@
   osmo_gprs_llc_pdu_encode(): 03fb1604d216f984
   memcmp() returns 0

+test_xid_dec_enc(): decoding testData[0] = 01001601f41a05df
+  osmo_gprs_llc_xid_decode() returns 3
+  field[00]: type=0x00 (LLC-Version) val=0
+  field[01]: type=0x05 (N201-U) val=500
+  field[02]: type=0x06 (N201-I) val=1503
+test_xid_dec_enc(): encoding decoded testData[0]
+  osmo_gprs_llc_xid_encode() returns 0
+  osmo_gprs_llc_xid_encode(): 01001601f41a05df
+  memcmp() returns 0
+
+test_xid_enc_dec(): encoding hand-crafted testData
+  osmo_gprs_llc_xid_encode() returns 0
+  osmo_gprs_llc_xid_encode(): 
01000e0fff110a8810424242428410deadbeefac1811223344556630
+test_xid_enc_dec(): decoding encoded hand-crafted testData
+  osmo_gprs_llc_xid_decode() returns 7
+  field[00]: type=0x00 (LLC-Version) val=0
+  field[01]: type=0x03 (T200) val=4095
+  field[02]: type=0x04 (N200) val=10
+  field[03]: type=0x02 (IOV-I) val=1111638594
+  field[04]: type=0x01 (IOV-UI) val=3735928559
+  field[05]: type=0x0b (L3-Params) data[] (len=6): 112233445566
+  field[06]: type=0x0c (Reset) val=0
+

--
To view, visit https://gerrit.osmocom.org/c/libosmo-gprs/+/29482
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: libosmo-gprs
Gerrit-Branch: master
Gerrit-Change-Id: I16b3f7ab82c489144098aad0dccc4c67d8382c67
Gerrit-Change-Number: 29482
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <[email protected]>
Gerrit-MessageType: newchange

Reply via email to