From d01ef75ab876d79c9a0a73cdefb4ccfc60bb47f8 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Fri, 21 Sep 2018 15:57:26 +0200 Subject: gsm0808: add BSSMAP Cell Identifier matching API Add * osmo_lai_cmp() (to use in gsm0808_cell_id_u_matches()) * osmo_cgi_cmp() (to use in gsm0808_cell_id_u_matches()) * gsm0808_cell_id_u_match() (to re-use for single IDs and lists) * gsm0808_cell_ids_match() * gsm0808_cell_id_matches_list() * Unit tests in gsm0808_test.c Rationale: For inter-BSC handover, it is interesting to find matches between *differing* Cell Identity kinds. For example, if a cell as CGI 23-42-3-5, and a HO for LAC-CI 3-5 should be handled, we need to see the match. This is most interesting for osmo-msc, i.e. to direct the BSSMAP Handover Request towards the correct BSC or MSC. It is also interesting for osmo-bsc's VTY interface, to be able to manage cells' neighbors and to trigger manual handovers by various Cell Identity handles, as the user would expect them. Change-Id: I5535f0d149c2173294538df75764dd181b023312 --- src/gsm/gsm0808_utils.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++ src/gsm/gsm23003.c | 34 ++++++++++++ src/gsm/libosmogsm.map | 2 + 3 files changed, 181 insertions(+) (limited to 'src/gsm') diff --git a/src/gsm/gsm0808_utils.c b/src/gsm/gsm0808_utils.c index 9fcccaec..54ec19c2 100644 --- a/src/gsm/gsm0808_utils.c +++ b/src/gsm/gsm0808_utils.c @@ -1391,6 +1391,151 @@ int gsm0808_cell_id_u_name(char *buf, size_t buflen, } } +/* Store individual Cell Identifier information in a CGI, without clearing the remaining ones. + * This is useful to supplement one CGI with information from more than one Cell Identifier, + * which in turn is useful to match Cell Identifiers of differing kinds to each other. + * Before first invocation, clear the *dst struct externally, this function does only write those members + * that are present in parameter u. + */ +static void cell_id_to_cgi(struct osmo_cell_global_id *dst, + enum CELL_IDENT discr, const union gsm0808_cell_id_u *u) +{ + switch (discr) { + case CELL_IDENT_WHOLE_GLOBAL: + *dst = u->global; + return; + + case CELL_IDENT_LAC_AND_CI: + dst->lai.lac = u->lac_and_ci.lac; + dst->cell_identity = u->lac_and_ci.ci; + return; + + case CELL_IDENT_CI: + dst->cell_identity = u->ci; + return; + + case CELL_IDENT_LAI_AND_LAC: + dst->lai = u->lai_and_lac; + return; + + case CELL_IDENT_LAC: + dst->lai.lac = u->lac; + return; + + case CELL_IDENT_NO_CELL: + case CELL_IDENT_BSS: + case CELL_IDENT_UTRAN_PLMN_LAC_RNC: + case CELL_IDENT_UTRAN_RNC: + case CELL_IDENT_UTRAN_LAC_RNC: + /* No values to set. */ + return; + } +} + +/*! Return true if the common information between the two Cell Identifiers match. + * For example, if a LAC+CI is compared to LAC, return true if the LAC are the same. + * Note that CELL_IDENT_NO_CELL will always return false. + * Also CELL_IDENT_BSS will always return false, since this function cannot possibly + * know the bounds of the BSS, so the caller must handle CELL_IDENT_BSS specially. + * \param[in] discr1 Cell Identifier type. + * \param[in] u1 Cell Identifier value. + * \param[in] discr2 Other Cell Identifier type. + * \param[in] u2 Other Cell Identifier value. + * \param[in] exact_match If true, return true only if the CELL_IDENT types and all values are identical. + * \returns True if the common fields of the above match. + */ +static bool gsm0808_cell_id_u_match(enum CELL_IDENT discr1, const union gsm0808_cell_id_u *u1, + enum CELL_IDENT discr2, const union gsm0808_cell_id_u *u2, + bool exact_match) +{ + struct osmo_cell_global_id a = {}; + struct osmo_cell_global_id b = {}; + + if (exact_match && discr1 != discr2) + return false; + + /* First handle the odd wildcard like CELL_IDENT kinds. We can't really match any of these. */ + switch (discr1) { + case CELL_IDENT_NO_CELL: + case CELL_IDENT_BSS: + return discr1 == discr2; + case CELL_IDENT_UTRAN_PLMN_LAC_RNC: + case CELL_IDENT_UTRAN_RNC: + case CELL_IDENT_UTRAN_LAC_RNC: + return false; + default: + break; + } + switch (discr2) { + case CELL_IDENT_NO_CELL: + case CELL_IDENT_UTRAN_PLMN_LAC_RNC: + case CELL_IDENT_UTRAN_RNC: + case CELL_IDENT_UTRAN_LAC_RNC: + case CELL_IDENT_BSS: + return false; + default: + break; + } + + /* Enrich both sides to full CGI, then compare those. First set the *other* ID's values in case + * they assign more items. For example: + * u1 = LAC:42 + * u2 = LAC+CI:23+5 + * 1) a <- LAC+CI:23+5 + * 2) a <- LAC:42 so that a = LAC+CI:42+5 + * Now we can compare those two and find a mismatch. If the LAC were the same, we would get + * identical LAC+CI and hence a match. */ + + cell_id_to_cgi(&a, discr2, u2); + cell_id_to_cgi(&a, discr1, u1); + + cell_id_to_cgi(&b, discr1, u1); + cell_id_to_cgi(&b, discr2, u2); + + return osmo_cgi_cmp(&a, &b) == 0; +} + +/*! Return true if the common information between the two Cell Identifiers match. + * For example, if a LAC+CI is compared to LAC, return true if the LAC are the same. + * Note that CELL_IDENT_NO_CELL will always return false. + * Also CELL_IDENT_BSS will always return false, since this function cannot possibly + * know the bounds of the BSS, so the caller must handle CELL_IDENT_BSS specially. + * \param[in] id1 Cell Identifier. + * \param[in] id2 Other Cell Identifier. + * \param[in] exact_match If true, return true only if the CELL_IDENT types and all values are identical. + * \returns True if the common fields of the above match. + */ +bool gsm0808_cell_ids_match(const struct gsm0808_cell_id *id1, const struct gsm0808_cell_id *id2, bool exact_match) +{ + return gsm0808_cell_id_u_match(id1->id_discr, &id1->id, id2->id_discr, &id2->id, exact_match); +} + +/*! Find an index in a Cell Identifier list that matches a given single Cell Identifer. + * Compare \a id against each entry in \a list using gsm0808_cell_ids_match(), and return the list index + * if a match is found. \a match_nr allows iterating all matches in the list. A match_nr <= 0 returns the + * first match in the list, match_nr == 1 the second match, etc., and if match_nr exceeds the available + * matches in the list, -1 is returned. + * \param[in] id Cell Identifier to match. + * \param[in] list Cell Identifier list to search in. + * \param[in] match_nr Ignore this many matches. + * \param[in] exact_match If true, consider as match only if the CELL_IDENT types and all values are identical. + * \returns -1 if no match is found, list index if a match is found. + */ +int gsm0808_cell_id_matches_list(const struct gsm0808_cell_id *id, const struct gsm0808_cell_id_list2 *list, + unsigned int match_nr, bool exact_match) +{ + int i; + for (i = 0; i < list->id_list_len; i++) { + if (gsm0808_cell_id_u_match(id->id_discr, &id->id, list->id_discr, &list->id_list[i], exact_match)) { + if (match_nr) + match_nr--; + else + return i; + } + } + return -1; +} + /*! value_string[] for enum CELL_IDENT. */ const struct value_string gsm0808_cell_id_discr_names[] = { { CELL_IDENT_WHOLE_GLOBAL, "CGI" }, diff --git a/src/gsm/gsm23003.c b/src/gsm/gsm23003.c index 1d9cefe6..95fca91a 100644 --- a/src/gsm/gsm23003.c +++ b/src/gsm/gsm23003.c @@ -324,6 +324,40 @@ int osmo_plmn_cmp(const struct osmo_plmn_id *a, const struct osmo_plmn_id *b) return osmo_mnc_cmp(a->mnc, a->mnc_3_digits, b->mnc, b->mnc_3_digits); } +/* Compare two LAI. + * The order of comparison is MCC, MNC, LAC. See also osmo_plmn_cmp(). + * \param a[in] "Left" side LAI. + * \param b[in] "Right" side LAI. + * \returns 0 if the LAI are equal, -1 if a < b, 1 if a > b. */ +int osmo_lai_cmp(const struct osmo_location_area_id *a, const struct osmo_location_area_id *b) +{ + int rc = osmo_plmn_cmp(&a->plmn, &b->plmn); + if (rc) + return rc; + if (a->lac < b->lac) + return -1; + if (a->lac > b->lac) + return 1; + return 0; +} + +/* Compare two CGI. + * The order of comparison is MCC, MNC, LAC, CI. See also osmo_lai_cmp(). + * \param a[in] "Left" side CGI. + * \param b[in] "Right" side CGI. + * \returns 0 if the CGI are equal, -1 if a < b, 1 if a > b. */ +int osmo_cgi_cmp(const struct osmo_cell_global_id *a, const struct osmo_cell_global_id *b) +{ + int rc = osmo_lai_cmp(&a->lai, &b->lai); + if (rc) + return rc; + if (a->cell_identity < b->cell_identity) + return -1; + if (a->cell_identity > b->cell_identity) + return 1; + return 0; +} + /*! Generate TS 23.003 Section 19.2 Home Network Realm/Domain (text form) * \param out[out] caller-provided output buffer, at least 33 bytes long * \param plmn[in] Osmocom representation of PLMN ID (MCC + MNC) diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index c440a79d..ea8c9be9 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -210,6 +210,8 @@ gsm0808_cell_id_list_name; gsm0808_cell_id_list_name_buf; gsm0808_cell_id_discr_names; gsm0808_cell_id_u_name; +gsm0808_cell_ids_match; +gsm0808_cell_id_matches_list; gsm0808_chan_type_to_speech_codec; gsm0808_speech_codec_from_chan_type; gsm0808_sc_cfg_from_gsm48_mr_cfg; -- cgit v1.2.3