heighbor_ident: add/del neighbor cells via ctrl interface

The VTY allows flexible control over the neighbor cell information via
the neighbor command, which can be found in the configure terminal under
the bts node. Lets add pendant of this command on the control interface
as well.

Change-Id: I343a40e18fa9b91e6c381912c0426a002841e079
Related: SYS#5641
This commit is contained in:
Philipp Maier 2021-10-27 10:57:41 +02:00 committed by laforge
parent 554e169784
commit 085a92584f
7 changed files with 958 additions and 29 deletions

View File

@ -120,4 +120,29 @@ RF Ctrl is enabled in the BSC Configuration.
Set/Get the value of maximum power reduction. Even values between 0 and 22 are
accepted.
=== add/del neighbor cell
The control interface allows for editing the neighbor cell configuration. Neighbor
cells can be added or removed during runtime. It is also possible to clear the
entire neighbor list if necessary.
.Variables available over control interface
[options="header",width="100%",cols="20%,5%,5%,50%,20%"]
|===
|Name|Access|Trap|Value|Comment
|bts.N.neighbor-bts.add|WO|No|"<num>"|Add neighbor cell by local BTS number.
|bts.N.neighbor-bts.del|WO|No|"<num>"|Delete neighbor cell by local BTS number.
|bts.N.neighbor-lac.add|WO|No|"<lac>[-<arfcn>-<bsic>]"|Add neighbor cell by LAC.
|bts.N.neighbor-lac.del|WO|No|"<lac>[-<arfcn>-<bsic>]"|Delete neighbor cell by LAC.
|bts.N.neighbor-lac-ci.add|WO|No|"<lac>-<ci>[-<arfcn>-<bsic>]"|Add neighbor cell by LAC and CI.
|bts.N.neighbor-lac-ci.del|WO|No|"<lac>-<ci>[-<arfcn>-<bsic>]"|Delete neighbor cell by LAC and CI.
|bts.N.neighbor-cgi.add|WO|No|"<mcc>-<mnc>-<lac>-<ci>[-<arfcn>-<bsic>]"|Add neighbor cell by cgi.
|bts.N.neighbor-cgi.del|WO|No|"<mcc>-<mnc>-<lac>-<ci>[-<arfcn>-<bsic>]"|Delete neighbor cell by cgi.
|bts.N.neighbor-cgi-ps.add|WO|No|"<mcc>-<mnc>-<lac>-<rac>-<ci>[-<arfcn>-<bsic>]"|Add neighbor cell by cgi (Packet Switched, with RAC)
|bts.N.neighbor-cgi-ps.del|WO|No|"<mcc>-<mnc>-<lac>-<rac>-<ci>[-<arfcn>-<bsic>]"|Delete neighbor cell by cgi (Packet Switched, with RAC).
|bts.N.neighbor-clear|WO|No|Ignored|Delete all neighbor cells.
|===
NOTE: The bsic-number (<bsic>) can also be set to "any" if no explcit bsic shall be given
FIXME: add variables defined in src/ctrl/control_if.c?

View File

@ -77,6 +77,10 @@ void neighbor_ident_vty_init();
void neighbor_ident_vty_write_bts(struct vty *vty, const char *indent, struct gsm_bts *bts);
void neighbor_ident_vty_write_network(struct vty *vty, const char *indent);
int neighbor_ident_add_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n);
int neighbor_ident_del_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n);
int neighbor_ident_ctrl_init(void);
int neighbors_check_cfg();
#define CELL_AB_VTY_PARAMS "arfcn <0-1023> bsic (<0-63>|any)"

View File

@ -78,6 +78,7 @@ libbsc_la_SOURCES = \
meas_rep.c \
neighbor_ident.c \
neighbor_ident_vty.c \
neighbor_ident_ctrl.c \
net_init.c \
nm_common_fsm.c \
nm_bb_transc_fsm.c \

View File

@ -35,6 +35,7 @@
#include <osmocom/bsc/osmo_bsc_rf.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/neighbor_ident.h>
static int verify_net_apply_config_file(struct ctrl_cmd *cmd, const char *value, void *_data)
{
@ -692,6 +693,8 @@ int bsc_base_ctrl_cmds_install(void)
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_states);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_c0_power_red);
rc |= neighbor_ident_ctrl_init();
rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_max_power);
rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_arfcn);
rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_rf_locked);

View File

