osmo-e1d/src/octoi/octoi_clnt_vty.c

299 lines
7.9 KiB
C

/*
* octoi_clnt_vty.c - VTY code for OCTOI client role
*
* (C) 2022 by Harald Welte <laforge@osmocom.org>
*
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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.
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <talloc.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/misc.h>
#include "octoi.h"
#include "octoi_sock.h"
#include "octoi_fsm.h"
#include "octoi_vty.h"
/***********************************************************************
* core data structures
***********************************************************************/
static struct octoi_client *octoi_client_alloc(void *ctx, const char *ip, uint16_t port)
{
struct octoi_client *clnt = talloc_zero(ctx, struct octoi_client);
int rc;
if (!clnt)
return NULL;
rc = osmo_sockaddr_str_from_str(&clnt->cfg.remote, ip, port);
if (rc < 0) {
talloc_free(clnt);
return NULL;
}
return clnt;
}
/* find a client for given remote IP + port */
struct octoi_client *octoi_client_find(const char *ip, uint16_t port)
{
struct octoi_client *clnt;
llist_for_each_entry(clnt, &g_octoi->clients, list) {
if (!strcmp(ip, clnt->cfg.remote.ip) && clnt->cfg.remote.port == port)
return clnt;
}
return NULL;
}
/***********************************************************************
* VTY
***********************************************************************/
static struct cmd_node clnt_node = {
(enum node_type) OCTOI_CLNT_NODE,
"%s(config-octoi-client)# ",
1,
};
static struct cmd_node clnt_account_node = {
(enum node_type) OCTOI_CLNT_ACCOUNT_NODE,
"%s(config-octoi-client-account)# ",
1,
};
DEFUN(cfg_client, cfg_client_cmd,
"octoi-client (A.B.C.D|X:X::X:X) <0-65535>",
"Configure an OCTOI client\n"
"Remote IPv4 address of OCTOI server\n"
"Remote IPv6 address of OCTOI server\n"
"Remote UDP port number of OCTOI server\n")
{
const char *ip = argv[0];
int port = atoi(argv[1]);
struct octoi_client *clnt = octoi_client_find(ip, port);
if (!clnt) {
clnt = octoi_client_alloc(g_octoi, ip, port);
OSMO_ASSERT(clnt);
llist_add_tail(&clnt->list, &g_octoi->clients);
}
vty->node = OCTOI_CLNT_NODE;
vty->index = clnt;
return CMD_SUCCESS;
}
#if 0
DEFUN(cfg_no_client, cfg_no_client_cmd,
"no octoi-client (A.B.C.D|X:X::X:X) <0-65535>",
NO_STR "Remove an OCTOI client\n")
"Remote IPv4 address of OCTOI server\n"
"Remote IPv6 address of OCTOI server\n"
"Remote UDP port number of OCTOI server\n")
{
}
#endif
DEFUN(cfg_clnt_local, cfg_clnt_local_cmd,
"local-bind (A.B.C.D|X:X::X:X) <0-65535>",
"Local OCTOI socket bind address/port\n"
"Local OCTOI IPv4 Address\n"
"Local OCTOI IPv6 Address\n"
"Local OCTOI UDP Port Number\n")
{
struct octoi_client *clnt = vty->index;
int rc;
rc = osmo_sockaddr_str_from_str(&clnt->cfg.local, argv[0], atoi(argv[1]));
if (rc < 0) {
vty_out(vty, "%% sockaddr Error: %s%s", strerror(errno), VTY_NEWLINE);
return CMD_WARNING;
}
if (clnt->sock)
octoi_sock_destroy(clnt->sock);
clnt->sock = octoi_sock_create_client(clnt, clnt, &clnt->cfg.local, &clnt->cfg.remote);
if (!clnt->sock) {
vty_out(vty, "%% failed to create/bind socket: %s%s", strerror(errno), VTY_NEWLINE);
return CMD_WARNING;
}
clnt->sock->rx_cb = octoi_clnt_fsm_rx_cb;
if (clnt->cfg.dscp) {
rc = octoi_sock_set_dscp(clnt->sock, clnt->cfg.dscp);
if (rc < 0) {
vty_out(vty, "%% failed to set DSCP on socket: %s%s", strerror(errno), VTY_NEWLINE);
return CMD_WARNING;
}
}
if (clnt->cfg.priority) {
rc = octoi_sock_set_priority(clnt->sock, clnt->cfg.priority);
if (rc < 0) {
vty_out(vty, "%% failed to set priority on socket: %s%s", strerror(errno), VTY_NEWLINE);
return CMD_WARNING;
}
}
return CMD_SUCCESS;
}
DEFUN(cfg_clnt_dscp, cfg_clnt_dscp_cmd,
"ip-dscp <0-63>",
"Set IP DSCP value for outbound packets\n"
"IP DSCP Value to use\n")
{
struct octoi_client *clnt = vty->index;
int rc;
clnt->cfg.dscp = atoi(argv[0]);
if (!clnt->sock)
return CMD_SUCCESS;
/* apply to already-existing server */
rc = octoi_sock_set_dscp(clnt->sock, clnt->cfg.dscp);
if (rc < 0) {
vty_out(vty, "%% failed to set DSCP on socket: %s%s", strerror(errno), VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_clnt_prio, cfg_clnt_prio_cmd,
"socket-priority <0-255>",
"Set socket priority value for outbound packets\n"
"Socket Priority\n")
{
struct octoi_client *clnt = vty->index;
int rc;
clnt->cfg.priority = atoi(argv[0]);
if (!clnt->sock)
return CMD_SUCCESS;
/* apply to already-existing server */
rc = octoi_sock_set_priority(clnt->sock, clnt->cfg.priority);
if (rc < 0) {
vty_out(vty, "%% failed to set priority on socket: %s%s", strerror(errno), VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_clnt_account, cfg_clnt_account_cmd,
"account USER_ID",
"Configure a local user account\n")
{
struct octoi_client *clnt = vty->index;
const char *user_id = argv[0];
struct octoi_account *ac = clnt->cfg.account;
if (!ac) {
ac = octoi_client_account_create(clnt, user_id);
OSMO_ASSERT(ac);
} else
osmo_talloc_replace_string(ac, &ac->user_id, user_id);
vty->node = OCTOI_CLNT_ACCOUNT_NODE;
vty->index = ac;
return CMD_SUCCESS;
}
DEFUN(show_clnt, show_clnt_cmd,
"show octoi-clients",
SHOW_STR "Display information about the OCTOI Clients\n")
{
struct octoi_client *clnt;
llist_for_each_entry(clnt, &g_octoi->clients, list) {
struct octoi_sock *sock = clnt->sock;
octoi_vty_show_one_account(vty, "", clnt->cfg.account);
vty_show_octoi_sock(vty, sock);
}
return CMD_SUCCESS;
}
static int config_write_octoi_clnt(struct vty *vty)
{
struct octoi_client *clnt;
llist_for_each_entry(clnt, &g_octoi->clients, list) {
vty_out(vty, "octoi-client %s %u%s", clnt->cfg.remote.ip, clnt->cfg.remote.port,
VTY_NEWLINE);
if (strlen(clnt->cfg.local.ip)) {
vty_out(vty, " local-bind %s %u%s", clnt->cfg.local.ip, clnt->cfg.local.port,
VTY_NEWLINE);
}
if (clnt->cfg.dscp)
vty_out(vty, " ip-dscp %u%s", clnt->cfg.dscp, VTY_NEWLINE);
if (clnt->cfg.priority)
vty_out(vty, " socket-priority %u%s", clnt->cfg.priority, VTY_NEWLINE);
octoi_vty_write_one_account(vty, clnt->cfg.account);
}
return 0;
}
void octoi_client_vty_init(void)
{
install_element_ve(&show_clnt_cmd);
install_node(&clnt_account_node, NULL);
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_ice1_serno_cmd);
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_ice1_line_cmd);
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_mode_cmd);
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_batching_factor_cmd);
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_force_all_ts_cmd);
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_no_force_all_ts_cmd);
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_prefill_frame_count_cmd);
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_buffer_reset_cmd);
#ifdef HAVE_DAHDI_TRUNKDEV
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_trunkdev_name_cmd);
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_trunkdev_line_cmd);
#endif /* HAVE_DAHDI_TRUNKDEV */
install_node(&clnt_node, config_write_octoi_clnt);
install_element(CONFIG_NODE, &cfg_client_cmd);
install_element(OCTOI_CLNT_NODE, &cfg_clnt_local_cmd);
install_element(OCTOI_CLNT_NODE, &cfg_clnt_dscp_cmd);
install_element(OCTOI_CLNT_NODE, &cfg_clnt_prio_cmd);
install_element(OCTOI_CLNT_NODE, &cfg_clnt_account_cmd);
}