334 lines
8.9 KiB
C
334 lines
8.9 KiB
C
/*
|
|
* (C) 2016 by Holger Hans Peter Freyther
|
|
* (C) 2018 by Harald Welte <laforge@gnumonks.org>
|
|
*
|
|
* All Rights Reserved
|
|
*
|
|
* 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 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 "vty.h"
|
|
#include "app.h"
|
|
#include "call.h"
|
|
#include "mncc.h"
|
|
|
|
#include <talloc.h>
|
|
|
|
#include <sofia-sip/su_log.h>
|
|
|
|
extern void *tall_mncc_ctx;
|
|
|
|
struct app_config g_app;
|
|
|
|
static int mncc_vty_go_parent(struct vty *vty);
|
|
static int mncc_vty_is_config_node(struct vty *vty, int node);
|
|
|
|
static struct cmd_node sip_node = {
|
|
SIP_NODE,
|
|
"%s(config-sip)# ",
|
|
1,
|
|
};
|
|
|
|
static struct cmd_node mncc_node = {
|
|
MNCC_NODE,
|
|
"%s(config-mncc)# ",
|
|
1,
|
|
};
|
|
|
|
static struct cmd_node app_node = {
|
|
APP_NODE,
|
|
"%s(config-app)# ",
|
|
1,
|
|
};
|
|
|
|
static struct vty_app_info vty_info = {
|
|
.name = "OsmoSIPcon",
|
|
.version = PACKAGE_VERSION,
|
|
.go_parent_cb = mncc_vty_go_parent,
|
|
.is_config_node = mncc_vty_is_config_node,
|
|
.copyright = "GNU AGPLv3+\n",
|
|
};
|
|
|
|
static int mncc_vty_go_parent(struct vty *vty)
|
|
{
|
|
switch (vty->node) {
|
|
case SIP_NODE:
|
|
case MNCC_NODE:
|
|
case APP_NODE:
|
|
vty->node = CONFIG_NODE;
|
|
vty->index = NULL;
|
|
break;
|
|
default:
|
|
if (mncc_vty_is_config_node(vty, vty->node))
|
|
vty->node = CONFIG_NODE;
|
|
else
|
|
vty->node = ENABLE_NODE;
|
|
vty->index = NULL;
|
|
break;
|
|
}
|
|
return vty->node;
|
|
}
|
|
|
|
static int mncc_vty_is_config_node(struct vty *vty, int node)
|
|
{
|
|
return node >= SIP_NODE;
|
|
}
|
|
|
|
static int config_write_sip(struct vty *vty)
|
|
{
|
|
vty_out(vty, "sip%s", VTY_NEWLINE);
|
|
vty_out(vty, " local %s %d%s", g_app.sip.local_addr, g_app.sip.local_port, VTY_NEWLINE);
|
|
vty_out(vty, " remote %s %d%s", g_app.sip.remote_addr, g_app.sip.remote_port, VTY_NEWLINE);
|
|
vty_out(vty, " sofia-sip log-level %d%s", g_app.sip.sofia_log_level, VTY_NEWLINE);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
static int config_write_mncc(struct vty *vty)
|
|
{
|
|
vty_out(vty, "mncc%s", VTY_NEWLINE);
|
|
vty_out(vty, " socket-path %s%s", g_app.mncc.path, VTY_NEWLINE);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
static int config_write_app(struct vty *vty)
|
|
{
|
|
vty_out(vty, "app%s", VTY_NEWLINE);
|
|
if (g_app.use_imsi_as_id)
|
|
vty_out(vty, " use-imsi%s", VTY_NEWLINE);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_sip, cfg_sip_cmd,
|
|
"sip", "SIP related commands\n")
|
|
{
|
|
vty->node = SIP_NODE;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_sip_local_addr, cfg_sip_local_addr_cmd,
|
|
"local A.B.C.D <1-65534>",
|
|
"Local information\nIPv4 bind address\nport\n")
|
|
{
|
|
talloc_free((char *) g_app.sip.local_addr);
|
|
g_app.sip.local_addr = talloc_strdup(tall_mncc_ctx, argv[0]);
|
|
g_app.sip.local_port = atoi(argv[1]);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_sip_remote_addr, cfg_sip_remote_addr_cmd,
|
|
"remote ADDR <1-65534>",
|
|
"Remote information\nSIP hostname\nport\n")
|
|
{
|
|
talloc_free((char *) g_app.sip.remote_addr);
|
|
g_app.sip.remote_addr = talloc_strdup(tall_mncc_ctx, argv[0]);
|
|
g_app.sip.remote_port = atoi(argv[1]);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_sip_sofia_log_level, cfg_sip_sofia_log_level_cmd,
|
|
"sofia-sip log-level <0-9>",
|
|
"sofia-sip library configuration\n"
|
|
"global log-level for sofia-sip\n"
|
|
"(0 = nothing, 9 = super-verbose)\n")
|
|
{
|
|
g_app.sip.sofia_log_level = atoi(argv[0]);
|
|
su_log_set_level(su_log_default, g_app.sip.sofia_log_level);
|
|
su_log_set_level(su_log_global, g_app.sip.sofia_log_level);
|
|
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_mncc, cfg_mncc_cmd,
|
|
"mncc",
|
|
"MNCC\n")
|
|
{
|
|
vty->node = MNCC_NODE;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_mncc_path, cfg_mncc_path_cmd,
|
|
"socket-path NAME",
|
|
"MNCC filepath\nFilename\n")
|
|
{
|
|
talloc_free((char *) g_app.mncc.path);
|
|
g_app.mncc.path = talloc_strdup(tall_mncc_ctx, argv[0]);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_app, cfg_app_cmd,
|
|
"app", "Application Handling\n")
|
|
{
|
|
vty->node = APP_NODE;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_use_imsi, cfg_use_imsi_cmd,
|
|
"use-imsi",
|
|
"Use the IMSI for MO calling and MT called address\n")
|
|
{
|
|
g_app.use_imsi_as_id = 1;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_no_use_imsi, cfg_no_use_imsi_cmd,
|
|
"no use-imsi",
|
|
NO_STR "Use the IMSI for MO calling and MT called address\n")
|
|
{
|
|
g_app.use_imsi_as_id = 0;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
static void dump_leg(struct vty *vty, struct call_leg *leg, const char *kind)
|
|
{
|
|
struct sip_call_leg *sip;
|
|
struct mncc_call_leg *mncc;
|
|
|
|
if (!leg)
|
|
return;
|
|
|
|
vty_out(vty, " %s leg of type: %s%s",
|
|
kind,
|
|
get_value_string(call_type_vals, leg->type),
|
|
VTY_NEWLINE);
|
|
|
|
switch (leg->type) {
|
|
case CALL_TYPE_SIP:
|
|
sip = (struct sip_call_leg *) leg;
|
|
vty_out(vty, " SIP nua_handle(%p)%s", sip->nua_handle, VTY_NEWLINE);
|
|
vty_out(vty, " SIP state(%s)%s",
|
|
get_value_string(sip_state_vals, sip->state), VTY_NEWLINE);
|
|
vty_out(vty, " SIP dir(%s)%s",
|
|
get_value_string(sip_dir_vals, sip->dir), VTY_NEWLINE);
|
|
vty_out(vty, " SIP wanted_codec(%s)%s", sip->wanted_codec, VTY_NEWLINE);
|
|
break;
|
|
case CALL_TYPE_MNCC:
|
|
mncc = (struct mncc_call_leg *) leg;
|
|
vty_out(vty, " MNCC state(%s)%s",
|
|
get_value_string(mncc_state_vals, mncc->state), VTY_NEWLINE);
|
|
vty_out(vty, " MNCC dir(%s)%s",
|
|
get_value_string(mncc_dir_vals, mncc->dir), VTY_NEWLINE);
|
|
vty_out(vty, " MNCC callref(%u)%s", mncc->callref, VTY_NEWLINE);
|
|
vty_out(vty, " MNCC called TON(%d) NPI(%d) NUM(%.32s)%s",
|
|
mncc->called.type, mncc->called.plan, mncc->called.number,
|
|
VTY_NEWLINE);
|
|
vty_out(vty, " MNCC calling TON(%d) NPI(%d) NUM(%.32s)%s",
|
|
mncc->calling.type, mncc->calling.plan, mncc->calling.number,
|
|
VTY_NEWLINE);
|
|
vty_out(vty, " MNCC imsi(%.16s)%s", mncc->imsi, VTY_NEWLINE);
|
|
vty_out(vty, " MNCC timer pending(%d)%s",
|
|
osmo_timer_pending(&mncc->cmd_timeout), VTY_NEWLINE);
|
|
break;
|
|
default:
|
|
vty_out(vty, " Unhandled type: %d%s", leg->type, VTY_NEWLINE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
DEFUN(show_calls, show_calls_cmd,
|
|
"show calls",
|
|
SHOW_STR "Current calls\n")
|
|
{
|
|
struct call *call;
|
|
|
|
llist_for_each_entry(call, &g_call_list, entry) {
|
|
vty_out(vty, "Call(%u) from %s to %s%s",
|
|
call->id, call->source, call->dest, VTY_NEWLINE);
|
|
dump_leg(vty, call->initial, "Initial");
|
|
dump_leg(vty, call->remote, "Remote");
|
|
}
|
|
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(show_calls_sum, show_calls_sum_cmd,
|
|
"show calls summary",
|
|
SHOW_STR "Current calls\nBrief overview\n")
|
|
{
|
|
/* don't print a table for zero active calls */
|
|
if(llist_empty(&g_call_list)) {
|
|
vty_out(vty, "No active calls.%s", VTY_NEWLINE);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/* table head */
|
|
vty_out(vty, "ID From To State%s", VTY_NEWLINE);
|
|
vty_out(vty, "----- -------------------------------- -------------------------------- ----------%s",
|
|
VTY_NEWLINE);
|
|
|
|
/* iterate over calls */
|
|
struct call *call;
|
|
llist_for_each_entry(call, &g_call_list, entry) {
|
|
/* only look at the initial=MNCC call */
|
|
if(call->initial->type == CALL_TYPE_MNCC) {
|
|
struct mncc_call_leg *leg = (struct mncc_call_leg *) call->initial;
|
|
|
|
/* table row */
|
|
char *called = talloc_strdup(tall_mncc_ctx, leg->called.number);
|
|
char *calling = talloc_strdup(tall_mncc_ctx, leg->calling.number);
|
|
char *state = talloc_strdup(tall_mncc_ctx, call_leg_state(call->initial));
|
|
vty_out(vty, "%5u %-32s %-32s %s%s", call->id, calling, called, state, VTY_NEWLINE);
|
|
|
|
/* clean up */
|
|
talloc_free(called);
|
|
talloc_free(calling);
|
|
talloc_free(state);
|
|
}
|
|
}
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(show_mncc_conn, show_mncc_conn_cmd,
|
|
"show mncc-connection",
|
|
SHOW_STR "MNCC Connection state\n")
|
|
{
|
|
vty_out(vty, "MNCC connection to path '%s' is in state %s%s",
|
|
g_app.mncc.path,
|
|
get_value_string(mncc_conn_state_vals, g_app.mncc.conn.state),
|
|
VTY_NEWLINE);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
void mncc_sip_vty_init(void)
|
|
{
|
|
/* default values */
|
|
g_app.mncc.path = talloc_strdup(tall_mncc_ctx, "/tmp/msc_mncc");
|
|
g_app.sip.local_addr = talloc_strdup(tall_mncc_ctx, "127.0.0.1");
|
|
g_app.sip.local_port = 5060;
|
|
g_app.sip.remote_addr = talloc_strdup(tall_mncc_ctx, "pbx");
|
|
g_app.sip.remote_port = 5060;
|
|
|
|
|
|
vty_init(&vty_info);
|
|
|
|
install_element(CONFIG_NODE, &cfg_sip_cmd);
|
|
install_node(&sip_node, config_write_sip);
|
|
install_element(SIP_NODE, &cfg_sip_local_addr_cmd);
|
|
install_element(SIP_NODE, &cfg_sip_remote_addr_cmd);
|
|
install_element(SIP_NODE, &cfg_sip_sofia_log_level_cmd);
|
|
|
|
install_element(CONFIG_NODE, &cfg_mncc_cmd);
|
|
install_node(&mncc_node, config_write_mncc);
|
|
install_element(MNCC_NODE, &cfg_mncc_path_cmd);
|
|
|
|
install_element(CONFIG_NODE, &cfg_app_cmd);
|
|
install_node(&app_node, config_write_app);
|
|
install_element(APP_NODE, &cfg_use_imsi_cmd);
|
|
install_element(APP_NODE, &cfg_no_use_imsi_cmd);
|
|
|
|
install_element_ve(&show_calls_cmd);
|
|
install_element_ve(&show_calls_sum_cmd);
|
|
install_element_ve(&show_mncc_conn_cmd);
|
|
}
|