@ -0,0 +1,713 @@
/* CTRL interface implementation to manage identity of neighboring BSS cells for inter-BSC handover. */
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* Author: Philipp Maier <pmaier@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 <time.h>
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/bsc/neighbor_ident.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/vty.h>
/* Continue to parse ARFCN and BSIC, which are optional parameters at the end of the parameter string in most of the
* commands. The result is ignored when parameter n is set to NULL. */
static int continue_parse_arfcn_and_bsic(char **saveptr, struct neighbor *n)
{
int arfcn;
int bsic;
char *tok;
tok = strtok_r(NULL, "-", saveptr);
/* No ARFCN and BSIC persent - stop */
if (!tok)
return 0;
if (osmo_str_to_int(&arfcn, tok, 10, 0, 1023) < 0)
return -EINVAL;
tok = strtok_r(NULL, "-", saveptr);
/* When an ARFCN is given, then the BSIC parameter is
* mandatory */
if (!tok)
return -EINVAL;
if (strcmp(tok, "any") == 0) {
bsic = BSIC_ANY;
} else {
if (osmo_str_to_int(&bsic, tok, 10, 0, 63) < 0)
return 1;
}
/* Make sure there are no excess parameters */
if (strtok_r(NULL, "-", saveptr))
return -EINVAL;
if (n) {
n->cell_id.ab_present = true;
n->cell_id.ab.arfcn = arfcn;
n->cell_id.ab.bsic = bsic;
}
return 0;
}
/* This and the following: Add/Remove a BTS as neighbor */
static int verify_neighbor_bts(struct ctrl_cmd *cmd, const char *value, void *_data)
{
struct gsm_bts *bts = cmd->node;
const int neigh_bts_nr = atoi(value);
struct gsm_bts *neigh_bts = gsm_bts_num(bts->network, neigh_bts_nr);
if (!neigh_bts) {
cmd->reply = "Invalid Neighbor BTS number - no such BTS";
return 1;
}
return 0;
}
static int verify_neighbor_bts_add(struct ctrl_cmd *cmd, const char *value, void *_data)
{
return verify_neighbor_bts(cmd, value, _data);
}
static int set_neighbor_bts_add(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts *bts = cmd->node;
const int bts_nr = atoi(cmd->value);
int rc;
struct neighbor n = {
.type = NEIGHBOR_TYPE_BTS_NR,
.bts_nr = bts_nr,
};
rc = neighbor_ident_add_neighbor(NULL, bts, &n);
if (rc != CMD_SUCCESS) {
cmd->reply = "Failed to add neighbor";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
/* Parameter format: "<num>"
* num: BTS number (0-255) */
CTRL_CMD_DEFINE_WO(neighbor_bts_add, "neighbor-bts add");
static int verify_neighbor_bts_del(struct ctrl_cmd *cmd, const char *value, void *_data)
{
return verify_neighbor_bts(cmd, value, _data);
}
static int set_neighbor_bts_del(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts *bts = cmd->node;
const int bts_nr = atoi(cmd->value);
int rc;
struct neighbor n = {
.type = NEIGHBOR_TYPE_BTS_NR,
.bts_nr = bts_nr,
};
rc = neighbor_ident_del_neighbor(NULL, bts, &n);
if (rc != CMD_SUCCESS) {
cmd->reply = "Failed to delete neighbor";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
/* Parameter format: (see "add" command above) */
CTRL_CMD_DEFINE_WO(neighbor_bts_del, "neighbor-bts del");
/* This and the following: Add/Remove a LAC as neighbor */
static int parse_lac(void *ctx, struct neighbor *n, const char *value)
{
char *tmp = NULL, *tok, *saveptr;
int rc = 0;
int lac;
if (n)
memset(n, 0, sizeof(*n));
tmp = talloc_strdup(ctx, value);
if (!tmp)
return -EINVAL;
/* Parse LAC */
tok = strtok_r(tmp, "-", &saveptr);
if (tok) {
if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
rc = -EINVAL;
goto exit;
}
} else {
rc = -EINVAL;
goto exit;
}
/* Optional parameters: ARFCN and BSIC */
if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
rc = -EINVAL;
goto exit;
}
if (n) {
n->type = NEIGHBOR_TYPE_CELL_ID;
n->cell_id.id.id_discr = CELL_IDENT_LAC;
n->cell_id.id.id.lac = lac;
}
exit:
talloc_free(tmp);
return rc;
}
static int verify_neighbor_lac_add(struct ctrl_cmd *cmd, const char *value, void *_data)
{
if (parse_lac(cmd, NULL, value))
return 1;
return 0;
}
static int set_neighbor_lac_add(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts *bts = cmd->node;
int rc;
struct neighbor n;
parse_lac(cmd, &n, cmd->value);
rc = neighbor_ident_add_neighbor(NULL, bts, &n);
if (rc != CMD_SUCCESS) {
cmd->reply = "Failed to add neighbor";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
/* Parameter format: "<lac>[-<arfcn>-<bsic>]"
* lac: Location area of neighbor cell (0-65535)
* arfcn: ARFCN of neighbor cell (0-1023)
* bsic: BSIC of neighbor cell */
CTRL_CMD_DEFINE_WO(neighbor_lac_add, "neighbor-lac add");
static int verify_neighbor_lac_del(struct ctrl_cmd *cmd, const char *value, void *_data)
{
if (parse_lac(cmd, NULL, value))
return 1;
return 0;
}
static int set_neighbor_lac_del(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts *bts = cmd->node;
int rc;
struct neighbor n;
parse_lac(cmd, &n, cmd->value);
rc = neighbor_ident_del_neighbor(NULL, bts, &n);
if (rc != CMD_SUCCESS) {
cmd->reply = "Failed to delete neighbor";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
/* Parameter format: (see "add" command above) */
CTRL_CMD_DEFINE_WO(neighbor_lac_del, "neighbor-lac del");
/* This and the following: Add/Remove a LAC-CI as neighbor */
static int parse_lac_ci(void *ctx, struct neighbor *n, const char *value)
{
char *tmp = NULL, *tok, *saveptr;
int rc = 0;
int lac;
int ci;
if (n)
memset(n, 0, sizeof(*n));
tmp = talloc_strdup(ctx, value);
if (!tmp)
return -EINVAL;
/* Parse LAC */
tok = strtok_r(tmp, "-", &saveptr);
if (tok) {
if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
rc = -EINVAL;
goto exit;
}
} else {
rc = -EINVAL;
goto exit;
}
/* Parse CI */
tok = strtok_r(NULL, "-", &saveptr);
if (tok) {
if (osmo_str_to_int(&ci, tok, 10, 0, 65535) < 0) {
rc = -EINVAL;
goto exit;
}
} else {
rc = -EINVAL;
goto exit;
}
/* Optional parameters: ARFCN and BSIC */
if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
rc = -EINVAL;
goto exit;
}
if (n) {
n->type = NEIGHBOR_TYPE_CELL_ID;
n->cell_id.id.id_discr = CELL_IDENT_LAC_AND_CI;
n->cell_id.id.id.lac = lac;
n->cell_id.id.id.ci = ci;
}
exit:
talloc_free(tmp);
return rc;
}
static int verify_neighbor_lac_ci_add(struct ctrl_cmd *cmd, const char *value, void *_data)
{
if (parse_lac_ci(cmd, NULL, value))
return 1;
return 0;
}
static int set_neighbor_lac_ci_add(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts *bts = cmd->node;
int rc;
struct neighbor n;
parse_lac_ci(cmd, &n, cmd->value);
rc = neighbor_ident_add_neighbor(NULL, bts, &n);
if (rc != CMD_SUCCESS) {
cmd->reply = "Failed to add neighbor";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
/* Parameter format: "<lac>-<ci>[-<arfcn>-<bsic>]"
* lac: Location area of neighbor cell (0-65535)
* ci: Cell ID of neighbor cell (0-65535)
* arfcn: ARFCN of neighbor cell (0-1023)
* bsic: BSIC of neighbor cell */
CTRL_CMD_DEFINE_WO(neighbor_lac_ci_add, "neighbor-lac-ci add");
static int verify_neighbor_lac_ci_del(struct ctrl_cmd *cmd, const char *value, void *_data)
{
if (parse_lac_ci(cmd, NULL, value))
return 1;
return 0;
}
static int set_neighbor_lac_ci_del(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts *bts = cmd->node;
int rc;
struct neighbor n;
parse_lac_ci(cmd, &n, cmd->value);
rc = neighbor_ident_del_neighbor(NULL, bts, &n);
if (rc != CMD_SUCCESS) {
cmd->reply = "Failed to delete neighbor";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
/* Parameter format: (see "add" command above) */
CTRL_CMD_DEFINE_WO(neighbor_lac_ci_del, "neighbor-lac-ci del");
/* This and the following: Add/Remove a CGI as neighbor */
static int parse_cgi(void *ctx, struct neighbor *n, const char *value)
{
char *tmp = NULL, *tok, *saveptr;
int rc = 0;
uint16_t mcc;
uint16_t mnc;
bool mnc_3_digits;
int lac;
int ci;
if (n)
memset(n, 0, sizeof(*n));
tmp = talloc_strdup(ctx, value);
if (!tmp)
return -EINVAL;
/* Parse MCC */
tok = strtok_r(tmp, "-", &saveptr);
if (tok) {
if (osmo_mcc_from_str(tok, &mcc)) {
rc = -EINVAL;
goto exit;
}
} else {
rc = -EINVAL;
goto exit;
}
/* Parse MNC */
tok = strtok_r(NULL, "-", &saveptr);
if (tok) {
if (osmo_mnc_from_str(tok, &mnc, &mnc_3_digits)) {
rc = -EINVAL;
goto exit;
}
} else {
rc = -EINVAL;
goto exit;
}
/* Parse LAC */
tok = strtok_r(NULL, "-", &saveptr);
if (tok) {
if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
rc = -EINVAL;
goto exit;
}
} else {
rc = -EINVAL;
goto exit;
}
/* Parse CI */
tok = strtok_r(NULL, "-", &saveptr);
if (tok) {
if (osmo_str_to_int(&ci, tok, 10, 0, 65535) < 0) {
rc = -EINVAL;
goto exit;
}
} else {
rc = -EINVAL;
goto exit;
}
/* Optional parameters: ARFCN and BSIC */
if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
rc = -EINVAL;
goto exit;
}
if (n) {
n->type = NEIGHBOR_TYPE_CELL_ID;
n->cell_id.id.id_discr = CELL_IDENT_WHOLE_GLOBAL;
n->cell_id.id.id.global.lai.lac = lac;
n->cell_id.id.id.global.lai.plmn.mcc = mcc;
n->cell_id.id.id.global.lai.plmn.mnc = mnc;
n->cell_id.id.id.global.lai.plmn.mnc_3_digits = mnc_3_digits;
n->cell_id.id.id.global.cell_identity = ci;
}
exit:
talloc_free(tmp);
return rc;
}
static int verify_neighbor_cgi_add(struct ctrl_cmd *cmd, const char *value, void *_data)
{
if (parse_cgi(cmd, NULL, value))
return 1;
return 0;
}
static int set_neighbor_cgi_add(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts *bts = cmd->node;
int rc;
struct neighbor n;
parse_cgi(cmd, &n, cmd->value);
rc = neighbor_ident_add_neighbor(NULL, bts, &n);
if (rc != CMD_SUCCESS) {
cmd->reply = "Failed to add neighbor";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
/* Parameter format: "<mcc>-<mnc>-<lac>-<ci>[-<arfcn>-<bsic>]"
* mcc: Mobile country code of neighbor cell (0-999)
* mnc: Mobile network code of neighbor cell (0-999)
* lac: Location area of neighbor cell (0-65535)
* ci: Cell ID of neighbor cell (0-65535)
* arfcn: ARFCN of neighbor cell (0-1023)
* bsic: BSIC of neighbor cell */
CTRL_CMD_DEFINE_WO(neighbor_cgi_add, "neighbor-cgi add");
static int verify_neighbor_cgi_del(struct ctrl_cmd *cmd, const char *value, void *_data)
{
if (parse_cgi(cmd, NULL, value))
return 1;
return 0;
}
static int set_neighbor_cgi_del(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts *bts = cmd->node;
int rc;
struct neighbor n;
parse_cgi(cmd, &n, cmd->value);
rc = neighbor_ident_del_neighbor(NULL, bts, &n);
if (rc != CMD_SUCCESS) {
cmd->reply = "Failed to delete neighbor";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
/* Parameter format: (see "add" command above) */
CTRL_CMD_DEFINE_WO(neighbor_cgi_del, "neighbor-cgi del");
/* This and the following: Add/Remove a CGI-PS as neighbor */
static int parse_cgi_ps(void *ctx, struct neighbor *n, const char *value)
{
char *tmp = NULL, *tok, *saveptr;
int rc = 0;
uint16_t mcc;
uint16_t mnc;
bool mnc_3_digits;
int lac;
int rac;
int ci;
if (n)
memset(n, 0, sizeof(*n));
tmp = talloc_strdup(ctx, value);
if (!tmp)
return -EINVAL;
/* Parse MCC */
tok = strtok_r(tmp, "-", &saveptr);
if (tok) {
if (osmo_mcc_from_str(tok, &mcc)) {
rc = -EINVAL;
goto exit;
}
} else {
rc = -EINVAL;
goto exit;
}
/* Parse MNC */
tok = strtok_r(NULL, "-", &saveptr);
if (tok) {
if (osmo_mnc_from_str(tok, &mnc, &mnc_3_digits)) {
rc = -EINVAL;
goto exit;
}
} else {
rc = -EINVAL;
goto exit;
}
/* Parse LAC */
tok = strtok_r(NULL, "-", &saveptr);
if (tok) {
if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
rc = -EINVAL;
goto exit;
}
} else {
rc = -EINVAL;
goto exit;
}
/* Parse RAC */
tok = strtok_r(NULL, "-", &saveptr);
if (tok) {
if (osmo_str_to_int(&rac, tok, 10, 0, 255) < 0) {
rc = -EINVAL;
goto exit;
}
} else {
rc = -EINVAL;
goto exit;
}
/* Parse CI */
tok = strtok_r(NULL, "-", &saveptr);
if (tok) {
if (osmo_str_to_int(&ci, tok, 10, 0, 65535) < 0) {
rc = -EINVAL;
goto exit;
}
} else {
rc = -EINVAL;
goto exit;
}
/* Optional parameters: ARFCN and BSIC */
if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
rc = -EINVAL;
goto exit;
}
if (n) {
n->type = NEIGHBOR_TYPE_CELL_ID;
n->cell_id.id.id_discr = CELL_IDENT_WHOLE_GLOBAL_PS;
n->cell_id.id.id.global_ps.rai.lac.lac = lac;
n->cell_id.id.id.global_ps.rai.rac = lac;
n->cell_id.id.id.global_ps.rai.lac.plmn.mcc = mcc;
n->cell_id.id.id.global_ps.rai.lac.plmn.mnc = mnc;
n->cell_id.id.id.global_ps.rai.lac.plmn.mnc_3_digits = mnc_3_digits;
n->cell_id.id.id.global_ps.cell_identity = ci;
}
exit:
talloc_free(tmp);
return rc;
}
static int verify_neighbor_cgi_ps_add(struct ctrl_cmd *cmd, const char *value, void *_data)
{
if (parse_cgi_ps(cmd, NULL, value))
return 1;
return 0;
}
static int set_neighbor_cgi_ps_add(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts *bts = cmd->node;
int rc;
struct neighbor n;
parse_cgi_ps(cmd, &n, cmd->value);
rc = neighbor_ident_add_neighbor(NULL, bts, &n);
if (rc != CMD_SUCCESS) {
cmd->reply = "Failed to add neighbor";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
/* Parameter format: "<mcc>-<mnc>-<lac>-<rac>-<ci>[-<arfcn>-<bsic>]"
* mcc: Mobile country code of neighbor cell (0-999)
* mnc: Mobile network code of neighbor cell (0-999)
* lac: Location area of neighbor cell (0-65535)
* rac: Routing area of neighbor cell (0-65535)
* ci: Cell ID of neighbor cell (0-65535)
* arfcn: ARFCN of neighbor cell (0-1023)
* bsic: BSIC of neighbor cell */
CTRL_CMD_DEFINE_WO(neighbor_cgi_ps_add, "neighbor-cgi-ps add");
static int verify_neighbor_cgi_ps_del(struct ctrl_cmd *cmd, const char *value, void *_data)
{
if (parse_cgi_ps(cmd, NULL, value))
return 1;
return 0;
}
static int set_neighbor_cgi_ps_del(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts *bts = cmd->node;
int rc;
struct neighbor n;
parse_cgi_ps(cmd, &n, cmd->value);
rc = neighbor_ident_del_neighbor(NULL, bts, &n);
if (rc != CMD_SUCCESS) {
cmd->reply = "Failed to delete neighbor";
return CTRL_CMD_ERROR;
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
/* Parameter format: (see "add" command above) */
CTRL_CMD_DEFINE_WO(neighbor_cgi_ps_del, "neighbor-cgi-ps del");
/* This and the following: clear all neighbor cell information */
static int set_neighbor_clear(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts *bts = cmd->node;
struct neighbor *neighbor;
struct neighbor *neighbor_tmp;
llist_for_each_entry_safe(neighbor, neighbor_tmp, &bts->neighbors, entry) {
llist_del(&neighbor->entry);
talloc_free(neighbor);
}
cmd->reply = "OK";
return CTRL_CMD_REPLY;
}
CTRL_CMD_DEFINE_WO_NOVRF(neighbor_clear, "neighbor-clear");
/* Register control interface commands implemented above */
int neighbor_ident_ctrl_init(void)
{
int rc = 0;
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_bts_add);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_bts_del);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_add);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_del);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_ci_add);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_ci_del);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_add);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_del);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_ps_add);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_ps_del);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_clear);
return rc;
}

View File

@ -146,23 +146,32 @@ void neighbor_ident_vty_parse_arfcn_bsic(struct cell_ab *ab, const char **argv)
};
}
static int add_neighbor(struct vty *vty, struct neighbor *n)
#define LOGPORVTY(vty, fmt, args...) \
{ \
if (vty) \
vty_out(vty, "%% " fmt "%s", ## args, VTY_NEWLINE); \
else \
LOGP(DLINP, LOGL_NOTICE, fmt "\n", ## args); \
} while (0) \
/* Delete a neighbor from neighborlist. When the parameter *vty is set to NULL all error messages are redirected to the
* logtext. */
int neighbor_ident_add_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n)
{
struct gsm_bts *bts = vty->index;
struct neighbor *neighbor;
OSMO_ASSERT((vty->node == BTS_NODE) && bts);
OSMO_ASSERT(bts);
OSMO_ASSERT(!vty || (vty->node == BTS_NODE));
llist_for_each_entry(neighbor, &bts->neighbors, entry) {
/* Check against duplicates */
if (neighbor_same(neighbor, n, false)) {
/* Found a match on Cell ID or BTS number, without ARFCN+BSIC. If they are fully identical, ignore the
* duplicate. If the ARFCN+BSIC part differs, it's an error. */
vty_out(vty, "%% BTS %u already had neighbor %s%s", bts->nr, neighbor_to_str_c(OTC_SELECT, neighbor),
VTY_NEWLINE);
LOGPORVTY(vty, "BTS %u already had neighbor %s", bts->nr, neighbor_to_str_c(OTC_SELECT, neighbor));
if (!neighbor_same(neighbor, n, true)) {
vty_out(vty, "%% ERROR: duplicate Cell ID in neighbor config, with differing ARFCN+BSIC: %s%s",
neighbor_to_str_c(OTC_SELECT, n), VTY_NEWLINE);
LOGPORVTY(vty, "ERROR: duplicate Cell ID in neighbor config, with differing ARFCN+BSIC: %s",
neighbor_to_str_c(OTC_SELECT, n));
return CMD_WARNING;
}
/* Exact same neighbor again, just ignore. */
@ -173,9 +182,9 @@ static int add_neighbor(struct vty *vty, struct neighbor *n)
if (n->type == NEIGHBOR_TYPE_CELL_ID
&& n->cell_id.ab_present && neighbor->cell_id.ab_present
&& cell_ab_match(&n->cell_id.ab, &neighbor->cell_id.ab, true)) {
vty_out(vty, "%% Error: only one Cell Identifier entry is allowed per remote neighbor."
" Already have: BTS %u -> %s%s", bts->nr,
neighbor_to_str_c(OTC_SELECT, neighbor), VTY_NEWLINE);
LOGPORVTY(vty, "Error: only one Cell Identifier entry is allowed per remote neighbor."
" Already have: BTS %u -> %s", bts->nr,
neighbor_to_str_c(OTC_SELECT, neighbor));
return CMD_WARNING;
}
}
@ -186,12 +195,14 @@ static int add_neighbor(struct vty *vty, struct neighbor *n)
return CMD_SUCCESS;
}
static int del_neighbor(struct vty *vty, struct neighbor *n)
/* Delete a neighbor from neighborlist. When the parameter *vty is set to NULL all error messages are redirected to the
* logtext. */
int neighbor_ident_del_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n)
{
struct gsm_bts *bts = vty->index;
struct neighbor *neighbor;
OSMO_ASSERT((vty->node == BTS_NODE) && bts);
OSMO_ASSERT(bts);
OSMO_ASSERT(!vty || (vty->node == BTS_NODE));
llist_for_each_entry(neighbor, &bts->neighbors, entry) {
if (neighbor->type != n->type)
@ -216,8 +227,8 @@ static int del_neighbor(struct vty *vty, struct neighbor *n)
return CMD_SUCCESS;
}
vty_out(vty, "%% Error: no such neighbor on BTS %d: %s%s",
bts->nr, neighbor_to_str_c(OTC_SELECT, n), VTY_NEWLINE);
LOGPORVTY(vty, "Error: no such neighbor on BTS %d: %s",
bts->nr, neighbor_to_str_c(OTC_SELECT, n));
return CMD_WARNING;
}
@ -271,7 +282,7 @@ DEFUN(cfg_neighbor_add_bts_nr, cfg_neighbor_add_bts_nr_cmd,
.type = NEIGHBOR_TYPE_BTS_NR,
.bts_nr = atoi(argv[0]),
};
return add_neighbor(vty, &n);
return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_lac, cfg_neighbor_add_lac_cmd,
@ -283,7 +294,7 @@ DEFUN(cfg_neighbor_add_lac, cfg_neighbor_add_lac_cmd,
};
if (neighbor_ident_vty_parse_lac(vty, &n.cell_id.id, argv))
return CMD_WARNING;
return add_neighbor(vty, &n);
return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_lac_ci, cfg_neighbor_add_lac_ci_cmd,
@ -295,7 +306,7 @@ DEFUN(cfg_neighbor_add_lac_ci, cfg_neighbor_add_lac_ci_cmd,
};
if (neighbor_ident_vty_parse_lac_ci(vty, &n.cell_id.id, argv))
return CMD_WARNING;
return add_neighbor(vty, &n);
return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_cgi, cfg_neighbor_add_cgi_cmd,
@ -307,7 +318,7 @@ DEFUN(cfg_neighbor_add_cgi, cfg_neighbor_add_cgi_cmd,
};
if (neighbor_ident_vty_parse_cgi(vty, &n.cell_id.id, argv))
return CMD_WARNING;
return add_neighbor(vty, &n);
return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_cgi_ps, cfg_neighbor_add_cgi_ps_cmd,
@ -319,7 +330,7 @@ DEFUN(cfg_neighbor_add_cgi_ps, cfg_neighbor_add_cgi_ps_cmd,
};
if (neighbor_ident_vty_parse_cgi_ps(vty, &n.cell_id.id, argv))
return CMD_WARNING;
return add_neighbor(vty, &n);
return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
static int neighbor_del_all(struct vty *vty)
@ -354,7 +365,7 @@ DEFUN(cfg_neighbor_add_lac_arfcn_bsic, cfg_neighbor_add_lac_arfcn_bsic_cmd,
if (neighbor_ident_vty_parse_lac(vty, &n.cell_id.id, argv))
return CMD_WARNING;
neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + LAC_ARGC);
return add_neighbor(vty, &n);
return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_lac_ci_arfcn_bsic, cfg_neighbor_add_lac_ci_arfcn_bsic_cmd,
@ -368,7 +379,7 @@ DEFUN(cfg_neighbor_add_lac_ci_arfcn_bsic, cfg_neighbor_add_lac_ci_arfcn_bsic_cmd
if (neighbor_ident_vty_parse_lac_ci(vty, &n.cell_id.id, argv))
return CMD_WARNING;
neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + LAC_CI_ARGC);
return add_neighbor(vty, &n);
return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_cgi_arfcn_bsic, cfg_neighbor_add_cgi_arfcn_bsic_cmd,
@ -382,7 +393,7 @@ DEFUN(cfg_neighbor_add_cgi_arfcn_bsic, cfg_neighbor_add_cgi_arfcn_bsic_cmd,
if (neighbor_ident_vty_parse_cgi(vty, &n.cell_id.id, argv))
return CMD_WARNING;
neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + CGI_ARGC);
return add_neighbor(vty, &n);
return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_cgi_ps_arfcn_bsic, cfg_neighbor_add_cgi_ps_arfcn_bsic_cmd,
@ -396,7 +407,7 @@ DEFUN(cfg_neighbor_add_cgi_ps_arfcn_bsic, cfg_neighbor_add_cgi_ps_arfcn_bsic_cmd
if (neighbor_ident_vty_parse_cgi_ps(vty, &n.cell_id.id, argv))
return CMD_WARNING;
neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + CGI_PS_ARGC);
return add_neighbor(vty, &n);
return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_bts_nr, cfg_neighbor_del_bts_nr_cmd,
@ -407,7 +418,7 @@ DEFUN(cfg_neighbor_del_bts_nr, cfg_neighbor_del_bts_nr_cmd,
.type = NEIGHBOR_TYPE_BTS_NR,
.bts_nr = atoi(argv[0]),
};
return del_neighbor(vty, &n);
return neighbor_ident_del_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_lac, cfg_neighbor_del_lac_cmd,
@ -419,7 +430,7 @@ DEFUN(cfg_neighbor_del_lac, cfg_neighbor_del_lac_cmd,
};
if (neighbor_ident_vty_parse_lac(vty, &n.cell_id.id, argv))
return CMD_WARNING;
return del_neighbor(vty, &n);
return neighbor_ident_del_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_lac_ci, cfg_neighbor_del_lac_ci_cmd,
@ -431,7 +442,7 @@ DEFUN(cfg_neighbor_del_lac_ci, cfg_neighbor_del_lac_ci_cmd,
};
if (neighbor_ident_vty_parse_lac_ci(vty, &n.cell_id.id, argv))
return CMD_WARNING;
return del_neighbor(vty, &n);
return neighbor_ident_del_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_cgi, cfg_neighbor_del_cgi_cmd,
@ -443,7 +454,7 @@ DEFUN(cfg_neighbor_del_cgi, cfg_neighbor_del_cgi_cmd,
};
if (neighbor_ident_vty_parse_cgi(vty, &n.cell_id.id, argv))
return CMD_WARNING;
return del_neighbor(vty, &n);
return neighbor_ident_del_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_cgi_ps, cfg_neighbor_del_cgi_ps_cmd,
@ -455,7 +466,7 @@ DEFUN(cfg_neighbor_del_cgi_ps, cfg_neighbor_del_cgi_ps_cmd,
};
if (neighbor_ident_vty_parse_cgi_ps(vty, &n.cell_id.id, argv))
return CMD_WARNING;
return del_neighbor(vty, &n);
return neighbor_ident_del_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_arfcn_bsic, cfg_neighbor_del_arfcn_bsic_cmd,

View File

@ -562,6 +562,177 @@ class TestCtrlBSCNeighbor(TestCtrlBase):
self.assertEqual(r['var'], 'neighbor_resolve_cgi_ps_from_lac_ci.1.6969.23.32')
self.assertEqual(r['value'], '023-42-423-2-5')
class TestCtrlBSCNeighborCell(TestCtrlBase):
def tearDown(self):
TestCtrlBase.tearDown(self)
os.unlink("tmp_dummy_sock")
def ctrl_command(self):
return ["./src/osmo-bsc/osmo-bsc", "-r", "tmp_dummy_sock", "-c",
"tests/ctrl/osmo-bsc-neigh-test.cfg"]
def ctrl_app(self):
return (4249, "./src/osmo-bsc/osmo-bsc", "OsmoBSC", "bsc")
def testCtrlAddDelBTS(self):
r = self.do_set('bts.0.neighbor-bts.add', '1')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-bts.add')
self.assertEqual(r['value'], 'OK')
r = self.do_set('bts.0.neighbor-bts.del', '1')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-bts.del')
self.assertEqual(r['value'], 'OK')
def testCtrlAddDelLAC(self):
# without ARFCN+BSIC:
r = self.do_set('bts.0.neighbor-lac.add', '100')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-lac.add')
self.assertEqual(r['value'], 'OK')
r = self.do_set('bts.0.neighbor-lac.del', '100')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-lac.del')
self.assertEqual(r['value'], 'OK')
# with ARFCN+BSIC:
r = self.do_set('bts.0.neighbor-lac.add', '100-123-4')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-lac.add')
self.assertEqual(r['value'], 'OK')
r = self.do_set('bts.0.neighbor-lac.del', '100-123-4')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-lac.del')
self.assertEqual(r['value'], 'OK')
def testCtrlAddDelLACCI(self):
# without ARFCN+BSIC:
r = self.do_set('bts.0.neighbor-lac-ci.add', '100-200')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.add')
self.assertEqual(r['value'], 'OK')
r = self.do_set('bts.0.neighbor-lac-ci.del', '100-200')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.del')
self.assertEqual(r['value'], 'OK')
# with ARFCN+BSIC:
r = self.do_set('bts.0.neighbor-lac-ci.add', '100-200-123-any')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.add')
self.assertEqual(r['value'], 'OK')
r = self.do_set('bts.0.neighbor-lac-ci.del', '100-200-123-any')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.del')
self.assertEqual(r['value'], 'OK')
def testCtrlAddDelCGI(self):
# without ARFCN+BSIC:
r = self.do_set('bts.0.neighbor-cgi.add', '001-01-100-200')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-cgi.add')
self.assertEqual(r['value'], 'OK')
r = self.do_set('bts.0.neighbor-cgi.del', '001-01-100-200')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-cgi.del')
self.assertEqual(r['value'], 'OK')
# with ARFCN+BSIC:
r = self.do_set('bts.0.neighbor-cgi.add', '001-01-100-200-123-4')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-cgi.add')
self.assertEqual(r['value'], 'OK')
r = self.do_set('bts.0.neighbor-cgi.del', '001-01-100-200-123-4')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-cgi.del')
self.assertEqual(r['value'], 'OK')
def testCtrlAddDelCGIPS(self):
# without ARFCN+BSIC:
r = self.do_set('bts.0.neighbor-cgi-ps.add', '001-01-100-33-200')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.add')
self.assertEqual(r['value'], 'OK')
r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.del')
self.assertEqual(r['value'], 'OK')
# with ARFCN+BSIC:
r = self.do_set('bts.0.neighbor-cgi-ps.add', '001-01-100-33-200-123-any')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.add')
self.assertEqual(r['value'], 'OK')
r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200-123-any')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.del')
self.assertEqual(r['value'], 'OK')
def testCtrlClearNeighbors(self):
r = self.do_set('bts.0.neighbor-clear', 'ignored')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.neighbor-clear')
self.assertEqual(r['value'], 'OK')
def testCtrlErrs(self):
# Missing BSIC
r = self.do_set('bts.0.neighbor-lac.add', '100-123')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'ERROR')
self.assertEqual(r['error'], 'Value failed verification.')
# Short value (missing RAC)
r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-200-123-1')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'ERROR')
self.assertEqual(r['error'], 'Value failed verification.')
# Long value
r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200-123-1-2')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'ERROR')
self.assertEqual(r['error'], 'Value failed verification.')
# Out of range values
r = self.do_set('bts.0.neighbor-cgi.add', '100001-1123401-100-200')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'ERROR')
self.assertEqual(r['error'], 'Value failed verification.')
# Garbage
r = self.do_set('bts.0.neighbor-lac-ci.add', '0G1-Z1-1U0-a3-2p0')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'ERROR')
self.assertEqual(r['error'], 'Value failed verification.')
# Delete something that shouldn't be there
r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200-123-any')
print('respose: ' + str(r))
self.assertEqual(r['mtype'], 'ERROR')
self.assertEqual(r['error'], 'Failed to delete neighbor')
def add_bsc_test(suite, workdir, klass):
if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")):
print("Skipping the BSC test")
@ -601,5 +772,6 @@ if __name__ == '__main__':
suite = unittest.TestSuite()
add_bsc_test(suite, workdir, TestCtrlBSC)
add_bsc_test(suite, workdir, TestCtrlBSCNeighbor)
add_bsc_test(suite, workdir, TestCtrlBSCNeighborCell)
res = unittest.TextTestRunner(verbosity=verbose_level).run(suite)
sys.exit(len(res.errors) + len(res.failures))