diff options
-rw-r--r-- | include/osmocom/bsc/Makefile.am | 1 | ||||
-rw-r--r-- | include/osmocom/bsc/gsm_data.h | 22 | ||||
-rw-r--r-- | include/osmocom/bsc/handover.h | 6 | ||||
-rw-r--r-- | include/osmocom/bsc/neighbor_ident.h | 63 | ||||
-rw-r--r-- | src/osmo-bsc/Makefile.am | 2 | ||||
-rw-r--r-- | src/osmo-bsc/bsc_init.c | 1 | ||||
-rw-r--r-- | src/osmo-bsc/bsc_vty.c | 5 | ||||
-rw-r--r-- | src/osmo-bsc/gsm_data.c | 114 | ||||
-rw-r--r-- | src/osmo-bsc/handover_logic.c | 44 | ||||
-rw-r--r-- | src/osmo-bsc/neighbor_ident.c | 296 | ||||
-rw-r--r-- | src/osmo-bsc/neighbor_ident_vty.c | 568 | ||||
-rw-r--r-- | src/osmo-bsc/net_init.c | 1 | ||||
-rw-r--r-- | src/osmo-bsc/system_information.c | 51 | ||||
-rw-r--r-- | tests/bsc/Makefile.am | 1 | ||||
-rw-r--r-- | tests/gsm0408/Makefile.am | 1 | ||||
-rw-r--r-- | tests/handover/Makefile.am | 18 | ||||
-rw-r--r-- | tests/handover/neighbor_ident_test.c | 278 | ||||
-rw-r--r-- | tests/handover/neighbor_ident_test.err | 0 | ||||
-rw-r--r-- | tests/handover/neighbor_ident_test.ok | 249 | ||||
-rw-r--r-- | tests/neighbor_ident.vty | 260 | ||||
-rw-r--r-- | tests/testsuite.at | 7 |
21 files changed, 1983 insertions, 5 deletions
diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am index dfab5929c..0b576cddd 100644 --- a/include/osmocom/bsc/Makefile.am +++ b/include/osmocom/bsc/Makefile.am @@ -28,6 +28,7 @@ noinst_HEADERS = \ meas_feed.h \ meas_rep.h \ misdn.h \ + neighbor_ident.h \ network_listen.h \ openbscdefines.h \ osmo_bsc.h \ diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h index 579461732..a16a4b743 100644 --- a/include/osmocom/bsc/gsm_data.h +++ b/include/osmocom/bsc/gsm_data.h @@ -35,6 +35,7 @@ struct mgcp_client_conf; struct mgcp_client; struct mgcp_ctx; +struct gsm0808_cell_id; /** annotations for msgb ownership */ #define __uses @@ -750,6 +751,12 @@ struct load_counter { unsigned int used; }; +/* Useful to track N-N relations between BTS, for example neighbors. */ +struct gsm_bts_ref { + struct llist_head entry; + struct gsm_bts *bts; +}; + /* One BTS */ struct gsm_bts { /* list header in net->bts_list */ @@ -984,6 +991,11 @@ struct gsm_bts { struct handover_cfg *ho; + /* A list of struct gsm_bts_ref, indicating neighbors of this BTS. + * When the si_common neigh_list is in automatic mode, it is populated from this list as well as + * gsm_network->neighbor_bss_cells. */ + struct llist_head local_neighbors; + /* BTS-specific overrides for timer values from struct gsm_network. */ uint8_t T3122; /* ASSIGMENT REJECT wait indication */ @@ -998,6 +1010,13 @@ struct gsm_network *gsm_network_init(void *ctx); struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num); struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num); +bool gsm_bts_matches_lai(const struct gsm_bts *bts, const struct osmo_location_area_id *lai); +bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cell_id *cell_id); +struct gsm_bts *gsm_bts_by_cell_id(const struct gsm_network *net, + const struct gsm0808_cell_id *cell_id, + int match_idx); +int gsm_bts_local_neighbor_add(struct gsm_bts *bts, struct gsm_bts *neighbor); +int gsm_bts_local_neighbor_del(struct gsm_bts *bts, const struct gsm_bts *neighbor); struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts); struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num); @@ -1281,6 +1300,9 @@ struct gsm_network { struct mgcp_client_conf *conf; struct mgcp_client *client; } mgw; + + /* Remote BSS Cell Identifier Lists */ + struct neighbor_ident_list *neighbor_bss_cells; }; static inline const struct osmo_location_area_id *bts_lai(struct gsm_bts *bts) diff --git a/include/osmocom/bsc/handover.h b/include/osmocom/bsc/handover.h index 772ab98e2..aa117cc24 100644 --- a/include/osmocom/bsc/handover.h +++ b/include/osmocom/bsc/handover.h @@ -6,6 +6,8 @@ #include <osmocom/core/timer.h> #include <osmocom/gsm/gsm_utils.h> +#include <osmocom/bsc/neighbor_ident.h> + struct gsm_lchan; struct gsm_bts; struct gsm_subscriber_connection; @@ -94,3 +96,7 @@ struct handover_decision_callbacks { void handover_decision_callbacks_register(struct handover_decision_callbacks *hdc); struct handover_decision_callbacks *handover_decision_callbacks_get(int hodec_id); + +struct gsm_bts *bts_by_neighbor_ident(const struct gsm_network *net, + const struct neighbor_ident_key *search_for); +struct neighbor_ident_key *bts_ident_key(const struct gsm_bts *bts); diff --git a/include/osmocom/bsc/neighbor_ident.h b/include/osmocom/bsc/neighbor_ident.h new file mode 100644 index 000000000..bba18c3e3 --- /dev/null +++ b/include/osmocom/bsc/neighbor_ident.h @@ -0,0 +1,63 @@ +/* Manage identity of neighboring BSS cells for inter-BSC handover */ +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/linuxlist.h> + +struct vty; +struct gsm_network; +struct gsm_bts; +struct neighbor_ident_list; +struct gsm0808_cell_id_list2; + +enum bsic_kind { + BSIC_NONE, + BSIC_6BIT, + BSIC_9BIT, +}; + +#define NEIGHBOR_IDENT_KEY_ANY_BTS -1 + +struct neighbor_ident_key { + int from_bts; /*< BTS nr 0..255 or NEIGHBOR_IDENT_KEY_ANY_BTS */ + uint16_t arfcn; + enum bsic_kind bsic_kind; + uint16_t bsic; +}; + +const char *neighbor_ident_key_name(const struct neighbor_ident_key *ni_key); + +struct neighbor_ident_list *neighbor_ident_init(void *talloc_ctx); +void neighbor_ident_free(struct neighbor_ident_list *nil); + +bool neighbor_ident_key_match(const struct neighbor_ident_key *entry, + const struct neighbor_ident_key *search_for, + bool exact_match); + +int neighbor_ident_add(struct neighbor_ident_list *nil, const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val); +const struct gsm0808_cell_id_list2 *neighbor_ident_get(const struct neighbor_ident_list *nil, + const struct neighbor_ident_key *key); +bool neighbor_ident_del(struct neighbor_ident_list *nil, const struct neighbor_ident_key *key); +void neighbor_ident_clear(struct neighbor_ident_list *nil); + +void neighbor_ident_iter(const struct neighbor_ident_list *nil, + bool (* iter_cb )(const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val, + void *cb_data), + void *cb_data); + +void neighbor_ident_vty_init(struct gsm_network *net, struct neighbor_ident_list *nil); +void neighbor_ident_vty_write(struct vty *vty, const char *indent, struct gsm_bts *bts); + +#define NEIGHBOR_IDENT_VTY_KEY_PARAMS "arfcn <0-1023> (bsic|bsic9) (<0-511>|any)" +#define NEIGHBOR_IDENT_VTY_KEY_DOC \ + "ARFCN of neighbor cell\n" "ARFCN value\n" \ + "BSIC of neighbor cell\n" "9-bit BSIC of neighbor cell\n" "BSIC value\n" \ + "for all BSICs / use any BSIC in this ARFCN\n" +bool neighbor_ident_vty_parse_key_params(struct vty *vty, const char **argv, + struct neighbor_ident_key *key); +bool neighbor_ident_bts_parse_key_params(struct vty *vty, struct gsm_bts *bts, const char **argv, + struct neighbor_ident_key *key); diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am index db63a99cc..bf7ef1143 100644 --- a/src/osmo-bsc/Makefile.am +++ b/src/osmo-bsc/Makefile.am @@ -65,6 +65,8 @@ osmo_bsc_SOURCES = \ handover_vty.c \ meas_feed.c \ meas_rep.c \ + neighbor_ident.c \ + neighbor_ident_vty.c \ net_init.c \ osmo_bsc_api.c \ osmo_bsc_audio.c \ diff --git a/src/osmo-bsc/bsc_init.c b/src/osmo-bsc/bsc_init.c index b6bd41025..1fe484761 100644 --- a/src/osmo-bsc/bsc_init.c +++ b/src/osmo-bsc/bsc_init.c @@ -247,6 +247,7 @@ static struct gsm_network *bsc_network_init(void *ctx) net->ho = ho_cfg_init(net, NULL); net->hodec2.congestion_check_interval_s = HO_CFG_CONGESTION_CHECK_DEFAULT; + net->neighbor_bss_cells = neighbor_ident_init(net); /* init statistics */ net->bsc_ctrs = rate_ctr_group_alloc(net, &bsc_ctrg_desc, 0); diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c index 77a534d51..b1e6d78db 100644 --- a/src/osmo-bsc/bsc_vty.c +++ b/src/osmo-bsc/bsc_vty.c @@ -62,6 +62,8 @@ #include <osmocom/bsc/gsm_04_08_utils.h> #include <osmocom/bsc/acc_ramp.h> #include <osmocom/bsc/meas_feed.h> +#include <osmocom/bsc/neighbor_ident.h> +#include <osmocom/bsc/handover.h> #include <inttypes.h> @@ -909,6 +911,8 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) VTY_NEWLINE); } + neighbor_ident_vty_write(vty, " ", bts); + vty_out(vty, " codec-support fr"); if (bts->codec.hr) vty_out(vty, " hr"); @@ -4969,6 +4973,7 @@ int bsc_vty_init(struct gsm_network *network) install_element(BTS_NODE, &cfg_bts_no_acc_ramping_cmd); install_element(BTS_NODE, &cfg_bts_acc_ramping_step_interval_cmd); install_element(BTS_NODE, &cfg_bts_acc_ramping_step_size_cmd); + neighbor_ident_vty_init(network, network->neighbor_bss_cells); /* See also handover commands added on bts level from handover_vty.c */ install_element(BTS_NODE, &cfg_trx_cmd); diff --git a/src/osmo-bsc/gsm_data.c b/src/osmo-bsc/gsm_data.c index 7885dad15..fc12d0f71 100644 --- a/src/osmo-bsc/gsm_data.c +++ b/src/osmo-bsc/gsm_data.c @@ -33,6 +33,7 @@ #include <osmocom/core/statistics.h> #include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/gsm0808_utils.h> #include <osmocom/bsc/gsm_data.h> #include <osmocom/bsc/bsc_msc_data.h> @@ -563,6 +564,118 @@ struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num) return NULL; } +bool gsm_bts_matches_lai(const struct gsm_bts *bts, const struct osmo_location_area_id *lai) +{ + return osmo_plmn_cmp(&lai->plmn, &bts->network->plmn) == 0 + && lai->lac == bts->location_area_code; +} + +bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cell_id *cell_id) +{ + const union gsm0808_cell_id_u *id = &cell_id->id; + if (!bts || !cell_id) + return false; + + switch (cell_id->id_discr) { + case CELL_IDENT_WHOLE_GLOBAL: + return gsm_bts_matches_lai(bts, &id->global.lai) + && id->global.cell_identity == bts->cell_identity; + case CELL_IDENT_LAC_AND_CI: + return id->lac_and_ci.lac == bts->location_area_code + && id->lac_and_ci.ci == bts->cell_identity; + case CELL_IDENT_CI: + return id->ci == bts->cell_identity; + case CELL_IDENT_NO_CELL: + return false; + case CELL_IDENT_LAI_AND_LAC: + return gsm_bts_matches_lai(bts, &id->lai_and_lac); + case CELL_IDENT_LAC: + return id->lac == bts->location_area_code; + case CELL_IDENT_BSS: + return true; + case CELL_IDENT_UTRAN_PLMN_LAC_RNC: + case CELL_IDENT_UTRAN_RNC: + case CELL_IDENT_UTRAN_LAC_RNC: + return false; + default: + OSMO_ASSERT(false); + } +} + +/* From a list of local BTSes that match the cell_id, return the Nth one, or NULL if there is no such + * match. */ +struct gsm_bts *gsm_bts_by_cell_id(const struct gsm_network *net, + const struct gsm0808_cell_id *cell_id, + int match_idx) +{ + struct gsm_bts *bts; + int i = 0; + llist_for_each_entry(bts, &net->bts_list, list) { + if (!gsm_bts_matches_cell_id(bts, cell_id)) + continue; + if (i < match_idx) { + /* this is only the i'th match, we're looking for a later one... */ + i++; + continue; + } + return bts; + } + return NULL; +} + +struct gsm_bts_ref *gsm_bts_ref_find(const struct llist_head *list, const struct gsm_bts *bts) +{ + struct gsm_bts_ref *ref; + if (!bts) + return NULL; + llist_for_each_entry(ref, list, entry) { + if (ref->bts == bts) + return ref; + } + return NULL; +} + +/* Add a BTS reference to the local_neighbors list. + * Return 1 if added, 0 if such an entry already existed, and negative on errors. */ +int gsm_bts_local_neighbor_add(struct gsm_bts *bts, struct gsm_bts *neighbor) +{ + struct gsm_bts_ref *ref; + if (!bts || !neighbor) + return -ENOMEM; + + if (bts == neighbor) + return -EINVAL; + + /* Already got this entry? */ + ref = gsm_bts_ref_find(&bts->local_neighbors, neighbor); + if (ref) + return 0; + + ref = talloc_zero(bts, struct gsm_bts_ref); + if (!ref) + return -ENOMEM; + ref->bts = neighbor; + llist_add_tail(&ref->entry, &bts->local_neighbors); + return 1; +} + +/* Remove a BTS reference from the local_neighbors list. + * Return 1 if removed, 0 if no such entry existed, and negative on errors. */ +int gsm_bts_local_neighbor_del(struct gsm_bts *bts, const struct gsm_bts *neighbor) +{ + struct gsm_bts_ref *ref; + if (!bts || !neighbor) + return -ENOMEM; + + ref = gsm_bts_ref_find(&bts->local_neighbors, neighbor); + if (!ref) + return 0; + + llist_del(&ref->entry); + talloc_free(ref); + return 1; +} + struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) { struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx); @@ -756,6 +869,7 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num) INIT_LLIST_HEAD(&bts->abis_queue); INIT_LLIST_HEAD(&bts->loc_list); + INIT_LLIST_HEAD(&bts->local_neighbors); /* Enable all codecs by default. These get reset to a more fine grained selection IF a * 'codec-support' config appears in the config file (see bsc_vty.c). */ diff --git a/src/osmo-bsc/handover_logic.c b/src/osmo-bsc/handover_logic.c index 509de533b..0f664c629 100644 --- a/src/osmo-bsc/handover_logic.c +++ b/src/osmo-bsc/handover_logic.c @@ -389,6 +389,50 @@ static int ho_meas_rep(struct gsm_meas_rep *mr) return 0; } +struct gsm_bts *bts_by_neighbor_ident(const struct gsm_network *net, + const struct neighbor_ident_key *search_for) +{ + struct gsm_bts *found = NULL; + struct gsm_bts *bts; + struct gsm_bts *wildcard_match = NULL; + + llist_for_each_entry(bts, &net->bts_list, list) { + struct neighbor_ident_key entry = { + .from_bts = NEIGHBOR_IDENT_KEY_ANY_BTS, + .arfcn = bts->c0->arfcn, + .bsic_kind = BSIC_6BIT, + .bsic = bts->bsic, + }; + if (neighbor_ident_key_match(&entry, search_for, true)) { + if (found) { + LOGP(DHO, LOGL_ERROR, "CONFIG ERROR: Multiple BTS match %s: %d and %d\n", + neighbor_ident_key_name(search_for), + found->nr, bts->nr); + return found; + } + found = bts; + } + if (neighbor_ident_key_match(&entry, search_for, false)) + wildcard_match = bts; + } + + if (found) + return found; + + return wildcard_match; +} + +struct neighbor_ident_key *bts_ident_key(const struct gsm_bts *bts) +{ + static struct neighbor_ident_key key; + key = (struct neighbor_ident_key){ + .arfcn = bts->c0->arfcn, + .bsic_kind = BSIC_6BIT, + .bsic = bts->bsic, + }; + return &key; +} + static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { diff --git a/src/osmo-bsc/neighbor_ident.c b/src/osmo-bsc/neighbor_ident.c new file mode 100644 index 000000000..8a7c580ae --- /dev/null +++ b/src/osmo-bsc/neighbor_ident.c @@ -0,0 +1,296 @@ +/* Manage identity of neighboring BSS cells for inter-BSC handover. + * + * Measurement reports tell us about neighbor ARFCN and BSIC. If that ARFCN and BSIC is not managed by + * this local BSS, we need to tell the MSC a cell identity, like CGI, LAC+CI, etc. -- hence we need a + * mapping from ARFCN+BSIC to Cell Identifier List, which needs to be configured by the user. + */ +/* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <errno.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/gsm0808.h> + +#include <osmocom/bsc/neighbor_ident.h> + +struct neighbor_ident_list { + struct llist_head list; +}; + +struct neighbor_ident { + struct llist_head entry; + + struct neighbor_ident_key key; + struct gsm0808_cell_id_list2 val; +}; + +#define APPEND_THING(func, args...) do { \ + int remain = buflen - (pos - buf); \ + int l = func(pos, remain, ##args); \ + if (l < 0 || l > remain) \ + pos = buf + buflen; \ + else \ + pos += l; \ + } while(0) +#define APPEND_STR(fmt, args...) APPEND_THING(snprintf, fmt, ##args) + +const char *_neighbor_ident_key_name(char *buf, size_t buflen, const struct neighbor_ident_key *ni_key) +{ + char *pos = buf; + + APPEND_STR("BTS "); + if (ni_key->from_bts == NEIGHBOR_IDENT_KEY_ANY_BTS) + APPEND_STR("*"); + else if (ni_key->from_bts >= 0 && ni_key->from_bts <= 255) + APPEND_STR("%d", ni_key->from_bts); + else + APPEND_STR("invalid(%d)", ni_key->from_bts); + + APPEND_STR(" to "); + switch (ni_key->bsic_kind) { + default: + case BSIC_NONE: + APPEND_STR("ARFCN %u (any BSIC)", ni_key->arfcn); + break; + case BSIC_6BIT: + APPEND_STR("ARFCN %u BSIC %u", ni_key->arfcn, ni_key->bsic & 0x3f); + break; + case BSIC_9BIT: + APPEND_STR("ARFCN %u BSIC %u(9bit)", ni_key->arfcn, ni_key->bsic & 0x1ff); + break; + } + return buf; +} + +const char *neighbor_ident_key_name(const struct neighbor_ident_key *ni_key) +{ + static char buf[64]; + return _neighbor_ident_key_name(buf, sizeof(buf), ni_key); +} + +struct neighbor_ident_list *neighbor_ident_init(void *talloc_ctx) +{ + struct neighbor_ident_list *nil = talloc_zero(talloc_ctx, struct neighbor_ident_list); + OSMO_ASSERT(nil); + INIT_LLIST_HEAD(&nil->list); + return nil; +} + +void neighbor_ident_free(struct neighbor_ident_list *nil) +{ + if (!nil) + return; + talloc_free(nil); +} + +/* Return true when the entry matches the search_for requirements. + * If exact_match is false, a BSIC_NONE entry acts as wildcard to match any search_for on that ARFCN, + * and a BSIC_NONE in search_for likewise returns any one entry that matches the ARFCN; + * also a from_bts == NEIGHBOR_IDENT_KEY_ANY_BTS in either entry or search_for will match. + * If exact_match is true, only identical bsic_kind values and identical from_bts values return a match. + * Note, typically wildcard BSICs are only in entry, e.g. the user configured list, and search_for + * contains a specific BSIC, e.g. as received from a Measurement Report. */ +bool neighbor_ident_key_match(const struct neighbor_ident_key *entry, + const struct neighbor_ident_key *search_for, + bool exact_match) +{ + uint16_t bsic_mask; + + if (exact_match + && entry->from_bts != search_for->from_bts) + return false; + + if (search_for->from_bts != NEIGHBOR_IDENT_KEY_ANY_BTS + && entry->from_bts != NEIGHBOR_IDENT_KEY_ANY_BTS + && entry->from_bts != search_for->from_bts) + return false; + + if (entry->arfcn != search_for->arfcn) + return false; + + switch (entry->bsic_kind) { + default: + return false; + case BSIC_NONE: + if (!exact_match) { + /* The neighbor identifier list entry matches any BSIC for this ARFCN. */ + return true; + } + /* Match exact entry */ + bsic_mask = 0; + break; + case BSIC_6BIT: + bsic_mask = 0x3f; + break; + case BSIC_9BIT: + bsic_mask = 0x1ff; + break; + } + if (!exact_match && search_for->bsic_kind == BSIC_NONE) { + /* The search is looking only for an ARFCN with any BSIC */ + return true; + } + if (search_for->bsic_kind == entry->bsic_kind + && (search_for->bsic & bsic_mask) == (entry->bsic & bsic_mask)) + return true; + return false; +} + +static struct neighbor_ident *_neighbor_ident_get(const struct neighbor_ident_list *nil, + const struct neighbor_ident_key *key, + bool exact_match) +{ + struct neighbor_ident *ni; + struct neighbor_ident *wildcard_match = NULL; + + /* Do both exact-bsic and wildcard matching in the same iteration: + * Any exact match returns immediately, while for a wildcard match we still go through all + * remaining items in case an exact match exists. */ + llist_for_each_entry(ni, &nil->list, entry) { + if (neighbor_ident_key_match(&ni->key, key, true)) + return ni; + if (!exact_match) { + if (neighbor_ident_key_match(&ni->key, key, false)) + wildcard_match = ni; + } + } + return wildcard_match; +} + +static void _neighbor_ident_free(struct neighbor_ident *ni) +{ + llist_del(&ni->entry); + talloc_free(ni); +} + +bool neighbor_ident_key_valid(const struct neighbor_ident_key *key) +{ + if (key->from_bts != NEIGHBOR_IDENT_KEY_ANY_BTS + && (key->from_bts < 0 || key->from_bts > 255)) + return false; + + switch (key->bsic_kind) { + case BSIC_6BIT: + if (key->bsic > 0x3f) + return false; + break; + case BSIC_9BIT: + if (key->bsic > 0x1ff) + return false; + break; + case BSIC_NONE: + break; + default: + return false; + } + return true; +} + +/*! Add Cell Identifiers to an ARFCN+BSIC entry. + * Exactly one kind of identifier is allowed per ARFCN+BSIC entry, and any number of entries of that kind + * may be added up to the capacity of gsm0808_cell_id_list2, by one or more calls to this function. To + * replace an existing entry, first call neighbor_ident_del(nil, key). + * \returns number of entries in the resulting identifier list, or negative on error: + * see gsm0808_cell_id_list_add() for the meaning of returned error codes; + * return -ENOMEM when the list is not initialized, -ERANGE when the BSIC value is too large. */ +int neighbor_ident_add(struct neighbor_ident_list *nil, const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val) +{ + struct neighbor_ident *ni; + int rc; + + if (!nil) + return -ENOMEM; + + if (!neighbor_ident_key_valid(key)) + return -ERANGE; + + ni = _neighbor_ident_get(nil, key, true); + if (!ni) { + ni = talloc_zero(nil, struct neighbor_ident); + OSMO_ASSERT(ni); + *ni = (struct neighbor_ident){ + .key = *key, + .val = *val, + }; + llist_add_tail(&ni->entry, &nil->list); + return ni->val.id_list_len; + } + + rc = gsm0808_cell_id_list_add(&ni->val, val); + + if (rc < 0) + return rc; + + return ni->val.id_list_len; +} + +/*! Find cell identity for given BTS, ARFCN and BSIC, as previously added by neighbor_ident_add(). + */ +const struct gsm0808_cell_id_list2 *neighbor_ident_get(const struct neighbor_ident_list *nil, + const struct neighbor_ident_key *key) +{ + struct neighbor_ident *ni; + if (!nil) + return NULL; + ni = _neighbor_ident_get(nil, key, false); + if (!ni) + return NULL; + return &ni->val; +} + +bool neighbor_ident_del(struct neighbor_ident_list *nil, const struct neighbor_ident_key *key) +{ + struct neighbor_ident *ni; + if (!nil) + return false; + ni = _neighbor_ident_get(nil, key, true); + if (!ni) + return false; + _neighbor_ident_free(ni); + return true; +} + +void neighbor_ident_clear(struct neighbor_ident_list *nil) +{ + struct neighbor_ident *ni; + while ((ni = llist_first_entry_or_null(&nil->list, struct neighbor_ident, entry))) + _neighbor_ident_free(ni); +} + +/*! Iterate all neighbor_ident_list entries and call iter_cb for each. + * If iter_cb returns false, the iteration is stopped. */ +void neighbor_ident_iter(const struct neighbor_ident_list *nil, + bool (* iter_cb )(const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val, + void *cb_data), + void *cb_data) +{ + struct neighbor_ident *ni, *ni_next; + if (!nil) + return; + llist_for_each_entry_safe(ni, ni_next, &nil->list, entry) { + if (!iter_cb(&ni->key, &ni->val, cb_data)) + return; + } +} diff --git a/src/osmo-bsc/neighbor_ident_vty.c b/src/osmo-bsc/neighbor_ident_vty.c new file mode 100644 index 000000000..44830abfe --- /dev/null +++ b/src/osmo-bsc/neighbor_ident_vty.c @@ -0,0 +1,568 @@ +/* Quagga VTY implementation to manage identity of neighboring BSS cells for inter-BSC handover. */ +/* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/vty/command.h> +#include <osmocom/gsm/gsm0808.h> + +#include <osmocom/bsc/vty.h> +#include <osmocom/bsc/neighbor_ident.h> +#include <osmocom/bsc/gsm_data.h> + +static struct gsm_network *g_net = NULL; +static struct neighbor_ident_list *g_neighbor_cells = NULL; + +/* Parse VTY parameters matching NEIGHBOR_IDENT_VTY_KEY_PARAMS. Pass a pointer so that argv[0] is the + * ARFCN value followed by the BSIC keyword and value. vty *must* reference a BTS_NODE. */ +bool neighbor_ident_vty_parse_key_params(struct vty *vty, const char **argv, + struct neighbor_ident_key *key) +{ + struct gsm_bts *bts = vty->index; + + OSMO_ASSERT(vty->node == BTS_NODE); + OSMO_ASSERT(bts); + + return neighbor_ident_bts_parse_key_params(vty, bts, argv, key); +} + +/* same as neighbor_ident_vty_parse_key_params() but pass an explicit bts, so it works on any node. */ +bool neighbor_ident_bts_parse_key_params(struct vty *vty, struct gsm_bts *bts, const char **argv, + struct neighbor_ident_key *key) +{ + const char *arfcn_str = argv[0]; + const char *bsic_kind = argv[1]; + const char *bsic_str = argv[2]; + + OSMO_ASSERT(bts); + + *key = (struct neighbor_ident_key){ + .from_bts = bts->nr, + .arfcn = atoi(arfcn_str), + }; + + if (!strcmp(bsic_str, "any")) + key->bsic_kind = BSIC_NONE; + else { + key->bsic_kind = (!strcmp(bsic_kind, "bsic9")) ? BSIC_9BIT : BSIC_6BIT; + key->bsic = atoi(bsic_str); + if (key->bsic_kind == BSIC_6BIT && key->bsic > 0x3f) { + vty_out(vty, "%% Error: BSIC value surpasses 6-bit range: %u, use 'bsic9' instead%s", + key->bsic, VTY_NEWLINE); + return false; + } + } + return true; +} + +#define NEIGHBOR_ADD_CMD "neighbor add " +#define NEIGHBOR_DEL_CMD "neighbor del " +#define NEIGHBOR_DOC "Neighbor cell list\n" +#define NEIGHBOR_ADD_DOC NEIGHBOR_DOC "Add local or remote-BSS neighbor cell\n" +#define NEIGHBOR_DEL_DOC NEIGHBOR_DOC "Remove local or remote-BSS neighbor cell\n" + +static struct gsm_bts *neighbor_ident_vty_parse_bts_nr(struct vty *vty, const char **argv) +{ + const char *bts_nr_str = argv[0]; + struct gsm_bts *bts = gsm_bts_num(g_net, atoi(bts_nr_str)); + if (!bts) + vty_out(vty, "%% No such BTS: nr = %s%s\n", bts_nr_str, VTY_NEWLINE); + return bts; +} + +static struct gsm_bts *bts_by_cell_id(struct vty *vty, struct gsm0808_cell_id *cell_id) +{ + struct gsm_bts *bts = gsm_bts_by_cell_id(g_net, cell_id, 0); + if (!bts) + vty_out(vty, "%% No such BTS: %s%s\n", gsm0808_cell_id_name(cell_id), VTY_NEWLINE); + return bts; +} + +static struct gsm0808_cell_id *neighbor_ident_vty_parse_lac(struct vty *vty, const char **argv) +{ + static struct gsm0808_cell_id cell_id; + cell_id = (struct gsm0808_cell_id){ + .id_discr = CELL_IDENT_LAC, + .id.lac = atoi(argv[0]), + }; + return &cell_id; +} + +static struct gsm0808_cell_id *neighbor_ident_vty_parse_lac_ci(struct vty *vty, const char **argv) +{ + static struct gsm0808_cell_id cell_id; + cell_id = (struct gsm0808_cell_id){ + .id_discr = CELL_IDENT_LAC_AND_CI, + .id.lac_and_ci = { + .lac = atoi(argv[0]), + .ci = atoi(argv[1]), + }, + }; + return &cell_id; +} + +static struct gsm0808_cell_id *neighbor_ident_vty_parse_cgi(struct vty *vty, const char **argv) +{ + static struct gsm0808_cell_id cell_id; + cell_id = (struct gsm0808_cell_id){ + .id_discr = CELL_IDENT_WHOLE_GLOBAL, + }; + struct osmo_cell_global_id *cgi = &cell_id.id.global; + const char *mcc = argv[0]; + const char *mnc = argv[1]; + const char *lac = argv[2]; + const char *ci = argv[3]; + + if (osmo_mcc_from_str(mcc, &cgi->lai.plmn.mcc)) { + vty_out(vty, "%% Error decoding MCC: %s%s", mcc, VTY_NEWLINE); + return NULL; + } + + if (osmo_mnc_from_str(mnc, &cgi->lai.plmn.mnc, &cgi->lai.plmn.mnc_3_digits)) { + vty_out(vty, "%% Error decoding MNC: %s%s", mnc, VTY_NEWLINE); + return NULL; + } + + cgi->lai.lac = atoi(lac); + cgi->cell_identity = atoi(ci); + return &cell_id; +} + +static int add_local_bts(struct vty *vty, struct gsm_bts *neigh) +{ + int rc; + struct gsm_bts *bts = vty->index; + if (vty->node != BTS_NODE) { + vty_out(vty, "%% Error: cannot add local BTS neighbor, not on BTS node%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (!bts) { + vty_out(vty, "%% Error: cannot add local BTS neighbor, no BTS on this node%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (!neigh) { + vty_out(vty, "%% Error: cannot add local BTS neighbor to BTS %u, no such neighbor BTS%s" + "%% (To add remote-BSS neighbors, pass full ARFCN and BSIC as well)%s", + bts->nr, VTY_NEWLINE, VTY_NEWLINE); + return CMD_WARNING; + } + rc = gsm_bts_local_neighbor_add(bts, neigh); + if (rc < 0) { + vty_out(vty, "%% Error: cannot add local BTS %u as neighbor to BTS %u: %s%s", + neigh->nr, bts->nr, strerror(-rc), VTY_NEWLINE); + return CMD_WARNING; + } else + vty_out(vty, "%% BTS %u %s local neighbor BTS %u with LAC %u CI %u and ARFCN %u BSIC %u%s", + bts->nr, rc? "now has" : "already had", + neigh->nr, neigh->location_area_code, neigh->cell_identity, + neigh->c0->arfcn, neigh->bsic, VTY_NEWLINE); + return CMD_SUCCESS; +} + +static int del_local_bts(struct vty *vty, struct gsm_bts *neigh) +{ + int rc; + struct gsm_bts *bts = vty->index; + if (vty->node != BTS_NODE) { + vty_out(vty, "%% Error: cannot remove local BTS neighbor, not on BTS node%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (!bts) { + vty_out(vty, "%% Error: cannot remove local BTS neighbor, no BTS on this node%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (!neigh) { + vty_out(vty, "%% Error: cannot remove local BTS neighbor from BTS %u, no such neighbor BTS%s", + bts->nr, VTY_NEWLINE); + return CMD_WARNING; + } + rc = gsm_bts_local_neighbor_del(bts, neigh); + if (rc < 0) { + vty_out(vty, "%% Error: cannot remove local BTS %u neighbor from BTS %u: %s%s", + neigh->nr, bts->nr, strerror(-rc), VTY_NEWLINE); + return CMD_WARNING; + } + if (rc == 0) + vty_out(vty, "%% BTS %u is no neighbor of BTS %u%s", + neigh->nr, bts->nr, VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN(cfg_neighbor_add_bts_nr, cfg_neighbor_add_bts_nr_cmd, + NEIGHBOR_ADD_CMD "bts <0-255>", + NEIGHBOR_ADD_DOC "Neighbor cell by local BTS number\n" "BTS number\n") +{ + return add_local_bts(vty, neighbor_ident_vty_parse_bts_nr(vty, argv)); +} + +DEFUN(cfg_neighbor_add_lac, cfg_neighbor_add_lac_cmd, + NEIGHBOR_ADD_CMD "lac <0-65535>", + NEIGHBOR_ADD_DOC "Neighbor cell by LAC\n" "LAC\n") +{ + return add_local_bts(vty, bts_by_cell_id(vty, neighbor_ident_vty_parse_lac(vty, argv))); +} + +DEFUN(cfg_neighbor_add_lac_ci, cfg_neighbor_add_lac_ci_cmd, + NEIGHBOR_ADD_CMD "lac-ci <0-65535> <0-255>", + NEIGHBOR_ADD_DOC "Neighbor cell by LAC and CI\n" "LAC\n" "CI\n") +{ + return add_local_bts(vty, bts_by_cell_id(vty, neighbor_ident_vty_parse_lac_ci(vty, argv))); +} + +bool neighbor_ident_key_matches_bts(const struct neighbor_ident_key *key, struct gsm_bts *bts) +{ + if (!bts || !key) + return false; + return key->arfcn == bts->c0->arfcn + && (key->bsic_kind == BSIC_NONE || key->bsic == bts->bsic); +} + +static int add_remote_or_local_bts(struct vty *vty, const struct gsm0808_cell_id *cell_id, + const struct neighbor_ident_key *key) +{ + int rc; + struct gsm_bts *local_neigh; + struct gsm0808_cell_id_list2 cil; + struct gsm_bts *bts = vty->index; + + if (vty->node != BTS_NODE) { + vty_out(vty, "%% Error: cannot add BTS neighbor, not on BTS node%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (!bts) { + vty_out(vty, "%% Error: cannot add BTS neighbor, no BTS on this node%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Is there a local BTS that matches the cell_id? */ + local_neigh = gsm_bts_by_cell_id(g_net, cell_id, 0); + if (local_neigh) { + /* But do the advertised ARFCN and BSIC match as intended? + * The user may omit ARFCN and BSIC for local cells, but if they are provided, + * they need to match. */ + if (!neighbor_ident_key_matches_bts(key, local_neigh)) { + vty_out(vty, "%% Error: bts %u: neighbor cell id %s indicates local BTS %u," + " but it does not match ARFCN+BSIC %s%s", + bts->nr, gsm0808_cell_id_name(cell_id), local_neigh->nr, + neighbor_ident_key_name(key), VTY_NEWLINE); + /* TODO: error out fatally for non-interactive VTY? */ + return CMD_WARNING; + } + return add_local_bts(vty, local_neigh); + } + + /* The cell_id is not known in this BSS, so it must be a remote cell. */ + gsm0808_cell_id_to_list(&cil, cell_id); + rc = neighbor_ident_add(g_neighbor_cells, key, &cil); + + if (rc < 0) { + const char *reason; + switch (rc) { + case -EINVAL: + reason = ": mismatching type between current and newly added cell identifier"; + break; + case -ENOSPC: + reason = ": list is full"; + break; + default: + reason = ""; + break; + } + + vty_out(vty, "%% Error adding neighbor-BSS Cell Identifier %s%s%s", + gsm0808_cell_id_name(cell_id), reason, VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out(vty, "%% %s now has %d remote BSS Cell Identifier List %s%s", + neighbor_ident_key_name(key), rc, rc == 1? "entry" : "entries", VTY_NEWLINE); + return CMD_SUCCESS; +} + +static int del_by_key(struct vty *vty, const struct neighbor_ident_key *key) +{ + int removed = 0; + int rc; + struct gsm_bts *bts = vty->index; + struct gsm_bts_ref *neigh, *safe; + + if (vty->node != BTS_NODE) { + vty_out(vty, "%% Error: cannot remove BTS neighbor, not on BTS node%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (!bts) { + vty_out(vty, "%% Error: cannot remove BTS neighbor, no BTS on this node%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Is there a local BTS that matches the key? */ + llist_for_each_entry_safe(neigh, safe, &bts->local_neighbors, entry) { + struct gsm_bts *neigh_bts = neigh->bts; + if (!neighbor_ident_key_matches_bts(key, neigh->bts)) + continue; + rc = gsm_bts_local_neighbor_del(bts, neigh->bts); + if (rc > 0) { + vty_out(vty, "%% Removed local neighbor bts %u to bts %u%s", + bts->nr, neigh_bts->nr, VTY_NEWLINE); + removed += rc; + } + } + + if (neighbor_ident_del(g_neighbor_cells, key)) { + vty_out(vty, "%% Removed remote BSS neighbor %s%s", + neighbor_ident_key_name(key), VTY_NEWLINE); + removed ++; + } + + if (!removed) { + vty_out(vty, "%% Cannot remove, no such neighbor: %s%s", + neighbor_ident_key_name(key), VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +DEFUN(cfg_neighbor_add_lac_arfcn_bsic, cfg_neighbor_add_lac_arfcn_bsic_cmd, + NEIGHBOR_ADD_CMD "lac <0-65535> " NEIGHBOR_IDENT_VTY_KEY_PARAMS, + NEIGHBOR_ADD_DOC "Neighbor cell by lac\n" "lac\n" NEIGHBOR_IDENT_VTY_KEY_DOC) +{ + struct neighbor_ident_key nik; + struct gsm0808_cell_id *cell_id = neighbor_ident_vty_parse_lac(vty, argv); + if (!cell_id) + return CMD_WARNING; + if (!neighbor_ident_vty_parse_key_params(vty, argv + 1, &nik)) + return CMD_WARNING; + return add_remote_or_local_bts(vty, cell_id, &nik); +} + +DEFUN(cfg_neighbor_add_lac_ci_arfcn_bsic, cfg_neighbor_add_lac_ci_arfcn_bsic_cmd, + NEIGHBOR_ADD_CMD "lac-ci <0-65535> <0-255> " NEIGHBOR_IDENT_VTY_KEY_PARAMS, + NEIGHBOR_ADD_DOC "Neighbor cell by LAC and CI\n" "LAC\n" "CI\n" NEIGHBOR_IDENT_VTY_KEY_DOC) +{ + struct neighbor_ident_key nik; + struct gsm0808_cell_id *cell_id = neighbor_ident_vty_parse_lac_ci(vty, argv); + if (!cell_id) + return CMD_WARNING; + if (!neighbor_ident_vty_parse_key_params(vty, argv + 2, &nik)) + return CMD_WARNING; + return add_remote_or_local_bts(vty, cell_id, &nik); +} + +DEFUN(cfg_neighbor_add_cgi_arfcn_bsic, cfg_neighbor_add_cgi_arfcn_bsic_cmd, + NEIGHBOR_ADD_CMD "cgi <0-999> <0-999> <0-65535> <0-255> " NEIGHBOR_IDENT_VTY_KEY_PARAMS, + NEIGHBOR_ADD_DOC "Neighbor cell by cgi\n" "MCC\n" "MNC\n" "LAC\n" "CI\n" NEIGHBOR_IDENT_VTY_KEY_DOC) +{ + struct neighbor_ident_key nik; + struct gsm0808_cell_id *cell_id = neighbor_ident_vty_parse_cgi(vty, argv); + if (!cell_id) + return CMD_WARNING; + if (!neighbor_ident_vty_parse_key_params(vty, argv + 4, &nik)) + return CMD_WARNING; + return add_remote_or_local_bts(vty, cell_id, &nik); +} + +DEFUN(cfg_neighbor_del_bts_nr, cfg_neighbor_del_bts_nr_cmd, + NEIGHBOR_DEL_CMD "bts <0-255>", + NEIGHBOR_DEL_DOC "Neighbor cell by local BTS number\n" "BTS number\n") +{ + return del_local_bts(vty, neighbor_ident_vty_parse_bts_nr(vty, argv)); +} + +DEFUN(cfg_neighbor_del_arfcn_bsic, cfg_neighbor_del_arfcn_bsic_cmd, + NEIGHBOR_DEL_CMD NEIGHBOR_IDENT_VTY_KEY_PARAMS, + NEIGHBOR_DEL_DOC NEIGHBOR_IDENT_VTY_KEY_DOC) +{ + struct neighbor_ident_key key; + + if (!neighbor_ident_vty_parse_key_params(vty, argv, &key)) + return CMD_WARNING; + + return del_by_key(vty, &key); +} + +struct write_neighbor_ident_entry_data { + struct vty *vty; + const char *indent; + struct gsm_bts *bts; +}; + +static bool write_neighbor_ident_list(const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val, + void *cb_data) +{ + struct write_neighbor_ident_entry_data *d = cb_data; + struct vty *vty = d->vty; + int i; + + if (d->bts) { + if (d->bts->nr != key->from_bts) + return true; + } else if (key->from_bts != NEIGHBOR_IDENT_KEY_ANY_BTS) + return true; + +#define NEIGH_BSS_WRITE(fmt, args...) do { \ + vty_out(vty, "%sneighbor add " fmt " arfcn %u ", d->indent, ## args, key->arfcn); \ + switch (key->bsic_kind) { \ + default: \ + case BSIC_NONE: \ + vty_out(vty, "bsic any"); \ + break; \ + case BSIC_6BIT: \ + vty_out(vty, "bsic %u", key->bsic & 0x3f); \ + break; \ + case BSIC_9BIT: \ + vty_out(vty, "bsic9 %u", key->bsic & 0x1ff); \ + break; \ + } \ + vty_out(vty, "%s", VTY_NEWLINE); \ + } while(0) + + switch (val->id_discr) { + case CELL_IDENT_LAC: + for (i = 0; i < val->id_list_len; i++) { + NEIGH_BSS_WRITE("lac %u", val->id_list[i].lac); + } + break; + case CELL_IDENT_LAC_AND_CI: + for (i = 0; i < val->id_list_len; i++) { + NEIGH_BSS_WRITE("lac-ci %u %u", + val->id_list[i].lac_and_ci.lac, + val->id_list[i].lac_and_ci.ci); + } + break; + case CELL_IDENT_WHOLE_GLOBAL: + for (i = 0; i < val->id_list_len; i++) { + const struct osmo_cell_global_id *cgi = &val->id_list[i].global; + NEIGH_BSS_WRITE("cgi %s %s %u %u", + osmo_mcc_name(cgi->lai.plmn.mcc), + osmo_mnc_name(cgi->lai.plmn.mnc, cgi->lai.plmn.mnc_3_digits), + cgi->lai.lac, cgi->cell_identity); + } + break; + default: + vty_out(vty, "%% Unsupported Cell Identity%s", VTY_NEWLINE); + } +#undef NEIGH_BSS_WRITE + + return true; +} + +void neighbor_ident_vty_write_remote_bss(struct vty *vty, const char *indent, struct gsm_bts *bts) +{ + struct write_neighbor_ident_entry_data d = { + .vty = vty, + .indent = indent, + .bts = bts, + }; + + neighbor_ident_iter(g_neighbor_cells, write_neighbor_ident_list, &d); +} + +void neighbor_ident_vty_write_local_neighbors(struct vty *vty, const char *indent, struct gsm_bts *bts) +{ + struct gsm_bts_ref *neigh; + + llist_for_each_entry(neigh, &bts->local_neighbors, entry) { + vty_out(vty, "%sneighbor add lac-ci %u %u%s", + indent, neigh->bts->location_area_code, neigh->bts->cell_identity, + VTY_NEWLINE); + } +} + +void neighbor_ident_vty_write(struct vty *vty, const char *indent, struct gsm_bts *bts) +{ + neighbor_ident_vty_write_local_neighbors(vty, indent, bts); + neighbor_ident_vty_write_remote_bss(vty, indent, bts); +} + +DEFUN(show_bts_neighbor, show_bts_neighbor_cmd, + "show bts <0-255> neighbor " NEIGHBOR_IDENT_VTY_KEY_PARAMS, + SHOW_STR "Display information about a BTS\n" "BTS number\n" + "Query which cell would be the target for this neighbor ARFCN+BSIC\n" + NEIGHBOR_IDENT_VTY_KEY_DOC) +{ + int found = 0; + struct neighbor_ident_key key; + struct gsm_bts_ref *neigh; + const struct gsm0808_cell_id_list2 *res; + struct gsm_bts *bts = gsm_bts_num(g_net, atoi(argv[0])); + struct write_neighbor_ident_entry_data d = { + .vty = vty, + .indent = "% ", + .bts = bts, + }; + + if (!bts) { + vty_out(vty, "%% Error: cannot find BTS '%s'%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + if (!neighbor_ident_bts_parse_key_params(vty, bts, &argv[1], &key)) + return CMD_WARNING; + + /* Is there a local BTS that matches the key? */ + llist_for_each_entry(neigh, &bts->local_neighbors, entry) { + if (!neighbor_ident_key_matches_bts(&key, neigh->bts)) + continue; + vty_out(vty, "%% %s resolves to local BTS %u lac-ci %u %u%s", + neighbor_ident_key_name(&key), neigh->bts->nr, neigh->bts->location_area_code, + neigh->bts->cell_identity, VTY_NEWLINE); + found++; + } + + res = neighbor_ident_get(g_neighbor_cells, &key); + if (res) { + write_neighbor_ident_list(&key, res, &d); + found++; + } + + if (!found) + vty_out(vty, "%% No entry for %s%s", neighbor_ident_key_name(&key), VTY_NEWLINE); + + return CMD_SUCCESS; +} + +void neighbor_ident_vty_init(struct gsm_network *net, struct neighbor_ident_list *nil) +{ + g_net = net; + g_neighbor_cells = nil; + install_element(BTS_NODE, &cfg_neighbor_add_bts_nr_cmd); + install_element(BTS_NODE, &cfg_neighbor_add_lac_cmd); + install_element(BTS_NODE, &cfg_neighbor_add_lac_ci_cmd); + install_element(BTS_NODE, &cfg_neighbor_add_lac_arfcn_bsic_cmd); + install_element(BTS_NODE, &cfg_neighbor_add_lac_ci_arfcn_bsic_cmd); + install_element(BTS_NODE, &cfg_neighbor_add_cgi_arfcn_bsic_cmd); + install_element(BTS_NODE, &cfg_neighbor_del_bts_nr_cmd); + install_element(BTS_NODE, &cfg_neighbor_del_arfcn_bsic_cmd); + install_element_ve(&show_bts_neighbor_cmd); +} diff --git a/src/osmo-bsc/net_init.c b/src/osmo-bsc/net_init.c index 3ee35fe11..db3d01c1c 100644 --- a/src/osmo-bsc/net_init.c +++ b/src/osmo-bsc/net_init.c @@ -22,6 +22,7 @@ #include <osmocom/bsc/gsm_04_08_utils.h> #include <osmocom/bsc/handover_cfg.h> #include <osmocom/bsc/chan_alloc.h> +#include <osmocom/bsc/neighbor_ident.h> /* Initialize the bare minimum of struct gsm_network, minimizing required dependencies. * This part is shared among the thin programs in osmo-bsc/src/utils/. diff --git a/src/osmo-bsc/system_information.c b/src/osmo-bsc/system_information.c index f810f526c..3c0c76c46 100644 --- a/src/osmo-bsc/system_information.c +++ b/src/osmo-bsc/system_information.c @@ -40,6 +40,9 @@ #include <osmocom/bsc/arfcn_range_encode.h> #include <osmocom/bsc/gsm_04_08_utils.h> #include <osmocom/bsc/acc_ramp.h> +#include <osmocom/bsc/neighbor_ident.h> + +struct gsm0808_cell_id_list2; /* * DCS1800 and PCS1900 have overlapping ARFCNs. We would need to set the @@ -588,6 +591,25 @@ static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv, return bitvec2freq_list(chan_list, bv, bts, false, false); } +struct generate_bcch_chan_list__ni_iter_data { + struct gsm_bts *bts; + struct bitvec *bv; +}; + +static bool generate_bcch_chan_list__ni_iter_cb(const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val, + void *cb_data) +{ + struct generate_bcch_chan_list__ni_iter_data *data = cb_data; + + if (key->from_bts != NEIGHBOR_IDENT_KEY_ANY_BTS + && key->from_bts != data->bts->nr) + return true; + + bitvec_set_bit_pos(data->bv, key->arfcn, 1); + return true; +} + /*! generate a cell channel list as per Section 10.5.2.22 of 04.08 * \param[out] chan_list caller-provided output buffer * \param[in] bts BTS descriptor used for input data @@ -602,6 +624,7 @@ static int generate_bcch_chan_list(uint8_t *chan_list, struct gsm_bts *bts, struct bitvec *bv; int rc; + /* first we generate a bitvec of the BCCH ARFCN's in our BSC */ if (si5 && bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) bv = &bts->si_common.si5_neigh_list; else @@ -612,11 +635,29 @@ static int generate_bcch_chan_list(uint8_t *chan_list, struct gsm_bts *bts, /* Zero-initialize the bit-vector */ memset(bv->data, 0, bv->data_len); - /* first we generate a bitvec of the BCCH ARFCN's in our BSC */ - llist_for_each_entry(cur_bts, &bts->network->bts_list, list) { - if (cur_bts == bts) - continue; - bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1); + if (llist_empty(&bts->local_neighbors)) { + /* There are no explicit neighbors, assume all BTS are. */ + llist_for_each_entry(cur_bts, &bts->network->bts_list, list) { + if (cur_bts == bts) + continue; + bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1); + } + } else { + /* Only add explicit neighbor cells */ + struct gsm_bts_ref *neigh; + llist_for_each_entry(neigh, &bts->local_neighbors, entry) { + bitvec_set_bit_pos(bv, neigh->bts->c0->arfcn, 1); + } + } + + /* Also add neighboring BSS cells' ARFCNs */ + { + struct generate_bcch_chan_list__ni_iter_data data = { + .bv = bv, + .bts = bts, + }; + neighbor_ident_iter(bts->network->neighbor_bss_cells, + generate_bcch_chan_list__ni_iter_cb, &data); } } diff --git a/tests/bsc/Makefile.am b/tests/bsc/Makefile.am index a930629fa..2e34d7995 100644 --- a/tests/bsc/Makefile.am +++ b/tests/bsc/Makefile.am @@ -45,6 +45,7 @@ bsc_test_LDADD = \ $(top_builddir)/src/osmo-bsc/gsm_data.o \ $(top_builddir)/src/osmo-bsc/handover_cfg.o \ $(top_builddir)/src/osmo-bsc/handover_logic.o \ + $(top_builddir)/src/osmo-bsc/neighbor_ident.o \ $(top_builddir)/src/osmo-bsc/net_init.o \ $(top_builddir)/src/osmo-bsc/paging.o \ $(top_builddir)/src/osmo-bsc/pcu_sock.o \ diff --git a/tests/gsm0408/Makefile.am b/tests/gsm0408/Makefile.am index 6d10b9fd9..3eb47f650 100644 --- a/tests/gsm0408/Makefile.am +++ b/tests/gsm0408/Makefile.am @@ -28,6 +28,7 @@ gsm0408_test_LDADD = \ $(top_builddir)/src/osmo-bsc/net_init.o \ $(top_builddir)/src/osmo-bsc/rest_octets.o \ $(top_builddir)/src/osmo-bsc/system_information.o \ + $(top_builddir)/src/osmo-bsc/neighbor_ident.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOABIS_LIBS) \ diff --git a/tests/handover/Makefile.am b/tests/handover/Makefile.am index 07491d57f..140d8ff05 100644 --- a/tests/handover/Makefile.am +++ b/tests/handover/Makefile.am @@ -19,10 +19,13 @@ AM_LDFLAGS = \ EXTRA_DIST = \ handover_test.ok \ + neighbor_ident_test.ok \ + neighbor_ident_test.err \ $(NULL) noinst_PROGRAMS = \ handover_test \ + neighbor_ident_test \ $(NULL) handover_test_SOURCES = \ @@ -56,6 +59,7 @@ handover_test_LDADD = \ $(top_builddir)/src/osmo-bsc/handover_decision_2.o \ $(top_builddir)/src/osmo-bsc/handover_logic.o \ $(top_builddir)/src/osmo-bsc/meas_rep.o \ + $(top_builddir)/src/osmo-bsc/neighbor_ident.o \ $(top_builddir)/src/osmo-bsc/osmo_bsc_lcls.o \ $(top_builddir)/src/osmo-bsc/net_init.o \ $(top_builddir)/src/osmo-bsc/paging.o \ @@ -69,3 +73,17 @@ handover_test_LDADD = \ $(LIBOSMOSIGTRAN_LIBS) \ $(LIBOSMOMGCPCLIENT_LIBS) \ $(NULL) + +neighbor_ident_test_SOURCES = \ + neighbor_ident_test.c \ + $(NULL) + +neighbor_ident_test_LDADD = \ + $(top_builddir)/src/osmo-bsc/neighbor_ident.o \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(NULL) + +.PHONY: update_exp +update_exp: + $(builddir)/neighbor_ident_test >$(srcdir)/neighbor_ident_test.ok 2>$(srcdir)/neighbor_ident_test.err diff --git a/tests/handover/neighbor_ident_test.c b/tests/handover/neighbor_ident_test.c new file mode 100644 index 000000000..b67219cdf --- /dev/null +++ b/tests/handover/neighbor_ident_test.c @@ -0,0 +1,278 @@ +/* Test the neighbor_ident.h API */ +/* + * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> + * + * 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 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <talloc.h> +#include <stdio.h> +#include <errno.h> + +#include <osmocom/gsm/gsm0808.h> + +#include <osmocom/bsc/neighbor_ident.h> + +struct neighbor_ident_list *nil; + +static const struct neighbor_ident_key *k(int from_bts, uint16_t arfcn, enum bsic_kind kind, uint16_t bsic) +{ + static struct neighbor_ident_key key; + key = (struct neighbor_ident_key){ + .from_bts = from_bts, + .arfcn = arfcn, + .bsic_kind = kind, + .bsic = bsic, + }; + return &key; +} + +static 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, + } + }, + }, +}; + +static 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 = false }, + .lac = 3, + }, + .cell_identity = 4, + } + }, + { + .global = { + .lai = { + .plmn = { .mcc = 5, .mnc = 6, .mnc_3_digits = true }, + .lac = 7, + }, + .cell_identity = 8, + } + }, + }, +}; + +static const struct gsm0808_cell_id_list2 lac1 = { + .id_discr = CELL_IDENT_LAC, + .id_list_len = 1, + .id_list = { + { + .lac = 123 + }, + }, +}; + +static const struct gsm0808_cell_id_list2 lac2 = { + .id_discr = CELL_IDENT_LAC, + .id_list_len = 2, + .id_list = { + { + .lac = 456 + }, + { + .lac = 789 + }, + }, +}; + +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; + default: + printf(" Unimplemented id_disc\n"); + } +} + +static int print_nil_i; + +bool nil_cb(const struct neighbor_ident_key *key, const struct gsm0808_cell_id_list2 *val, + void *cb_data) +{ + printf(" %2d: %s\n", print_nil_i++, neighbor_ident_key_name(key)); + print_cil(val); + return true; +} + +void print_nil() +{ + print_nil_i = 0; + neighbor_ident_iter(nil, nil_cb, NULL); + if (!print_nil_i) + printf(" (empty)\n"); +} + +#define check_add(key, val, expect_rc) \ + do { \ + int rc; \ + rc = neighbor_ident_add(nil, key, val); \ + printf("neighbor_ident_add(" #key ", " #val ") --> expect rc=" #expect_rc ", got %d\n", rc); \ + if (rc != expect_rc) \ + printf("ERROR\n"); \ + print_nil(); \ + } while(0) + +#define check_del(key, expect_rc) \ + do { \ + bool rc; \ + rc = neighbor_ident_del(nil, key); \ + printf("neighbor_ident_del(" #key ") --> %s\n", rc ? "entry deleted" : "nothing deleted"); \ + if (rc != expect_rc) \ + printf("ERROR: expected: %s\n", expect_rc ? "entry deleted" : "nothing deleted"); \ + print_nil(); \ + } while(0) + +#define check_get(key, expect_rc) \ + do { \ + const struct gsm0808_cell_id_list2 *rc; \ + rc = neighbor_ident_get(nil, key); \ + printf("neighbor_ident_get(" #key ") --> %s\n", \ + rc ? "entry returned" : "NULL"); \ + if (((bool)expect_rc) != ((bool) rc)) \ + printf("ERROR: expected %s\n", expect_rc ? "an entry" : "NULL"); \ + if (rc) \ + print_cil(rc); \ + } while(0) + +int main(void) +{ + void *ctx = talloc_named_const(NULL, 0, "neighbor_ident_test"); + + printf("\n--- testing NULL neighbor_ident_list\n"); + nil = NULL; + check_add(k(0, 1, BSIC_6BIT, 2), &cgi1, -ENOMEM); + check_get(k(0, 1, BSIC_6BIT, 2), false); + check_del(k(0, 1, BSIC_6BIT, 2), false); + + printf("\n--- adding entries, test that no two identical entries are added\n"); + nil = neighbor_ident_init(ctx); + check_add(k(0, 1, BSIC_6BIT, 2), &cgi1, 1); + check_get(k(0, 1, BSIC_6BIT, 2), true); + check_add(k(0, 1, BSIC_6BIT, 2), &cgi1, 1); + check_add(k(0, 1, BSIC_6BIT, 2), &cgi2, 2); + check_add(k(0, 1, BSIC_6BIT, 2), &cgi2, 2); + check_del(k(0, 1, BSIC_6BIT, 2), true); + + printf("\n--- Cannot mix cell identifier types for one entry\n"); + check_add(k(0, 1, BSIC_6BIT, 2), &cgi1, 1); + check_add(k(0, 1, BSIC_6BIT, 2), &lac1, -EINVAL); + check_del(k(0, 1, BSIC_6BIT, 2), true); + neighbor_ident_free(nil); + + printf("\n--- BTS matching: specific BTS is stronger\n"); + nil = neighbor_ident_init(ctx); + check_add(k(NEIGHBOR_IDENT_KEY_ANY_BTS, 1, BSIC_6BIT, 2), &lac1, 1); + check_add(k(3, 1, BSIC_6BIT, 2), &lac2, 2); + check_get(k(2, 1, BSIC_6BIT, 2), true); + check_get(k(3, 1, BSIC_6BIT, 2), true); + check_get(k(4, 1, BSIC_6BIT, 2), true); + check_get(k(NEIGHBOR_IDENT_KEY_ANY_BTS, 1, BSIC_6BIT, 2), true); + neighbor_ident_free(nil); + + printf("\n--- BSIC matching: 6bit and 9bit are different realms, and wildcard match is weaker\n"); + nil = neighbor_ident_init(ctx); + check_add(k(0, 1, BSIC_NONE, 0), &cgi1, 1); + check_add(k(0, 1, BSIC_6BIT, 2), &lac1, 1); + check_add(k(0, 1, BSIC_9BIT, 2), &lac2, 2); + check_get(k(0, 1, BSIC_6BIT, 2), true); + check_get(k(0, 1, BSIC_9BIT, 2), true); + printf("--- wildcard matches both 6bit and 9bit BSIC regardless:\n"); + check_get(k(0, 1, BSIC_6BIT, 23), true); + check_get(k(0, 1, BSIC_9BIT, 23), true); + neighbor_ident_free(nil); + + printf("\n--- Value ranges\n"); + nil = neighbor_ident_init(ctx); + check_add(k(0, 6, BSIC_6BIT, 1 << 6), &lac1, -ERANGE); + check_add(k(0, 9, BSIC_9BIT, 1 << 9), &lac1, -ERANGE); + check_add(k(0, 6, BSIC_6BIT, -1), &lac1, -ERANGE); + check_add(k(0, 9, BSIC_9BIT, -1), &lac1, -ERANGE); + check_add(k(NEIGHBOR_IDENT_KEY_ANY_BTS - 1, 1, BSIC_NONE, 1), &cgi2, -ERANGE); + check_add(k(256, 1, BSIC_NONE, 1), &cgi2, -ERANGE); + check_add(k(0, 0, BSIC_NONE, 0), &cgi1, 1); + check_add(k(255, 65535, BSIC_NONE, 65535), &lac1, 1); + check_add(k(0, 0, BSIC_6BIT, 0), &cgi2, 2); + check_add(k(255, 65535, BSIC_6BIT, 0x3f), &lac2, 2); + check_add(k(0, 0, BSIC_9BIT, 0), &cgi1, 1); + check_add(k(255, 65535, BSIC_9BIT, 0x1ff), &cgi2, 2); + + neighbor_ident_free(nil); + + printf("\n--- size limits\n"); + { + int i; + struct gsm0808_cell_id_list2 a = { .id_discr = CELL_IDENT_LAC }; + struct gsm0808_cell_id_list2 b = { + .id_discr = CELL_IDENT_LAC, + .id_list = { + { .lac = 423 } + }, + .id_list_len = 1, + }; + for (i = 0; i < ARRAY_SIZE(a.id_list); i++) { + a.id_list[a.id_list_len ++].lac = i; + } + + nil = neighbor_ident_init(ctx); + + i = neighbor_ident_add(nil, k(0, 1, BSIC_6BIT, 2), &a); + printf("Added first cell identifier list (added %u) --> rc = %d\n", a.id_list_len, i); + i = neighbor_ident_add(nil, k(0, 1, BSIC_6BIT, 2), &b); + printf("Added second cell identifier list (tried to add %u) --> rc = %d\n", b.id_list_len, i); + if (i != -ENOSPC) + printf("ERROR: expected rc=%d\n", -ENOSPC); + neighbor_ident_free(nil); + } + + OSMO_ASSERT(talloc_total_blocks(ctx) == 1); + talloc_free(ctx); + + return 0; +} diff --git a/tests/handover/neighbor_ident_test.err b/tests/handover/neighbor_ident_test.err new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/handover/neighbor_ident_test.err diff --git a/tests/handover/neighbor_ident_test.ok b/tests/handover/neighbor_ident_test.ok new file mode 100644 index 000000000..280b6f209 --- /dev/null +++ b/tests/handover/neighbor_ident_test.ok @@ -0,0 +1,249 @@ + +--- testing NULL neighbor_ident_list +neighbor_ident_add(k(0, 1, BSIC_6BIT, 2), &cgi1) --> expect rc=-ENOMEM, got -12 + (empty) +neighbor_ident_get(k(0, 1, BSIC_6BIT, 2)) --> NULL +neighbor_ident_del(k(0, 1, BSIC_6BIT, 2)) --> nothing deleted + (empty) + +--- adding entries, test that no two identical entries are added +neighbor_ident_add(k(0, 1, BSIC_6BIT, 2), &cgi1) --> expect rc=1, got 1 + 0: BTS 0 to ARFCN 1 BSIC 2 + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } +neighbor_ident_get(k(0, 1, BSIC_6BIT, 2)) --> entry returned + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } +neighbor_ident_add(k(0, 1, BSIC_6BIT, 2), &cgi1) --> expect rc=1, got 1 + 0: BTS 0 to ARFCN 1 BSIC 2 + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } +neighbor_ident_add(k(0, 1, BSIC_6BIT, 2), &cgi2) --> expect rc=2, got 2 + 0: BTS 0 to ARFCN 1 BSIC 2 + cell_id_list cgi[2] = { + 0: 001-02-3-4 + 1: 005-006-7-8 + } +neighbor_ident_add(k(0, 1, BSIC_6BIT, 2), &cgi2) --> expect rc=2, got 2 + 0: BTS 0 to ARFCN 1 BSIC 2 + cell_id_list cgi[2] = { + 0: 001-02-3-4 + 1: 005-006-7-8 + } +neighbor_ident_del(k(0, 1, BSIC_6BIT, 2)) --> entry deleted + (empty) + +--- Cannot mix cell identifier types for one entry +neighbor_ident_add(k(0, 1, BSIC_6BIT, 2), &cgi1) --> expect rc=1, got 1 + 0: BTS 0 to ARFCN 1 BSIC 2 + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } +neighbor_ident_add(k(0, 1, BSIC_6BIT, 2), &lac1) --> expect rc=-EINVAL, got -22 + 0: BTS 0 to ARFCN 1 BSIC 2 + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } +neighbor_ident_del(k(0, 1, BSIC_6BIT, 2)) --> entry deleted + (empty) + +--- BTS matching: specific BTS is stronger +neighbor_ident_add(k(NEIGHBOR_IDENT_KEY_ANY_BTS, 1, BSIC_6BIT, 2), &lac1) --> expect rc=1, got 1 + 0: BTS * to ARFCN 1 BSIC 2 + cell_id_list lac[1] = { + 0: 123 + } +neighbor_ident_add(k(3, 1, BSIC_6BIT, 2), &lac2) --> expect rc=2, got 2 + 0: BTS * to ARFCN 1 BSIC 2 + cell_id_list lac[1] = { + 0: 123 + } + 1: BTS 3 to ARFCN 1 BSIC 2 + cell_id_list lac[2] = { + 0: 456 + 1: 789 + } +neighbor_ident_get(k(2, 1, BSIC_6BIT, 2)) --> entry returned + cell_id_list lac[1] = { + 0: 123 + } +neighbor_ident_get(k(3, 1, BSIC_6BIT, 2)) --> entry returned + cell_id_list lac[2] = { + 0: 456 + 1: 789 + } +neighbor_ident_get(k(4, 1, BSIC_6BIT, 2)) --> entry returned + cell_id_list lac[1] = { + 0: 123 + } +neighbor_ident_get(k(NEIGHBOR_IDENT_KEY_ANY_BTS, 1, BSIC_6BIT, 2)) --> entry returned + cell_id_list lac[1] = { + 0: 123 + } + +--- BSIC matching: 6bit and 9bit are different realms, and wildcard match is weaker +neighbor_ident_add(k(0, 1, BSIC_NONE, 0), &cgi1) --> expect rc=1, got 1 + 0: BTS 0 to ARFCN 1 (any BSIC) + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } +neighbor_ident_add(k(0, 1, BSIC_6BIT, 2), &lac1) --> expect rc=1, got 1 + 0: BTS 0 to ARFCN 1 (any BSIC) + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } + 1: BTS 0 to ARFCN 1 BSIC 2 + cell_id_list lac[1] = { + 0: 123 + } +neighbor_ident_add(k(0, 1, BSIC_9BIT, 2), &lac2) --> expect rc=2, got 2 + 0: BTS 0 to ARFCN 1 (any BSIC) + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } + 1: BTS 0 to ARFCN 1 BSIC 2 + cell_id_list lac[1] = { + 0: 123 + } + 2: BTS 0 to ARFCN 1 BSIC 2(9bit) + cell_id_list lac[2] = { + 0: 456 + 1: 789 + } +neighbor_ident_get(k(0, 1, BSIC_6BIT, 2)) --> entry returned + cell_id_list lac[1] = { + 0: 123 + } +neighbor_ident_get(k(0, 1, BSIC_9BIT, 2)) --> entry returned + cell_id_list lac[2] = { + 0: 456 + 1: 789 + } +--- wildcard matches both 6bit and 9bit BSIC regardless: +neighbor_ident_get(k(0, 1, BSIC_6BIT, 23)) --> entry returned + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } +neighbor_ident_get(k(0, 1, BSIC_9BIT, 23)) --> entry returned + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } + +--- Value ranges +neighbor_ident_add(k(0, 6, BSIC_6BIT, 1 << 6), &lac1) --> expect rc=-ERANGE, got -34 + (empty) +neighbor_ident_add(k(0, 9, BSIC_9BIT, 1 << 9), &lac1) --> expect rc=-ERANGE, got -34 + (empty) +neighbor_ident_add(k(0, 6, BSIC_6BIT, -1), &lac1) --> expect rc=-ERANGE, got -34 + (empty) +neighbor_ident_add(k(0, 9, BSIC_9BIT, -1), &lac1) --> expect rc=-ERANGE, got -34 + (empty) +neighbor_ident_add(k(NEIGHBOR_IDENT_KEY_ANY_BTS - 1, 1, BSIC_NONE, 1), &cgi2) --> expect rc=-ERANGE, got -34 + (empty) +neighbor_ident_add(k(256, 1, BSIC_NONE, 1), &cgi2) --> expect rc=-ERANGE, got -34 + (empty) +neighbor_ident_add(k(0, 0, BSIC_NONE, 0), &cgi1) --> expect rc=1, got 1 + 0: BTS 0 to ARFCN 0 (any BSIC) + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } +neighbor_ident_add(k(255, 65535, BSIC_NONE, 65535), &lac1) --> expect rc=1, got 1 + 0: BTS 0 to ARFCN 0 (any BSIC) + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } + 1: BTS 255 to ARFCN 65535 (any BSIC) + cell_id_list lac[1] = { + 0: 123 + } +neighbor_ident_add(k(0, 0, BSIC_6BIT, 0), &cgi2) --> expect rc=2, got 2 + 0: BTS 0 to ARFCN 0 (any BSIC) + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } + 1: BTS 255 to ARFCN 65535 (any BSIC) + cell_id_list lac[1] = { + 0: 123 + } + 2: BTS 0 to ARFCN 0 BSIC 0 + cell_id_list cgi[2] = { + 0: 001-02-3-4 + 1: 005-006-7-8 + } +neighbor_ident_add(k(255, 65535, BSIC_6BIT, 0x3f), &lac2) --> expect rc=2, got 2 + 0: BTS 0 to ARFCN 0 (any BSIC) + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } + 1: BTS 255 to ARFCN 65535 (any BSIC) + cell_id_list lac[1] = { + 0: 123 + } + 2: BTS 0 to ARFCN 0 BSIC 0 + cell_id_list cgi[2] = { + 0: 001-02-3-4 + 1: 005-006-7-8 + } + 3: BTS 255 to ARFCN 65535 BSIC 63 + cell_id_list lac[2] = { + 0: 456 + 1: 789 + } +neighbor_ident_add(k(0, 0, BSIC_9BIT, 0), &cgi1) --> expect rc=1, got 1 + 0: BTS 0 to ARFCN 0 (any BSIC) + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } + 1: BTS 255 to ARFCN 65535 (any BSIC) + cell_id_list lac[1] = { + 0: 123 + } + 2: BTS 0 to ARFCN 0 BSIC 0 + cell_id_list cgi[2] = { + 0: 001-02-3-4 + 1: 005-006-7-8 + } + 3: BTS 255 to ARFCN 65535 BSIC 63 + cell_id_list lac[2] = { + 0: 456 + 1: 789 + } + 4: BTS 0 to ARFCN 0 BSIC 0(9bit) + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } +neighbor_ident_add(k(255, 65535, BSIC_9BIT, 0x1ff), &cgi2) --> expect rc=2, got 2 + 0: BTS 0 to ARFCN 0 (any BSIC) + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } + 1: BTS 255 to ARFCN 65535 (any BSIC) + cell_id_list lac[1] = { + 0: 123 + } + 2: BTS 0 to ARFCN 0 BSIC 0 + cell_id_list cgi[2] = { + 0: 001-02-3-4 + 1: 005-006-7-8 + } + 3: BTS 255 to ARFCN 65535 BSIC 63 + cell_id_list lac[2] = { + 0: 456 + 1: 789 + } + 4: BTS 0 to ARFCN 0 BSIC 0(9bit) + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } + 5: BTS 255 to ARFCN 65535 BSIC 511(9bit) + cell_id_list cgi[2] = { + 0: 001-02-3-4 + 1: 005-006-7-8 + } + +--- size limits +Added first cell identifier list (added 127) --> rc = 127 +Added second cell identifier list (tried to add 1) --> rc = -28 diff --git a/tests/neighbor_ident.vty b/tests/neighbor_ident.vty new file mode 100644 index 000000000..11227ed0d --- /dev/null +++ b/tests/neighbor_ident.vty @@ -0,0 +1,260 @@ +OsmoBSC> ### Neighbor-BSS Cell Identifier List config + +OsmoBSC> list +... + show bts <0-255> neighbor arfcn <0-1023> (bsic|bsic9) (<0-511>|any) +... + +OsmoBSC> enable +OsmoBSC# list +... + show bts <0-255> neighbor arfcn <0-1023> (bsic|bsic9) (<0-511>|any) +... + +OsmoBSC# configure terminal +OsmoBSC(config)# network + +OsmoBSC(config-net)# bts 0 +OsmoBSC(config-net-bts)# type sysmobts +OsmoBSC(config-net-bts)# base_station_id_code 10 +OsmoBSC(config-net-bts)# location_area_code 20 +OsmoBSC(config-net-bts)# cell_identity 30 +OsmoBSC(config-net-bts)# trx 0 +OsmoBSC(config-net-bts-trx)# arfcn 40 +OsmoBSC(config-net-bts-trx)# exit +OsmoBSC(config-net-bts)# exit + +OsmoBSC(config-net)# bts 1 +OsmoBSC(config-net-bts)# type sysmobts +OsmoBSC(config-net-bts)# base_station_id_code 11 +OsmoBSC(config-net-bts)# location_area_code 21 +OsmoBSC(config-net-bts)# cell_identity 31 +OsmoBSC(config-net-bts)# trx 0 +OsmoBSC(config-net-bts-trx)# arfcn 41 +OsmoBSC(config-net-bts-trx)# exit +OsmoBSC(config-net-bts)# exit + +OsmoBSC(config-net)# bts 2 +OsmoBSC(config-net-bts)# type sysmobts +OsmoBSC(config-net-bts)# base_station_id_code 12 +OsmoBSC(config-net-bts)# location_area_code 22 +OsmoBSC(config-net-bts)# cell_identity 32 +OsmoBSC(config-net-bts)# trx 0 +OsmoBSC(config-net-bts-trx)# arfcn 42 +OsmoBSC(config-net-bts-trx)# exit +OsmoBSC(config-net-bts)# exit + +OsmoBSC(config-net)# show running-config +... + bts 0 +... + cell_identity 30 + location_area_code 20 + base_station_id_code 10 +... + trx 0 +... + arfcn 40 +... + bts 1 +... + cell_identity 31 + location_area_code 21 + base_station_id_code 11 +... + trx 0 +... + arfcn 41 +... + bts 2 +... + cell_identity 32 + location_area_code 22 + base_station_id_code 12 +... + trx 0 +... + arfcn 42 +... + +OsmoBSC(config-net)# bts 0 +OsmoBSC(config-net-bts)# list +... + neighbor add bts <0-255> + neighbor add lac <0-65535> + neighbor add lac-ci <0-65535> <0-255> + neighbor add lac <0-65535> arfcn <0-1023> (bsic|bsic9) (<0-511>|any) + neighbor add lac-ci <0-65535> <0-255> arfcn <0-1023> (bsic|bsic9) (<0-511>|any) + neighbor add cgi <0-999> <0-999> <0-65535> <0-255> arfcn <0-1023> (bsic|bsic9) (<0-511>|any) + neighbor del bts <0-255> + neighbor del arfcn <0-1023> (bsic|bsic9) (<0-511>|any) +... + +OsmoBSC(config-net-bts)# show running-config +... !neighbor add + +OsmoBSC(config-net-bts)# neighbor add cgi 23 42 423 5 arfcn 23 bsic 64 +% Error: BSIC value surpasses 6-bit range: 64, use 'bsic9' instead + +OsmoBSC(config-net-bts)# neighbor add bts 0 +% Error: cannot add local BTS 0 as neighbor to BTS 0: Invalid argument + +OsmoBSC(config-net-bts)# show running-config +... !neighbor add + +OsmoBSC(config-net-bts)# neighbor add bts 1 +% BTS 0 now has local neighbor BTS 1 with LAC 21 CI 31 and ARFCN 41 BSIC 11 + +OsmoBSC(config-net-bts)# neighbor add lac 22 +% BTS 0 now has local neighbor BTS 2 with LAC 22 CI 32 and ARFCN 42 BSIC 12 + +OsmoBSC(config-net-bts)# neighbor add cgi 23 42 423 5 arfcn 23 bsic 42 +% BTS 0 to ARFCN 23 BSIC 42 now has 1 remote BSS Cell Identifier List entry + +OsmoBSC(config-net-bts)# ### adding the same entry again results in no change +OsmoBSC(config-net-bts)# neighbor add bts 1 +% BTS 0 already had local neighbor BTS 1 with LAC 21 CI 31 and ARFCN 41 BSIC 11 +OsmoBSC(config-net-bts)# neighbor add lac-ci 21 31 +% BTS 0 already had local neighbor BTS 1 with LAC 21 CI 31 and ARFCN 41 BSIC 11 +OsmoBSC(config-net-bts)# neighbor add cgi 23 42 423 5 arfcn 23 bsic 42 +% BTS 0 to ARFCN 23 BSIC 42 now has 1 remote BSS Cell Identifier List entry +OsmoBSC(config-net-bts)# neighbor add cgi 23 42 423 5 arfcn 23 bsic 42 +% BTS 0 to ARFCN 23 BSIC 42 now has 1 remote BSS Cell Identifier List entry +OsmoBSC(config-net-bts)# neighbor add cgi 23 42 423 5 arfcn 23 bsic 42 +% BTS 0 to ARFCN 23 BSIC 42 now has 1 remote BSS Cell Identifier List entry + +OsmoBSC(config-net-bts)# neighbor add cgi 23 042 423 6 arfcn 23 bsic 42 +% BTS 0 to ARFCN 23 BSIC 42 now has 2 remote BSS Cell Identifier List entries + +OsmoBSC(config-net-bts)# neighbor add lac 456 arfcn 123 bsic 45 +% BTS 0 to ARFCN 123 BSIC 45 now has 1 remote BSS Cell Identifier List entry + +OsmoBSC(config-net-bts)# neighbor add cgi 23 042 234 56 arfcn 23 bsic 42 +% BTS 0 to ARFCN 23 BSIC 42 now has 3 remote BSS Cell Identifier List entries + +OsmoBSC(config-net-bts)# neighbor add lac-ci 789 10 arfcn 423 bsic any +% BTS 0 to ARFCN 423 (any BSIC) now has 1 remote BSS Cell Identifier List entry + +OsmoBSC(config-net-bts)# neighbor add lac-ci 789 10 arfcn 423 bsic9 511 +% BTS 0 to ARFCN 423 BSIC 511(9bit) now has 1 remote BSS Cell Identifier List entry + +OsmoBSC(config-net-bts)# neighbor add lac-ci 789 10 arfcn 423 bsic9 1 +% BTS 0 to ARFCN 423 BSIC 1(9bit) now has 1 remote BSS Cell Identifier List entry + +OsmoBSC(config-net-bts)# neighbor add lac-ci 789 10 arfcn 423 bsic 1 +% BTS 0 to ARFCN 423 BSIC 1 now has 1 remote BSS Cell Identifier List entry + +OsmoBSC(config-net-bts)# show running-config +... +network +... !neighbor add + bts 0 +... !neighbor add + neighbor add lac-ci 21 31 + neighbor add lac-ci 22 32 + neighbor add cgi 023 42 423 5 arfcn 23 bsic 42 + neighbor add cgi 023 042 423 6 arfcn 23 bsic 42 + neighbor add cgi 023 042 234 56 arfcn 23 bsic 42 + neighbor add lac 456 arfcn 123 bsic 45 + neighbor add lac-ci 789 10 arfcn 423 bsic any + neighbor add lac-ci 789 10 arfcn 423 bsic9 511 + neighbor add lac-ci 789 10 arfcn 423 bsic9 1 + neighbor add lac-ci 789 10 arfcn 423 bsic 1 +... !neighbor add + +OsmoBSC(config-net-bts)# do show bts 0 neighbor arfcn 99 bsic any +% No entry for BTS 0 to ARFCN 99 (any BSIC) + +OsmoBSC(config-net-bts)# do show bts 0 neighbor arfcn 41 bsic any +% BTS 0 to ARFCN 41 (any BSIC) resolves to local BTS 1 lac-ci 21 31 + +OsmoBSC(config-net-bts)# do show bts 0 neighbor arfcn 423 bsic 1 +% neighbor add lac-ci 789 10 arfcn 423 bsic 1 + +OsmoBSC(config-net-bts)# do show bts 0 neighbor arfcn 423 bsic 23 +% neighbor add lac-ci 789 10 arfcn 423 bsic 23 + +OsmoBSC(config-net-bts)# neighbor del arfcn 99 bsic 7 +% Cannot remove, no such neighbor: BTS 0 to ARFCN 99 BSIC 7 + +OsmoBSC(config-net-bts)# neighbor del arfcn 23 bsic 42 +% Removed remote BSS neighbor BTS 0 to ARFCN 23 BSIC 42 + +OsmoBSC(config-net-bts)# show running-config +... !neighbor add + neighbor add lac-ci 21 31 + neighbor add lac-ci 22 32 + neighbor add lac 456 arfcn 123 bsic 45 + neighbor add lac-ci 789 10 arfcn 423 bsic any + neighbor add lac-ci 789 10 arfcn 423 bsic9 511 + neighbor add lac-ci 789 10 arfcn 423 bsic9 1 + neighbor add lac-ci 789 10 arfcn 423 bsic 1 +... !neighbor add + +OsmoBSC(config-net-bts)# neighbor del arfcn 123 bsic 45 +% Removed remote BSS neighbor BTS 0 to ARFCN 123 BSIC 45 + +OsmoBSC(config-net-bts)# show running-config +... !neighbor add + neighbor add lac-ci 21 31 + neighbor add lac-ci 22 32 + neighbor add lac-ci 789 10 arfcn 423 bsic any + neighbor add lac-ci 789 10 arfcn 423 bsic9 511 + neighbor add lac-ci 789 10 arfcn 423 bsic9 1 + neighbor add lac-ci 789 10 arfcn 423 bsic 1 +... !neighbor add + +OsmoBSC(config-net-bts)# neighbor del arfcn 423 bsic any +% Removed remote BSS neighbor BTS 0 to ARFCN 423 (any BSIC) + +OsmoBSC(config-net-bts)# show running-config +... !neighbor add + neighbor add lac-ci 21 31 + neighbor add lac-ci 22 32 + neighbor add lac-ci 789 10 arfcn 423 bsic9 511 + neighbor add lac-ci 789 10 arfcn 423 bsic9 1 + neighbor add lac-ci 789 10 arfcn 423 bsic 1 +... !neighbor add + +OsmoBSC(config-net-bts)# neighbor del arfcn 423 bsic9 511 +% Removed remote BSS neighbor BTS 0 to ARFCN 423 BSIC 511(9bit) +OsmoBSC(config-net-bts)# neighbor del arfcn 423 bsic9 1 +% Removed remote BSS neighbor BTS 0 to ARFCN 423 BSIC 1(9bit) + +OsmoBSC(config-net-bts)# show running-config +... !neighbor add + neighbor add lac-ci 21 31 + neighbor add lac-ci 22 32 + neighbor add lac-ci 789 10 arfcn 423 bsic 1 +... !neighbor add + +OsmoBSC(config-net-bts)# neighbor del arfcn 423 bsic 1 +% Removed remote BSS neighbor BTS 0 to ARFCN 423 BSIC 1 + +OsmoBSC(config-net-bts)# show running-config +... !neighbor add + neighbor add lac-ci 21 31 + neighbor add lac-ci 22 32 +... !neighbor add + +OsmoBSC(config-net-bts)# neighbor del arfcn 41 bsic any +% Removed local neighbor bts 0 to bts 1 + +OsmoBSC(config-net-bts)# show running-config +... !neighbor add + neighbor add lac-ci 22 32 +... !neighbor add + +OsmoBSC(config-net-bts)# neighbor del arfcn 41 bsic any +% Cannot remove, no such neighbor: BTS 0 to ARFCN 41 (any BSIC) + +OsmoBSC(config-net-bts)# show running-config +... !neighbor add + neighbor add lac-ci 22 32 +... !neighbor add + +OsmoBSC(config-net-bts)# neighbor del arfcn 42 bsic 12 +% Removed local neighbor bts 0 to bts 2 + +OsmoBSC(config-net-bts)# show running-config +... !neighbor add diff --git a/tests/testsuite.at b/tests/testsuite.at index f38465554..1a190dda3 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -38,6 +38,13 @@ cat $abs_srcdir/nanobts_omlattr/nanobts_omlattr_test.ok > expout AT_CHECK([$abs_top_builddir/tests/nanobts_omlattr/nanobts_omlattr_test], [], [expout], [ignore]) AT_CLEANUP +AT_SETUP([neighbor_ident]) +AT_KEYWORDS([neighbor_ident]) +cat $abs_srcdir/handover/neighbor_ident_test.ok > expout +cat $abs_srcdir/handover/neighbor_ident_test.err > experr +AT_CHECK([$abs_top_builddir/tests/handover/neighbor_ident_test], [], [expout], [experr]) +AT_CLEANUP + AT_SETUP([handover test 0]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout |