From f8ac734849a2ecf004858042930b274e74bbcf81 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 23 Apr 2023 12:22:25 +0200 Subject: ASCI: Add VTY to configure GCR (Group Call Register) Change-Id: I5bd034a62fc8b483f550d29103c2f7587198f590 Related: OS#4854 --- include/osmocom/msc/Makefile.am | 1 + include/osmocom/msc/asci_vty.h | 25 +++ include/osmocom/msc/vty.h | 4 + src/libmsc/Makefile.am | 1 + src/libmsc/asci_vty.c | 434 ++++++++++++++++++++++++++++++++++++++++ src/libmsc/msc_vty.c | 2 + src/osmo-msc/msc_main.c | 10 + tests/test_nodes.vty | 4 + 8 files changed, 481 insertions(+) create mode 100644 include/osmocom/msc/asci_vty.h create mode 100644 src/libmsc/asci_vty.c 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 + * 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 . + */ + +#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 + * 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 . + */ + +#include +#include +#include +#include +#include + +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 #include #include +#include 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 -- cgit v1.2.3