Change in osmo-bsc[master]: neighbor vty: identify neighbors by gsm0808 cell id

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Neels Hofmeyr gerrit-no-reply at lists.osmocom.org
Thu Sep 27 01:32:56 UTC 2018


Neels Hofmeyr has uploaded this change for review. ( https://gerrit.osmocom.org/11128


Change subject: neighbor vty: identify neighbors by gsm0808 cell id
......................................................................

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
---
M include/osmocom/bsc/gsm_data.h
M src/osmo-bsc/Makefile.am
M src/osmo-bsc/neighbor_ident_vty.c
A src/osmo-bsc/neighbor_iter.c
M tests/neighbor_ident.vty
5 files changed, 502 insertions(+), 50 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-bsc refs/changes/28/11128/1

diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index 33a5a8d..ae65f09 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -1141,6 +1141,21 @@
 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 364228d..c96ebb5 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -73,6 +73,7 @@
 	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 409153b..1dbced8 100644
--- a/src/osmo-bsc/neighbor_ident_vty.c
+++ b/src/osmo-bsc/neighbor_ident_vty.c
@@ -86,6 +86,13 @@
 #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 @@
 	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 @@
 			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)
@@ -342,7 +460,7 @@
 			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;
 		}
@@ -405,7 +523,7 @@
 	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,
@@ -420,12 +538,76 @@
 	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)
@@ -440,42 +622,9 @@
 	} 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;
 }
 
@@ -490,12 +639,17 @@
 	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);
 	}
 }
 
@@ -505,11 +659,25 @@
 	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;
@@ -553,6 +721,57 @@
 	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;
@@ -566,5 +785,12 @@
 	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 0000000..7980773
--- /dev/null
+++ b/src/osmo-bsc/neighbor_iter.c
@@ -0,0 +1,152 @@
+/* Copyright (C) 2018 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels at hofmeyr.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/>.
+ *
+ */
+
+/* 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 <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/neighbor_ident.h>
+
+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 4f86383..9e2b100 100644
--- a/tests/neighbor_ident.vty
+++ b/tests/neighbor_ident.vty
@@ -162,8 +162,11 @@
   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)# 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
 
@@ -264,6 +288,40 @@
 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
 
@@ -323,7 +381,7 @@
 ... !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 
@@ -339,7 +397,7 @@
 ... !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 

-- 
To view, visit https://gerrit.osmocom.org/11128
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-bsc
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I198fe7055d1e09b128693eb51276e3d8cde1c0ba
Gerrit-Change-Number: 11128
Gerrit-PatchSet: 1
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20180927/4af7fd10/attachment.htm>


More information about the gerrit-log mailing list