aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2020-08-26 16:11:28 +0200
committerlaforge <laforge@osmocom.org>2020-08-29 07:57:59 +0000
commit641f7f08450f2d0c4b8e8a9f6a36b0a6b2788816 (patch)
treee116f1ff3144234194ac97ab3d79a1f5abecb56f
parent657f3109dcc8ed0808157a6aedcf5e99f84cbffb (diff)
CBSP: rewrite the CBSP link setup and 'cbc' VTY section
Firstly, make CBSP server and client mutually exclusive: Do not allow osmo-bsc to be configured as CBC client *and* CBC server at the same time. cbsp_link.c expects at most one CBSP link to be established, and, upon sending CBSP messages, probes whether to send the message to a CBSP server or client link. When both listen-port and remote-ip are configured (regardless of an actual CBSP connection), osmo-bsc gets confused about where to send CBSP messages. One solution would be more accurate probing for an actual established TCP connection. But the simpler and less confusing solution is to force the user to configure only server or only client mode, never both. Introduce 'cbc' / 'mode (server|client|disabled)'. Secondly, clarify the 'cbc' config structure into distinct 'server' and 'client' subnodes. Refactor the 'cbc' VTY node in such a way that the IP addresses for server and client mode can remain configured when the CBSP link is switched between server/client/disabled modes. To implement the above, switch the struct bsc_cbc_link to use osmo_sockaddr_str for address configuration. Related: OS#4702 Related: I7eea0dd39de50ed80af79e0f10c836b8685d8644 (osmo-ttcn3-hacks) Related: I9e9760121265b3661f1c179610e975cf7a0873f1 (docker-playground) Change-Id: Icaa2775cc20a99227dabe38a775ff808b374cf98
-rw-r--r--include/osmocom/bsc/smscb.h26
-rw-r--r--include/osmocom/bsc/vty.h2
-rw-r--r--src/osmo-bsc/bsc_init.c10
-rw-r--r--src/osmo-bsc/cbsp_link.c242
-rw-r--r--tests/cbc.vty156
5 files changed, 342 insertions, 94 deletions
diff --git a/include/osmocom/bsc/smscb.h b/include/osmocom/bsc/smscb.h
index 22a258da9..c7002f694 100644
--- a/include/osmocom/bsc/smscb.h
+++ b/include/osmocom/bsc/smscb.h
@@ -2,6 +2,7 @@
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/sockaddr_str.h>
#include <osmocom/netif/stream.h>
#include <osmocom/gsm/cbsp.h>
@@ -27,21 +28,25 @@ int bts_smscb_rx_cbch_load_ind(struct gsm_bts *bts, bool cbch_extended, bool is_
uint8_t slot_count);
void bts_cbch_timer_schedule(struct gsm_bts *bts);
+enum bsc_cbc_link_mode {
+ BSC_CBC_LINK_MODE_DISABLED = 0,
+ BSC_CBC_LINK_MODE_SERVER,
+ BSC_CBC_LINK_MODE_CLIENT,
+};
+
+extern const struct value_string bsc_cbc_link_mode_names[];
+static inline const char *bsc_cbc_link_mode_name(enum bsc_cbc_link_mode val)
+{ return get_value_string(bsc_cbc_link_mode_names, val); }
+
+extern const struct osmo_sockaddr_str bsc_cbc_default_server_local_addr;
+
/* cbsp_link.c */
struct bsc_cbc_link {
struct gsm_network *net;
- struct {
- /* hostname/IP of CBC */
- char *cbc_hostname;
- /* TCP port (Default: 48049) of CBC */
- int cbc_port;
- /* local listening port (0 for disabling local server) */
- int listen_port;
- /* local listening hostname/IP */
- char *listen_hostname;
- } config;
+ enum bsc_cbc_link_mode mode;
/* for handling inbound TCP connections */
struct {
+ struct osmo_sockaddr_str local_addr;
struct osmo_stream_srv *srv;
struct osmo_stream_srv_link *link;
char *sock_name;
@@ -49,6 +54,7 @@ struct bsc_cbc_link {
} server;
/* for handling outbound TCP connections */
struct {
+ struct osmo_sockaddr_str remote_addr;
struct osmo_stream_cli *cli;
char *sock_name;
struct msgb *msg;
diff --git a/include/osmocom/bsc/vty.h b/include/osmocom/bsc/vty.h
index 10ce16b2d..ba44f5e27 100644
--- a/include/osmocom/bsc/vty.h
+++ b/include/osmocom/bsc/vty.h
@@ -25,6 +25,8 @@ enum bsc_vty_node {
OM2K_CON_GROUP_NODE,
BSC_NODE,
CBC_NODE,
+ CBC_SERVER_NODE,
+ CBC_CLIENT_NODE,
};
struct log_info;
diff --git a/src/osmo-bsc/bsc_init.c b/src/osmo-bsc/bsc_init.c
index 22eba50d1..e7221998a 100644
--- a/src/osmo-bsc/bsc_init.c
+++ b/src/osmo-bsc/bsc_init.c
@@ -129,11 +129,11 @@ static struct gsm_network *bsc_network_init(void *ctx)
osmo_timer_schedule(&net->t3122_chan_load_timer, T3122_CHAN_LOAD_SAMPLE_INTERVAL, 0);
net->cbc->net = net;
- /* no cbc_hostname: client not started by default */
- net->cbc->config.cbc_port = CBSP_TCP_PORT;
- /* listen_port == -1: server not started by default */
- net->cbc->config.listen_port = -1;
- net->cbc->config.listen_hostname = talloc_strdup(net->cbc, "127.0.0.1");
+ net->cbc->mode = BSC_CBC_LINK_MODE_DISABLED;
+ net->cbc->server.local_addr = bsc_cbc_default_server_local_addr;
+ /* For CBSP client mode: default remote CBSP server port is CBSP_TCP_PORT == 48049. Leave the IP address unset.
+ * Also leave the local bind for the CBSP client disabled (unconfigured). */
+ net->cbc->client.remote_addr = (struct osmo_sockaddr_str){ .port = CBSP_TCP_PORT, };
return net;
diff --git a/src/osmo-bsc/cbsp_link.c b/src/osmo-bsc/cbsp_link.c
index d93bd1a5a..d58767576 100644
--- a/src/osmo-bsc/cbsp_link.c
+++ b/src/osmo-bsc/cbsp_link.c
@@ -35,6 +35,19 @@
* TCP port, we expect the CBC to connect to us. If neither of the two is configured,
* CBSP is effectively disabled */
+const struct value_string bsc_cbc_link_mode_names[] = {
+ { BSC_CBC_LINK_MODE_DISABLED, "disabled" },
+ { BSC_CBC_LINK_MODE_SERVER, "server" },
+ { BSC_CBC_LINK_MODE_CLIENT, "client" },
+ {}
+};
+
+const struct osmo_sockaddr_str bsc_cbc_default_server_local_addr = {
+ .af = AF_INET,
+ .ip = "127.0.0.1",
+ .port = CBSP_TCP_PORT,
+};
+
/*********************************************************************************
* CBSP Server (inbound TCP connection from CBC)
*********************************************************************************/
@@ -195,7 +208,7 @@ int bsc_cbc_link_restart(void)
struct bsc_cbc_link *cbc = bsc_gsmnet->cbc;
/* shut down client, if no longer configured */
- if (cbc->client.cli && !cbc->config.cbc_hostname) {
+ if (cbc->client.cli && cbc->mode != BSC_CBC_LINK_MODE_CLIENT) {
LOGP(DCBS, LOGL_NOTICE, "Stopping CBSP client\n");
osmo_stream_cli_close(cbc->client.cli);
osmo_stream_cli_destroy(cbc->client.cli);
@@ -203,7 +216,7 @@ int bsc_cbc_link_restart(void)
}
/* shut down server, if no longer configured */
- if (cbc->config.listen_port == -1) {
+ if (cbc->mode != BSC_CBC_LINK_MODE_SERVER) {
if (cbc->server.srv || cbc->server.link)
LOGP(DCBS, LOGL_NOTICE, "Stopping CBSP server\n");
if (cbc->server.srv) {
@@ -217,10 +230,16 @@ int bsc_cbc_link_restart(void)
}
}
- /* start client, if configured */
- if (cbc->config.cbc_hostname) {
- LOGP(DCBS, LOGL_NOTICE, "Starting CBSP Client (to CBC at %s:%u)\n",
- cbc->config.cbc_hostname, cbc->config.cbc_port);
+ switch (cbc->mode) {
+ case BSC_CBC_LINK_MODE_CLIENT:
+ if (!osmo_sockaddr_str_is_nonzero(&cbc->client.remote_addr)) {
+ LOGP(DCBS, LOGL_ERROR,
+ "Cannot start CBSP in client mode: invalid remote-ip or -port in 'cbc' / 'client')\n");
+ return -1;
+ }
+
+ LOGP(DCBS, LOGL_NOTICE, "Starting CBSP Client (to CBC at " OSMO_SOCKADDR_STR_FMT ")\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->client.remote_addr));
if (!cbc->client.cli) {
cbc->client.cli = osmo_stream_cli_create(cbc);
osmo_stream_cli_set_data(cbc->client.cli, cbc);
@@ -229,32 +248,44 @@ int bsc_cbc_link_restart(void)
osmo_stream_cli_set_read_cb(cbc->client.cli, cbsp_client_read_cb);
}
/* CBC side */
- osmo_stream_cli_set_addr(cbc->client.cli, cbc->config.cbc_hostname);
- osmo_stream_cli_set_port(cbc->client.cli, cbc->config.cbc_port);
+ osmo_stream_cli_set_addr(cbc->client.cli, cbc->client.remote_addr.ip);
+ osmo_stream_cli_set_port(cbc->client.cli, cbc->client.remote_addr.port);
/* Close/Reconnect? */
- osmo_stream_cli_open(cbc->client.cli);
- }
+ if (osmo_stream_cli_open(cbc->client.cli) < 0) {
+ LOGP(DCBS, LOGL_ERROR, "Cannot open CBSP client link to " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->client.remote_addr));
+ return -1;
+ }
+ return 0;
- /* start server, if configured */
- if (cbc->config.listen_port != -1) {
- LOGP(DCBS, LOGL_NOTICE, "Starting CBSP Server (bound to %s:%u)\n",
- cbc->config.listen_hostname, cbc->config.listen_port);
+ case BSC_CBC_LINK_MODE_SERVER:
+ if (!osmo_sockaddr_str_is_set(&cbc->server.local_addr)) {
+ LOGP(DCBS, LOGL_ERROR,
+ "Cannot start CBSP in server mode: invalid local-ip or -port in 'cbc' / 'server')\n");
+ return -1;
+ }
+ LOGP(DCBS, LOGL_NOTICE, "Starting CBSP Server (listening at " OSMO_SOCKADDR_STR_FMT ")\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->server.local_addr));
if (!cbc->server.link) {
LOGP(DCBS, LOGL_NOTICE, "Creating CBSP Server\n");
cbc->server.link = osmo_stream_srv_link_create(cbc);
osmo_stream_srv_link_set_data(cbc->server.link, cbc);
osmo_stream_srv_link_set_accept_cb(cbc->server.link, cbsp_srv_link_accept_cb);
- osmo_stream_srv_link_set_addr(cbc->server.link, cbc->config.listen_hostname);
- osmo_stream_srv_link_set_port(cbc->server.link, cbc->config.listen_port);
+ osmo_stream_srv_link_set_addr(cbc->server.link, cbc->server.local_addr.ip);
+ osmo_stream_srv_link_set_port(cbc->server.link, cbc->server.local_addr.port);
if (osmo_stream_srv_link_open(cbc->server.link) < 0) {
- LOGP(DCBS, LOGL_ERROR, "Cannot open CBSP Server link on %s:%u\n",
- cbc->config.listen_hostname, cbc->config.listen_port);
+ LOGP(DCBS, LOGL_ERROR, "Cannot open CBSP Server link at " OSMO_SOCKADDR_STR_FMT ")\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->server.local_addr));
+ return -1;
}
}
+ return 0;
+
+ default:
+ return 0;
}
- return 0;
}
/*! Encode + Transmit a 'decoded' CBSP message over given CBC link
@@ -301,60 +332,71 @@ DEFUN(cfg_cbc, cfg_cbc_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_cbc_remote_ip, cfg_cbc_remote_ip_cmd,
- "remote-ip A.B.C.D",
- "IP Address of the Cell Broadcast Centre\n"
- "IP Address of the Cell Broadcast Centre\n")
+DEFUN(cfg_cbc_mode, cfg_cbc_mode_cmd,
+ "mode (server|client|disabled)",
+ "Set OsmoBSC as CBSP server or client\n"
+ "CBSP Server: listen for inbound TCP connections from a remote Cell Broadcast Centre\n"
+ "CBSP Client: establish outbound TCP connection to a remote Cell Broadcast Centre\n"
+ "Disable CBSP link\n")
{
struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- osmo_talloc_replace_string(cbc, &cbc->config.cbc_hostname, argv[0]);
+ cbc->mode = get_string_value(bsc_cbc_link_mode_names, argv[0]);
+ OSMO_ASSERT(cbc->mode >= 0);
+ bsc_cbc_link_restart();
return CMD_SUCCESS;
}
-DEFUN(cfg_cbc_no_remote_ip, cfg_cbc_no_remote_ip_cmd,
- "no remote-ip",
- NO_STR "Remove IP address of CBC; disables outbound CBSP connections\n")
+
+DEFUN(cfg_cbc_server, cfg_cbc_server_cmd,
+ "server", "Configure OsmoBSC's CBSP server role\n")
{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- talloc_free(cbc->config.cbc_hostname);
- cbc->config.cbc_hostname = NULL;
+ vty->node = CBC_SERVER_NODE;
return CMD_SUCCESS;
}
-DEFUN(cfg_cbc_remote_port, cfg_cbc_remote_port_cmd,
- "remote-port <1-65535>",
- "TCP Port number of the Cell Broadcast Centre (Default: 48049)\n"
- "TCP Port number of the Cell Broadcast Centre (Default: 48049)\n")
+DEFUN(cfg_cbc_server_local_ip, cfg_cbc_server_local_ip_cmd,
+ "local-ip " VTY_IPV46_CMD,
+ "Set IP Address to listen on for inbound CBSP from a Cell Broadcast Centre\n"
+ "IPv4 address\n" "IPv6 address\n")
{
struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- cbc->config.cbc_port = atoi(argv[0]);
+ osmo_sockaddr_str_from_str(&cbc->server.local_addr, argv[0], cbc->server.local_addr.port);
return CMD_SUCCESS;
}
-DEFUN(cfg_cbc_listen_port, cfg_cbc_listen_port_cmd,
- "listen-port <1-65535>",
- "Local TCP port at which BSC listens for incoming CBSP connections from CBC\n"
- "Local TCP port at which BSC listens for incoming CBSP connections from CBC\n")
+DEFUN(cfg_cbc_server_local_port, cfg_cbc_server_local_port_cmd,
+ "local-port <1-65535>",
+ "Set TCP port to listen on for inbound CBSP from a Cell Broadcast Centre\n"
+ "CBSP port number (Default: " OSMO_STRINGIFY_VAL(CBSP_TCP_PORT) ")\n")
{
struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- cbc->config.listen_port = atoi(argv[0]);
+ cbc->server.local_addr.port = atoi(argv[0]);
return CMD_SUCCESS;
}
-DEFUN(cfg_cbc_no_listen_port, cfg_cbc_no_listen_port_cmd,
- "no listen-port",
- NO_STR "Remove CBSP Listen Port; disables inbound CBSP connections\n")
+
+DEFUN(cfg_cbc_client, cfg_cbc_client_cmd,
+ "client", "Configure OsmoBSC's CBSP client role\n")
+{
+ vty->node = CBC_CLIENT_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cbc_client_remote_ip, cfg_cbc_client_remote_ip_cmd,
+ "remote-ip " VTY_IPV46_CMD,
+ "Set IP Address of the Cell Broadcast Centre, to establish CBSP link to\n"
+ "IPv4 address\n" "IPv6 address\n")
{
struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- cbc->config.listen_port = -1;
+ osmo_sockaddr_str_from_str(&cbc->client.remote_addr, argv[0], cbc->client.remote_addr.port);
return CMD_SUCCESS;
}
-DEFUN(cfg_cbc_listen_ip, cfg_cbc_listen_ip_cmd,
- "listen-ip A.B.C.D",
- "Local IP Address where BSC listens for incoming CBC connections (Default: 0.0.0.0)\n"
- "Local IP Address where BSC listens for incoming CBC connections\n")
+DEFUN(cfg_cbc_client_remote_port, cfg_cbc_client_remote_port_cmd,
+ "remote-port <1-65535>",
+ "Set TCP port of the Cell Broadcast Centre, to establish CBSP link to\n"
+ "CBSP port number (Default: " OSMO_STRINGIFY_VAL(CBSP_TCP_PORT) ")\n")
{
struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- osmo_talloc_replace_string(cbc, &cbc->config.listen_hostname, argv[0]);
+ cbc->client.remote_addr.port = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -364,23 +406,56 @@ static struct cmd_node cbc_node = {
1,
};
+static struct cmd_node cbc_server_node = {
+ CBC_SERVER_NODE,
+ "%s(config-cbc-server)# ",
+ 1,
+};
+
+static struct cmd_node cbc_client_node = {
+ CBC_CLIENT_NODE,
+ "%s(config-cbc-client)# ",
+ 1,
+};
+
static int config_write_cbc(struct vty *vty)
{
struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ bool default_server_local;
+ bool default_client_remote;
+
+ default_server_local = !osmo_sockaddr_str_cmp(&cbc->server.local_addr,
+ &bsc_cbc_default_server_local_addr);
+ default_client_remote = !osmo_sockaddr_str_is_set(&cbc->client.remote_addr);
+
+ /* If all reflects default values, skip the 'cbc' section */
+ if (cbc->mode == BSC_CBC_LINK_MODE_DISABLED
+ && default_server_local
+ && default_client_remote)
+ return 0;
+
vty_out(vty, "cbc%s", VTY_NEWLINE);
+ vty_out(vty, " mode %s%s", bsc_cbc_link_mode_name(cbc->mode), VTY_NEWLINE);
- if (cbc->config.cbc_hostname) {
- vty_out(vty, " remote-ip %s%s", cbc->config.cbc_hostname, VTY_NEWLINE);
- vty_out(vty, " remote-port %u%s", cbc->config.cbc_port, VTY_NEWLINE);
- } else
- vty_out(vty, " no remote-ip%s", VTY_NEWLINE);
+ if (!default_server_local) {
+ vty_out(vty, " server%s", VTY_NEWLINE);
- if (cbc->config.listen_port >= 0) {
- vty_out(vty, " listen-port %u%s", cbc->config.listen_port, VTY_NEWLINE);
- vty_out(vty, " listen-ip %s%s", cbc->config.listen_hostname, VTY_NEWLINE);
- } else
- vty_out(vty, " no listen-port%s", VTY_NEWLINE);
+ if (strcmp(cbc->server.local_addr.ip, bsc_cbc_default_server_local_addr.ip))
+ vty_out(vty, " local-ip %s%s", cbc->server.local_addr.ip, VTY_NEWLINE);
+ if (cbc->server.local_addr.port != bsc_cbc_default_server_local_addr.port)
+ vty_out(vty, " local-port %u%s", cbc->server.local_addr.port, VTY_NEWLINE);
+ }
+
+ if (!default_client_remote) {
+ vty_out(vty, " client%s", VTY_NEWLINE);
+
+ if (osmo_sockaddr_str_is_set(&cbc->client.remote_addr)) {
+ vty_out(vty, " remote-ip %s%s", cbc->client.remote_addr.ip, VTY_NEWLINE);
+ if (cbc->client.remote_addr.port != CBSP_TCP_PORT)
+ vty_out(vty, " remote-port %u%s", cbc->client.remote_addr.port, VTY_NEWLINE);
+ }
+ }
return 0;
}
@@ -391,34 +466,43 @@ DEFUN(show_cbc, show_cbc_cmd,
{
struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- if (!cbc->config.cbc_hostname)
- vty_out(vty, "CBSP Client Config: Disabled%s", VTY_NEWLINE);
- else {
- vty_out(vty, "CBSP Client Config: CBC IP=%s, CBC Port=%u%s",
- cbc->config.cbc_hostname, cbc->config.cbc_port, VTY_NEWLINE);
- vty_out(vty, "CBSP Client Connection: %s%s",
- cbc->client.sock_name ? cbc->client.sock_name : "Disconnected", VTY_NEWLINE);
- }
- if (cbc->config.listen_port < 0)
- vty_out(vty, "CBSP Server Config: Disabled%s\n", VTY_NEWLINE);
- else {
- vty_out(vty, "CBSP Server Config: Listen IP=%s, Port=%u%s\n",
- cbc->config.listen_hostname, cbc->config.listen_port, VTY_NEWLINE);
+ switch (cbc->mode) {
+ case BSC_CBC_LINK_MODE_DISABLED:
+ vty_out(vty, "CBSP link is disabled%s", VTY_NEWLINE);
+ break;
+
+ case BSC_CBC_LINK_MODE_SERVER:
+ vty_out(vty, "OsmoBSC is configured as CBSP Server on " OSMO_SOCKADDR_STR_FMT "%s",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->server.local_addr), VTY_NEWLINE);
vty_out(vty, "CBSP Server Connection: %s%s",
cbc->server.sock_name ? cbc->server.sock_name : "Disconnected", VTY_NEWLINE);
+ break;
+
+ case BSC_CBC_LINK_MODE_CLIENT:
+ vty_out(vty, "OsmoBSC is configured as CBSP Client to remote CBC at " OSMO_SOCKADDR_STR_FMT "%s",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->client.remote_addr), VTY_NEWLINE);
+ vty_out(vty, "CBSP Client Connection: %s%s",
+ cbc->client.sock_name ? cbc->client.sock_name : "Disconnected", VTY_NEWLINE);
+ break;
}
return CMD_SUCCESS;
}
void cbc_vty_init(void)
{
- install_element(VIEW_NODE, &show_cbc_cmd);
+ install_element_ve(&show_cbc_cmd);
+
install_element(CONFIG_NODE, &cfg_cbc_cmd);
install_node(&cbc_node, config_write_cbc);
- install_element(CBC_NODE, &cfg_cbc_remote_ip_cmd);
- install_element(CBC_NODE, &cfg_cbc_no_remote_ip_cmd);
- install_element(CBC_NODE, &cfg_cbc_remote_port_cmd);
- install_element(CBC_NODE, &cfg_cbc_listen_port_cmd);
- install_element(CBC_NODE, &cfg_cbc_no_listen_port_cmd);
- install_element(CBC_NODE, &cfg_cbc_listen_ip_cmd);
+ install_element(CBC_NODE, &cfg_cbc_mode_cmd);
+
+ install_element(CBC_NODE, &cfg_cbc_server_cmd);
+ install_node(&cbc_server_node, NULL);
+ install_element(CBC_SERVER_NODE, &cfg_cbc_server_local_ip_cmd);
+ install_element(CBC_SERVER_NODE, &cfg_cbc_server_local_port_cmd);
+
+ install_element(CBC_NODE, &cfg_cbc_client_cmd);
+ install_node(&cbc_client_node, NULL);
+ install_element(CBC_CLIENT_NODE, &cfg_cbc_client_remote_ip_cmd);
+ install_element(CBC_CLIENT_NODE, &cfg_cbc_client_remote_port_cmd);
}
diff --git a/tests/cbc.vty b/tests/cbc.vty
new file mode 100644
index 000000000..2cad609fa
--- /dev/null
+++ b/tests/cbc.vty
@@ -0,0 +1,156 @@
+OsmoBSC> ### CBSP link config
+
+OsmoBSC> list
+...
+ show cbc
+...
+
+OsmoBSC> enable
+OsmoBSC# list
+...
+ show cbc
+...
+
+OsmoBSC# show running-config
+... !cbc
+
+OsmoBSC# show cbc
+CBSP link is disabled
+
+OsmoBSC# configure terminal
+OsmoBSC(config)# cbc
+
+OsmoBSC(config-cbc)# list
+...
+ mode (server|client|disabled)
+ server
+ client
+
+OsmoBSC(config-cbc)# ?
+...
+ mode Set OsmoBSC as CBSP server or client
+ server Configure OsmoBSC's CBSP server role
+ client Configure OsmoBSC's CBSP client role
+
+OsmoBSC(config-cbc)# mode ?
+ server CBSP Server: listen for inbound TCP connections from a remote Cell Broadcast Centre
+ client CBSP Client: establish outbound TCP connection to a remote Cell Broadcast Centre
+ disabled Disable CBSP link
+
+OsmoBSC(config-cbc)# server
+OsmoBSC(config-cbc-server)# list
+...
+ local-ip (A.B.C.D|X:X::X:X)
+ local-port <1-65535>
+
+OsmoBSC(config-cbc-server)# ?
+...
+ local-ip Set IP Address to listen on for inbound CBSP from a Cell Broadcast Centre
+ local-port Set TCP port to listen on for inbound CBSP from a Cell Broadcast Centre
+
+OsmoBSC(config-cbc-server)# local-ip ?
+ A.B.C.D IPv4 address
+ X:X::X:X IPv6 address
+OsmoBSC(config-cbc-server)# local-port ?
+ <1-65535> CBSP port number (Default: 48049)
+
+OsmoBSC(config-cbc-server)# local-ip 1.2.3.4
+OsmoBSC(config-cbc-server)# local-port 12345
+OsmoBSC(config-cbc-server)# show running-config
+...
+cbc
+ mode disabled
+ server
+ local-ip 1.2.3.4
+ local-port 12345
+...
+
+OsmoBSC(config-cbc-server)# local-port 48049
+
+OsmoBSC(config-cbc-server)# show running-config
+...
+cbc
+ mode disabled
+ server
+ local-ip 1.2.3.4
+... !local-port
+
+OsmoBSC(config-cbc-server)# local-ip ::1
+OsmoBSC(config-cbc-server)# show running-config
+...
+cbc
+ mode disabled
+ server
+ local-ip ::1
+...
+
+OsmoBSC(config-cbc-server)# do show cbc
+CBSP link is disabled
+
+OsmoBSC(config-cbc-server)# exit
+
+OsmoBSC(config-cbc)# client
+OsmoBSC(config-cbc-client)# list
+...
+ remote-ip (A.B.C.D|X:X::X:X)
+ remote-port <1-65535>
+
+OsmoBSC(config-cbc-client)# ?
+...
+ remote-ip Set IP Address of the Cell Broadcast Centre, to establish CBSP link to
+ remote-port Set TCP port of the Cell Broadcast Centre, to establish CBSP link to
+
+OsmoBSC(config-cbc-client)# remote-ip ?
+ A.B.C.D IPv4 address
+ X:X::X:X IPv6 address
+OsmoBSC(config-cbc-client)# remote-port ?
+ <1-65535> CBSP port number (Default: 48049)
+
+OsmoBSC(config-cbc-client)# remote-ip 1.2.3.4
+OsmoBSC(config-cbc-client)# remote-port 12345
+OsmoBSC(config-cbc-client)# show running-config
+...
+cbc
+...
+ client
+ remote-ip 1.2.3.4
+ remote-port 12345
+...
+
+OsmoBSC(config-cbc-client)# remote-port 48049
+
+OsmoBSC(config-cbc-client)# show running-config
+...
+cbc
+...
+ client
+ remote-ip 1.2.3.4
+... !remote-port
+
+OsmoBSC(config-cbc-client)# remote-ip 1:2:3:4::5
+OsmoBSC(config-cbc-client)# show running-config
+...
+cbc
+...
+ client
+ remote-ip 1:2:3:4::5
+...
+
+OsmoBSC(config-cbc-client)# do show cbc
+CBSP link is disabled
+
+OsmoBSC(config-cbc-client)# exit
+
+OsmoBSC(config-cbc)# mode server
+OsmoBSC(config-cbc)# do show cbc
+OsmoBSC is configured as CBSP Server on [::1]:48049
+CBSP Server Connection: Disconnected
+
+OsmoBSC(config-cbc)# mode client
+OsmoBSC(config-cbc)# do show cbc
+OsmoBSC is configured as CBSP Client to remote CBC at [1:2:3:4::5]:48049
+CBSP Client Connection: Disconnected
+
+OsmoBSC(config-cbc)# mode disabled
+OsmoBSC(config-cbc)# do show cbc
+CBSP link is disabled