aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2023-04-23 12:22:25 +0200
committerAndreas Eversberg <jolly@eversberg.eu>2023-07-21 12:28:03 +0200
commitf8ac734849a2ecf004858042930b274e74bbcf81 (patch)
tree7b4ca431f74e54b55d1a647e2f26945a0cff3d48
parente2a408861904b555d4e66eb03d91ec745f1e7587 (diff)
ASCI: Add VTY to configure GCR (Group Call Register)
-rw-r--r--include/osmocom/msc/Makefile.am1
-rw-r--r--include/osmocom/msc/asci_vty.h25
-rw-r--r--include/osmocom/msc/vty.h4
-rw-r--r--src/libmsc/Makefile.am1
-rw-r--r--src/libmsc/asci_vty.c434
-rw-r--r--src/libmsc/msc_vty.c2
-rw-r--r--src/osmo-msc/msc_main.c10
-rw-r--r--tests/test_nodes.vty4
8 files changed, 481 insertions, 0 deletions
diff --git a/include/osmocom/msc/Makefile.am b/include/osmocom/msc/Makefile.am
index 9fa06eb8e..a940056fc 100644
--- a/include/osmocom/msc/Makefile.am
+++ b/include/osmocom/msc/Makefile.am
@@ -57,4 +57,5 @@ noinst_HEADERS = \
vlr_sgs.h \
vty.h \
asci_gcr.h \
+ asci_vty.h \
$(NULL)
diff --git a/include/osmocom/msc/asci_vty.h b/include/osmocom/msc/asci_vty.h
new file mode 100644
index 000000000..5fe11f6f5
--- /dev/null
+++ b/include/osmocom/msc/asci_vty.h
@@ -0,0 +1,25 @@
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0+
+ *
+ * Author: Andreas Eversberg
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+void asci_vty_init(struct gsm_network *msc_network);
diff --git a/include/osmocom/msc/vty.h b/include/osmocom/msc/vty.h
index 11d9ed113..a7f1db7ca 100644
--- a/include/osmocom/msc/vty.h
+++ b/include/osmocom/msc/vty.h
@@ -26,6 +26,10 @@ enum bsc_vty_node {
HLR_NODE,
CFG_SGS_NODE,
SMSC_NODE,
+ ASCI_NODE,
+ GCR_NODE,
+ VGC_NODE,
+ VBC_NODE,
};
int bsc_vty_init_extra(void);
diff --git a/src/libmsc/Makefile.am b/src/libmsc/Makefile.am
index 2e6240284..dff4b2db0 100644
--- a/src/libmsc/Makefile.am
+++ b/src/libmsc/Makefile.am
@@ -78,6 +78,7 @@ libmsc_a_SOURCES = \
sgs_server.c \
sgs_vty.c \
asci_gcr.c \
+ asci_vty.c \
$(NULL)
if BUILD_IU
diff --git a/src/libmsc/asci_vty.c b/src/libmsc/asci_vty.c
new file mode 100644
index 000000000..a138eda51
--- /dev/null
+++ b/src/libmsc/asci_vty.c
@@ -0,0 +1,434 @@
+/* GCR interface to VTY */
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0+
+ *
+ * Author: Andreas Eversberg
+ *
+ * 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 <osmocom/msc/vty.h>
+#include <osmocom/msc/transaction.h>
+#include <osmocom/msc/msc_vgcs.h>
+#include <osmocom/msc/asci_vty.h>
+#include <osmocom/msc/asci_gcr.h>
+
+static struct gsm_network *gsmnet;
+
+/***********************************************************************
+ * ASCI Node
+ ***********************************************************************/
+
+#define ASCI_STR "Advanced Speech Call Items\n"
+
+static void asci_disabled(struct vty *vty)
+{
+ vty_out(vty, "%%Advanced Speech Call Items are disabled.%s", VTY_NEWLINE);
+}
+
+DEFUN(asci_call, asci_call_cmd,
+ "asci (initiate|terminate) (vgc|vbc) CALLREF",
+ ASCI_STR "Initiate a call\nTerminate a call\nVoice Group Call\nVoice Broadcast Call\nCall reference")
+{
+ struct gcr *gcr;
+ const char *error;
+
+ if (!gsmnet->asci.enable) {
+ asci_disabled(vty);
+ return CMD_WARNING;
+ }
+
+ gcr = gcr_by_group_id(gsmnet, (argv[1][1] == 'g') ? TRANS_GCC : TRANS_BCC, argv[2]);
+ if (!gcr) {
+ vty_out(vty, "%%Given call ref does not exist in GCR.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (argv[0][0] == 'i')
+ error = vgcs_vty_initiate(gsmnet, gcr);
+ else
+ error = vgcs_vty_terminate(gsmnet, gcr);
+ if (error) {
+ vty_out(vty, "%%%s%s", error, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(asci_show, asci_show_cmd,
+ "show asci calls",
+ SHOW_STR ASCI_STR "Show all Voice Group/Broadcast Calls")
+{
+ struct gsm_trans *trans;
+ const char *typestr;
+ struct vgcs_bss *bss;
+ struct vgcs_bss_cell *cell;
+
+ if (!gsmnet->asci.enable) {
+ asci_disabled(vty);
+ return CMD_WARNING;
+ }
+
+ llist_for_each_entry(trans, &gsmnet->trans_list, entry) {
+ if (trans->type == TRANS_GCC)
+ typestr = "Group";
+ else if (trans->type == TRANS_BCC)
+ typestr = "Broadcast";
+ else
+ continue;
+ vty_out(vty, "Call Reference %s (Voice %s Call).%s", gsm44068_group_id_string(trans->callref),
+ typestr, VTY_NEWLINE);
+ vty_out(vty, " Call state : %s%s", vgcs_bcc_gcc_state_name(trans->gcc.fi), VTY_NEWLINE);
+ vty_out(vty, " Uplink state: %s%s", (trans->gcc.uplink_busy) ? "busy" : "free", VTY_NEWLINE);
+ if (trans->gcc.uplink_busy)
+ vty_out(vty, " Talker : %s subscriber%s",
+ (trans->gcc.uplink_originator) ? "calling" : "other", VTY_NEWLINE);
+ llist_for_each_entry(bss, &trans->gcc.bss_list, list) {
+ vty_out(vty, " BSS %8s: listening%s%s", osmo_ss7_pointcode_print(NULL, bss->pc),
+ (trans->gcc.uplink_busy && bss == trans->gcc.uplink_bss) ? "+talking" : "",
+ VTY_NEWLINE);
+ llist_for_each_entry(cell, &bss->cell_list, list_bss) {
+ vty_out(vty, " Cell %6d: listening%s%s", cell->cell_id,
+ (trans->gcc.uplink_busy && cell == trans->gcc.uplink_cell) ? "+talking" : "",
+ VTY_NEWLINE);
+ }
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+/***********************************************************************
+ * GCR Config Node
+ ***********************************************************************/
+
+static struct cmd_node asci_node = {
+ ASCI_NODE,
+ "%s(config-asci)# ",
+ 1,
+};
+
+static struct cmd_node gcr_node = {
+ GCR_NODE,
+ "%s(config-gcr)# ",
+ 1,
+};
+
+char conf_prompt[64];
+
+static struct cmd_node vgc_node = {
+ VGC_NODE,
+ conf_prompt,
+ 1,
+};
+
+static struct cmd_node vbc_node = {
+ VBC_NODE,
+ conf_prompt,
+ 1,
+};
+
+DEFUN(cfg_asci, cfg_asci_cmd,
+ "asci", "Enable and configure " ASCI_STR)
+{
+ vty->node = ASCI_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_enable_disable, cfg_enable_disable_cmd,
+ "(enable|disable)", "Enable " ASCI_STR "Disable " ASCI_STR)
+{
+ gsmnet->asci.enable = (argv[0][0] == 'e');
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gcr, cfg_gcr_cmd,
+ "gcr", "Configure Group Call Register")
+{
+ vty->node = GCR_NODE;
+ return CMD_SUCCESS;
+}
+
+static bool valid_group_id(const char *id, struct vty *vty)
+{
+ int i;
+
+ if (strlen(id) < 1 || strlen(id) > 8) {
+ vty_out(vty, "%%Given group ID is not valid. Use up to 8 numeric digits!%s", VTY_NEWLINE);
+ return false;
+ }
+ for (i = 0; i < strlen(id); i++) {
+ if (id[i] < '0' || id[i] > '9') {
+ vty_out(vty, "%%Given group ID is not valid. Use numeric digits only!%s", VTY_NEWLINE);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+DEFUN(cfg_vgc, cfg_vgc_cmd,
+ "vgc ID", "Configure Voice Group Call\n" "Group ID")
+{
+ struct gcr *gcr;
+
+ if (!valid_group_id(argv[0], vty))
+ return CMD_WARNING;
+
+ gcr = gcr_by_group_id(gsmnet, TRANS_GCC, argv[0]);
+ if (!gcr)
+ gcr = gcr_create(gsmnet, TRANS_GCC, argv[0]);
+ if (!gcr)
+ return CMD_WARNING;
+
+ sprintf(conf_prompt, "%%s(vgc-%s)# ", gcr->group_id);
+ vty->node = VGC_NODE;
+ vty->index = gcr;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_vgc, cfg_no_vgc_cmd,
+ "no vgc ID", NO_STR "Configure Voice Group Call\n" "Group ID")
+{
+ struct gcr *gcr;
+
+ if (!valid_group_id(argv[0], vty))
+ return CMD_WARNING;
+
+ gcr = gcr_by_group_id(gsmnet, TRANS_GCC, argv[0]);
+ if (!gcr) {
+ vty_out(vty, "%%Voice group call with given group ID does not exit!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ gcr_destroy(gcr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_vbc, cfg_vbc_cmd,
+ "vbc ID", "Configure Voice Broadcast Call\n" "Group ID")
+{
+ struct gcr *gcr;
+
+ if (!valid_group_id(argv[0], vty))
+ return CMD_WARNING;
+
+ gcr = gcr_by_group_id(gsmnet, TRANS_BCC, argv[0]);
+ if (!gcr)
+ gcr = gcr_create(gsmnet, TRANS_BCC, argv[0]);
+ if (!gcr)
+ return CMD_WARNING;
+
+ sprintf(conf_prompt, "%%s(vbc-%s)# ", gcr->group_id);
+ vty->node = VBC_NODE;
+ vty->index = gcr;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_vbc, cfg_no_vbc_cmd,
+ "no vbc ID", NO_STR "Configure Voice Broadcast Call\n" "Group ID")
+{
+ struct gcr *gcr;
+
+ if (!valid_group_id(argv[0], vty))
+ return CMD_WARNING;
+
+ gcr = gcr_by_group_id(gsmnet, TRANS_BCC, argv[0]);
+ if (!gcr) {
+ vty_out(vty, "%%Voice broadcast call with given group ID does not exit!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ gcr_destroy(gcr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mute, cfg_mute_cmd,
+ "mute-talker", "Mute talker's downlink")
+{
+ struct gcr *gcr = vty->index;
+
+ gcr->mute_talker = true;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_unmute, cfg_unmute_cmd,
+ "unmute-talker", "Unmute talker's downlink")
+{
+ struct gcr *gcr = vty->index;
+
+ gcr->mute_talker = false;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_timeout, cfg_timeout_cmd,
+ "timeout <1-65535>", "Set inactivity timer\n" "Timeout in seconds")
+{
+ struct gcr *gcr = vty->index;
+
+ gcr->timeout = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_timeout, cfg_no_timeout_cmd,
+ "no timeout", NO_STR "Unset inactivity timer")
+{
+ struct gcr *gcr = vty->index;
+
+ gcr->timeout = 0;
+
+ return CMD_SUCCESS;
+}
+
+#define PC_ID_STR "Point code of MSC\nCell ID of BTS"
+
+DEFUN(cfg_no_cell, cfg_no_cell_cmd,
+ "no cell POINT_CODE [<0-65535>]", NO_STR "Remove BSS/cell from current group\n" PC_ID_STR)
+{
+ struct osmo_ss7_instance *ss7 = osmo_ss7_instance_find(0);
+ struct gcr *gcr = vty->index;
+ struct gcr_bss *bss;
+ int pc = osmo_ss7_pointcode_parse(ss7, argv[0]);
+ uint16_t cell_id;
+
+ if (pc < 0 || !osmo_ss7_pc_is_valid((uint32_t)pc)) {
+ vty_out(vty, "Invalid point code (%s)%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bss = gcr_find_bss(gcr, pc);
+ if (!bss) {
+ vty_out(vty, "%%Given BSS point code does not exit in list!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (argc > 1) {
+ cell_id = atoi(argv[1]);
+ if (!gcr_find_cell(bss, cell_id)) {
+ vty_out(vty, "%%Given cell does not exit in list!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Remove cell only. Exit if there are still cells for this BSS. */
+ gcr_rm_cell(bss, cell_id);
+ if (!llist_empty(&bss->cell_list))
+ return CMD_SUCCESS;
+ }
+
+ gcr_rm_bss(gcr, pc);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cell, cfg_cell_cmd,
+ "cell POINT_CODE <0-65535>", "Add cell to current group\n" PC_ID_STR)
+{
+ struct osmo_ss7_instance *ss7 = osmo_ss7_instance_find(0);
+ struct gcr *gcr = vty->index;
+ struct gcr_bss *bss;
+ int pc = osmo_ss7_pointcode_parse(ss7, argv[0]);
+ uint16_t cell_id = atoi(argv[1]);
+
+ if (pc < 0 || !osmo_ss7_pc_is_valid((uint32_t)pc)) {
+ vty_out(vty, "Invalid point code (%s)%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bss = gcr_find_bss(gcr, pc);
+ if (!bss)
+ bss = gcr_add_bss(gcr, pc);
+ if (!bss)
+ return CMD_WARNING;
+
+ if (gcr_find_cell(bss, cell_id))
+ return CMD_SUCCESS;
+
+ gcr_add_cell(bss, cell_id);
+
+ return CMD_SUCCESS;
+}
+
+static int config_write_asci(struct vty *vty)
+{
+ struct osmo_ss7_instance *ss7 = osmo_ss7_instance_find(0);
+ struct gcr *gcr;
+ struct gcr_bss *b;
+ struct gcr_cell *c;
+
+ vty_out(vty, "asci%s", VTY_NEWLINE);
+
+ vty_out(vty, " %s%s", (gsmnet->asci.enable) ? "enable" : "disable", VTY_NEWLINE);
+
+ vty_out(vty, " gcr%s", VTY_NEWLINE);
+
+ llist_for_each_entry(gcr, &gsmnet->asci.gcr_lists, list) {
+ vty_out(vty, " %s %s%s", (gcr->trans_type == TRANS_GCC) ? "vgc" : "vbc", gcr->group_id, VTY_NEWLINE);
+ if (gcr->trans_type == TRANS_GCC) {
+ if (gcr->timeout)
+ vty_out(vty, " timeout %d%s", gcr->timeout, VTY_NEWLINE);
+ else
+ vty_out(vty, " no timeout%s", VTY_NEWLINE);
+ }
+ if (gcr->mute_talker)
+ vty_out(vty, " mute-talker%s", VTY_NEWLINE);
+ else
+ vty_out(vty, " unmute-talker%s", VTY_NEWLINE);
+ if (llist_empty(&gcr->bss_list))
+ vty_out(vty, " ! Please add cell(s) here!%s", VTY_NEWLINE);
+ llist_for_each_entry(b, &gcr->bss_list, list) {
+ llist_for_each_entry(c, &b->cell_list, list)
+ vty_out(vty, " cell %s %d%s", osmo_ss7_pointcode_print(ss7, b->pc), c->cell_id, VTY_NEWLINE);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+void asci_vty_init(struct gsm_network *msc_network)
+{
+ OSMO_ASSERT(gsmnet == NULL);
+ gsmnet = msc_network;
+
+ install_element_ve(&asci_show_cmd);
+ /* enable node */
+ install_element(ENABLE_NODE, &asci_call_cmd);
+ /* Config node */
+ install_element(CONFIG_NODE, &cfg_asci_cmd);
+ install_node(&asci_node, config_write_asci);
+ install_element(ASCI_NODE, &cfg_enable_disable_cmd);
+ install_element(ASCI_NODE, &cfg_gcr_cmd);
+ install_node(&gcr_node, NULL);
+ install_element(GCR_NODE, &cfg_vgc_cmd);
+ install_element(GCR_NODE, &cfg_no_vgc_cmd);
+ install_node(&vgc_node, NULL);
+ install_element(GCR_NODE, &cfg_vbc_cmd);
+ install_element(GCR_NODE, &cfg_no_vbc_cmd);
+ install_node(&vbc_node, NULL);
+ install_element(VGC_NODE, &cfg_mute_cmd);
+ install_element(VGC_NODE, &cfg_unmute_cmd);
+ install_element(VGC_NODE, &cfg_timeout_cmd);
+ install_element(VGC_NODE, &cfg_no_timeout_cmd);
+ install_element(VGC_NODE, &cfg_cell_cmd);
+ install_element(VGC_NODE, &cfg_no_cell_cmd);
+ /* Add all VGC_NODEs again for VBC_NODEs. */
+ install_element(VBC_NODE, &cfg_mute_cmd);
+ install_element(VBC_NODE, &cfg_unmute_cmd);
+ install_element(VBC_NODE, &cfg_cell_cmd);
+ install_element(VBC_NODE, &cfg_no_cell_cmd);
+}
diff --git a/src/libmsc/msc_vty.c b/src/libmsc/msc_vty.c
index d1b74c8e8..20321356f 100644
--- a/src/libmsc/msc_vty.c
+++ b/src/libmsc/msc_vty.c
@@ -68,6 +68,7 @@
#include <osmocom/msc/sccp_ran.h>
#include <osmocom/msc/ran_peer.h>
#include <osmocom/msc/ran_infra.h>
+#include <osmocom/msc/asci_vty.h>
static struct gsm_network *gsmnet = NULL;
@@ -2078,6 +2079,7 @@ void msc_vty_init(struct gsm_network *msc_network)
#endif
sgs_vty_init();
smsc_vty_init(msc_network);
+ asci_vty_init(msc_network);
osmo_fsm_vty_add_cmds();
diff --git a/src/osmo-msc/msc_main.c b/src/osmo-msc/msc_main.c
index 0c2525789..9808e8696 100644
--- a/src/osmo-msc/msc_main.c
+++ b/src/osmo-msc/msc_main.c
@@ -330,9 +330,19 @@ static int msc_vty_go_parent(struct vty *vty)
case SMPP_NODE:
case MSC_NODE:
case MNCC_INT_NODE:
+ case ASCI_NODE:
vty->node = CONFIG_NODE;
vty->index = NULL;
break;
+ case GCR_NODE:
+ vty->node = ASCI_NODE;
+ vty->index = NULL;
+ break;
+ case VGC_NODE:
+ case VBC_NODE:
+ vty->node = GCR_NODE;
+ vty->index = NULL;
+ break;
case SUBSCR_NODE:
vty->node = ENABLE_NODE;
vty->index = NULL;
diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty
index 400bc7d66..b8f031089 100644
--- a/tests/test_nodes.vty
+++ b/tests/test_nodes.vty
@@ -6,6 +6,7 @@ OsmoMSC(config)# list
msc
sgs
smsc
+ asci
mncc-int
hlr
...
@@ -181,6 +182,9 @@ smsc
database delete-expired 1
validity-period minimum 1
validity-period default 10080
+asci
+ disable
+ gcr
end
OsmoMSC# configure terminal