From d77bd741c1c0e3dd83c95419a2a8c34703457bd9 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Fri, 21 Sep 2018 03:01:00 +0200 Subject: [PATCH] neighbor vty: identify neighbors by gsm0808 cell id For deleting neighbor entries and for triggering manual handovers, it is useful to identify neighbors by the gsm0808_cell_id, instead of just by ARFCN+BSIC. This is not a hard requirement, since BTS+ARFCN+BSIC fully identifies a neighbor. But consider these contrived examples of the situation before this patch: # bts 0 # neighbor cgi 001 01 23 42 # no neighbor cgi 001 01 23 42 % No such command # no neighbor bts 5 (manually looked up that BTS 5 has above CGI) # neighbor lac 42 arfcn 23 bsic 5 # do handover any to lac 42 % No such command # do handover any to arfcn 23 bsic 5 (if multiple cells have the same ARFCN+BSIC, the identification is not unique) So this fills a gap, if only to help debugging / analyzing handover operations. Change-Id: I198fe7055d1e09b128693eb51276e3d8cde1c0ba --- include/osmocom/bsc/gsm_data.h | 15 ++ src/osmo-bsc/Makefile.am | 1 + src/osmo-bsc/neighbor_ident_vty.c | 318 +++++++++++++++++++++++++----- src/osmo-bsc/neighbor_iter.c | 152 ++++++++++++++ tests/neighbor_ident.vty | 66 ++++++- 5 files changed, 502 insertions(+), 50 deletions(-) create mode 100644 src/osmo-bsc/neighbor_iter.c diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h index 7897fea86..4ab2e59a9 100644 --- a/include/osmocom/bsc/gsm_data.h +++ b/include/osmocom/bsc/gsm_data.h @@ -1152,6 +1152,21 @@ struct gsm_bts *gsm_bts_by_cell_id(const struct gsm_network *net, 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); +typedef bool (*neighbors_find_by_cell_id_cb_t)(struct gsm_bts *from_bts, + struct gsm_bts *neighbor_bts, + const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val, + int val_idx, + void *cb_data); +int neighbors_find_by_cell_id(struct gsm_network *net, + struct gsm_bts *for_bts, + struct neighbor_ident_list *neighbor_bss_cells, + const struct gsm0808_cell_id *id, + bool remote_neighbors_exact_match, + bool remote_neighbors_all_matches, + neighbors_find_by_cell_id_cb_t cb, + void *cb_data); + 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); diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am index 364228d8c..c96ebb59c 100644 --- a/src/osmo-bsc/Makefile.am +++ b/src/osmo-bsc/Makefile.am @@ -73,6 +73,7 @@ osmo_bsc_SOURCES = \ mgw_endpoint_fsm.c \ neighbor_ident.c \ neighbor_ident_vty.c \ + neighbor_iter.c \ net_init.c \ gsm_08_08.c \ osmo_bsc_bssap.c \ diff --git a/src/osmo-bsc/neighbor_ident_vty.c b/src/osmo-bsc/neighbor_ident_vty.c index 203b15057..1847aa950 100644 --- a/src/osmo-bsc/neighbor_ident_vty.c +++ b/src/osmo-bsc/neighbor_ident_vty.c @@ -86,6 +86,13 @@ bool neighbor_ident_bts_parse_key_params(struct vty *vty, struct gsm_bts *bts, c #define LOCAL_BTS_PARAMS "bts <0-255>" #define LOCAL_BTS_DOC "Neighbor cell by local BTS number\n" "BTS number\n" +#define SHOW_BTS_PARAMS "show " LOCAL_BTS_PARAMS +#define SHOW_BTS_DOC SHOW_STR "Display information about a BTS\n" "BTS number\n" + +#define SHOW_BTS_NEIGHBOR_PARAMS SHOW_BTS_PARAMS " neighbor" +#define SHOW_BTS_NEIGHBOR_DOC SHOW_BTS_DOC \ + "List this cell's neighbors matching the given criteria\n" + static struct gsm_bts *neighbor_ident_vty_parse_bts_nr(struct vty *vty, const char **argv) { const char *bts_nr_str = argv[0]; @@ -186,7 +193,7 @@ static int add_local_bts(struct vty *vty, struct gsm_bts *neigh) return CMD_SUCCESS; } -static int del_local_bts(struct vty *vty, struct gsm_bts *neigh) +static int del_local_bts_neighbor(struct vty *vty, struct gsm_bts *neigh) { int rc; struct gsm_bts *bts = vty->index; @@ -211,12 +218,123 @@ static int del_local_bts(struct vty *vty, struct gsm_bts *neigh) neigh->nr, bts->nr, strerror(-rc), VTY_NEWLINE); return CMD_WARNING; } - if (rc == 0) + if (rc == 0) { vty_out(vty, "%% BTS %u is no neighbor of BTS %u%s", neigh->nr, bts->nr, VTY_NEWLINE); + return CMD_WARNING; + } + vty_out(vty, "%% Removed from BTS %u local neighbor BTS %u%s", bts->nr, neigh->nr, VTY_NEWLINE); return CMD_SUCCESS; } +static int del_remote_neighbor(struct vty *vty, const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val) +{ + vty_out(vty, "%% Removing remote neighbor: %s %s%s", + neighbor_ident_key_name(key), + gsm0808_cell_id_list_name(val), + VTY_NEWLINE); + if (!neighbor_ident_del(g_neighbor_cells, key)) { + vty_out(vty, "%% Error: removing neighbor failed%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +struct del_local_or_remote_neighbor_iter_data { + struct vty *vty; + struct gsm_bts *bts; + const struct gsm0808_cell_id *id; + int count_local; + int count_remote; +}; + +static bool del_local_or_remote_neighbor_iter_cb_count(struct gsm_bts *from_bts, + struct gsm_bts *neighbor_bts, + const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val, + int val_idx, + void *cb_data) +{ + struct del_local_or_remote_neighbor_iter_data *d = cb_data; + if (neighbor_bts) + d->count_local++; + else + d->count_remote++; + return true; +} + +static bool del_local_or_remote_neighbor_iter_cb_list(struct gsm_bts *from_bts, + struct gsm_bts *neighbor_bts, + const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val, + int val_idx, + void *cb_data) +{ + struct del_local_or_remote_neighbor_iter_data *d = cb_data; + struct vty *vty = d->vty; + if (neighbor_bts) + vty_out(vty, "%% BTS %u has local neighbor BTS %u%s", + d->bts->nr, neighbor_bts->nr, VTY_NEWLINE); + else + vty_out(vty, "%% Remote neighbor %s %s%s", + neighbor_ident_key_name(key), + gsm0808_cell_id_list_name(val), VTY_NEWLINE); + return true; +} + +static bool del_local_or_remote_neighbor_iter_cb_del(struct gsm_bts *from_bts, + struct gsm_bts *neighbor_bts, + const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val, + int val_idx, + void *cb_data) +{ + struct del_local_or_remote_neighbor_iter_data *d = cb_data; + + if (neighbor_bts) + del_local_bts_neighbor(d->vty, neighbor_bts); + else + del_remote_neighbor(d->vty, key, val); + return true; +} + +static int del_local_or_remote_neighbor(struct vty *vty, const struct gsm0808_cell_id *id, + bool allow_multiple_matches) +{ + struct del_local_or_remote_neighbor_iter_data d = { + .vty = vty, + .bts = vty->index, + .id = id, + }; + int total; + + if (vty->node != BTS_NODE) + d.bts = NULL; + + neighbors_find_by_cell_id(g_net, d.bts, g_neighbor_cells, id, true, true, + del_local_or_remote_neighbor_iter_cb_count, &d); + + total = d.count_local + d.count_remote; + if (!total) { + vty_out(vty, "%% No matching neighbor%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (total > 1 && !allow_multiple_matches) { + vty_out(vty, "%% More than one match found:%s", VTY_NEWLINE); + neighbors_find_by_cell_id(g_net, d.bts, g_neighbor_cells, id, true, true, + del_local_or_remote_neighbor_iter_cb_list, + &d); + return CMD_WARNING; + } + + total = neighbors_find_by_cell_id(g_net, d.bts, g_neighbor_cells, id, true, true, + del_local_or_remote_neighbor_iter_cb_del, &d); + return CMD_SUCCESS; +} + + DEFUN(cfg_neighbor_add_bts_nr, cfg_neighbor_add_bts_nr_cmd, NEIGHBOR_ADD_CMD LOCAL_BTS_PARAMS, NEIGHBOR_ADD_DOC LOCAL_BTS_DOC) @@ -352,7 +470,7 @@ static int del_by_key(struct vty *vty, const struct neighbor_ident_key *key) 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", + vty_out(vty, "%% Removed from BTS %u local neighbor BTS %u%s", bts->nr, neigh_bts->nr, VTY_NEWLINE); removed += rc; } @@ -415,7 +533,7 @@ DEFUN(cfg_neighbor_del_bts_nr, cfg_neighbor_del_bts_nr_cmd, NEIGHBOR_DEL_CMD LOCAL_BTS_PARAMS, NEIGHBOR_DEL_DOC LOCAL_BTS_DOC) { - return del_local_bts(vty, neighbor_ident_vty_parse_bts_nr(vty, argv)); + return del_local_bts_neighbor(vty, neighbor_ident_vty_parse_bts_nr(vty, argv)); } DEFUN(cfg_neighbor_del_arfcn_bsic, cfg_neighbor_del_arfcn_bsic_cmd, @@ -430,12 +548,76 @@ DEFUN(cfg_neighbor_del_arfcn_bsic, cfg_neighbor_del_arfcn_bsic_cmd, return del_by_key(vty, &key); } +DEFUN(cfg_neighbor_del_lac, cfg_neighbor_del_lac_cmd, + NEIGHBOR_DEL_CMD LAC_PARAMS, + NEIGHBOR_DEL_DOC LAC_DOC) +{ + return del_local_or_remote_neighbor(vty, neighbor_ident_vty_parse_lac(vty, argv), false); +} + +DEFUN(cfg_neighbor_del_lac_ci, cfg_neighbor_del_lac_ci_cmd, + NEIGHBOR_DEL_CMD LAC_CI_PARAMS, + NEIGHBOR_DEL_DOC LAC_CI_DOC) +{ + return del_local_or_remote_neighbor(vty, neighbor_ident_vty_parse_lac(vty, argv), false); +} + +DEFUN(cfg_neighbor_del_cgi, cfg_neighbor_del_cgi_cmd, + NEIGHBOR_DEL_CMD CGI_PARAMS, + NEIGHBOR_DEL_DOC CGI_DOC) +{ + return del_local_or_remote_neighbor(vty, neighbor_ident_vty_parse_lac(vty, argv), false); +} + + struct write_neighbor_ident_entry_data { struct vty *vty; const char *indent; struct gsm_bts *bts; }; +static void write_neighbor_ident_list_entry(struct vty *vty, + const char *indent, + const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val, + int idx) +{ + if (idx >= val->id_list_len) + return; + +#define NEIGH_BSS_WRITE(fmt, args...) do { \ + vty_out(vty, "%sneighbor " fmt " arfcn %u ", indent, ## args, key->arfcn); \ + if (key->bsic == BSIC_ANY) \ + vty_out(vty, "bsic any"); \ + else \ + vty_out(vty, "bsic %u", key->bsic & 0x3f); \ + vty_out(vty, "%s", VTY_NEWLINE); \ + } while(0) + + switch (val->id_discr) { + case CELL_IDENT_LAC: + NEIGH_BSS_WRITE("lac %u", val->id_list[idx].lac); + break; + case CELL_IDENT_LAC_AND_CI: + NEIGH_BSS_WRITE("lac-ci %u %u", + val->id_list[idx].lac_and_ci.lac, + val->id_list[idx].lac_and_ci.ci); + break; + case CELL_IDENT_WHOLE_GLOBAL: + { + const struct osmo_cell_global_id *cgi = &val->id_list[idx].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 type: %d%s", val->id_discr, VTY_NEWLINE); + } +#undef NEIGH_BSS_WRITE +} + static bool write_neighbor_ident_list(const struct neighbor_ident_key *key, const struct gsm0808_cell_id_list2 *val, void *cb_data) @@ -450,42 +632,9 @@ static bool write_neighbor_ident_list(const struct neighbor_ident_key *key, } else if (key->from_bts != NEIGHBOR_IDENT_KEY_ANY_BTS) return true; -#define NEIGH_BSS_WRITE(fmt, args...) do { \ - vty_out(vty, "%sneighbor " fmt " arfcn %u ", d->indent, ## args, key->arfcn); \ - if (key->bsic == BSIC_ANY) \ - vty_out(vty, "bsic any"); \ - else \ - vty_out(vty, "bsic %u", key->bsic & 0x3f); \ - 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); + for (i = 0; i < val->id_list_len; i++) { + write_neighbor_ident_list_entry(vty, d->indent, key, val, i); } -#undef NEIGH_BSS_WRITE - return true; } @@ -500,12 +649,17 @@ void neighbor_ident_vty_write_remote_bss(struct vty *vty, const char *indent, st neighbor_ident_iter(g_neighbor_cells, write_neighbor_ident_list, &d); } +void neighbor_ident_vty_write_local_neighbor(struct vty *vty, const char *indent, struct gsm_bts *neighbor_bts) +{ + vty_out(vty, "%sneighbor bts %u%s", indent, neighbor_bts->nr, VTY_NEWLINE); +} + 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 bts %u%s", indent, neigh->bts->nr, VTY_NEWLINE); + neighbor_ident_vty_write_local_neighbor(vty, indent, neigh->bts); } } @@ -515,11 +669,25 @@ void neighbor_ident_vty_write(struct vty *vty, const char *indent, struct gsm_bt 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) +DEFUN(show_bts_neighbor_all, show_bts_neighbor_all_cmd, + SHOW_BTS_NEIGHBOR_PARAMS " all", + SHOW_BTS_NEIGHBOR_DOC "List all neighbors\n") +{ + struct gsm_bts *bts = gsm_bts_num(g_net, atoi(argv[0])); + + if (!bts) { + vty_out(vty, "%% Error: cannot find BTS '%s'%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + neighbor_ident_vty_write(vty, "% ", bts); + return CMD_SUCCESS; +} + +DEFUN(show_bts_neighbor_arfcn, show_bts_neighbor_arfcn_cmd, + SHOW_BTS_NEIGHBOR_PARAMS " " NEIGHBOR_IDENT_VTY_KEY_PARAMS, + SHOW_BTS_NEIGHBOR_DOC NEIGHBOR_IDENT_VTY_KEY_DOC) { int found = 0; struct neighbor_ident_key key; @@ -563,6 +731,57 @@ DEFUN(show_bts_neighbor, show_bts_neighbor_cmd, return CMD_SUCCESS; } +static bool show_neighbor_by_cell_id_cb(struct gsm_bts *from_bts, + struct gsm_bts *neighbor_bts, + const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val, + int val_idx, + void *cb_data) +{ + struct vty *vty = cb_data; + if (neighbor_bts) + neighbor_ident_vty_write_local_neighbor(vty, "% ", neighbor_bts); + else + write_neighbor_ident_list_entry(vty, "% ", key, val, val_idx); + return true; +} + +static int show_neighbor_by_cell_id(struct vty *vty, const char **argv, const struct gsm0808_cell_id *id) +{ + struct gsm_bts *bts = gsm_bts_num(g_net, atoi(argv[0])); + + if (!bts) { + vty_out(vty, "%% Error: cannot find BTS '%s'%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + neighbors_find_by_cell_id(g_net, bts, g_neighbor_cells, id, false, true, + show_neighbor_by_cell_id_cb, vty); + return CMD_SUCCESS; +} + +DEFUN(show_bts_neighbor_lac, show_bts_neighbor_lac_cmd, + SHOW_BTS_NEIGHBOR_PARAMS " " LAC_PARAMS, + SHOW_BTS_NEIGHBOR_DOC LAC_DOC) +{ + return show_neighbor_by_cell_id(vty, argv, neighbor_ident_vty_parse_lac(vty, &argv[1])); +} + +DEFUN(show_bts_neighbor_lac_ci, show_bts_neighbor_lac_ci_cmd, + SHOW_BTS_NEIGHBOR_PARAMS " " LAC_CI_PARAMS, + SHOW_BTS_NEIGHBOR_DOC LAC_CI_DOC) +{ + return show_neighbor_by_cell_id(vty, argv, neighbor_ident_vty_parse_lac_ci(vty, &argv[1])); +} + +DEFUN(show_bts_neighbor_cgi, show_bts_neighbor_cgi_cmd, + SHOW_BTS_NEIGHBOR_PARAMS " " CGI_PARAMS, + SHOW_BTS_NEIGHBOR_DOC CGI_DOC) +{ + return show_neighbor_by_cell_id(vty, argv, neighbor_ident_vty_parse_cgi(vty, &argv[1])); +} + void neighbor_ident_vty_init(struct gsm_network *net, struct neighbor_ident_list *nil) { g_net = net; @@ -576,5 +795,12 @@ void neighbor_ident_vty_init(struct gsm_network *net, struct neighbor_ident_list 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); + install_element(BTS_NODE, &cfg_neighbor_del_lac_cmd); + install_element(BTS_NODE, &cfg_neighbor_del_lac_ci_cmd); + install_element(BTS_NODE, &cfg_neighbor_del_cgi_cmd); + install_element_ve(&show_bts_neighbor_all_cmd); + install_element_ve(&show_bts_neighbor_arfcn_cmd); + install_element_ve(&show_bts_neighbor_lac_cmd); + install_element_ve(&show_bts_neighbor_lac_ci_cmd); + install_element_ve(&show_bts_neighbor_cgi_cmd); } diff --git a/src/osmo-bsc/neighbor_iter.c b/src/osmo-bsc/neighbor_iter.c new file mode 100644 index 000000000..79807735a --- /dev/null +++ b/src/osmo-bsc/neighbor_iter.c @@ -0,0 +1,152 @@ +/* Copyright (C) 2018 by sysmocom - s.f.m.c. GmbH + * + * All Rights Reserved + * + * Author: Neels Hofmeyr + * + * + * 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 . + * + */ + +/* This file implements iteration of both local and remote neighbors, which has dependencies to both + * gsm_data.c as well as neighbor_ident.c. Placing this in gsm_data.c would require various tools to + * include the neighbor_ident.c implementations. In turn, neighbor_ident.c is gsm_data.c agnostic. */ + +#include +#include + +static int bts_local_neighbors_find_by_cell_id(struct gsm_bts *for_bts, + const struct gsm0808_cell_id *id, + neighbors_find_by_cell_id_cb_t cb, + void *cb_data) +{ + int count = 0; + struct gsm_bts_ref *ref, *ref_next; + llist_for_each_entry_safe(ref, ref_next, &for_bts->local_neighbors, entry) { + if (!id || gsm_bts_matches_cell_id(ref->bts, id)) { + if (cb) + cb(for_bts, ref->bts, NULL, NULL, -1, cb_data); + count ++; + } + } + return count; +} + +static int all_local_neighbors_find_by_cell_id(struct gsm_network *net, + const struct gsm0808_cell_id *id, + neighbors_find_by_cell_id_cb_t cb, + void *cb_data) +{ + struct gsm_bts *bts, *bts_next; + int count = 0; + llist_for_each_entry_safe(bts, bts_next, &net->bts_list, list) { + count += bts_local_neighbors_find_by_cell_id(bts, id, cb, cb_data); + } + return count; +} + +struct neighbors_find_by_cell_id_iter_cb_data { + struct gsm_network *net; + const struct gsm0808_cell_id *id; + bool all_matches; + neighbors_find_by_cell_id_cb_t cb; + void *cb_data; + int count; +}; + +static bool neighbors_find_by_cell_id_iter_cb(const struct neighbor_ident_key *key, + const struct gsm0808_cell_id_list2 *val, + void *cb_data) +{ + struct neighbors_find_by_cell_id_iter_cb_data *d = cb_data; + unsigned int match_nr; + int match_idx; + + for (match_nr = 0; ; match_nr ++) { + /* On mismatch, just continue iterating. */ + match_idx = gsm0808_cell_id_matches_list(d->id, val, match_nr); + if (match_idx < 0) + return true; + + /* Match! */ + if (d->cb) + d->cb(d->net ? gsm_bts_num(d->net, key->from_bts) : NULL, + NULL, + key, val, + match_idx, + d->cb_data); + d->count ++; + + /* If neighbors_find_by_cell_id() was invoked with remote_neighbors_all_matches == false, + * stop looking after the first match in this list. */ + if (!d->all_matches) + return true; + } + return true; +} + +/* Find all neighbors that match a given Cell Identifier. + * If 'for_bts' is given, only neighbors for that cell are returned; NULL matches all cells' neighbors. + * If 'neighbor_bss_cells' is NULL, no remote neighbors are returned. + * If 'id' is NULL, all neighbors are returned. The id restricts the matches, where a CGI type is most + * restrictive, and a LAC type might still match a neighbor with LAC+CI or a neighbor with full CGI that + * contains this LAC. + * Results are returned by calling the cb(). If cb() returns false, further iteration is stopped. + * It is safe to remove any neighbor entries, except the neighbor entry *following* the one passed to + * cb(), i.e. you may remove the neighbor passed to cb(), but not the adjacent following llist entry. + * + * With remote_neighbors_exact_match == true, ignore remote-BSS neighbors with a cell id list that have a + * CELL_IDENT that differs from the id->id_discr. With false, any matching cell id item counts, e.g. a + * LAC of 23 matches a CGI that contains a LAC = 23. + * + * With remote_neighbors_all_matches == false, return only the first match in each cell id list of a + * remote neighbor. With true, cb() will be invoked for each matching val_idx in the given cell id list. + */ +int neighbors_find_by_cell_id(struct gsm_network *net, + struct gsm_bts *for_bts, + struct neighbor_ident_list *neighbor_bss_cells, + const struct gsm0808_cell_id *id, + bool remote_neighbors_exact_match, + bool remote_neighbors_all_matches, + neighbors_find_by_cell_id_cb_t cb, + void *cb_data) +{ + int count = 0; + + /* Local neighbors */ + if (for_bts) { + count += bts_local_neighbors_find_by_cell_id(for_bts, id, cb, cb_data); + if (!net) + net = for_bts->network; + } else if (net) + count += all_local_neighbors_find_by_cell_id(net, id, cb, cb_data); + + /* Remote neighbors */ + if (neighbor_bss_cells) { + struct neighbors_find_by_cell_id_iter_cb_data d = { + .net = net, + .id = id, + .all_matches = remote_neighbors_all_matches, + .cb = cb, + .cb_data = cb_data, + }; + + neighbor_ident_iter(neighbor_bss_cells, + neighbors_find_by_cell_id_iter_cb, + &d); + count += d.count; + } + return count; +} diff --git a/tests/neighbor_ident.vty b/tests/neighbor_ident.vty index 4aeb6cc7c..53f50a1dd 100644 --- a/tests/neighbor_ident.vty +++ b/tests/neighbor_ident.vty @@ -162,8 +162,11 @@ OsmoBSC(config-net-bts)# no neighbor? neighbor Remove local or remote-BSS neighbor cell OsmoBSC(config-net-bts)# no neighbor ? - bts Neighbor cell by local BTS number - arfcn ARFCN of neighbor cell + bts Neighbor cell by local BTS number + arfcn ARFCN of neighbor cell + lac Neighbor cell by LAC + lac-ci Neighbor cell by LAC and CI + cgi Neighbor cell by cgi OsmoBSC(config-net-bts)# no neighbor bts ? <0-255> BTS number @@ -195,10 +198,31 @@ OsmoBSC(config-net-bts)# show running-config OsmoBSC(config-net-bts)# neighbor bts 1 % BTS 0 now has local neighbor BTS 1 with LAC 21 CI 31 and ARFCN 41 BSIC 11 +OsmoBSC(config-net-bts)# no neighbor bts 1 +% Removed from BTS 0 local neighbor BTS 1 + +OsmoBSC(config-net-bts)# neighbor lac 21 +% BTS 0 now has local neighbor BTS 1 with LAC 21 CI 31 and ARFCN 41 BSIC 11 +OsmoBSC(config-net-bts)# no neighbor lac 21 +% Removed from BTS 0 local neighbor BTS 1 + +OsmoBSC(config-net-bts)# neighbor lac-ci 21 31 +% BTS 0 now has local neighbor BTS 1 with LAC 21 CI 31 and ARFCN 41 BSIC 11 +OsmoBSC(config-net-bts)# no neighbor lac-ci 21 31 +% Removed from BTS 0 local neighbor BTS 1 + +OsmoBSC(config-net-bts)# neighbor cgi 901 70 21 31 +% BTS 0 now has local neighbor BTS 1 with LAC 21 CI 31 and ARFCN 41 BSIC 11 +OsmoBSC(config-net-bts)# no neighbor cgi 901 70 21 31 +% No matching neighbor + +OsmoBSC(config-net-bts)# neighbor 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 lac 22 % BTS 0 now has local neighbor BTS 2 with LAC 22 CI 65535 and ARFCN 42 BSIC 12 OsmoBSC(config-net-bts)# no neighbor bts 2 +% Removed from BTS 0 local neighbor BTS 2 OsmoBSC(config-net-bts)# neighbor cgi 901 70 22 65535 % BTS 0 now has local neighbor BTS 2 with LAC 22 CI 65535 and ARFCN 42 BSIC 12 @@ -260,6 +284,40 @@ OsmoBSC(config-net-bts)# do show bts 0 neighbor arfcn 423 bsic 1 OsmoBSC(config-net-bts)# do show bts 0 neighbor arfcn 423 bsic 23 % neighbor lac-ci 789 10 arfcn 423 bsic 23 +OsmoBSC(config-net-bts)# do show bts 0 neighbor all +% neighbor bts 1 +% neighbor bts 2 +% neighbor cgi 023 42 423 5 arfcn 23 bsic 42 +% neighbor cgi 023 042 423 6 arfcn 23 bsic 42 +% neighbor cgi 023 042 234 56 arfcn 23 bsic 42 +% neighbor lac 456 arfcn 123 bsic 45 +% neighbor lac-ci 789 10 arfcn 423 bsic any +% neighbor lac-ci 789 10 arfcn 423 bsic 63 +% neighbor lac-ci 789 10 arfcn 423 bsic 1 + +OsmoBSC(config-net-bts)# do show bts 0 neighbor cgi 23 42 423 5 +% neighbor cgi 023 42 423 5 arfcn 23 bsic 42 + +OsmoBSC(config-net-bts)# do show bts 0 neighbor lac 456 +% neighbor lac 456 arfcn 123 bsic 45 + +OsmoBSC(config-net-bts)# do show bts 0 neighbor lac-ci 789 10 +% neighbor lac-ci 789 10 arfcn 423 bsic any +% neighbor lac-ci 789 10 arfcn 423 bsic 63 +% neighbor lac-ci 789 10 arfcn 423 bsic 1 + +OsmoBSC(config-net-bts)# do show bts 0 neighbor lac 789 +% neighbor lac-ci 789 10 arfcn 423 bsic any +% neighbor lac-ci 789 10 arfcn 423 bsic 63 +% neighbor lac-ci 789 10 arfcn 423 bsic 1 + +OsmoBSC(config-net-bts)# do show bts 0 neighbor lac 423 +% neighbor cgi 023 42 423 5 arfcn 23 bsic 42 +% neighbor cgi 023 042 423 6 arfcn 23 bsic 42 + +OsmoBSC(config-net-bts)# do show bts 0 neighbor lac-ci 423 6 +% neighbor cgi 023 042 423 6 arfcn 23 bsic 42 + OsmoBSC(config-net-bts)# no neighbor arfcn 99 bsic 7 % Cannot remove, no such neighbor: BTS 0 to ARFCN 99 BSIC 7 @@ -312,7 +370,7 @@ OsmoBSC(config-net-bts)# show running-config ... !neighbor OsmoBSC(config-net-bts)# no neighbor arfcn 41 bsic any -% Removed local neighbor bts 0 to bts 1 +% Removed from BTS 0 local neighbor BTS 1 OsmoBSC(config-net-bts)# show running-config ... !neighbor @@ -328,7 +386,7 @@ OsmoBSC(config-net-bts)# show running-config ... !neighbor OsmoBSC(config-net-bts)# no neighbor arfcn 42 bsic 12 -% Removed local neighbor bts 0 to bts 2 +% Removed from BTS 0 local neighbor BTS 2 OsmoBSC(config-net-bts)# show running-config ... !neighbor