Review at  https://gerrit.osmocom.org/7787

add gsm0808_cell_id_list_add() to combine two cell identifier lists

This will be used by the upcoming neighbor_ident API in osmo-bsc, where the vty
interface allows composing neihbor BSS cell identifier lists, and we want to
allow adding individual items from individual user commands.

It will also be useful to accumulate cell identifiers in case a subscriber sees
multiple alternative cells from a neighboring BSS, and we want to pass these on
to the MSC in a Handover Required.

Related: OS#2283 (inter-BSC HO, BSC side)
Change-Id: I5781f5fa5339c92ab2e2620489b002829d206925
---
M include/osmocom/gsm/gsm0808_utils.h
M src/gsm/gsm0808_utils.c
M src/gsm/libosmogsm.map
M tests/gsm0808/gsm0808_test.c
M tests/gsm0808/gsm0808_test.ok
5 files changed, 361 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/87/7787/1

diff --git a/include/osmocom/gsm/gsm0808_utils.h 
b/include/osmocom/gsm/gsm0808_utils.h
index 363fc39..34eae43 100644
--- a/include/osmocom/gsm/gsm0808_utils.h
+++ b/include/osmocom/gsm/gsm0808_utils.h
@@ -77,6 +77,7 @@
 int gsm0808_dec_cell_id_list(struct gsm0808_cell_id_list *cil,
                             const uint8_t *elem, uint8_t len)
                             OSMO_DEPRECATED("use gsm0808_dec_cell_id_list2 
instead");
+int gsm0808_cell_id_list_add(struct gsm0808_cell_id_list2 *dst, const struct 
gsm0808_cell_id_list2 *src);
 int gsm0808_chan_type_to_speech_codec(uint8_t perm_spch);
 int gsm0808_speech_codec_from_chan_type(struct gsm0808_speech_codec *sc,
                                        uint8_t perm_spch);
diff --git a/src/gsm/gsm0808_utils.c b/src/gsm/gsm0808_utils.c
index e4872b8..996456e 100644
--- a/src/gsm/gsm0808_utils.c
+++ b/src/gsm/gsm0808_utils.c
@@ -909,6 +909,85 @@
        return (int)(elem - old_elem);
 }
 
+static bool same_cell_id_list_entries(const struct gsm0808_cell_id_list2 *a, 
int ai,
+                                     const struct gsm0808_cell_id_list2 *b, 
int bi)
+{
+       struct gsm0808_cell_id_list2 tmp = {
+               .id_discr = a->id_discr,
+               .id_list_len = 1,
+       };
+       uint8_t buf_a[32 + sizeof(struct msgb)];
+       uint8_t buf_b[32 + sizeof(struct msgb)];
+       struct msgb *msg_a = (void*)buf_a;
+       struct msgb *msg_b = (void*)buf_b;
+
+       msg_a->data_len = 32;
+       msg_b->data_len = 32;
+       msgb_reset(msg_a);
+       msgb_reset(msg_b);
+
+       if (a->id_discr != b->id_discr)
+               return false;
+       if (ai >= a->id_list_len
+           || bi >= b->id_list_len)
+               return false;
+
+       tmp.id_list[0] = a->id_list[ai];
+       gsm0808_enc_cell_id_list2(msg_a, &tmp);
+
+       tmp.id_list[0] = b->id_list[bi];
+       gsm0808_enc_cell_id_list2(msg_b, &tmp);
+
+       if (msg_a->len != msg_b->len)
+               return false;
+       if (memcmp(msg_a->data, msg_b->data, msg_a->len))
+               return false;
+
+       return true;
+}
+
+/*! Append entries from one Cell Identifier List to another.
+ * The cell identifier types must be identical between the two lists.
+ * \param dst[out]  Append entries to this list.
+ * \param src[in]   Append these entries to \a dst.
+ * \returns the nr of items added, or negative on error: -EINVAL if the 
id_discr mismatch
+ *   between the lists, -ENOSPC if the destination list does not have enough 
space. If an error is
+ *   returned, \a dst may have already been changed (particularly on -ENOSPC). 
Note that a return value
+ *   of zero may occur when the src->id_list_len is zero, or when all entries 
from \a src already exist
+ *   in \a dst, and does not indicate error per se. */
+int gsm0808_cell_id_list_add(struct gsm0808_cell_id_list2 *dst, const struct 
gsm0808_cell_id_list2 *src)
+{
+       int i, j;
+       int added = 0;
+
+       if (dst->id_list_len == 0
+           && dst->id_discr != CELL_IDENT_BSS)
+               dst->id_discr = src->id_discr;
+       else if (dst->id_discr != src->id_discr)
+               return -EINVAL;
+
+       for (i = 0; i < src->id_list_len; i++) {
+               /* don't add duplicate entries */
+               bool skip = false;
+               for (j = 0; j < dst->id_list_len; j++) {
+                       if (same_cell_id_list_entries(dst, j, src, i)) {
+                               skip = true;
+                               break;
+                       }
+               }
+               if (skip)
+                       continue;
+
+               if (dst->id_list_len >= ARRAY_SIZE(dst->id_list))
+                       return -ENOSPC;
+
+               dst->id_list[dst->id_list_len++] = src->id_list[i];
+               added ++;
+       }
+
+       return added;
+}
+
 /*! Convert the representation of the permitted speech codec identifier
  *  that is used in struct gsm0808_channel_type to the speech codec
  *  representation we use in struct gsm0808_speech_codec.
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index a6ea47d..d99121e 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -176,6 +176,7 @@
 gsm0808_enc_cell_id_list2;
 gsm0808_dec_cell_id_list;
 gsm0808_dec_cell_id_list2;
+gsm0808_cell_id_list_add;
 gsm0808_chan_type_to_speech_codec;
 gsm0808_speech_codec_from_chan_type;
 gsm0808_speech_codec_type_names;
diff --git a/tests/gsm0808/gsm0808_test.c b/tests/gsm0808/gsm0808_test.c
index 9730be2..f467099 100644
--- a/tests/gsm0808/gsm0808_test.c
+++ b/tests/gsm0808/gsm0808_test.c
@@ -28,6 +28,7 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <errno.h>
 
 #define VERIFY(msg, data, len)                                                 
\
        if (msgb_l3len(msg) != len) {                                   \
@@ -1063,6 +1064,198 @@
        msgb_free(msg);
 }
 
+static void print_cil(const struct gsm0808_cell_id_list2 *cil)
+{
+       unsigned int i;
+       if (!cil) {
+               printf("     cell_id_list == NULL\n");
+               return;
+       }
+       switch (cil->id_discr) {
+       case CELL_IDENT_WHOLE_GLOBAL:
+               printf("     cell_id_list cgi[%u] = {\n", cil->id_list_len);
+               for (i = 0; i < cil->id_list_len; i++)
+                       printf("       %2d: %s\n", i, 
osmo_cgi_name(&cil->id_list[i].global));
+               printf("     }\n");
+               break;
+       case CELL_IDENT_LAC:
+               printf("     cell_id_list lac[%u] = {\n", cil->id_list_len);
+               for (i = 0; i < cil->id_list_len; i++)
+                       printf("      %2d: %u\n", i, cil->id_list[i].lac);
+               printf("     }\n");
+               break;
+       case CELL_IDENT_BSS:
+               printf("     cell_id_list bss[%u]\n", cil->id_list_len);
+               break;
+       case CELL_IDENT_NO_CELL:
+               printf("     cell_id_list no_cell[%u]\n", cil->id_list_len);
+               break;
+       default:
+               printf("     Unimplemented id_disc\n");
+       }
+}
+
+void test_cell_id_list_add() {
+       const struct gsm0808_cell_id_list2 cgi1 = {
+               .id_discr = CELL_IDENT_WHOLE_GLOBAL,
+               .id_list_len = 1,
+               .id_list = {
+                       {
+                               .global = {
+                                       .lai = {
+                                               .plmn = { .mcc = 1, .mnc = 2, 
.mnc_3_digits = false },
+                                               .lac = 3,
+                                       },
+                                       .cell_identity = 4,
+                               }
+                       },
+               },
+       };
+
+       const struct gsm0808_cell_id_list2 cgi2 = {
+               .id_discr = CELL_IDENT_WHOLE_GLOBAL,
+               .id_list_len = 2,
+               .id_list = {
+                       {
+                               .global = {
+                                       .lai = {
+                                               .plmn = { .mcc = 1, .mnc = 2, 
.mnc_3_digits = true },
+                                               .lac = 3,
+                                       },
+                                       .cell_identity = 4,
+                               }
+                       },
+                       {
+                               .global = {
+                                       .lai = {
+                                               .plmn = { .mcc = 5, .mnc = 6, 
.mnc_3_digits = true },
+                                               .lac = 7,
+                                       },
+                                       .cell_identity = 8,
+                               }
+                       },
+               },
+       };
+
+       const struct gsm0808_cell_id_list2 cgi2a = {
+               .id_discr = CELL_IDENT_WHOLE_GLOBAL,
+               .id_list_len = 2,
+               .id_list = {
+                       {
+                               .global = cgi2.id_list[0].global
+                       },
+                       {
+                               .global = {
+                                       .lai = {
+                                               .plmn = { .mcc = 9, .mnc = 10, 
.mnc_3_digits = true },
+                                               .lac = 11,
+                                       },
+                                       .cell_identity = 12,
+                               }
+                       },
+               },
+       };
+
+       const struct gsm0808_cell_id_list2 cgi3 = {
+               .id_discr = CELL_IDENT_WHOLE_GLOBAL,
+               .id_list_len = 2,
+               .id_list = {
+                       {
+                               .global = {
+                                       .lai = {
+                                               .plmn = { .mcc = 13, .mnc = 14, 
.mnc_3_digits = true },
+                                               .lac = 15,
+                                       },
+                                       .cell_identity = 16,
+                               }
+                       },
+                       {
+                               .global = {
+                                       .lai = {
+                                               .plmn = { .mcc = 16, .mnc = 17, 
.mnc_3_digits = true },
+                                               .lac = 18,
+                                       },
+                                       .cell_identity = 19,
+                               }
+                       },
+               },
+       };
+
+
+       const struct gsm0808_cell_id_list2 lac1 = {
+               .id_discr = CELL_IDENT_LAC,
+               .id_list_len = 1,
+               .id_list = {
+                       {
+                               .lac = 123
+                       },
+               },
+       };
+
+       const struct gsm0808_cell_id_list2 lac2 = {
+               .id_discr = CELL_IDENT_LAC,
+               .id_list_len = 2,
+               .id_list = {
+                       {
+                               .lac = 456
+                       },
+                       {
+                               .lac = 789
+                       },
+               },
+       };
+
+       struct gsm0808_cell_id_list2 cil = {};
+
+       printf("------- %s\n", __func__);
+
+       print_cil(&cil);
+
+#define ADD_QUIET(other_cil, expect_rc) do { \
+               int rc = gsm0808_cell_id_list_add(&cil, &other_cil); \
+               printf("\ngsm0808_cell_id_list_add(&cil, &" #other_cil ") --> 
rc = %d\n", rc); \
+               OSMO_ASSERT(rc == expect_rc); \
+       } while(0)
+
+#define ADD(other_cil, expect_rc) ADD_QUIET(other_cil, expect_rc); 
print_cil(&cil)
+
+       ADD(lac1, 1);
+       ADD(lac1, 0);
+       ADD(lac2, 2);
+       ADD(lac2, 0);
+       ADD(cil, 0);
+       ADD(cgi1, -EINVAL);
+
+       printf("\ncan't add to BSS list\n");
+       cil.id_list_len = 0;
+       cil.id_discr = CELL_IDENT_BSS;
+       print_cil(&cil);
+       ADD(lac1, -EINVAL);
+
+       printf("\nother types (including NO_CELL) take on new type iff 
empty\n");
+       cil.id_list_len = 0;
+       cil.id_discr = CELL_IDENT_NO_CELL;
+       print_cil(&cil);
+       ADD(cgi1, 1);
+       ADD(cgi1, 0);
+       ADD(cgi2, 2);
+       ADD(cgi2, 0);
+
+       cil.id_list_len = GSM0808_CELL_ID_LIST2_MAXLEN - 1;
+       printf("\ncil.id_list_len = %u", cil.id_list_len);
+       ADD_QUIET(cgi2a, 1);
+       printf("cil.id_list_len = %u\n", cil.id_list_len);
+
+       cil.id_list_len = GSM0808_CELL_ID_LIST2_MAXLEN - 1;
+       printf("\ncil.id_list_len = %u", cil.id_list_len);
+       ADD_QUIET(cgi3, -ENOSPC);
+       printf("cil.id_list_len = %u", cil.id_list_len);
+       ADD_QUIET(cgi2a, -ENOSPC);
+       printf("cil.id_list_len = %u\n", cil.id_list_len);
+
+       printf("------- %s done\n", __func__);
+}
+
 int main(int argc, char **argv)
 {
        printf("Testing generation of GSM0808 messages\n");
@@ -1103,6 +1296,8 @@
        test_gsm0808_enc_dec_cell_id_list_multi_lac_and_ci();
        test_gsm0808_enc_dec_cell_id_list_multi_global();
 
+       test_cell_id_list_add();
+
        printf("Done\n");
        return EXIT_SUCCESS;
 }
diff --git a/tests/gsm0808/gsm0808_test.ok b/tests/gsm0808/gsm0808_test.ok
index e101d65..a049dec 100644
--- a/tests/gsm0808/gsm0808_test.ok
+++ b/tests/gsm0808/gsm0808_test.ok
@@ -19,4 +19,89 @@
 Testing creating Paging Request
 Testing creating DTAP
 Testing prepend DTAP
+------- test_cell_id_list_add
+     cell_id_list cgi[0] = {
+     }
+
+gsm0808_cell_id_list_add(&cil, &lac1) --> rc = 1
+     cell_id_list lac[1] = {
+       0: 123
+     }
+
+gsm0808_cell_id_list_add(&cil, &lac1) --> rc = 0
+     cell_id_list lac[1] = {
+       0: 123
+     }
+
+gsm0808_cell_id_list_add(&cil, &lac2) --> rc = 2
+     cell_id_list lac[3] = {
+       0: 123
+       1: 456
+       2: 789
+     }
+
+gsm0808_cell_id_list_add(&cil, &lac2) --> rc = 0
+     cell_id_list lac[3] = {
+       0: 123
+       1: 456
+       2: 789
+     }
+
+gsm0808_cell_id_list_add(&cil, &cil) --> rc = 0
+     cell_id_list lac[3] = {
+       0: 123
+       1: 456
+       2: 789
+     }
+
+gsm0808_cell_id_list_add(&cil, &cgi1) --> rc = -22
+     cell_id_list lac[3] = {
+       0: 123
+       1: 456
+       2: 789
+     }
+
+can't add to BSS list
+     cell_id_list bss[0]
+
+gsm0808_cell_id_list_add(&cil, &lac1) --> rc = -22
+     cell_id_list bss[0]
+
+other types (including NO_CELL) take on new type iff empty
+     cell_id_list no_cell[0]
+
+gsm0808_cell_id_list_add(&cil, &cgi1) --> rc = 1
+     cell_id_list cgi[1] = {
+        0: 001-02-3-4
+     }
+
+gsm0808_cell_id_list_add(&cil, &cgi1) --> rc = 0
+     cell_id_list cgi[1] = {
+        0: 001-02-3-4
+     }
+
+gsm0808_cell_id_list_add(&cil, &cgi2) --> rc = 2
+     cell_id_list cgi[3] = {
+        0: 001-02-3-4
+        1: 001-002-3-4
+        2: 005-006-7-8
+     }
+
+gsm0808_cell_id_list_add(&cil, &cgi2) --> rc = 0
+     cell_id_list cgi[3] = {
+        0: 001-02-3-4
+        1: 001-002-3-4
+        2: 005-006-7-8
+     }
+
+cil.id_list_len = 126
+gsm0808_cell_id_list_add(&cil, &cgi2a) --> rc = 1
+cil.id_list_len = 127
+
+cil.id_list_len = 126
+gsm0808_cell_id_list_add(&cil, &cgi3) --> rc = -28
+cil.id_list_len = 127
+gsm0808_cell_id_list_add(&cil, &cgi2a) --> rc = -28
+cil.id_list_len = 127
+------- test_cell_id_list_add done
 Done

-- 
To view, visit https://gerrit.osmocom.org/7787
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I5781f5fa5339c92ab2e2620489b002829d206925
Gerrit-PatchSet: 1
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Owner: Neels Hofmeyr <nhofm...@sysmocom.de>

Reply via email to