pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmo-sigtran/+/40456?usp=email )
Change subject: Split AS/ASP/XUA_SRV VTY code to its own files ......................................................................
Split AS/ASP/XUA_SRV VTY code to its own files
This allows readers finding more easily and focusing on the relevant subset of VTY code for each node/object.
Change-Id: I90643bea41098cd6b711e493d4dc9852e88504b1 --- M src/Makefile.am A src/ss7_as_vty.c A src/ss7_asp_vty.c M src/ss7_internal.h M src/ss7_vty.c A src/ss7_vty.h A src/ss7_xua_srv_vty.c 7 files changed, 2,234 insertions(+), 2,015 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmo-sigtran refs/changes/56/40456/1
diff --git a/src/Makefile.am b/src/Makefile.am index bba3d22..53b80c0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,6 +15,7 @@ ss7_route.h \ ss7_route_table.h \ ss7_user.h \ + ss7_vty.h \ ss7_xua_srv.h \ xua_asp_fsm.h \ xua_as_fsm.h \ @@ -54,7 +55,9 @@ sccp_vty.c \ ss7.c \ ss7_as.c \ + ss7_as_vty.c \ ss7_asp.c \ + ss7_asp_vty.c \ ss7_asp_peer.c \ ss7_combined_linkset.c \ ss7_hmrt.c \ @@ -63,6 +66,7 @@ ss7_linkset.c \ ss7_vty.c \ ss7_xua_srv.c \ + ss7_xua_srv_vty.c \ ss7_route.c \ ss7_route_table.c \ ss7_user.c \ diff --git a/src/ss7_as_vty.c b/src/ss7_as_vty.c new file mode 100644 index 0000000..6293de2 --- /dev/null +++ b/src/ss7_as_vty.c @@ -0,0 +1,627 @@ +/* SS7 AS VTY Interface */ + +/* (C) 2015-2021 by Harald Welte laforge@gnumonks.org + * (C) 2025 by sysmocom s.f.m.c. GmbH info@sysmocom.de + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> + +#include <osmocom/vty/vty.h> +#include <osmocom/vty/command.h> +#include <osmocom/vty/logging.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/misc.h> + +#include <osmocom/sigtran/protocol/mtp.h> + +#include "ss7_as.h" +#include "ss7_asp.h" +#include "ss7_route.h" +#include "ss7_route_table.h" +#include "ss7_internal.h" +#include "ss7_vty.h" + +/*********************************************************************** + * Application Server + ***********************************************************************/ + +static struct cmd_node as_node = { + L_CS7_AS_NODE, + "%s(config-cs7-as)# ", + 1, +}; + +DEFUN_ATTR(cs7_as, cs7_as_cmd, + "as NAME " XUA_VAR_STR, + "Configure an Application Server\n" + "Name of the Application Server\n" + XUA_VAR_HELP_STR, + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_instance *inst = vty->index; + struct osmo_ss7_as *as; + const char *name = argv[0]; + enum osmo_ss7_asp_protocol protocol = parse_asp_proto(argv[1]); + + if (protocol == OSMO_SS7_ASP_PROT_NONE) { + vty_out(vty, "invalid protocol '%s'%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + as = osmo_ss7_as_find_or_create(inst, name, protocol); + if (!as) { + vty_out(vty, "cannot create AS '%s'%s", name, VTY_NEWLINE); + return CMD_WARNING; + } + + as->cfg.name = talloc_strdup(as, name); + + vty->node = L_CS7_AS_NODE; + vty->index = as; + vty->index_sub = &as->cfg.description; + + return CMD_SUCCESS; +} + +DEFUN_ATTR(no_cs7_as, no_cs7_as_cmd, + "no as NAME", + NO_STR "Disable Application Server\n" + "Name of AS\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_instance *inst = vty->index; + const char *name = argv[0]; + struct osmo_ss7_as *as; + + as = osmo_ss7_as_find_by_name(inst, name); + if (!as) { + vty_out(vty, "No AS named '%s' found%s", name, VTY_NEWLINE); + return CMD_WARNING; + } + osmo_ss7_as_destroy(as); + return CMD_SUCCESS; +} + +/* TODO: routing-key */ +DEFUN_ATTR(as_asp, as_asp_cmd, + "asp NAME", + "Specify that a given ASP is part of this AS\n" + "Name of ASP to be added to AS\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_as *as = vty->index; + + if (osmo_ss7_as_add_asp(as, argv[0])) { + vty_out(vty, "cannot find ASP '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN_ATTR(as_no_asp, as_no_asp_cmd, + "no asp NAME", + NO_STR "Specify ASP to be removed from this AS\n" + "Name of ASP to be removed\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_as *as = vty->index; + + if (osmo_ss7_as_del_asp(as, argv[0])) { + vty_out(vty, "cannot find ASP '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN_USRATTR(as_traf_mode, as_traf_mode_cmd, + OSMO_SCCP_LIB_ATTR_RSTRT_ASP, + "traffic-mode (broadcast | roundrobin | override)", + "Specifies traffic mode of operation of the ASP within the AS\n" + "Broadcast to all ASP within AS\n" + "Round-Robin between all ASP within AS\n" + "Override\n") +{ + struct osmo_ss7_as *as = vty->index; + + as->cfg.mode = get_string_value(osmo_ss7_as_traffic_mode_vals, argv[0]); + as->cfg.mode_set_by_vty = true; + return CMD_SUCCESS; +} + +DEFUN_USRATTR(as_traf_mode_loadshare, as_traf_mode_loadshare_cmd, + OSMO_SCCP_LIB_ATTR_RSTRT_ASP, + "traffic-mode loadshare [bindings] [sls] [opc-sls] [opc-shift] [<0-2>]", + "Specifies traffic mode of operation of the ASP within the AS\n" + "Share Load among all ASP within AS\n" + "Configure Loadshare parameters\n" + "Configure Loadshare SLS generation parameters\n" + "Generate extended SLS with OPC information\n" + "Shift OPC bits used during routing decision\n" + "How many bits from ITU OPC field (starting from least-significant-bit) to skip (default=0). 6 bits are always used\n" + ) +{ + struct osmo_ss7_as *as = vty->index; + + as->cfg.mode = OSMO_SS7_AS_TMOD_LOADSHARE; + as->cfg.mode_set_by_vty = true; + if (argc < 3) { + as->cfg.loadshare.opc_sls = false; + as->cfg.loadshare.opc_shift = 0; + return CMD_SUCCESS; + } + as->cfg.loadshare.opc_sls = true; + if (argc < 5) { + as->cfg.loadshare.opc_shift = 0; + return CMD_SUCCESS; + } + as->cfg.loadshare.opc_shift = atoi(argv[4]); + return CMD_SUCCESS; +} + +DEFUN_USRATTR(as_no_traf_mode, as_no_traf_mode_cmd, + OSMO_SCCP_LIB_ATTR_RSTRT_ASP, + "no traffic-mode", + NO_STR "Remove explicit traffic mode of operation of this AS\n") +{ + struct osmo_ss7_as *as = vty->index; + + as->cfg.mode = 0; + as->cfg.mode_set_by_vty = false; + + as->cfg.loadshare.sls_shift = 0; + as->cfg.loadshare.opc_sls = false; + as->cfg.loadshare.opc_shift = 0; + return CMD_SUCCESS; +} + +DEFUN_ATTR(as_sls_shift, as_sls_shift_cmd, + "sls-shift <0-3>", + "Shift SLS bits used during routing decision\n" + "How many bits from SLS field (starting from least-significant-bit) to skip\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_as *as = vty->index; + as->cfg.loadshare.sls_shift = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN_ATTR(as_bindingtable_reset, as_bindingtable_reset_cmd, + "binding-table reset", + "AS Loadshare binding table operations\n" + "Reset loadshare binding table\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_as *as = vty->index; + ss7_as_loadshare_binding_table_reset(as); + return CMD_SUCCESS; +} + +DEFUN_ATTR(as_recov_tout, as_recov_tout_cmd, + "recovery-timeout <1-2000>", + "Specifies the recovery timeout value in milliseconds\n" + "Recovery Timeout in Milliseconds\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_as *as = vty->index; + as->cfg.recovery_timeout_msec = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN_ATTR(as_qos_clas, as_qos_class_cmd, + "qos-class " QOS_CLASS_RANGE_STR, + "Specity QoS Class of AS\n" + QOS_CLASS_RANGE_HELP_STR, + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_as *as = vty->index; + as->cfg.qos_class = atoi(argv[0]); + return CMD_SUCCESS; +} + +const struct value_string mtp_si_vals[] = { + { MTP_SI_SCCP, "sccp" }, + { MTP_SI_TUP, "tup" }, + { MTP_SI_ISUP, "isup" }, + { MTP_SI_DUP, "dup" }, + { MTP_SI_TESTING, "testing" }, + { MTP_SI_B_ISUP, "b-isup" }, + { MTP_SI_SAT_ISUP, "sat-isup" }, + { MTP_SI_AAL2_SIG, "aal2" }, + { MTP_SI_BICC, "bicc" }, + { MTP_SI_GCP, "h248" }, + { 0, NULL } +}; + +#define ROUTING_KEY_CMD "routing-key RCONTEXT DPC" +#define ROUTING_KEY_CMD_STRS \ + "Define a routing key\n" \ + "Routing context number\n" \ + "Destination Point Code\n" +#define ROUTING_KEY_SI_ARG " si (aal2|bicc|b-isup|h248|isup|sat-isup|sccp|tup)" +#define ROUTING_KEY_SI_ARG_STRS \ + "Match on Service Indicator\n" \ + "ATM Adaption Layer 2\n" \ + "Bearer Independent Call Control\n" \ + "Broadband ISDN User Part\n" \ + "H.248\n" \ + "ISDN User Part\n" \ + "Sattelite ISDN User Part\n" \ + "Signalling Connection Control Part\n" \ + "Telephony User Part\n" +#define ROUTING_KEY_SSN_ARG " ssn SSN" +#define ROUTING_KEY_SSN_ARG_STRS \ + "Match on Sub-System Number\n" \ + "Sub-System Number to match on\n" + +static int _rout_key(struct vty *vty, + const char *rcontext, const char *dpc, + const char *si, const char *ssn) +{ + struct osmo_ss7_as *as = vty->index; + struct osmo_ss7_routing_key *rkey = &as->cfg.routing_key; + struct osmo_ss7_route *rt; + int pc; + + if (as->cfg.proto == OSMO_SS7_ASP_PROT_IPA && atoi(rcontext) != 0) { + vty_out(vty, "IPA doesn't support routing contexts; only permitted routing context " + "is 0\n"); + return CMD_WARNING; + } + + pc = osmo_ss7_pointcode_parse(as->inst, dpc); + if (pc < 0) { + vty_out(vty, "Invalid point code (%s)%s", dpc, VTY_NEWLINE); + return CMD_WARNING; + } + + /* When libosmo-sigtran is used in ASP role, the VTY routing table node + * (config-cs7-rt) is not available. However, when we add a routing key + * to an AS we still have to put a matching route into the routing + * table. This is done automatically by first removing the old route + * (users may change the routing key via VTY during runtime) and then + * putting a new route (see below). */ + if (cs7_role == CS7_ROLE_ASP) { + rt = ss7_route_table_find_route_by_dpc_mask(as->inst->rtable_system, rkey->pc, 0xffffff); + if (rt) + ss7_route_destroy(rt); + } + + rkey->pc = pc; + + rkey->context = atoi(rcontext); /* FIXME: input validation */ + rkey->si = si ? get_string_value(mtp_si_vals, si) : 0; /* FIXME: input validation */ + rkey->ssn = ssn ? atoi(ssn) : 0; /* FIXME: input validation */ + + /* automatically add new route (see also comment above) */ + if (cs7_role == CS7_ROLE_ASP) { + if (!ss7_route_create(as->inst->rtable_system, rkey->pc, 0xffffff, as->cfg.name)) { + vty_out(vty, "Cannot create route (pc=%s, linkset=%s) to AS %s", dpc, as->cfg.name, VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +DEFUN_ATTR(as_rout_key, as_rout_key_cmd, + ROUTING_KEY_CMD, + ROUTING_KEY_CMD_STRS, + CMD_ATTR_IMMEDIATE) +{ + return _rout_key(vty, argv[0], argv[1], NULL, NULL); +} + +DEFUN_ATTR(as_rout_key_si, as_rout_key_si_cmd, + ROUTING_KEY_CMD ROUTING_KEY_SI_ARG, + ROUTING_KEY_CMD_STRS ROUTING_KEY_SI_ARG_STRS, + CMD_ATTR_IMMEDIATE) +{ + return _rout_key(vty, argv[0], argv[1], argv[2], NULL); +} + +DEFUN_ATTR(as_rout_key_ssn, as_rout_key_ssn_cmd, + ROUTING_KEY_CMD ROUTING_KEY_SSN_ARG, + ROUTING_KEY_CMD_STRS ROUTING_KEY_SSN_ARG_STRS, + CMD_ATTR_IMMEDIATE) +{ + return _rout_key(vty, argv[0], argv[1], NULL, argv[2]); +} + +DEFUN_ATTR(as_rout_key_si_ssn, as_rout_key_si_ssn_cmd, + ROUTING_KEY_CMD ROUTING_KEY_SI_ARG ROUTING_KEY_SSN_ARG, + ROUTING_KEY_CMD_STRS ROUTING_KEY_SI_ARG_STRS ROUTING_KEY_SSN_ARG_STRS, + CMD_ATTR_IMMEDIATE) +{ + return _rout_key(vty, argv[0], argv[1], argv[2], argv[3]); +} + +DEFUN_ATTR(as_pc_override, as_pc_override_cmd, + "point-code override dpc PC", + "Point Code Specific Features\n" + "Override (force) a point-code to hard-coded value\n" + "Override Source Point Code\n" + "Override Destination Point Code\n" + "New Point Code\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_as *as = vty->index; + int pc = osmo_ss7_pointcode_parse(as->inst, argv[0]); + if (pc < 0) { + vty_out(vty, "Invalid point code (%s)%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + if (as->cfg.proto != OSMO_SS7_ASP_PROT_IPA) { + vty_out(vty, "Only IPA type AS support point-code override. " + "Be happy that you don't need it!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + as->cfg.pc_override.dpc = pc; + + return CMD_SUCCESS; +} + +DEFUN_ATTR(as_pc_patch_sccp, as_pc_patch_sccp_cmd, + "point-code override patch-sccp (disabled|both)", + "Point Code Specific Features\n" + "Override (force) a point-code to hard-coded value\n" + "Patch point code values into SCCP called/calling address\n" + "Don't patch any point codes into SCCP called/calling address\n" + "Patch both origin and destination point codes into SCCP called/calling address\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_as *as = vty->index; + + if (as->cfg.proto != OSMO_SS7_ASP_PROT_IPA) { + vty_out(vty, "Only IPA type AS support point-code patch-into-sccp. " + "Be happy that you don't need it!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[0], "disabled")) + as->cfg.pc_override.sccp_mode = OSMO_SS7_PATCH_NONE; + else + as->cfg.pc_override.sccp_mode = OSMO_SS7_PATCH_BOTH; + + return CMD_SUCCESS; +} + +void ss7_vty_write_one_as(struct vty *vty, struct osmo_ss7_as *as, bool show_dyn_config) +{ + struct osmo_ss7_routing_key *rkey; + unsigned int i; + + /* skip any dynamically allocated AS definitions */ + if ((as->rkm_dyn_allocated || as->simple_client_allocated) + && !show_dyn_config) + return; + + vty_out(vty, " as %s %s%s", as->cfg.name, + osmo_ss7_asp_protocol_name(as->cfg.proto), VTY_NEWLINE); + if (as->cfg.description) + vty_out(vty, " description %s%s", as->cfg.description, VTY_NEWLINE); + for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) { + struct osmo_ss7_asp *asp = as->cfg.asps[i]; + if (!asp) + continue; + /* skip any dynamically created ASPs (e.g. auto-created at connect time) */ + if ((asp->dyn_allocated || asp->simple_client_allocated) + && !show_dyn_config) + continue; + vty_out(vty, " asp %s%s", asp->cfg.name, VTY_NEWLINE); + } + if (as->cfg.mode_set_by_vty) { + vty_out(vty, " traffic-mode %s%s", osmo_ss7_as_traffic_mode_name(as->cfg.mode), VTY_NEWLINE); + if (as->cfg.mode == OSMO_SS7_AS_TMOD_LOADSHARE) { + if (as->cfg.loadshare.opc_sls) { + vty_out(vty, " bindings sls opc-sls"); + if (as->cfg.loadshare.opc_shift != 0) + vty_out(vty, " opc-shift %u", as->cfg.loadshare.opc_shift); + } + vty_out(vty, "%s", VTY_NEWLINE); + } + + if (as->cfg.loadshare.sls_shift != 0) + vty_out(vty, " sls-shift %u%s", as->cfg.loadshare.sls_shift, VTY_NEWLINE); + } + + if (as->cfg.recovery_timeout_msec != 2000) { + vty_out(vty, " recovery-timeout %u%s", + as->cfg.recovery_timeout_msec, VTY_NEWLINE); + } + if (as->cfg.qos_class) + vty_out(vty, " qos-class %u%s", as->cfg.qos_class, VTY_NEWLINE); + rkey = &as->cfg.routing_key; + vty_out(vty, " routing-key %u %s", rkey->context, + osmo_ss7_pointcode_print(as->inst, rkey->pc)); + if (rkey->si) + vty_out(vty, " si %s", + get_value_string(mtp_si_vals, rkey->si)); + if (rkey->ssn) + vty_out(vty, " ssn %u", rkey->ssn); + vty_out(vty, "%s", VTY_NEWLINE); + + if (as->cfg.pc_override.dpc) + vty_out(vty, " point-code override dpc %s%s", + osmo_ss7_pointcode_print(as->inst, as->cfg.pc_override.dpc), VTY_NEWLINE); + + if (as->cfg.pc_override.sccp_mode) + vty_out(vty, " point-code override patch-sccp both%s", VTY_NEWLINE); +} + +static void show_one_as(struct vty *vty, struct osmo_ss7_as *as) +{ + vty_out(vty, "%-12s %-12s %-10u %-13s %4s %13s %3s %5s %4s %10s%s", + as->cfg.name, osmo_fsm_inst_state_name(as->fi), as->cfg.routing_key.context, + osmo_ss7_pointcode_print(as->inst, as->cfg.routing_key.pc), + "", "", "", "", "", osmo_ss7_as_traffic_mode_name(as->cfg.mode), + VTY_NEWLINE); +} + +static int show_as(struct vty *vty, int id, const char *as_name, const char *filter) +{ + struct osmo_ss7_instance *inst; + struct osmo_ss7_as *as = NULL; + + inst = osmo_ss7_instance_find(id); + if (!inst) { + vty_out(vty, "%% No SS7 instance %d found%s", id, VTY_NEWLINE); + return CMD_WARNING; + } + + if (as_name) { + as = osmo_ss7_as_find_by_name(inst, as_name); + if (!as) { + vty_out(vty, "%% No AS '%s' found%s", as_name, VTY_NEWLINE); + return CMD_WARNING; + } + } + + vty_out(vty, " Routing Routing Key Cic Cic Traffic%s", VTY_NEWLINE); + vty_out(vty, "AS Name State Context Dpc Si Opc Ssn Min Max Mode%s", VTY_NEWLINE); + vty_out(vty, "------------ ------------ ---------- ------------- ---- ------------- --- ----- ----- -------%s", VTY_NEWLINE); + + if (as) { + show_one_as(vty, as); + return CMD_SUCCESS; + } + + llist_for_each_entry(as, &inst->as_list, list) { + if (filter && !strcmp(filter, "m3ua") && as->cfg.proto != OSMO_SS7_ASP_PROT_M3UA) + continue; + if (filter && !strcmp(filter, "sua") && as->cfg.proto != OSMO_SS7_ASP_PROT_SUA) + continue; + if (filter && !strcmp(filter, "active") && !osmo_ss7_as_active(as)) + continue; + show_one_as(vty, as); + } + return CMD_SUCCESS; +} + +DEFUN(show_cs7_as, show_cs7_as_cmd, + "show cs7 instance <0-15> as (active|all|m3ua|sua)", + SHOW_STR CS7_STR INST_STR INST_STR "Application Server (AS)\n" + "Display all active ASs\n" + "Display all ASs (default)\n" + "Display all m3ua ASs\n" + "Display all SUA ASs\n") +{ + const char *filter = argv[1]; + int id = atoi(argv[0]); + + return show_as(vty, id, NULL, filter); +} + +DEFUN(show_cs7_as_name, show_cs7_as_name_cmd, + "show cs7 instance <0-15> as name AS_NAME", + SHOW_STR CS7_STR INST_STR INST_STR "Application Server (AS)\n" + "Look up AS with a given name\n" + "Name of the Application Server (AS)\n") +{ + int id = atoi(argv[0]); + const char *as_name = argv[1]; + + return show_as(vty, id, as_name, NULL); +} + +DEFUN(show_cs7_as_bindingtable_name, show_cs7_as_bindingtable_name_cmd, + "show cs7 instance <0-15> as binding-table name AS_NAME", + SHOW_STR CS7_STR INST_STR INST_STR "Application Server (AS)\n" + "Display binding table\n" + "Look up AS with a given name\n" + "Name of the Application Server (AS)\n") +{ + int id = atoi(argv[0]); + const char *as_name = argv[1]; + struct osmo_ss7_instance *inst; + struct osmo_ss7_as *as = NULL; + + inst = osmo_ss7_instance_find(id); + if (!inst) { + vty_out(vty, "No SS7 instance %d found%s", id, VTY_NEWLINE); + return CMD_WARNING; + } + + if (as_name) { + as = osmo_ss7_as_find_by_name(inst, as_name); + if (!as) { + vty_out(vty, "No AS %s found%s", as_name, VTY_NEWLINE); + return CMD_WARNING; + } + } + + vty_out(vty, "Loadshare Seed Normal ASP Active Alternative ASP Active%s", VTY_NEWLINE); + vty_out(vty, "-------------- --------------- ------ --------------- ------%s", VTY_NEWLINE); + + for (unsigned int i = 0; i < ARRAY_SIZE(as->aesls_table); i++) { + struct osmo_ss7_as_esls_entry *e = &as->aesls_table[i]; + vty_out(vty, "%-15u %-16s %-7s %-16s %-7s%s", + i, + e->normal_asp ? e->normal_asp->cfg.name : "-", + e->normal_asp ? (osmo_ss7_asp_active(e->normal_asp) ? "Yes" : "No") : "-", + e->alt_asp ? e->alt_asp->cfg.name : "-", + e->alt_asp ? (osmo_ss7_asp_active(e->alt_asp) ? "Yes" : "No") : "-", + VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +int ss7_vty_node_as_go_parent(struct vty *vty) +{ + struct osmo_ss7_as *as = vty->index; + vty->node = L_CS7_NODE; + vty->index = as->inst; + return 0; +} + +void ss7_vty_init_node_as(void) +{ + install_node(&as_node, NULL); + install_lib_element_ve(&show_cs7_as_cmd); + install_lib_element_ve(&show_cs7_as_name_cmd); + install_lib_element_ve(&show_cs7_as_bindingtable_name_cmd); + install_lib_element(L_CS7_NODE, &cs7_as_cmd); + install_lib_element(L_CS7_NODE, &no_cs7_as_cmd); + install_lib_element(L_CS7_AS_NODE, &cfg_description_cmd); + install_lib_element(L_CS7_AS_NODE, &as_asp_cmd); + install_lib_element(L_CS7_AS_NODE, &as_no_asp_cmd); + install_lib_element(L_CS7_AS_NODE, &as_traf_mode_cmd); + install_lib_element(L_CS7_AS_NODE, &as_traf_mode_loadshare_cmd); + install_lib_element(L_CS7_AS_NODE, &as_no_traf_mode_cmd); + install_lib_element(L_CS7_AS_NODE, &as_sls_shift_cmd); + install_lib_element(L_CS7_AS_NODE, &as_bindingtable_reset_cmd); + install_lib_element(L_CS7_AS_NODE, &as_recov_tout_cmd); + install_lib_element(L_CS7_AS_NODE, &as_qos_class_cmd); + install_lib_element(L_CS7_AS_NODE, &as_rout_key_cmd); + install_lib_element(L_CS7_AS_NODE, &as_rout_key_si_cmd); + install_lib_element(L_CS7_AS_NODE, &as_rout_key_ssn_cmd); + install_lib_element(L_CS7_AS_NODE, &as_rout_key_si_ssn_cmd); + install_lib_element(L_CS7_AS_NODE, &as_pc_override_cmd); + install_lib_element(L_CS7_AS_NODE, &as_pc_patch_sccp_cmd); +} diff --git a/src/ss7_asp_vty.c b/src/ss7_asp_vty.c new file mode 100644 index 0000000..2934ea3 --- /dev/null +++ b/src/ss7_asp_vty.c @@ -0,0 +1,1163 @@ +/* SS7 ASP VTY Interface */ + +/* (C) 2015-2021 by Harald Welte laforge@gnumonks.org + * (C) 2025 by sysmocom s.f.m.c. GmbH info@sysmocom.de + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> + +#include <netdb.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> + +#include <osmocom/core/sockaddr_str.h> + +#include <osmocom/vty/vty.h> +#include <osmocom/vty/command.h> +#include <osmocom/vty/logging.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/misc.h> + +#include <osmocom/netif/stream.h> + +#include <osmocom/sigtran/osmo_ss7.h> + +#include "xua_internal.h" +#include "ss7_as.h" +#include "ss7_asp.h" +#include "ss7_combined_linkset.h" +#include <ss7_linkset.h> +#include "ss7_internal.h" +#include "ss7_vty.h" + +#include <netinet/tcp.h> + +#ifdef HAVE_LIBSCTP +#include <netinet/sctp.h> +#include <osmocom/netif/sctp.h> +#endif + +/*********************************************************************** + * Application Server Process + ***********************************************************************/ + +static struct cmd_node asp_node = { + L_CS7_ASP_NODE, + "%s(config-cs7-asp)# ", + 1, +}; + +/* netinet/tcp.h */ +static const struct value_string tcp_info_state_values[] = { + { TCP_ESTABLISHED, "ESTABLISHED" }, + { TCP_SYN_SENT, "SYN_SENT" }, + { TCP_SYN_RECV, "SYN_RECV" }, + { TCP_FIN_WAIT1, "FIN_WAIT1" }, + { TCP_FIN_WAIT2, "FIN_WAIT2" }, + { TCP_TIME_WAIT, "TIME_WAIT" }, + { TCP_CLOSE, "CLOSE" }, + { TCP_CLOSE_WAIT, "CLOSE_WAIT" }, + { TCP_LAST_ACK, "LAST_ACK" }, + { TCP_LISTEN, "LISTEN" }, + { TCP_CLOSING, "CLOSING" }, + {} +}; + + +static const struct value_string asp_quirk_names[] = { + { OSMO_SS7_ASP_QUIRK_NO_NOTIFY, "no_notify" }, + { OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP, "daud_in_asp" }, + { OSMO_SS7_ASP_QUIRK_SNM_INACTIVE, "snm_inactive" }, + { 0, NULL } +}; + +static const struct value_string asp_quirk_descs[] = { + { OSMO_SS7_ASP_QUIRK_NO_NOTIFY, "Peer SG doesn't send NTFY(AS-INACTIVE) after ASP-UP" }, + { OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP, "Allow Rx of DAUD in ASP role" }, + { OSMO_SS7_ASP_QUIRK_SNM_INACTIVE, "Allow Rx of [S]SNM in AS-INACTIVE state" }, + { 0, NULL } +}; + +DEFUN_ATTR(cs7_asp, cs7_asp_cmd, + "asp NAME <0-65535> <0-65535> " XUA_VAR_STR, + "Configure Application Server Process\n" + "Name of ASP\n" + "Remote port number\n" + "Local port number\n" + XUA_VAR_HELP_STR, + CMD_ATTR_NODE_EXIT) +{ + struct osmo_ss7_instance *inst = vty->index; + const char *name = argv[0]; + uint16_t remote_port = atoi(argv[1]); + uint16_t local_port = atoi(argv[2]); + enum osmo_ss7_asp_protocol proto = parse_asp_proto(argv[3]); + struct osmo_ss7_asp *asp; + int trans_proto; + + if (proto == OSMO_SS7_ASP_PROT_NONE) { + vty_out(vty, "invalid protocol '%s'%s", argv[3], VTY_NEWLINE); + return CMD_WARNING; + } + + /* argv[4] can be supplied by an alias (see below) */ + if (argc > 4) + trans_proto = parse_trans_proto(argv[4]); + else /* default transport protocol */ + trans_proto = ss7_default_trans_proto_for_asp_proto(proto); + if (trans_proto < 0) + return CMD_WARNING; + + asp = osmo_ss7_asp_find2(inst, name, + remote_port, local_port, + trans_proto, proto); + if (!asp) { + asp = osmo_ss7_asp_find_or_create2(inst, name, + remote_port, local_port, + trans_proto, proto); + if (!asp) { + vty_out(vty, "cannot create ASP '%s'%s", name, VTY_NEWLINE); + return CMD_WARNING; + } + asp->cfg.is_server = true; + asp->cfg.role = OSMO_SS7_ASP_ROLE_SG; + } + + /* Reset value, will be checked at osmo_ss7_vty_go_parent() */ + asp->cfg.explicit_shutdown_state_by_vty_since_node_enter = false; + + vty->node = L_CS7_ASP_NODE; + vty->index = asp; + vty->index_sub = &asp->cfg.description; + return CMD_SUCCESS; +} + +/* XXX: workaround for https://osmocom.org/issues/6360, can be removed once it's fixed. + * Currently we hit an assert if we make the IPPROTO_VAR_STR optional in cs7_asp_cmd. */ +ALIAS_ATTR(cs7_asp, cs7_asp_trans_proto_cmd, + "asp NAME <0-65535> <0-65535> " XUA_VAR_STR " " IPPROTO_VAR_STR, + "Configure Application Server Process\n" + "Name of ASP\n" + "Remote port number\n" + "Local port number\n" + XUA_VAR_HELP_STR + IPPROTO_VAR_HELP_STR, + CMD_ATTR_NODE_EXIT); + +DEFUN_ATTR(no_cs7_asp, no_cs7_asp_cmd, + "no asp NAME", + NO_STR "Disable Application Server Process\n" + "Name of ASP\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_instance *inst = vty->index; + const char *name = argv[0]; + struct osmo_ss7_asp *asp; + + asp = osmo_ss7_asp_find_by_name(inst, name); + if (!asp) { + vty_out(vty, "No ASP named '%s' found%s", name, VTY_NEWLINE); + return CMD_WARNING; + } + osmo_ss7_asp_destroy(asp); + return CMD_SUCCESS; +} + +DEFUN_ATTR(asp_local_ip, asp_local_ip_cmd, + "local-ip " VTY_IPV46_CMD " [primary]", + "Specify Local IP Address from which to contact ASP\n" + "Local IPv4 Address from which to contact of ASP\n" + "Local IPv6 Address from which to contact of ASP\n" + "Signal the SCTP peer to use this address as Primary Address\n", + CMD_ATTR_NODE_EXIT) +{ + struct osmo_ss7_asp *asp = vty->index; + bool is_primary = argc > 1; + int old_idx_primary = asp->cfg.local.idx_primary; + int old_host_count = asp->cfg.local.host_cnt; + int rc; + + if (ss7_asp_peer_add_host2(&asp->cfg.local, asp, argv[0], is_primary) != 0) { + vty_out(vty, "%% Failed adding host '%s' to set%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + if (!ss7_asp_is_started(asp)) + return CMD_SUCCESS; + if (asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA) + return CMD_SUCCESS; + /* The SCTP socket is already created. */ + + /* dynamically apply the new address if it was added to the set: */ + if (asp->cfg.local.host_cnt > old_host_count) { + if ((rc = ss7_asp_apply_new_local_address(asp, asp->cfg.local.host_cnt - 1)) < 0) { + /* Failed, rollback changes: */ + TALLOC_FREE(asp->cfg.local.host[asp->cfg.local.host_cnt - 1]); + asp->cfg.local.host_cnt--; + vty_out(vty, "%% Failed adding new local address '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + vty_out(vty, "%% Local address '%s' added to the active socket bind set%s", argv[0], VTY_NEWLINE); + } + + /* dynamically apply the new primary if it changed: */ + if (is_primary && asp->cfg.local.idx_primary != old_idx_primary) { + if ((rc = ss7_asp_apply_peer_primary_address(asp)) < 0) { + /* Failed, rollback changes: */ + asp->cfg.local.idx_primary = old_idx_primary; + vty_out(vty, "%% Failed announcing primary '%s' to peer%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + vty_out(vty, "%% Local address '%s' announced as primary to the peer on the active socket%s", argv[0], VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN_ATTR(asp_no_local_ip, asp_no_local_ip_cmd, + "no local-ip " VTY_IPV46_CMD, + NO_STR "Specify Local IP Address from which to contact ASP\n" + "Local IPv4 Address from which to contact of ASP\n" + "Local IPv6 Address from which to contact of ASP\n", + CMD_ATTR_NODE_EXIT) +{ + struct osmo_ss7_asp *asp = vty->index; + int idx = ss7_asp_peer_find_host(&asp->cfg.local, argv[0]); + int rc; + + if (idx < 0) { + vty_out(vty, "%% Local address '%s' not found in set%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + if (ss7_asp_is_started(asp)) { + if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA) { + if ((rc = ss7_asp_apply_drop_local_address(asp, idx)) < 0) { + vty_out(vty, "%% Failed removing local address '%s' from existing socket%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + vty_out(vty, "%% Local address '%s' removed from active socket connection%s", argv[0], VTY_NEWLINE); + } + } + + if (ss7_asp_peer_del_host(&asp->cfg.local, argv[0]) != 0) { + vty_out(vty, "%% Failed deleting local address '%s' from set%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +DEFUN_ATTR(asp_remote_ip, asp_remote_ip_cmd, + "remote-ip " VTY_IPV46_CMD " [primary]", + "Specify Remote IP Address of ASP\n" + "Remote IPv4 Address of ASP\n" + "Remote IPv6 Address of ASP\n" + "Set remote address as SCTP Primary Address\n", + CMD_ATTR_NODE_EXIT) +{ + struct osmo_ss7_asp *asp = vty->index; + bool is_primary = argc > 1; + int old_idx_primary = asp->cfg.remote.idx_primary; + int rc; + + if (ss7_asp_peer_add_host2(&asp->cfg.remote, asp, argv[0], is_primary) != 0) { + vty_out(vty, "%% Failed adding host '%s' to set%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + if (!ss7_asp_is_started(asp)) + return CMD_SUCCESS; + if (asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA) + return CMD_SUCCESS; + + /* The SCTP socket is already created, dynamically apply the new primary if it changed: */ + if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA && ss7_asp_is_started(asp)) { + if ((rc = ss7_asp_apply_primary_address(asp)) < 0) { + /* Failed, rollback changes: */ + asp->cfg.remote.idx_primary = old_idx_primary; + vty_out(vty, "%% Failed applying primary on host '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +DEFUN_ATTR(asp_no_remote_ip, asp_no_remote_ip_cmd, + "no remote-ip " VTY_IPV46_CMD, + NO_STR "Specify Remote IP Address of ASP\n" + "Remote IPv4 Address of ASP\n" + "Remote IPv6 Address of ASP\n", + CMD_ATTR_NODE_EXIT) +{ + struct osmo_ss7_asp *asp = vty->index; + int idx = ss7_asp_peer_find_host(&asp->cfg.remote, argv[0]); + + if (idx < 0) { + vty_out(vty, "%% Remote address '%s' not found in set%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + if (ss7_asp_peer_del_host(&asp->cfg.remote, argv[0]) != 0) { + vty_out(vty, "%% Failed deleting remote address '%s' from set%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +DEFUN_ATTR(asp_qos_clas, asp_qos_class_cmd, + "qos-class " QOS_CLASS_RANGE_STR, + "Specify QoS Class of ASP\n" + QOS_CLASS_RANGE_HELP_STR, + CMD_ATTR_NODE_EXIT) +{ + struct osmo_ss7_asp *asp = vty->index; + asp->cfg.qos_class = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN_ATTR(asp_role, asp_role_cmd, + "role (sg|asp|ipsp)", + "Specify the xUA role for this ASP\n" + "SG (Signaling Gateway)\n" + "ASP (Application Server Process)\n" + "IPSP (IP Signalling Point)\n", + CMD_ATTR_NODE_EXIT) +{ + struct osmo_ss7_asp *asp = vty->index; + + if (!strcmp(argv[0], "ipsp")) { + vty_out(vty, "IPSP role isn't supported yet%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[0], "sg")) + asp->cfg.role = OSMO_SS7_ASP_ROLE_SG; + else if (!strcmp(argv[0], "asp")) + asp->cfg.role = OSMO_SS7_ASP_ROLE_ASP; + else + OSMO_ASSERT(0); + + asp->cfg.role_set_by_vty = true; + return CMD_SUCCESS; +} + +DEFUN_ATTR(asp_transport_role, asp_transport_role_cmd, + "transport-role (client|server)", + "Specify the transport layer role for this ASP\n" + "Operate as a client; connect to a server\n" + "Operate as a server; wait for client connections\n", + CMD_ATTR_NODE_EXIT) +{ + struct osmo_ss7_asp *asp = vty->index; + + if (!strcmp(argv[0], "client")) + asp->cfg.is_server = false; + else if (!strcmp(argv[0], "server")) + asp->cfg.is_server = true; + else + OSMO_ASSERT(0); + + asp->cfg.trans_role_set_by_vty = true; + return CMD_SUCCESS; +} + +ALIAS_ATTR(asp_transport_role, asp_sctp_role_cmd, + "sctp-role (client|server)", + "Specify the SCTP role for this ASP\n" + "Operate as SCTP client; connect to a server\n" + "Operate as SCTP server; wait for client connections\n", + CMD_ATTR_HIDDEN | CMD_ATTR_NODE_EXIT); + +#define ASP_SCTP_PARAM_INIT_DESC \ + "Configure SCTP parameters\n" \ + "Configure INIT related parameters\n" \ + "Configure INIT Number of Outbound Streams\n" \ + "Configure INIT Maximum Inboud Streams\n" \ + "Configure INIT Maximum Attempts\n" \ + "Configure INIT Timeout (milliseconds)\n" +#define ASP_SCTP_PARAM_INIT_FIELDS "(num-ostreams|max-instreams|max-attempts|timeout)" + +DEFUN_ATTR(asp_sctp_param_init, asp_sctp_param_init_cmd, + "sctp-param init " ASP_SCTP_PARAM_INIT_FIELDS " <0-65535>", + ASP_SCTP_PARAM_INIT_DESC + "Value of the parameter\n", + CMD_ATTR_NODE_EXIT) +{ + struct osmo_ss7_asp *asp = vty->index; + + uint16_t val = atoi(argv[1]); + + if (strcmp(argv[0], "num-ostreams") == 0) { + asp->cfg.sctp_init.num_ostreams_present = true; + asp->cfg.sctp_init.num_ostreams_value = val; + } else if (strcmp(argv[0], "max-instreams") == 0) { + asp->cfg.sctp_init.max_instreams_present = true; + asp->cfg.sctp_init.max_instreams_value = val; + } else if (strcmp(argv[0], "max-attempts") == 0) { + asp->cfg.sctp_init.max_attempts_present = true; + asp->cfg.sctp_init.max_attempts_value = val; + } else if (strcmp(argv[0], "timeout") == 0) { + asp->cfg.sctp_init.max_init_timeo_present = true; + asp->cfg.sctp_init.max_init_timeo_value = val; + } else { + OSMO_ASSERT(0); + } + return CMD_SUCCESS; +} + +DEFUN_ATTR(asp_no_sctp_param_init, asp_no_sctp_param_init_cmd, + "no sctp-param init " ASP_SCTP_PARAM_INIT_FIELDS, + NO_STR ASP_SCTP_PARAM_INIT_DESC, + CMD_ATTR_NODE_EXIT) +{ + struct osmo_ss7_asp *asp = vty->index; + + if (strcmp(argv[0], "num-ostreams") == 0) + asp->cfg.sctp_init.num_ostreams_present = false; + else if (strcmp(argv[0], "max-instreams") == 0) + asp->cfg.sctp_init.max_instreams_present = false; + else if (strcmp(argv[0], "max-attempts") == 0) + asp->cfg.sctp_init.max_attempts_present = false; + else if (strcmp(argv[0], "timeout") == 0) + asp->cfg.sctp_init.max_init_timeo_present = false; + else + OSMO_ASSERT(0); + return CMD_SUCCESS; +} + +DEFUN_ATTR(asp_block, asp_block_cmd, + "block", + "Allows a SCTP Association with ASP, but doesn't let it become active\n", + CMD_ATTR_NODE_EXIT) +{ + /* TODO */ + vty_out(vty, "Not supported yet%s", VTY_NEWLINE); + return CMD_WARNING; +} + +DEFUN_ATTR(asp_shutdown, asp_shutdown_cmd, + "shutdown", + "Terminates SCTP association; New associations will be rejected\n", + CMD_ATTR_NODE_EXIT) +{ + struct osmo_ss7_asp *asp = vty->index; + + LOGPASP(asp, DLSS7, LOGL_NOTICE, "Applying Adm State change: %s -> %s\n", + get_value_string(osmo_ss7_asp_admin_state_names, asp->cfg.adm_state), + get_value_string(osmo_ss7_asp_admin_state_names, OSMO_SS7_ASP_ADM_S_SHUTDOWN)); + + asp->cfg.explicit_shutdown_state_by_vty_since_node_enter = true; + asp->cfg.adm_state = OSMO_SS7_ASP_ADM_S_SHUTDOWN; + ss7_asp_restart_after_reconfigure(asp); + return CMD_SUCCESS; +} + +DEFUN_ATTR(asp_no_shutdown, asp_no_shutdown_cmd, + "no shutdown", + NO_STR "Terminates SCTP association; New associations will be rejected\n", + CMD_ATTR_NODE_EXIT) +{ + struct osmo_ss7_asp *asp = vty->index; + + LOGPASP(asp, DLSS7, LOGL_NOTICE, "Applying Adm State change: %s -> %s\n", + get_value_string(osmo_ss7_asp_admin_state_names, asp->cfg.adm_state), + get_value_string(osmo_ss7_asp_admin_state_names, OSMO_SS7_ASP_ADM_S_ENABLED)); + + asp->cfg.explicit_shutdown_state_by_vty_since_node_enter = true; + asp->cfg.adm_state = OSMO_SS7_ASP_ADM_S_ENABLED; + ss7_asp_restart_after_reconfigure(asp); + return CMD_SUCCESS; +} + +DEFUN_ATTR(asp_quirk, asp_quirk_cmd, + "OVERWRITTEN", + "OVERWRITTEN\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_asp *asp = vty->index; + int quirk = get_string_value(asp_quirk_names, argv[0]); + + if (quirk < 0) + return CMD_WARNING; + + asp->cfg.quirks |= quirk; + return CMD_SUCCESS; +} + +DEFUN_ATTR(asp_no_quirk, asp_no_quirk_cmd, + "OVERWRITTEN", + "OVERWRITTEN\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_asp *asp = vty->index; + int quirk = get_string_value(asp_quirk_names, argv[0]); + + if (quirk < 0) + return CMD_WARNING; + + asp->cfg.quirks &= ~quirk; + return CMD_SUCCESS; +} + +/* timer lm <name> <1-999999> + * (cmdstr and doc are dynamically generated from ss7_asp_lm_timer_names.) */ +DEFUN_ATTR(asp_timer, asp_timer_cmd, + NULL, NULL, CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_asp *asp = vty->index; + enum ss7_asp_lm_timer timer = get_string_value(ss7_asp_lm_timer_names, argv[0]); + + if (timer <= 0 || timer >= SS7_ASP_LM_TIMERS_LEN) { + vty_out(vty, "%% Invalid timer: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + osmo_tdef_set(asp->cfg.T_defs_lm, timer, atoi(argv[1]), OSMO_TDEF_S); + return CMD_SUCCESS; +} + +static void gen_asp_timer_cmd_strs(struct cmd_element *cmd) +{ + int i; + char *cmd_str = NULL; + char *doc_str = NULL; + + OSMO_ASSERT(cmd->string == NULL); + OSMO_ASSERT(cmd->doc == NULL); + + osmo_talloc_asprintf(tall_vty_ctx, cmd_str, "timer lm ("); + osmo_talloc_asprintf(tall_vty_ctx, doc_str, + "Configure ASP default timer values\n" + "Configure ASP default lm timer values\n"); + + for (i = 0; ss7_asp_lm_timer_names[i].str; i++) { + const struct osmo_tdef *def; + enum ss7_asp_lm_timer timer; + + timer = ss7_asp_lm_timer_names[i].value; + def = osmo_tdef_get_entry((struct osmo_tdef *)&ss7_asp_lm_timer_defaults, timer); + OSMO_ASSERT(def); + + osmo_talloc_asprintf(tall_vty_ctx, cmd_str, "%s%s", + i ? "|" : "", + ss7_asp_lm_timer_names[i].str); + osmo_talloc_asprintf(tall_vty_ctx, doc_str, "%s (default: %lu)\n", + def->desc, + def->default_val); + } + + osmo_talloc_asprintf(tall_vty_ctx, cmd_str, ") <1-999999>"); + osmo_talloc_asprintf(tall_vty_ctx, doc_str, + "Timer value, in seconds\n"); + + cmd->string = cmd_str; + cmd->doc = doc_str; +} + +static void write_asp_timers(struct vty *vty, const char *indent, + struct osmo_ss7_asp *asp) +{ + int i; + + for (i = 0; ss7_asp_lm_timer_names[i].str; i++) { + const struct osmo_tdef *tdef = osmo_tdef_get_entry(asp->cfg.T_defs_lm, ss7_asp_lm_timer_names[i].value); + if (!tdef) + continue; + if (tdef->val == tdef->default_val) + continue; + vty_out(vty, "%stimer lm %s %lu%s", indent, ss7_asp_lm_timer_names[i].str, + tdef->val, VTY_NEWLINE); + } +} + +static char *as_list_for_asp(const struct osmo_ss7_asp *asp, char *buf, size_t buf_len) +{ + struct osmo_strbuf sb = { .buf = buf, .len = buf_len }; + const struct osmo_ss7_as *as; + unsigned int count = 0; + llist_for_each_entry(as, &asp->inst->as_list, list) { + if (!osmo_ss7_as_has_asp(as, asp)) + continue; + OSMO_STRBUF_PRINTF(sb, "%s%s", count != 0 ? "," : "", as->cfg.name); + count++; + break; + } + + if (count == 0) + OSMO_STRBUF_PRINTF(sb, "?"); + return buf; +} + +/* Similar to osmo_sock_multiaddr_get_name_buf(), but aimed at listening sockets (only local part): */ +static char *get_sockname_buf(char *buf, size_t buf_len, int fd, int proto, bool local) +{ + char hostbuf[OSMO_SOCK_MAX_ADDRS][INET6_ADDRSTRLEN]; + size_t num_hostbuf = ARRAY_SIZE(hostbuf); + char portbuf[6]; + struct osmo_strbuf sb = { .buf = buf, .len = buf_len }; + bool need_more_bufs; + int rc; + + rc = osmo_sock_multiaddr_get_ip_and_port(fd, proto, &hostbuf[0][0], + &num_hostbuf, sizeof(hostbuf[0]), + portbuf, sizeof(portbuf), local); + if (rc < 0) + return NULL; + + need_more_bufs = num_hostbuf > ARRAY_SIZE(hostbuf); + if (need_more_bufs) + num_hostbuf = ARRAY_SIZE(hostbuf); + OSMO_STRBUF_APPEND(sb, osmo_multiaddr_ip_and_port_snprintf, + &hostbuf[0][0], num_hostbuf, sizeof(hostbuf[0]), portbuf); + if (need_more_bufs) + OSMO_STRBUF_PRINTF(sb, "<need-more-bufs!>"); + + return buf; +} + +static void show_one_asp(struct vty *vty, struct osmo_ss7_asp *asp) +{ + char as_buf[64]; + char buf_loc[OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN]; + char buf_rem[sizeof(buf_loc)]; + + int fd = ss7_asp_get_fd(asp); + if (fd > 0) { + const int trans_proto = asp->cfg.trans_proto; + if (!get_sockname_buf(buf_loc, sizeof(buf_loc), fd, trans_proto, true)) + OSMO_STRLCPY_ARRAY(buf_loc, "<sockname-error>"); + if (!get_sockname_buf(buf_rem, sizeof(buf_rem), fd, trans_proto, false)) + OSMO_STRLCPY_ARRAY(buf_rem, "<sockname-error>"); + } else { + ss7_asp_peer_snprintf(buf_loc, sizeof(buf_loc), &asp->cfg.local); + ss7_asp_peer_snprintf(buf_rem, sizeof(buf_rem), &asp->cfg.remote); + } + + vty_out(vty, "%-12s %-12s %-13s %-4s %-4s %-9s %-23s %-23s%s", + asp->cfg.name, + as_list_for_asp(asp, as_buf, sizeof(as_buf)), + asp->fi ? osmo_fsm_inst_state_name(asp->fi) : "uninitialized", + get_value_string(osmo_ss7_asp_protocol_vals, asp->cfg.proto), + osmo_str_tolower(get_value_string(osmo_ss7_asp_role_names, asp->cfg.role)), + asp->cfg.is_server ? "server" : "client", + buf_loc, buf_rem, + VTY_NEWLINE); +} + +static int show_asp(struct vty *vty, int id, const char *asp_name) +{ + struct osmo_ss7_instance *inst; + struct osmo_ss7_asp *asp = NULL; + + inst = osmo_ss7_instance_find(id); + if (!inst) { + vty_out(vty, "No SS7 instance %d found%s", id, VTY_NEWLINE); + return CMD_WARNING; + } + + if (asp_name) { + asp = osmo_ss7_asp_find_by_name(inst, asp_name); + if (!asp) { + vty_out(vty, "No ASP %s found%s", asp_name, VTY_NEWLINE); + return CMD_WARNING; + } + } + + vty_out(vty, "ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses%s", VTY_NEWLINE); + vty_out(vty, "------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------%s", VTY_NEWLINE); + + if (asp) { + show_one_asp(vty, asp); + return CMD_SUCCESS; + } + + llist_for_each_entry(asp, &inst->asp_list, list) + show_one_asp(vty, asp); + return CMD_SUCCESS; +} + +DEFUN(show_cs7_asp, show_cs7_asp_cmd, + "show cs7 instance <0-15> asp", + SHOW_STR CS7_STR INST_STR INST_STR + "Application Server Process (ASP)\n") +{ + int id = atoi(argv[0]); + + return show_asp(vty, id, NULL); +} + +DEFUN(show_cs7_asp_name, show_cs7_asp_name_cmd, + "show cs7 instance <0-15> asp name ASP_NAME", + SHOW_STR CS7_STR INST_STR INST_STR + "Application Server Process (ASP)\n" + "Lookup ASP with a given name\n" + "Name of the Application Server Process (ASP)\n") +{ + int id = atoi(argv[0]); + const char *asp_name = argv[1]; + + return show_asp(vty, id, asp_name); +} + +static void show_one_asp_remaddr_tcp(struct vty *vty, struct osmo_ss7_asp *asp) +{ + struct osmo_sockaddr osa = {}; + struct tcp_info tcpi = {}; + socklen_t len; + int fd, rc; + + fd = ss7_asp_get_fd(asp); + if (fd < 0) { + vty_out(vty, "%-12s %-46s uninitialized%s", asp->cfg.name, "", VTY_NEWLINE); + return; + } + + len = sizeof(osa.u.sas); + rc = getpeername(fd, &osa.u.sa, &len); + + len = sizeof(tcpi); + rc = getsockopt(fd, SOL_TCP, TCP_INFO, &tcpi, &len); + if (rc < 0) { + char buf_err[128]; + strerror_r(errno, buf_err, sizeof(buf_err)); + vty_out(vty, "%-12s %-46s getsockopt(TCP_INFO) failed: %s%s", + asp->cfg.name, osmo_sockaddr_to_str(&osa), buf_err, VTY_NEWLINE); + return; + } + + vty_out(vty, "%-12s %-46s TCP_%-19s %-8u %-8u %-8u %-8u%s", + asp->cfg.name, + osmo_sockaddr_to_str(&osa), + get_value_string(tcp_info_state_values, tcpi.tcpi_state), + tcpi.tcpi_snd_cwnd, tcpi.tcpi_rtt, + tcpi.tcpi_rto, tcpi.tcpi_pmtu, + VTY_NEWLINE); +} + +#ifdef HAVE_LIBSCTP +static void show_one_asp_remaddr_sctp(struct vty *vty, struct osmo_ss7_asp *asp) +{ + struct sctp_paddrinfo pinfo[OSMO_SOCK_MAX_ADDRS]; + struct osmo_sockaddr osa = {}; + size_t pinfo_cnt = ARRAY_SIZE(pinfo); + bool more_needed; + int fd, rc; + unsigned int i; + + fd = ss7_asp_get_fd(asp); + if (fd < 0) { + vty_out(vty, "%-12s %-46s uninitialized%s", asp->cfg.name, "", VTY_NEWLINE); + return; + } + + rc = osmo_sock_sctp_get_peer_addr_info(fd, &pinfo[0], &pinfo_cnt); + if (rc < 0) { + char buf_err[128]; + strerror_r(errno, buf_err, sizeof(buf_err)); + vty_out(vty, "%-12s %-46s getsockopt(SCTP_GET_PEER_ADDR_INFO) failed: %s%s", asp->cfg.name, "", buf_err, VTY_NEWLINE); + return; + } + + more_needed = pinfo_cnt > ARRAY_SIZE(pinfo); + if (pinfo_cnt > ARRAY_SIZE(pinfo)) + pinfo_cnt = ARRAY_SIZE(pinfo); + + for (i = 0; i < pinfo_cnt; i++) { + osa.u.sas = pinfo[i].spinfo_address; + vty_out(vty, "%-12s %-46s SCTP_%-18s %-8u %-8u %-8u %-8u%s", + asp->cfg.name, + osmo_sockaddr_to_str(&osa), + osmo_sctp_spinfo_state_str(pinfo[i].spinfo_state), + pinfo[i].spinfo_cwnd, pinfo[i].spinfo_srtt, + pinfo[i].spinfo_rto, pinfo[i].spinfo_mtu, + VTY_NEWLINE); + } + + if (more_needed) + vty_out(vty, "%-12s more address buffers needed!%s", asp->cfg.name, VTY_NEWLINE); +} +#endif + +static void show_one_asp_remaddr(struct vty *vty, struct osmo_ss7_asp *asp) +{ + switch (asp->cfg.trans_proto) { + case IPPROTO_TCP: + show_one_asp_remaddr_tcp(vty, asp); + break; +#ifdef HAVE_LIBSCTP + case IPPROTO_SCTP: + show_one_asp_remaddr_sctp(vty, asp); + break; +#endif + default: + vty_out(vty, "%-12s %-46s unknown proto %d%s", + asp->cfg.name, "", asp->cfg.trans_proto, VTY_NEWLINE); + break; + } +} + +static int show_asp_remaddr(struct vty *vty, int id, const char *asp_name) +{ + struct osmo_ss7_instance *inst; + struct osmo_ss7_asp *asp = NULL; + + inst = osmo_ss7_instance_find(id); + if (!inst) { + vty_out(vty, "No SS7 instance %d found%s", id, VTY_NEWLINE); + return CMD_WARNING; + } + + if (asp_name) { + asp = osmo_ss7_asp_find_by_name(inst, asp_name); + if (!asp) { + vty_out(vty, "No ASP %s found%s", asp_name, VTY_NEWLINE); + return CMD_WARNING; + } + } + + vty_out(vty, "ASP Name Remote IP Address & Port State CWND SRTT RTO MTU%s", VTY_NEWLINE); + vty_out(vty, "------------ ---------------------------------------------- ----------------------- -------- -------- -------- --------%s", VTY_NEWLINE); + + if (asp) { + show_one_asp_remaddr(vty, asp); + return CMD_SUCCESS; + } + + llist_for_each_entry(asp, &inst->asp_list, list) { + show_one_asp_remaddr(vty, asp); + } + return CMD_SUCCESS; +} + +DEFUN(show_cs7_asp_remaddr, show_cs7_asp_remaddr_cmd, + "show cs7 instance <0-15> asp-remaddr", + SHOW_STR CS7_STR INST_STR INST_STR + "Application Server Process (ASP) remote addresses information\n") +{ + int id = atoi(argv[0]); + + return show_asp_remaddr(vty, id, NULL); +} + + +DEFUN(show_cs7_asp_remaddr_name, show_cs7_asp_remaddr_name_cmd, + "show cs7 instance <0-15> asp-remaddr name ASP_NAME", + SHOW_STR CS7_STR INST_STR INST_STR + "Application Server Process (ASP) remote addresses information\n" + "Lookup ASP with a given name\n" + "Name of the Application Server Process (ASP)\n") +{ + int id = atoi(argv[0]); + const char *asp_name = argv[1]; + + return show_asp_remaddr(vty, id, asp_name); +} + +static void show_one_asp_assoc_status_tcp(struct vty *vty, struct osmo_ss7_asp *asp) +{ + struct osmo_sockaddr osa = {}; + struct tcp_info tcpi = {}; + socklen_t len; + int fd, rc; + int rx_pend_bytes = 0; + + fd = ss7_asp_get_fd(asp); + if (fd < 0) { + vty_out(vty, "%-12s uninitialized%s", asp->cfg.name, VTY_NEWLINE); + return; + } + + len = sizeof(osa.u.sas); + rc = getpeername(fd, &osa.u.sa, &len); + + len = sizeof(tcpi); + rc = getsockopt(fd, SOL_TCP, TCP_INFO, &tcpi, &len); + if (rc < 0) { + char buf_err[128]; + strerror_r(errno, buf_err, sizeof(buf_err)); + vty_out(vty, "%-12s getsockopt(TCP_INFO) failed: %s%s", + asp->cfg.name, buf_err, VTY_NEWLINE); + return; + } + + rc = ioctl(fd, FIONREAD, &rx_pend_bytes); + + /* FIXME: RWND: struct tcp_info from linux/tcp.h contains more fields + * than the one from netinet/tcp.h we currently use, including + * "tcpi_rcv_wnd" which we could use to print RWND here. However, + * linux/tcp.h seems to be missing the state defines used in + * "tcp_info_state_values", so we cannot use that one instead. + */ + + vty_out(vty, "%-12s TCP_%-19s %-9s %-10s %-8s %-9u %-7u %-9u %-46s%s", + asp->cfg.name, + get_value_string(tcp_info_state_values, tcpi.tcpi_state), + "-", "-", "-", tcpi.tcpi_unacked, rx_pend_bytes, + tcpi.tcpi_pmtu, osmo_sockaddr_to_str(&osa), + VTY_NEWLINE); +} + +#ifdef HAVE_LIBSCTP +static void show_one_asp_assoc_status_sctp(struct vty *vty, struct osmo_ss7_asp *asp) +{ + struct osmo_sockaddr osa = {}; + struct sctp_status st; + socklen_t len; + int fd, rc; + + fd = ss7_asp_get_fd(asp); + if (fd < 0) { + vty_out(vty, "%-12s uninitialized%s", asp->cfg.name, VTY_NEWLINE); + return; + } + + memset(&st, 0, sizeof(st)); + len = sizeof(st); + rc = getsockopt(fd, IPPROTO_SCTP, SCTP_STATUS, &st, &len); + if (rc < 0) { + char buf_err[128]; + strerror_r(errno, buf_err, sizeof(buf_err)); + vty_out(vty, "%-12s getsockopt(SCTP_STATUS) failed: %s%s", asp->cfg.name, buf_err, VTY_NEWLINE); + return; + } + + osa.u.sas = st.sstat_primary.spinfo_address; + vty_out(vty, "%-12s SCTP_%-18s %-9u %-10u %-8u %-9u %-7u %-9u %-46s%s", + asp->cfg.name, + osmo_sctp_sstat_state_str(st.sstat_state), + st.sstat_instrms, st.sstat_outstrms, + st.sstat_rwnd, st.sstat_unackdata, st.sstat_penddata, + st.sstat_fragmentation_point, + osmo_sockaddr_to_str(&osa), + VTY_NEWLINE); +} +#endif + +static void show_one_asp_assoc_status(struct vty *vty, struct osmo_ss7_asp *asp) +{ + switch (asp->cfg.trans_proto) { + case IPPROTO_TCP: + show_one_asp_assoc_status_tcp(vty, asp); + break; +#ifdef HAVE_LIBSCTP + case IPPROTO_SCTP: + show_one_asp_assoc_status_sctp(vty, asp); + break; +#endif + default: + vty_out(vty, "%-12s unknown proto %d%s", + asp->cfg.name, asp->cfg.trans_proto, VTY_NEWLINE); + break; + } +} + +static int show_asp_assoc_status(struct vty *vty, int id, const char *asp_name) +{ + struct osmo_ss7_instance *inst; + struct osmo_ss7_asp *asp = NULL; + + inst = osmo_ss7_instance_find(id); + if (!inst) { + vty_out(vty, "No SS7 instance %d found%s", id, VTY_NEWLINE); + return CMD_WARNING; + } + + if (asp_name) { + asp = osmo_ss7_asp_find_by_name(inst, asp_name); + if (!asp) { + vty_out(vty, "No ASP %s found%s", asp_name, VTY_NEWLINE); + return CMD_WARNING; + } + } + + vty_out(vty, "ASP Name State InStreams OutStreams RWND UnackData PenData FragPoint Current Primary Remote IP Address & Port%s", VTY_NEWLINE); + vty_out(vty, "------------ ----------------------- --------- ---------- -------- --------- ------- --------- ----------------------------------------------%s", VTY_NEWLINE); + + if (asp) { + show_one_asp_assoc_status(vty, asp); + return CMD_SUCCESS; + } + + llist_for_each_entry(asp, &inst->asp_list, list) + show_one_asp_assoc_status(vty, asp); + return CMD_SUCCESS; +} + +DEFUN(show_cs7_asp_assoc_status, show_cs7_asp_assoc_status_cmd, + "show cs7 instance <0-15> asp-assoc-status", + SHOW_STR CS7_STR INST_STR INST_STR + "Application Server Process (ASP) SCTP association status\n") +{ + int id = atoi(argv[0]); + + return show_asp_assoc_status(vty, id, NULL); +} + + +DEFUN(show_cs7_asp_assoc_status_name, show_cs7_asp_assoc_status_name_cmd, + "show cs7 instance <0-15> asp-assoc-status name ASP_NAME", + SHOW_STR CS7_STR INST_STR INST_STR + "Application Server Process (ASP) SCTP association information\n" + "Lookup ASP with a given name\n" + "Name of the Application Server Process (ASP)\n") +{ + int id = atoi(argv[0]); + const char *asp_name = argv[1]; + + return show_asp_assoc_status(vty, id, asp_name); +} + +void ss7_vty_write_one_asp(struct vty *vty, struct osmo_ss7_asp *asp, bool show_dyn_config) +{ + int i; + /* skip any dynamically created ASPs (e.g. auto-created at connect time) */ + if ((asp->dyn_allocated || asp->simple_client_allocated) + && !show_dyn_config) + return; + + vty_out(vty, " asp %s %u %u %s", + asp->cfg.name, asp->cfg.remote.port, asp->cfg.local.port, + osmo_ss7_asp_protocol_name(asp->cfg.proto)); + if (asp->cfg.trans_proto != ss7_default_trans_proto_for_asp_proto(asp->cfg.proto)) + vty_out(vty, " %s", get_value_string(ipproto_vals, asp->cfg.trans_proto)); + vty_out(vty, "%s", VTY_NEWLINE); + if (asp->cfg.description) + vty_out(vty, " description %s%s", asp->cfg.description, VTY_NEWLINE); + for (i = 0; i < asp->cfg.local.host_cnt; i++) { + if (asp->cfg.local.host[i]) + vty_out(vty, " local-ip %s%s%s", asp->cfg.local.host[i], + asp->cfg.local.idx_primary == i ? " primary" : "", VTY_NEWLINE); + } + for (i = 0; i < asp->cfg.remote.host_cnt; i++) { + if (asp->cfg.remote.host[i]) + vty_out(vty, " remote-ip %s%s%s", asp->cfg.remote.host[i], + asp->cfg.remote.idx_primary == i ? " primary" : "", VTY_NEWLINE); + } + if (asp->cfg.qos_class) + vty_out(vty, " qos-class %u%s", asp->cfg.qos_class, VTY_NEWLINE); + vty_out(vty, " role %s%s", osmo_str_tolower(get_value_string(osmo_ss7_asp_role_names, asp->cfg.role)), + VTY_NEWLINE); + if (asp->cfg.trans_proto == IPPROTO_SCTP) + vty_out(vty, " sctp-role %s%s", asp->cfg.is_server ? "server" : "client", VTY_NEWLINE); + else + vty_out(vty, " transport-role %s%s", asp->cfg.is_server ? "server" : "client", VTY_NEWLINE); + if (asp->cfg.sctp_init.num_ostreams_present) + vty_out(vty, " sctp-param init num-ostreams %u%s", asp->cfg.sctp_init.num_ostreams_value, VTY_NEWLINE); + if (asp->cfg.sctp_init.max_instreams_present) + vty_out(vty, " sctp-param init max-instreams %u%s", asp->cfg.sctp_init.max_instreams_value, VTY_NEWLINE); + if (asp->cfg.sctp_init.max_attempts_present) + vty_out(vty, " sctp-param init max-attempts %u%s", asp->cfg.sctp_init.max_attempts_value, VTY_NEWLINE); + if (asp->cfg.sctp_init.max_init_timeo_present) + vty_out(vty, " sctp-param init timeout %u%s", asp->cfg.sctp_init.max_init_timeo_value, VTY_NEWLINE); + for (i = 0; i < sizeof(uint32_t) * 8; i++) { + if (!(asp->cfg.quirks & ((uint32_t) 1 << i))) + continue; + vty_out(vty, " quirk %s%s", get_value_string(asp_quirk_names, (1 << i)), VTY_NEWLINE); + } + write_asp_timers(vty, " ", asp); + + switch (asp->cfg.adm_state) { + case OSMO_SS7_ASP_ADM_S_SHUTDOWN: + vty_out(vty, " shutdown%s", VTY_NEWLINE); + break; + case OSMO_SS7_ASP_ADM_S_BLOCKED: + vty_out(vty, " blocked%s", VTY_NEWLINE); + break; + case OSMO_SS7_ASP_ADM_S_ENABLED: + /* Default, no need to print: */ + vty_out(vty, " no shutdown%s", VTY_NEWLINE); + break; + } +} + +int ss7_vty_node_asp_go_parent(struct vty *vty) +{ + struct osmo_ss7_asp *asp = vty->index; + + if (asp->cfg.explicit_shutdown_state_by_vty_since_node_enter) { + /* Interactive VTY, inform of new behavior upon use of new '[no] shutdown' commands: */ + if (vty->type != VTY_FILE) + vty_out(vty, "%% NOTE: Skipping automatic restart of ASP since an explicit '[no] shutdown' command was entered%s", VTY_NEWLINE); + asp->cfg.explicit_shutdown_state_by_vty_since_node_enter = false; + } else if (vty->type == VTY_FILE) { + /* Make sure config reading is backward compatible by starting the ASP if no explicit 'no shutdown' is read: */ + vty_out(vty, + "%% VTY node 'asp' without a '[no] shutdown' command at the end is deprecated, " + "please make sure you update your cfg file for future compatibility.%s", + VTY_NEWLINE); + ss7_asp_restart_after_reconfigure(asp); + } else { + /* Interactive VTY without '[no] shutdown' explicit cmd, remind the user that we are no + * longer automatically restarting the ASP when going out of the "asp" node: */ + vty_out(vty, + "%% NOTE: Make sure to use '[no] shutdown' command in 'asp' node " + "in order to restart the ASP for new configs to be applied.%s", + VTY_NEWLINE); + } + vty->node = L_CS7_NODE; + vty->index = asp->inst; + return 0; +} + +void ss7_vty_init_node_asp(void) +{ + asp_quirk_cmd.string = vty_cmd_string_from_valstr(g_ctx, asp_quirk_names, + "quirk (", "|", ")", VTY_DO_LOWER); + asp_quirk_cmd.doc = vty_cmd_string_from_valstr(g_ctx, asp_quirk_descs, + "Enable quirk to work around interop issues\n", + "\n", "\n", 0); + asp_no_quirk_cmd.string = vty_cmd_string_from_valstr(g_ctx, asp_quirk_names, + "no quirk (", "|", ")", VTY_DO_LOWER); + asp_no_quirk_cmd.doc = vty_cmd_string_from_valstr(g_ctx, asp_quirk_descs, + NO_STR "Disable quirk to work around interop issues\n", + "\n", "\n", 0); + + install_node(&asp_node, NULL); + install_lib_element_ve(&show_cs7_asp_cmd); + install_lib_element_ve(&show_cs7_asp_name_cmd); + install_lib_element_ve(&show_cs7_asp_remaddr_cmd); + install_lib_element_ve(&show_cs7_asp_remaddr_name_cmd); + install_lib_element_ve(&show_cs7_asp_assoc_status_cmd); + install_lib_element_ve(&show_cs7_asp_assoc_status_name_cmd); + install_lib_element(L_CS7_NODE, &cs7_asp_cmd); + install_lib_element(L_CS7_NODE, &cs7_asp_trans_proto_cmd); + install_lib_element(L_CS7_NODE, &no_cs7_asp_cmd); + install_lib_element(L_CS7_ASP_NODE, &cfg_description_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_remote_ip_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_no_remote_ip_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_local_ip_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_no_local_ip_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_qos_class_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_role_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_transport_role_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_sctp_role_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_sctp_param_init_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_no_sctp_param_init_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_quirk_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_no_quirk_cmd); + gen_asp_timer_cmd_strs(&asp_timer_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_timer_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_block_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_shutdown_cmd); + install_lib_element(L_CS7_ASP_NODE, &asp_no_shutdown_cmd); +} diff --git a/src/ss7_internal.h b/src/ss7_internal.h index 9475645..7dc2c57 100644 --- a/src/ss7_internal.h +++ b/src/ss7_internal.h @@ -28,3 +28,6 @@ int ss7_asp_xua_srv_conn_closed_cb(struct osmo_stream_srv *srv);
int xua_tcp_segmentation_cb(struct msgb *msg); + +/* VTY */ +#define XUA_VAR_STR "(sua|m3ua|ipa)" diff --git a/src/ss7_vty.c b/src/ss7_vty.c index 41872fe..862e9ff 100644 --- a/src/ss7_vty.c +++ b/src/ss7_vty.c @@ -1,4 +1,4 @@ -/* Core SS7 Instance/Linkset/Link/AS/ASP VTY Interface */ +/* Core SS7 Instance/Linkset/Link VTY Interface */
/* (C) 2015-2021 by Harald Welte laforge@gnumonks.org * All Rights Reserved @@ -26,20 +26,12 @@ #include <stdint.h> #include <string.h>
-#include <netdb.h> -#include <arpa/inet.h> -#include <sys/ioctl.h> - -#include <osmocom/core/sockaddr_str.h> - #include <osmocom/vty/vty.h> #include <osmocom/vty/command.h> #include <osmocom/vty/logging.h> #include <osmocom/vty/telnet_interface.h> #include <osmocom/vty/misc.h>
-#include <osmocom/netif/stream.h> - #include <osmocom/sigtran/osmo_ss7.h> #include <osmocom/sigtran/protocol/mtp.h>
@@ -53,34 +45,9 @@ #include "ss7_route.h" #include "ss7_route_table.h" #include "ss7_internal.h" +#include "ss7_vty.h" #include "ss7_xua_srv.h"
-#include <netinet/tcp.h> - -#ifdef HAVE_LIBSCTP -#include <netinet/sctp.h> -#include <osmocom/netif/sctp.h> -#endif - -#define XUA_VAR_STR "(sua|m3ua|ipa)" - -#define XUA_VAR_HELP_STR \ - "SCCP User Adaptation\n" \ - "MTP3 User Adaptation\n" \ - "IPA Multiplex (SCCP Lite)\n" - -#define IPPROTO_VAR_STR "(sctp|tcp)" -#define IPPROTO_VAR_HELP_STR \ - "SCTP (Stream Control Transmission Protocol)\n" \ - "TCP (Transmission Control Protocol)\n" - -#define QOS_CLASS_RANGE_STR "<0-7>" -#define QOS_CLASS_RANGE_HELP_STR "QoS Class\n" -#define QOS_CLASS_VAR_STR "(" QOS_CLASS_RANGE_STR "|default)" -#define QOS_CLASS_VAR_HELP_STR \ - QOS_CLASS_RANGE_HELP_STR \ - "Default QoS Class (0)\n" - #define ROUTE_PRIO_RANGE_STR "<1-9>" #define ROUTE_PRIO_RANGE_HELP_STR "Priority\n" #define ROUTE_PRIO_VAR_STR "(" ROUTE_PRIO_RANGE_STR "|default)" @@ -88,43 +55,28 @@ ROUTE_PRIO_RANGE_HELP_STR \ "Default Priority (5)\n"
-/* netinet/tcp.h */ -static const struct value_string tcp_info_state_values[] = { - { TCP_ESTABLISHED, "ESTABLISHED" }, - { TCP_SYN_SENT, "SYN_SENT" }, - { TCP_SYN_RECV, "SYN_RECV" }, - { TCP_FIN_WAIT1, "FIN_WAIT1" }, - { TCP_FIN_WAIT2, "FIN_WAIT2" }, - { TCP_TIME_WAIT, "TIME_WAIT" }, - { TCP_CLOSE, "CLOSE" }, - { TCP_CLOSE_WAIT, "CLOSE_WAIT" }, - { TCP_LAST_ACK, "LAST_ACK" }, - { TCP_LISTEN, "LISTEN" }, - { TCP_CLOSING, "CLOSING" }, - {} +const struct value_string ipproto_vals[] = { + { IPPROTO_SCTP, "sctp" }, + { IPPROTO_TCP, "tcp" }, + { 0, NULL }, };
-static const struct value_string asp_quirk_names[] = { - { OSMO_SS7_ASP_QUIRK_NO_NOTIFY, "no_notify" }, - { OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP, "daud_in_asp" }, - { OSMO_SS7_ASP_QUIRK_SNM_INACTIVE, "snm_inactive" }, - { 0, NULL } -}; +int parse_trans_proto(const char *protocol) +{ + return get_string_value(ipproto_vals, protocol); +}
-static const struct value_string asp_quirk_descs[] = { - { OSMO_SS7_ASP_QUIRK_NO_NOTIFY, "Peer SG doesn't send NTFY(AS-INACTIVE) after ASP-UP" }, - { OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP, "Allow Rx of DAUD in ASP role" }, - { OSMO_SS7_ASP_QUIRK_SNM_INACTIVE, "Allow Rx of [S]SNM in AS-INACTIVE state" }, - { 0, NULL } -}; +enum osmo_ss7_asp_protocol parse_asp_proto(const char *protocol) +{ + return get_string_value(osmo_ss7_asp_protocol_vals, protocol); +}
/*********************************************************************** * Core CS7 Configuration ***********************************************************************/
-enum cs7_role_t {CS7_ROLE_SG, CS7_ROLE_ASP}; -static enum cs7_role_t cs7_role; -static void *g_ctx; +enum cs7_role_t cs7_role; +void *g_ctx;
static struct cmd_node cs7_node = { L_CS7_NODE, @@ -737,293 +689,6 @@ return CMD_SUCCESS; }
-/*********************************************************************** - * xUA Listener Configuration (SG) - ***********************************************************************/ - -static const struct value_string ipproto_vals[] = { - { IPPROTO_SCTP, "sctp" }, - { IPPROTO_TCP, "tcp" }, - { 0, NULL }, -}; - -static int parse_trans_proto(const char *protocol) -{ - return get_string_value(ipproto_vals, protocol); -} - -static enum osmo_ss7_asp_protocol parse_asp_proto(const char *protocol) -{ - return get_string_value(osmo_ss7_asp_protocol_vals, protocol); -} - -static struct cmd_node xua_node = { - L_CS7_XUA_NODE, - "%s(config-cs7-listen)# ", - 1, -}; - -DEFUN_ATTR(cs7_xua, cs7_xua_cmd, - "listen " XUA_VAR_STR " <0-65534> [" IPPROTO_VAR_STR "]", - "Configure/Enable xUA Listener\n" - XUA_VAR_HELP_STR - "Port number\n" - IPPROTO_VAR_HELP_STR, - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_instance *inst = vty->index; - struct osmo_xua_server *xs; - enum osmo_ss7_asp_protocol proto = parse_asp_proto(argv[0]); - uint16_t port = atoi(argv[1]); - int trans_proto; - - if (argc > 2) - trans_proto = parse_trans_proto(argv[2]); - else /* default transport protocol */ - trans_proto = ss7_default_trans_proto_for_asp_proto(proto); - if (trans_proto < 0) - return CMD_WARNING; - - xs = ss7_xua_server_find2(inst, trans_proto, proto, port); - if (!xs) { - xs = ss7_xua_server_create2(inst, trans_proto, proto, port, NULL); - if (!xs) - return CMD_WARNING; - /* Drop first dummy address created automatically by _create(): */ - ss7_xua_server_set_local_hosts(xs, NULL, 0); - } - - vty->node = L_CS7_XUA_NODE; - vty->index = xs; - return CMD_SUCCESS; -} - -DEFUN_ATTR(no_cs7_xua, no_cs7_xua_cmd, - "no listen " XUA_VAR_STR " <0-65534> [" IPPROTO_VAR_STR "]", - NO_STR "Disable xUA Listener on given port\n" - XUA_VAR_HELP_STR - "Port number\n" - IPPROTO_VAR_HELP_STR, - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_instance *inst = vty->index; - struct osmo_xua_server *xs; - enum osmo_ss7_asp_protocol proto = parse_asp_proto(argv[0]); - uint16_t port = atoi(argv[1]); - int trans_proto; - - if (argc > 2) - trans_proto = parse_trans_proto(argv[2]); - else /* default transport protocol */ - trans_proto = ss7_default_trans_proto_for_asp_proto(proto); - if (trans_proto < 0) - return CMD_WARNING; - - xs = ss7_xua_server_find2(inst, trans_proto, proto, port); - if (!xs) { - vty_out(vty, "No xUA server for port %u found%s", port, VTY_NEWLINE); - return CMD_WARNING; - } - ss7_xua_server_destroy(xs); - return CMD_SUCCESS; -} - -DEFUN_ATTR(xua_local_ip, xua_local_ip_cmd, - "local-ip " VTY_IPV46_CMD, - "Configure the Local IP Address for xUA\n" - "IPv4 Address to use for XUA\n" - "IPv6 Address to use for XUA\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_xua_server *xs = vty->index; - - ss7_xua_server_add_local_host(xs, argv[0]); - - return CMD_SUCCESS; -} - -DEFUN_ATTR(xua_no_local_ip, xua_no_local_ip_cmd, - "no local-ip " VTY_IPV46_CMD, - NO_STR "Configure the Local IP Address for xUA\n" - "IPv4 Address to use for XUA\n" - "IPv6 Address to use for XUA\n", - CMD_ATTR_NODE_EXIT) -{ - struct osmo_xua_server *xs = vty->index; - - if (ss7_xua_server_del_local_host(xs, argv[0]) != 0) { - vty_out(vty, "%% Failed deleting local address '%s' from set%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -DEFUN_ATTR(xua_accept_dyn_asp, xua_accept_dyn_asp_cmd, - "accept-asp-connections (pre-configured|dynamic-permitted)", - "Define what kind of ASP connections to accept\n" - "Accept only pre-configured ASPs (source IP/port)\n" - "Accept any connection and dynamically create an ASP definition\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_xua_server *xs = vty->index; - - if (!strcmp(argv[0], "dynamic-permitted")) - xs->cfg.accept_dyn_reg = true; - else - xs->cfg.accept_dyn_reg = false; - - return CMD_SUCCESS; -} - -#define XUA_SRV_SCTP_PARAM_INIT_DESC \ - "Configure SCTP parameters\n" \ - "Configure INIT related parameters\n" \ - "Configure INIT Number of Outbound Streams\n" \ - "Configure INIT Maximum Inboud Streams\n" -#define XUA_SRV_SCTP_PARAM_INIT_FIELDS "(num-ostreams|max-instreams)" - -DEFUN_ATTR(xua_sctp_param_init, xua_sctp_param_init_cmd, - "sctp-param init " XUA_SRV_SCTP_PARAM_INIT_FIELDS " <0-65535>", - XUA_SRV_SCTP_PARAM_INIT_DESC - "Value of the parameter\n", - CMD_ATTR_NODE_EXIT) -{ - struct osmo_xua_server *xs = vty->index; - - uint16_t val = atoi(argv[1]); - - if (strcmp(argv[0], "num-ostreams") == 0) { - xs->cfg.sctp_init.num_ostreams_present = true; - xs->cfg.sctp_init.num_ostreams_value = val; - } else if (strcmp(argv[0], "max-instreams") == 0) { - xs->cfg.sctp_init.max_instreams_present = true; - xs->cfg.sctp_init.max_instreams_value = val; - } else { - OSMO_ASSERT(0); - } - return CMD_SUCCESS; -} - -DEFUN_ATTR(xua_no_sctp_param_init, xua_no_sctp_param_init_cmd, - "no sctp-param init " XUA_SRV_SCTP_PARAM_INIT_FIELDS, - NO_STR XUA_SRV_SCTP_PARAM_INIT_DESC, - CMD_ATTR_NODE_EXIT) -{ - struct osmo_xua_server *xs = vty->index; - - if (strcmp(argv[0], "num-ostreams") == 0) - xs->cfg.sctp_init.num_ostreams_present = false; - else if (strcmp(argv[0], "max-instreams") == 0) - xs->cfg.sctp_init.max_instreams_present = false; - else - OSMO_ASSERT(0); - return CMD_SUCCESS; -} - -static void write_one_xua(struct vty *vty, struct osmo_xua_server *xs) -{ - int i; - - vty_out(vty, " listen %s %u", - get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto), - xs->cfg.local.port); - if (xs->cfg.trans_proto != ss7_default_trans_proto_for_asp_proto(xs->cfg.proto)) - vty_out(vty, " %s", get_value_string(ipproto_vals, xs->cfg.trans_proto)); - vty_out(vty, "%s", VTY_NEWLINE); - - for (i = 0; i < xs->cfg.local.host_cnt; i++) { - if (xs->cfg.local.host[i]) - vty_out(vty, " local-ip %s%s", xs->cfg.local.host[i], VTY_NEWLINE); - } - if (xs->cfg.accept_dyn_reg) - vty_out(vty, " accept-asp-connections dynamic-permitted%s", VTY_NEWLINE); - if (xs->cfg.sctp_init.num_ostreams_present) - vty_out(vty, " sctp-param init num-ostreams %u%s", xs->cfg.sctp_init.num_ostreams_value, VTY_NEWLINE); - if (xs->cfg.sctp_init.max_instreams_present) - vty_out(vty, " sctp-param init max-instreams %u%s", xs->cfg.sctp_init.max_instreams_value, VTY_NEWLINE); -} - -static void vty_dump_xua_server(struct vty *vty, struct osmo_xua_server *xs) -{ - char buf[OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN]; - const char *proto = get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto); - int fd = xs->server ? osmo_stream_srv_link_get_fd(xs->server) : -1; - - if (fd < 0) { - if (ss7_asp_peer_snprintf(buf, sizeof(buf), &xs->cfg.local) < 0) - snprintf(buf, sizeof(buf), "<error>"); - } else { - char hostbuf[OSMO_SOCK_MAX_ADDRS][INET6_ADDRSTRLEN]; - size_t num_hostbuf = ARRAY_SIZE(hostbuf); - char portbuf[6]; - int rc; - rc = osmo_sock_multiaddr_get_ip_and_port(fd, xs->cfg.trans_proto, - &hostbuf[0][0], &num_hostbuf, sizeof(hostbuf[0]), - portbuf, sizeof(portbuf), true); - if (rc < 0) { - snprintf(buf, sizeof(buf), "<error>"); - } else { - if (num_hostbuf > ARRAY_SIZE(hostbuf)) - num_hostbuf = ARRAY_SIZE(hostbuf); - osmo_multiaddr_ip_and_port_snprintf(buf, sizeof(buf), - &hostbuf[0][0], num_hostbuf, sizeof(hostbuf[0]), - portbuf); - } - } - vty_out(vty, "xUA server for %s/%s on %s is %s%s", - proto, get_value_string(ipproto_vals, xs->cfg.trans_proto), - buf, fd >= 0 ? "listening" : "inactive", VTY_NEWLINE); -} - -static int _show_cs7_xua(struct vty *vty, - enum osmo_ss7_asp_protocol proto, - int trans_proto, int local_port) -{ - const struct osmo_ss7_instance *inst; - - llist_for_each_entry(inst, &osmo_ss7_instances, list) { - struct osmo_xua_server *xs; - - llist_for_each_entry(xs, &inst->xua_servers, list) { - if (xs->cfg.proto != proto) - continue; - if (local_port >= 0 && xs->cfg.local.port != local_port) /* optional */ - continue; - if (trans_proto >= 0 && xs->cfg.trans_proto != trans_proto) /* optional */ - continue; - vty_dump_xua_server(vty, xs); - } - } - - return CMD_SUCCESS; -} - -#define SHOW_CS7_XUA_CMD \ - "show cs7 " XUA_VAR_STR -#define SHOW_CS7_XUA_CMD_HELP \ - SHOW_STR CS7_STR XUA_VAR_HELP_STR - -DEFUN(show_cs7_xua, show_cs7_xua_cmd, - SHOW_CS7_XUA_CMD " [<0-65534>]", - SHOW_CS7_XUA_CMD_HELP "Local Port Number\n") -{ - enum osmo_ss7_asp_protocol proto = parse_asp_proto(argv[0]); - int local_port = (argc > 1) ? atoi(argv[1]) : -1; - - return _show_cs7_xua(vty, proto, -1, local_port); -} - -DEFUN(show_cs7_xua_trans_proto, show_cs7_xua_trans_proto_cmd, - SHOW_CS7_XUA_CMD " " IPPROTO_VAR_STR " [<0-65534>]", - SHOW_CS7_XUA_CMD_HELP IPPROTO_VAR_HELP_STR "Local Port Number\n") -{ - enum osmo_ss7_asp_protocol proto = parse_asp_proto(argv[0]); - int trans_proto = parse_trans_proto(argv[1]); - int local_port = (argc > 2) ? atoi(argv[2]) : -1; - - return _show_cs7_xua(vty, proto, trans_proto, local_port); -} - DEFUN(show_cs7_config, show_cs7_config_cmd, "show cs7 config", SHOW_STR CS7_STR "Currently running cs7 configuration") @@ -1059,1556 +724,6 @@
/*********************************************************************** - * Application Server Process - ***********************************************************************/ - -static struct cmd_node asp_node = { - L_CS7_ASP_NODE, - "%s(config-cs7-asp)# ", - 1, -}; - -DEFUN_ATTR(cs7_asp, cs7_asp_cmd, - "asp NAME <0-65535> <0-65535> " XUA_VAR_STR, - "Configure Application Server Process\n" - "Name of ASP\n" - "Remote port number\n" - "Local port number\n" - XUA_VAR_HELP_STR, - CMD_ATTR_NODE_EXIT) -{ - struct osmo_ss7_instance *inst = vty->index; - const char *name = argv[0]; - uint16_t remote_port = atoi(argv[1]); - uint16_t local_port = atoi(argv[2]); - enum osmo_ss7_asp_protocol proto = parse_asp_proto(argv[3]); - struct osmo_ss7_asp *asp; - int trans_proto; - - if (proto == OSMO_SS7_ASP_PROT_NONE) { - vty_out(vty, "invalid protocol '%s'%s", argv[3], VTY_NEWLINE); - return CMD_WARNING; - } - - /* argv[4] can be supplied by an alias (see below) */ - if (argc > 4) - trans_proto = parse_trans_proto(argv[4]); - else /* default transport protocol */ - trans_proto = ss7_default_trans_proto_for_asp_proto(proto); - if (trans_proto < 0) - return CMD_WARNING; - - asp = osmo_ss7_asp_find2(inst, name, - remote_port, local_port, - trans_proto, proto); - if (!asp) { - asp = osmo_ss7_asp_find_or_create2(inst, name, - remote_port, local_port, - trans_proto, proto); - if (!asp) { - vty_out(vty, "cannot create ASP '%s'%s", name, VTY_NEWLINE); - return CMD_WARNING; - } - asp->cfg.is_server = true; - asp->cfg.role = OSMO_SS7_ASP_ROLE_SG; - } - - /* Reset value, will be checked at osmo_ss7_vty_go_parent() */ - asp->cfg.explicit_shutdown_state_by_vty_since_node_enter = false; - - vty->node = L_CS7_ASP_NODE; - vty->index = asp; - vty->index_sub = &asp->cfg.description; - return CMD_SUCCESS; -} - -/* XXX: workaround for https://osmocom.org/issues/6360, can be removed once it's fixed. - * Currently we hit an assert if we make the IPPROTO_VAR_STR optional in cs7_asp_cmd. */ -ALIAS_ATTR(cs7_asp, cs7_asp_trans_proto_cmd, - "asp NAME <0-65535> <0-65535> " XUA_VAR_STR " " IPPROTO_VAR_STR, - "Configure Application Server Process\n" - "Name of ASP\n" - "Remote port number\n" - "Local port number\n" - XUA_VAR_HELP_STR - IPPROTO_VAR_HELP_STR, - CMD_ATTR_NODE_EXIT); - -DEFUN_ATTR(no_cs7_asp, no_cs7_asp_cmd, - "no asp NAME", - NO_STR "Disable Application Server Process\n" - "Name of ASP\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_instance *inst = vty->index; - const char *name = argv[0]; - struct osmo_ss7_asp *asp; - - asp = osmo_ss7_asp_find_by_name(inst, name); - if (!asp) { - vty_out(vty, "No ASP named '%s' found%s", name, VTY_NEWLINE); - return CMD_WARNING; - } - osmo_ss7_asp_destroy(asp); - return CMD_SUCCESS; -} - -DEFUN_ATTR(asp_local_ip, asp_local_ip_cmd, - "local-ip " VTY_IPV46_CMD " [primary]", - "Specify Local IP Address from which to contact ASP\n" - "Local IPv4 Address from which to contact of ASP\n" - "Local IPv6 Address from which to contact of ASP\n" - "Signal the SCTP peer to use this address as Primary Address\n", - CMD_ATTR_NODE_EXIT) -{ - struct osmo_ss7_asp *asp = vty->index; - bool is_primary = argc > 1; - int old_idx_primary = asp->cfg.local.idx_primary; - int old_host_count = asp->cfg.local.host_cnt; - int rc; - - if (ss7_asp_peer_add_host2(&asp->cfg.local, asp, argv[0], is_primary) != 0) { - vty_out(vty, "%% Failed adding host '%s' to set%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - if (!ss7_asp_is_started(asp)) - return CMD_SUCCESS; - if (asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA) - return CMD_SUCCESS; - /* The SCTP socket is already created. */ - - /* dynamically apply the new address if it was added to the set: */ - if (asp->cfg.local.host_cnt > old_host_count) { - if ((rc = ss7_asp_apply_new_local_address(asp, asp->cfg.local.host_cnt - 1)) < 0) { - /* Failed, rollback changes: */ - TALLOC_FREE(asp->cfg.local.host[asp->cfg.local.host_cnt - 1]); - asp->cfg.local.host_cnt--; - vty_out(vty, "%% Failed adding new local address '%s'%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - vty_out(vty, "%% Local address '%s' added to the active socket bind set%s", argv[0], VTY_NEWLINE); - } - - /* dynamically apply the new primary if it changed: */ - if (is_primary && asp->cfg.local.idx_primary != old_idx_primary) { - if ((rc = ss7_asp_apply_peer_primary_address(asp)) < 0) { - /* Failed, rollback changes: */ - asp->cfg.local.idx_primary = old_idx_primary; - vty_out(vty, "%% Failed announcing primary '%s' to peer%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - vty_out(vty, "%% Local address '%s' announced as primary to the peer on the active socket%s", argv[0], VTY_NEWLINE); - } - return CMD_SUCCESS; -} - -DEFUN_ATTR(asp_no_local_ip, asp_no_local_ip_cmd, - "no local-ip " VTY_IPV46_CMD, - NO_STR "Specify Local IP Address from which to contact ASP\n" - "Local IPv4 Address from which to contact of ASP\n" - "Local IPv6 Address from which to contact of ASP\n", - CMD_ATTR_NODE_EXIT) -{ - struct osmo_ss7_asp *asp = vty->index; - int idx = ss7_asp_peer_find_host(&asp->cfg.local, argv[0]); - int rc; - - if (idx < 0) { - vty_out(vty, "%% Local address '%s' not found in set%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - if (ss7_asp_is_started(asp)) { - if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA) { - if ((rc = ss7_asp_apply_drop_local_address(asp, idx)) < 0) { - vty_out(vty, "%% Failed removing local address '%s' from existing socket%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - vty_out(vty, "%% Local address '%s' removed from active socket connection%s", argv[0], VTY_NEWLINE); - } - } - - if (ss7_asp_peer_del_host(&asp->cfg.local, argv[0]) != 0) { - vty_out(vty, "%% Failed deleting local address '%s' from set%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -DEFUN_ATTR(asp_remote_ip, asp_remote_ip_cmd, - "remote-ip " VTY_IPV46_CMD " [primary]", - "Specify Remote IP Address of ASP\n" - "Remote IPv4 Address of ASP\n" - "Remote IPv6 Address of ASP\n" - "Set remote address as SCTP Primary Address\n", - CMD_ATTR_NODE_EXIT) -{ - struct osmo_ss7_asp *asp = vty->index; - bool is_primary = argc > 1; - int old_idx_primary = asp->cfg.remote.idx_primary; - int rc; - - if (ss7_asp_peer_add_host2(&asp->cfg.remote, asp, argv[0], is_primary) != 0) { - vty_out(vty, "%% Failed adding host '%s' to set%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - if (!ss7_asp_is_started(asp)) - return CMD_SUCCESS; - if (asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA) - return CMD_SUCCESS; - - /* The SCTP socket is already created, dynamically apply the new primary if it changed: */ - if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA && ss7_asp_is_started(asp)) { - if ((rc = ss7_asp_apply_primary_address(asp)) < 0) { - /* Failed, rollback changes: */ - asp->cfg.remote.idx_primary = old_idx_primary; - vty_out(vty, "%% Failed applying primary on host '%s'%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - } - return CMD_SUCCESS; -} - -DEFUN_ATTR(asp_no_remote_ip, asp_no_remote_ip_cmd, - "no remote-ip " VTY_IPV46_CMD, - NO_STR "Specify Remote IP Address of ASP\n" - "Remote IPv4 Address of ASP\n" - "Remote IPv6 Address of ASP\n", - CMD_ATTR_NODE_EXIT) -{ - struct osmo_ss7_asp *asp = vty->index; - int idx = ss7_asp_peer_find_host(&asp->cfg.remote, argv[0]); - - if (idx < 0) { - vty_out(vty, "%% Remote address '%s' not found in set%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - if (ss7_asp_peer_del_host(&asp->cfg.remote, argv[0]) != 0) { - vty_out(vty, "%% Failed deleting remote address '%s' from set%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -DEFUN_ATTR(asp_qos_clas, asp_qos_class_cmd, - "qos-class " QOS_CLASS_RANGE_STR, - "Specify QoS Class of ASP\n" - QOS_CLASS_RANGE_HELP_STR, - CMD_ATTR_NODE_EXIT) -{ - struct osmo_ss7_asp *asp = vty->index; - asp->cfg.qos_class = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN_ATTR(asp_role, asp_role_cmd, - "role (sg|asp|ipsp)", - "Specify the xUA role for this ASP\n" - "SG (Signaling Gateway)\n" - "ASP (Application Server Process)\n" - "IPSP (IP Signalling Point)\n", - CMD_ATTR_NODE_EXIT) -{ - struct osmo_ss7_asp *asp = vty->index; - - if (!strcmp(argv[0], "sg")) { - asp->cfg.role = OSMO_SS7_ASP_ROLE_SG; - } else if (!strcmp(argv[0], "asp")) { - asp->cfg.role = OSMO_SS7_ASP_ROLE_ASP; - } else if (!strcmp(argv[0], "ipsp")) { - vty_out(vty, "IPSP role isn't supported yet%s", VTY_NEWLINE); - return CMD_WARNING; - } else - OSMO_ASSERT(0); - - asp->cfg.role_set_by_vty = true; - return CMD_SUCCESS; -} - -DEFUN_ATTR(asp_transport_role, asp_transport_role_cmd, - "transport-role (client|server)", - "Specify the transport layer role for this ASP\n" - "Operate as a client; connect to a server\n" - "Operate as a server; wait for client connections\n", - CMD_ATTR_NODE_EXIT) -{ - struct osmo_ss7_asp *asp = vty->index; - - if (!strcmp(argv[0], "client")) - asp->cfg.is_server = false; - else if (!strcmp(argv[0], "server")) - asp->cfg.is_server = true; - else - OSMO_ASSERT(0); - - asp->cfg.trans_role_set_by_vty = true; - return CMD_SUCCESS; -} - -ALIAS_ATTR(asp_transport_role, asp_sctp_role_cmd, - "sctp-role (client|server)", - "Specify the SCTP role for this ASP\n" - "Operate as SCTP client; connect to a server\n" - "Operate as SCTP server; wait for client connections\n", - CMD_ATTR_HIDDEN | CMD_ATTR_NODE_EXIT); - -#define ASP_SCTP_PARAM_INIT_DESC \ - "Configure SCTP parameters\n" \ - "Configure INIT related parameters\n" \ - "Configure INIT Number of Outbound Streams\n" \ - "Configure INIT Maximum Inboud Streams\n" \ - "Configure INIT Maximum Attempts\n" \ - "Configure INIT Timeout (milliseconds)\n" -#define ASP_SCTP_PARAM_INIT_FIELDS "(num-ostreams|max-instreams|max-attempts|timeout)" - -DEFUN_ATTR(asp_sctp_param_init, asp_sctp_param_init_cmd, - "sctp-param init " ASP_SCTP_PARAM_INIT_FIELDS " <0-65535>", - ASP_SCTP_PARAM_INIT_DESC - "Value of the parameter\n", - CMD_ATTR_NODE_EXIT) -{ - struct osmo_ss7_asp *asp = vty->index; - - uint16_t val = atoi(argv[1]); - - if (strcmp(argv[0], "num-ostreams") == 0) { - asp->cfg.sctp_init.num_ostreams_present = true; - asp->cfg.sctp_init.num_ostreams_value = val; - } else if (strcmp(argv[0], "max-instreams") == 0) { - asp->cfg.sctp_init.max_instreams_present = true; - asp->cfg.sctp_init.max_instreams_value = val; - } else if (strcmp(argv[0], "max-attempts") == 0) { - asp->cfg.sctp_init.max_attempts_present = true; - asp->cfg.sctp_init.max_attempts_value = val; - } else if (strcmp(argv[0], "timeout") == 0) { - asp->cfg.sctp_init.max_init_timeo_present = true; - asp->cfg.sctp_init.max_init_timeo_value = val; - } else { - OSMO_ASSERT(0); - } - return CMD_SUCCESS; -} - -DEFUN_ATTR(asp_no_sctp_param_init, asp_no_sctp_param_init_cmd, - "no sctp-param init " ASP_SCTP_PARAM_INIT_FIELDS, - NO_STR ASP_SCTP_PARAM_INIT_DESC, - CMD_ATTR_NODE_EXIT) -{ - struct osmo_ss7_asp *asp = vty->index; - - if (strcmp(argv[0], "num-ostreams") == 0) - asp->cfg.sctp_init.num_ostreams_present = false; - else if (strcmp(argv[0], "max-instreams") == 0) - asp->cfg.sctp_init.max_instreams_present = false; - else if (strcmp(argv[0], "max-attempts") == 0) - asp->cfg.sctp_init.max_attempts_present = false; - else if (strcmp(argv[0], "timeout") == 0) - asp->cfg.sctp_init.max_init_timeo_present = false; - else - OSMO_ASSERT(0); - return CMD_SUCCESS; -} - -DEFUN_ATTR(asp_block, asp_block_cmd, - "block", - "Allows a SCTP Association with ASP, but doesn't let it become active\n", - CMD_ATTR_NODE_EXIT) -{ - /* TODO */ - vty_out(vty, "Not supported yet%s", VTY_NEWLINE); - return CMD_WARNING; -} - -DEFUN_ATTR(asp_shutdown, asp_shutdown_cmd, - "shutdown", - "Terminates SCTP association; New associations will be rejected\n", - CMD_ATTR_NODE_EXIT) -{ - struct osmo_ss7_asp *asp = vty->index; - - LOGPASP(asp, DLSS7, LOGL_NOTICE, "Applying Adm State change: %s -> %s\n", - get_value_string(osmo_ss7_asp_admin_state_names, asp->cfg.adm_state), - get_value_string(osmo_ss7_asp_admin_state_names, OSMO_SS7_ASP_ADM_S_SHUTDOWN)); - - asp->cfg.explicit_shutdown_state_by_vty_since_node_enter = true; - asp->cfg.adm_state = OSMO_SS7_ASP_ADM_S_SHUTDOWN; - ss7_asp_restart_after_reconfigure(asp); - return CMD_SUCCESS; -} - -DEFUN_ATTR(asp_no_shutdown, asp_no_shutdown_cmd, - "no shutdown", - NO_STR "Terminates SCTP association; New associations will be rejected\n", - CMD_ATTR_NODE_EXIT) -{ - struct osmo_ss7_asp *asp = vty->index; - - LOGPASP(asp, DLSS7, LOGL_NOTICE, "Applying Adm State change: %s -> %s\n", - get_value_string(osmo_ss7_asp_admin_state_names, asp->cfg.adm_state), - get_value_string(osmo_ss7_asp_admin_state_names, OSMO_SS7_ASP_ADM_S_ENABLED)); - - asp->cfg.explicit_shutdown_state_by_vty_since_node_enter = true; - asp->cfg.adm_state = OSMO_SS7_ASP_ADM_S_ENABLED; - ss7_asp_restart_after_reconfigure(asp); - return CMD_SUCCESS; -} - -DEFUN_ATTR(asp_quirk, asp_quirk_cmd, - "OVERWRITTEN", - "OVERWRITTEN\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_asp *asp = vty->index; - int quirk = get_string_value(asp_quirk_names, argv[0]); - - if (quirk < 0) - return CMD_WARNING; - - asp->cfg.quirks |= quirk; - return CMD_SUCCESS; -} - -DEFUN_ATTR(asp_no_quirk, asp_no_quirk_cmd, - "OVERWRITTEN", - "OVERWRITTEN\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_asp *asp = vty->index; - int quirk = get_string_value(asp_quirk_names, argv[0]); - - if (quirk < 0) - return CMD_WARNING; - - asp->cfg.quirks &= ~quirk; - return CMD_SUCCESS; -} - -/* timer lm <name> <1-999999> - * (cmdstr and doc are dynamically generated from ss7_asp_lm_timer_names.) */ -DEFUN_ATTR(asp_timer, asp_timer_cmd, - NULL, NULL, CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_asp *asp = vty->index; - enum ss7_asp_lm_timer timer = get_string_value(ss7_asp_lm_timer_names, argv[0]); - - if (timer <= 0 || timer >= SS7_ASP_LM_TIMERS_LEN) { - vty_out(vty, "%% Invalid timer: %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - osmo_tdef_set(asp->cfg.T_defs_lm, timer, atoi(argv[1]), OSMO_TDEF_S); - return CMD_SUCCESS; -} - -static void gen_asp_timer_cmd_strs(struct cmd_element *cmd) -{ - int i; - char *cmd_str = NULL; - char *doc_str = NULL; - - OSMO_ASSERT(cmd->string == NULL); - OSMO_ASSERT(cmd->doc == NULL); - - osmo_talloc_asprintf(tall_vty_ctx, cmd_str, "timer lm ("); - osmo_talloc_asprintf(tall_vty_ctx, doc_str, - "Configure ASP default timer values\n" - "Configure ASP default lm timer values\n"); - - for (i = 0; ss7_asp_lm_timer_names[i].str; i++) { - const struct osmo_tdef *def; - enum ss7_asp_lm_timer timer; - - timer = ss7_asp_lm_timer_names[i].value; - def = osmo_tdef_get_entry((struct osmo_tdef *)&ss7_asp_lm_timer_defaults, timer); - OSMO_ASSERT(def); - - osmo_talloc_asprintf(tall_vty_ctx, cmd_str, "%s%s", - i ? "|" : "", - ss7_asp_lm_timer_names[i].str); - osmo_talloc_asprintf(tall_vty_ctx, doc_str, "%s (default: %lu)\n", - def->desc, - def->default_val); - } - - osmo_talloc_asprintf(tall_vty_ctx, cmd_str, ") <1-999999>"); - osmo_talloc_asprintf(tall_vty_ctx, doc_str, - "Timer value, in seconds\n"); - - cmd->string = cmd_str; - cmd->doc = doc_str; -} - -static void write_asp_timers(struct vty *vty, const char *indent, - struct osmo_ss7_asp *asp) -{ - int i; - - for (i = 0; ss7_asp_lm_timer_names[i].str; i++) { - const struct osmo_tdef *tdef = osmo_tdef_get_entry(asp->cfg.T_defs_lm, ss7_asp_lm_timer_names[i].value); - if (!tdef) - continue; - if (tdef->val == tdef->default_val) - continue; - vty_out(vty, "%stimer lm %s %lu%s", indent, ss7_asp_lm_timer_names[i].str, - tdef->val, VTY_NEWLINE); - } -} - -static char *as_list_for_asp(const struct osmo_ss7_asp *asp, char *buf, size_t buf_len) -{ - struct osmo_strbuf sb = { .buf = buf, .len = buf_len }; - const struct osmo_ss7_as *as; - unsigned int count = 0; - llist_for_each_entry(as, &asp->inst->as_list, list) { - if (!osmo_ss7_as_has_asp(as, asp)) - continue; - OSMO_STRBUF_PRINTF(sb, "%s%s", count != 0 ? "," : "", as->cfg.name); - count++; - break; - } - - if (count == 0) - OSMO_STRBUF_PRINTF(sb, "?"); - return buf; -} - -/* Similar to osmo_sock_multiaddr_get_name_buf(), but aimed at listening sockets (only local part): */ -static char *get_sockname_buf(char *buf, size_t buf_len, int fd, int proto, bool local) -{ - char hostbuf[OSMO_SOCK_MAX_ADDRS][INET6_ADDRSTRLEN]; - size_t num_hostbuf = ARRAY_SIZE(hostbuf); - char portbuf[6]; - struct osmo_strbuf sb = { .buf = buf, .len = buf_len }; - bool need_more_bufs; - int rc; - - rc = osmo_sock_multiaddr_get_ip_and_port(fd, proto, &hostbuf[0][0], - &num_hostbuf, sizeof(hostbuf[0]), - portbuf, sizeof(portbuf), local); - if (rc < 0) - return NULL; - - need_more_bufs = num_hostbuf > ARRAY_SIZE(hostbuf); - if (need_more_bufs) - num_hostbuf = ARRAY_SIZE(hostbuf); - OSMO_STRBUF_APPEND(sb, osmo_multiaddr_ip_and_port_snprintf, - &hostbuf[0][0], num_hostbuf, sizeof(hostbuf[0]), portbuf); - if (need_more_bufs) - OSMO_STRBUF_PRINTF(sb, "<need-more-bufs!>"); - - return buf; -} - -static void show_one_asp(struct vty *vty, struct osmo_ss7_asp *asp) -{ - char as_buf[64]; - char buf_loc[OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN]; - char buf_rem[sizeof(buf_loc)]; - - int fd = ss7_asp_get_fd(asp); - if (fd > 0) { - const int trans_proto = asp->cfg.trans_proto; - if (!get_sockname_buf(buf_loc, sizeof(buf_loc), fd, trans_proto, true)) - OSMO_STRLCPY_ARRAY(buf_loc, "<sockname-error>"); - if (!get_sockname_buf(buf_rem, sizeof(buf_rem), fd, trans_proto, false)) - OSMO_STRLCPY_ARRAY(buf_rem, "<sockname-error>"); - } else { - ss7_asp_peer_snprintf(buf_loc, sizeof(buf_loc), &asp->cfg.local); - ss7_asp_peer_snprintf(buf_rem, sizeof(buf_rem), &asp->cfg.remote); - } - - vty_out(vty, "%-12s %-12s %-13s %-4s %-4s %-9s %-23s %-23s%s", - asp->cfg.name, - as_list_for_asp(asp, as_buf, sizeof(as_buf)), - asp->fi ? osmo_fsm_inst_state_name(asp->fi) : "uninitialized", - get_value_string(osmo_ss7_asp_protocol_vals, asp->cfg.proto), - osmo_str_tolower(get_value_string(osmo_ss7_asp_role_names, asp->cfg.role)), - asp->cfg.is_server ? "server" : "client", - buf_loc, buf_rem, - VTY_NEWLINE); -} - -static int show_asp(struct vty *vty, int id, const char *asp_name) -{ - struct osmo_ss7_instance *inst; - struct osmo_ss7_asp *asp = NULL; - - inst = osmo_ss7_instance_find(id); - if (!inst) { - vty_out(vty, "No SS7 instance %d found%s", id, VTY_NEWLINE); - return CMD_WARNING; - } - - if (asp_name) { - asp = osmo_ss7_asp_find_by_name(inst, asp_name); - if (!asp) { - vty_out(vty, "No ASP %s found%s", asp_name, VTY_NEWLINE); - return CMD_WARNING; - } - } - - vty_out(vty, "ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses%s", VTY_NEWLINE); - vty_out(vty, "------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------%s", VTY_NEWLINE); - - if (asp) { - show_one_asp(vty, asp); - return CMD_SUCCESS; - } - - llist_for_each_entry(asp, &inst->asp_list, list) - show_one_asp(vty, asp); - return CMD_SUCCESS; -} - -DEFUN(show_cs7_asp, show_cs7_asp_cmd, - "show cs7 instance <0-15> asp", - SHOW_STR CS7_STR INST_STR INST_STR - "Application Server Process (ASP)\n") -{ - int id = atoi(argv[0]); - - return show_asp(vty, id, NULL); -} - -DEFUN(show_cs7_asp_name, show_cs7_asp_name_cmd, - "show cs7 instance <0-15> asp name ASP_NAME", - SHOW_STR CS7_STR INST_STR INST_STR - "Application Server Process (ASP)\n" - "Lookup ASP with a given name\n" - "Name of the Application Server Process (ASP)\n") -{ - int id = atoi(argv[0]); - const char *asp_name = argv[1]; - - return show_asp(vty, id, asp_name); -} - -static void show_one_asp_remaddr_tcp(struct vty *vty, struct osmo_ss7_asp *asp) -{ - struct osmo_sockaddr osa = {}; - struct tcp_info tcpi = {}; - socklen_t len; - int fd, rc; - - fd = ss7_asp_get_fd(asp); - if (fd < 0) { - vty_out(vty, "%-12s %-46s uninitialized%s", asp->cfg.name, "", VTY_NEWLINE); - return; - } - - len = sizeof(osa.u.sas); - rc = getpeername(fd, &osa.u.sa, &len); - - len = sizeof(tcpi); - rc = getsockopt(fd, SOL_TCP, TCP_INFO, &tcpi, &len); - if (rc < 0) { - char buf_err[128]; - strerror_r(errno, buf_err, sizeof(buf_err)); - vty_out(vty, "%-12s %-46s getsockopt(TCP_INFO) failed: %s%s", - asp->cfg.name, osmo_sockaddr_to_str(&osa), buf_err, VTY_NEWLINE); - return; - } - - vty_out(vty, "%-12s %-46s TCP_%-19s %-8u %-8u %-8u %-8u%s", - asp->cfg.name, - osmo_sockaddr_to_str(&osa), - get_value_string(tcp_info_state_values, tcpi.tcpi_state), - tcpi.tcpi_snd_cwnd, tcpi.tcpi_rtt, - tcpi.tcpi_rto, tcpi.tcpi_pmtu, - VTY_NEWLINE); -} - -#ifdef HAVE_LIBSCTP -static void show_one_asp_remaddr_sctp(struct vty *vty, struct osmo_ss7_asp *asp) -{ - struct sctp_paddrinfo pinfo[OSMO_SOCK_MAX_ADDRS]; - struct osmo_sockaddr osa = {}; - size_t pinfo_cnt = ARRAY_SIZE(pinfo); - bool more_needed; - int fd, rc; - unsigned int i; - - fd = ss7_asp_get_fd(asp); - if (fd < 0) { - vty_out(vty, "%-12s %-46s uninitialized%s", asp->cfg.name, "", VTY_NEWLINE); - return; - } - - rc = osmo_sock_sctp_get_peer_addr_info(fd, &pinfo[0], &pinfo_cnt); - if (rc < 0) { - char buf_err[128]; - strerror_r(errno, buf_err, sizeof(buf_err)); - vty_out(vty, "%-12s %-46s getsockopt(SCTP_GET_PEER_ADDR_INFO) failed: %s%s", asp->cfg.name, "", buf_err, VTY_NEWLINE); - return; - } - - more_needed = pinfo_cnt > ARRAY_SIZE(pinfo); - if (pinfo_cnt > ARRAY_SIZE(pinfo)) - pinfo_cnt = ARRAY_SIZE(pinfo); - - for (i = 0; i < pinfo_cnt; i++) { - osa.u.sas = pinfo[i].spinfo_address; - vty_out(vty, "%-12s %-46s SCTP_%-18s %-8u %-8u %-8u %-8u%s", - asp->cfg.name, - osmo_sockaddr_to_str(&osa), - osmo_sctp_spinfo_state_str(pinfo[i].spinfo_state), - pinfo[i].spinfo_cwnd, pinfo[i].spinfo_srtt, - pinfo[i].spinfo_rto, pinfo[i].spinfo_mtu, - VTY_NEWLINE); - } - - if (more_needed) - vty_out(vty, "%-12s more address buffers needed!%s", asp->cfg.name, VTY_NEWLINE); -} -#endif - -static void show_one_asp_remaddr(struct vty *vty, struct osmo_ss7_asp *asp) -{ - switch (asp->cfg.trans_proto) { - case IPPROTO_TCP: - show_one_asp_remaddr_tcp(vty, asp); - break; -#ifdef HAVE_LIBSCTP - case IPPROTO_SCTP: - show_one_asp_remaddr_sctp(vty, asp); - break; -#endif - default: - vty_out(vty, "%-12s %-46s unknown proto %d%s", - asp->cfg.name, "", asp->cfg.trans_proto, VTY_NEWLINE); - break; - } -} - -static int show_asp_remaddr(struct vty *vty, int id, const char *asp_name) -{ - struct osmo_ss7_instance *inst; - struct osmo_ss7_asp *asp = NULL; - - inst = osmo_ss7_instance_find(id); - if (!inst) { - vty_out(vty, "No SS7 instance %d found%s", id, VTY_NEWLINE); - return CMD_WARNING; - } - - if (asp_name) { - asp = osmo_ss7_asp_find_by_name(inst, asp_name); - if (!asp) { - vty_out(vty, "No ASP %s found%s", asp_name, VTY_NEWLINE); - return CMD_WARNING; - } - } - - vty_out(vty, "ASP Name Remote IP Address & Port State CWND SRTT RTO MTU%s", VTY_NEWLINE); - vty_out(vty, "------------ ---------------------------------------------- ----------------------- -------- -------- -------- --------%s", VTY_NEWLINE); - - if (asp) { - show_one_asp_remaddr(vty, asp); - return CMD_SUCCESS; - } - - llist_for_each_entry(asp, &inst->asp_list, list) { - show_one_asp_remaddr(vty, asp); - } - return CMD_SUCCESS; -} - -DEFUN(show_cs7_asp_remaddr, show_cs7_asp_remaddr_cmd, - "show cs7 instance <0-15> asp-remaddr", - SHOW_STR CS7_STR INST_STR INST_STR - "Application Server Process (ASP) remote addresses information\n") -{ - int id = atoi(argv[0]); - - return show_asp_remaddr(vty, id, NULL); -} - - -DEFUN(show_cs7_asp_remaddr_name, show_cs7_asp_remaddr_name_cmd, - "show cs7 instance <0-15> asp-remaddr name ASP_NAME", - SHOW_STR CS7_STR INST_STR INST_STR - "Application Server Process (ASP) remote addresses information\n" - "Lookup ASP with a given name\n" - "Name of the Application Server Process (ASP)\n") -{ - int id = atoi(argv[0]); - const char *asp_name = argv[1]; - - return show_asp_remaddr(vty, id, asp_name); -} - -static void show_one_asp_assoc_status_tcp(struct vty *vty, struct osmo_ss7_asp *asp) -{ - struct osmo_sockaddr osa = {}; - struct tcp_info tcpi = {}; - socklen_t len; - int fd, rc; - int rx_pend_bytes = 0; - - fd = ss7_asp_get_fd(asp); - if (fd < 0) { - vty_out(vty, "%-12s uninitialized%s", asp->cfg.name, VTY_NEWLINE); - return; - } - - len = sizeof(osa.u.sas); - rc = getpeername(fd, &osa.u.sa, &len); - - len = sizeof(tcpi); - rc = getsockopt(fd, SOL_TCP, TCP_INFO, &tcpi, &len); - if (rc < 0) { - char buf_err[128]; - strerror_r(errno, buf_err, sizeof(buf_err)); - vty_out(vty, "%-12s getsockopt(TCP_INFO) failed: %s%s", - asp->cfg.name, buf_err, VTY_NEWLINE); - return; - } - - rc = ioctl(fd, FIONREAD, &rx_pend_bytes); - - /* FIXME: RWND: struct tcp_info from linux/tcp.h contains more fields - * than the one from netinet/tcp.h we currently use, including - * "tcpi_rcv_wnd" which we could use to print RWND here. However, - * linux/tcp.h seems to be missing the state defines used in - * "tcp_info_state_values", so we cannot use that one instead. - */ - - vty_out(vty, "%-12s TCP_%-19s %-9s %-10s %-8s %-9u %-7u %-9u %-46s%s", - asp->cfg.name, - get_value_string(tcp_info_state_values, tcpi.tcpi_state), - "-", "-", "-", tcpi.tcpi_unacked, rx_pend_bytes, - tcpi.tcpi_pmtu, osmo_sockaddr_to_str(&osa), - VTY_NEWLINE); -} - -#ifdef HAVE_LIBSCTP -static void show_one_asp_assoc_status_sctp(struct vty *vty, struct osmo_ss7_asp *asp) -{ - struct osmo_sockaddr osa = {}; - struct sctp_status st; - socklen_t len; - int fd, rc; - - fd = ss7_asp_get_fd(asp); - if (fd < 0) { - vty_out(vty, "%-12s uninitialized%s", asp->cfg.name, VTY_NEWLINE); - return; - } - - memset(&st, 0, sizeof(st)); - len = sizeof(st); - rc = getsockopt(fd, IPPROTO_SCTP, SCTP_STATUS, &st, &len); - if (rc < 0) { - char buf_err[128]; - strerror_r(errno, buf_err, sizeof(buf_err)); - vty_out(vty, "%-12s getsockopt(SCTP_STATUS) failed: %s%s", asp->cfg.name, buf_err, VTY_NEWLINE); - return; - } - - osa.u.sas = st.sstat_primary.spinfo_address; - vty_out(vty, "%-12s SCTP_%-18s %-9u %-10u %-8u %-9u %-7u %-9u %-46s%s", - asp->cfg.name, - osmo_sctp_sstat_state_str(st.sstat_state), - st.sstat_instrms, st.sstat_outstrms, - st.sstat_rwnd, st.sstat_unackdata, st.sstat_penddata, - st.sstat_fragmentation_point, - osmo_sockaddr_to_str(&osa), - VTY_NEWLINE); -} -#endif - -static void show_one_asp_assoc_status(struct vty *vty, struct osmo_ss7_asp *asp) -{ - switch (asp->cfg.trans_proto) { - case IPPROTO_TCP: - show_one_asp_assoc_status_tcp(vty, asp); - break; -#ifdef HAVE_LIBSCTP - case IPPROTO_SCTP: - show_one_asp_assoc_status_sctp(vty, asp); - break; -#endif - default: - vty_out(vty, "%-12s unknown proto %d%s", - asp->cfg.name, asp->cfg.trans_proto, VTY_NEWLINE); - break; - } -} - -static int show_asp_assoc_status(struct vty *vty, int id, const char *asp_name) -{ - struct osmo_ss7_instance *inst; - struct osmo_ss7_asp *asp = NULL; - - inst = osmo_ss7_instance_find(id); - if (!inst) { - vty_out(vty, "No SS7 instance %d found%s", id, VTY_NEWLINE); - return CMD_WARNING; - } - - if (asp_name) { - asp = osmo_ss7_asp_find_by_name(inst, asp_name); - if (!asp) { - vty_out(vty, "No ASP %s found%s", asp_name, VTY_NEWLINE); - return CMD_WARNING; - } - } - - vty_out(vty, "ASP Name State InStreams OutStreams RWND UnackData PenData FragPoint Current Primary Remote IP Address & Port%s", VTY_NEWLINE); - vty_out(vty, "------------ ----------------------- --------- ---------- -------- --------- ------- --------- ----------------------------------------------%s", VTY_NEWLINE); - - if (asp) { - show_one_asp_assoc_status(vty, asp); - return CMD_SUCCESS; - } - - llist_for_each_entry(asp, &inst->asp_list, list) - show_one_asp_assoc_status(vty, asp); - return CMD_SUCCESS; -} - -DEFUN(show_cs7_asp_assoc_status, show_cs7_asp_assoc_status_cmd, - "show cs7 instance <0-15> asp-assoc-status", - SHOW_STR CS7_STR INST_STR INST_STR - "Application Server Process (ASP) SCTP association status\n") -{ - int id = atoi(argv[0]); - - return show_asp_assoc_status(vty, id, NULL); -} - - -DEFUN(show_cs7_asp_assoc_status_name, show_cs7_asp_assoc_status_name_cmd, - "show cs7 instance <0-15> asp-assoc-status name ASP_NAME", - SHOW_STR CS7_STR INST_STR INST_STR - "Application Server Process (ASP) SCTP association information\n" - "Lookup ASP with a given name\n" - "Name of the Application Server Process (ASP)\n") -{ - int id = atoi(argv[0]); - const char *asp_name = argv[1]; - - return show_asp_assoc_status(vty, id, asp_name); -} - -static void write_one_asp(struct vty *vty, struct osmo_ss7_asp *asp, bool show_dyn_config) -{ - int i; - /* skip any dynamically created ASPs (e.g. auto-created at connect time) */ - if ((asp->dyn_allocated || asp->simple_client_allocated) - && !show_dyn_config) - return; - - vty_out(vty, " asp %s %u %u %s", - asp->cfg.name, asp->cfg.remote.port, asp->cfg.local.port, - osmo_ss7_asp_protocol_name(asp->cfg.proto)); - if (asp->cfg.trans_proto != ss7_default_trans_proto_for_asp_proto(asp->cfg.proto)) - vty_out(vty, " %s", get_value_string(ipproto_vals, asp->cfg.trans_proto)); - vty_out(vty, "%s", VTY_NEWLINE); - if (asp->cfg.description) - vty_out(vty, " description %s%s", asp->cfg.description, VTY_NEWLINE); - for (i = 0; i < asp->cfg.local.host_cnt; i++) { - if (asp->cfg.local.host[i]) - vty_out(vty, " local-ip %s%s%s", asp->cfg.local.host[i], - asp->cfg.local.idx_primary == i ? " primary" : "", VTY_NEWLINE); - } - for (i = 0; i < asp->cfg.remote.host_cnt; i++) { - if (asp->cfg.remote.host[i]) - vty_out(vty, " remote-ip %s%s%s", asp->cfg.remote.host[i], - asp->cfg.remote.idx_primary == i ? " primary" : "", VTY_NEWLINE); - } - if (asp->cfg.qos_class) - vty_out(vty, " qos-class %u%s", asp->cfg.qos_class, VTY_NEWLINE); - vty_out(vty, " role %s%s", osmo_str_tolower(get_value_string(osmo_ss7_asp_role_names, asp->cfg.role)), - VTY_NEWLINE); - if (asp->cfg.trans_proto == IPPROTO_SCTP) - vty_out(vty, " sctp-role %s%s", asp->cfg.is_server ? "server" : "client", VTY_NEWLINE); - else - vty_out(vty, " transport-role %s%s", asp->cfg.is_server ? "server" : "client", VTY_NEWLINE); - if (asp->cfg.sctp_init.num_ostreams_present) - vty_out(vty, " sctp-param init num-ostreams %u%s", asp->cfg.sctp_init.num_ostreams_value, VTY_NEWLINE); - if (asp->cfg.sctp_init.max_instreams_present) - vty_out(vty, " sctp-param init max-instreams %u%s", asp->cfg.sctp_init.max_instreams_value, VTY_NEWLINE); - if (asp->cfg.sctp_init.max_attempts_present) - vty_out(vty, " sctp-param init max-attempts %u%s", asp->cfg.sctp_init.max_attempts_value, VTY_NEWLINE); - if (asp->cfg.sctp_init.max_init_timeo_present) - vty_out(vty, " sctp-param init timeout %u%s", asp->cfg.sctp_init.max_init_timeo_value, VTY_NEWLINE); - for (i = 0; i < sizeof(uint32_t) * 8; i++) { - if (!(asp->cfg.quirks & ((uint32_t) 1 << i))) - continue; - vty_out(vty, " quirk %s%s", get_value_string(asp_quirk_names, (1 << i)), VTY_NEWLINE); - } - write_asp_timers(vty, " ", asp); - - switch (asp->cfg.adm_state) { - case OSMO_SS7_ASP_ADM_S_SHUTDOWN: - vty_out(vty, " shutdown%s", VTY_NEWLINE); - break; - case OSMO_SS7_ASP_ADM_S_BLOCKED: - vty_out(vty, " blocked%s", VTY_NEWLINE); - break; - case OSMO_SS7_ASP_ADM_S_ENABLED: - /* Default, no need to print: */ - vty_out(vty, " no shutdown%s", VTY_NEWLINE); - break; - } -} - - -/*********************************************************************** - * Application Server - ***********************************************************************/ - -static struct cmd_node as_node = { - L_CS7_AS_NODE, - "%s(config-cs7-as)# ", - 1, -}; - -DEFUN_ATTR(cs7_as, cs7_as_cmd, - "as NAME " XUA_VAR_STR, - "Configure an Application Server\n" - "Name of the Application Server\n" - XUA_VAR_HELP_STR, - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_instance *inst = vty->index; - struct osmo_ss7_as *as; - const char *name = argv[0]; - enum osmo_ss7_asp_protocol protocol = parse_asp_proto(argv[1]); - - if (protocol == OSMO_SS7_ASP_PROT_NONE) { - vty_out(vty, "invalid protocol '%s'%s", argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - as = osmo_ss7_as_find_or_create(inst, name, protocol); - if (!as) { - vty_out(vty, "cannot create AS '%s'%s", name, VTY_NEWLINE); - return CMD_WARNING; - } - - as->cfg.name = talloc_strdup(as, name); - - vty->node = L_CS7_AS_NODE; - vty->index = as; - vty->index_sub = &as->cfg.description; - - return CMD_SUCCESS; -} - -DEFUN_ATTR(no_cs7_as, no_cs7_as_cmd, - "no as NAME", - NO_STR "Disable Application Server\n" - "Name of AS\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_instance *inst = vty->index; - const char *name = argv[0]; - struct osmo_ss7_as *as; - - as = osmo_ss7_as_find_by_name(inst, name); - if (!as) { - vty_out(vty, "No AS named '%s' found%s", name, VTY_NEWLINE); - return CMD_WARNING; - } - osmo_ss7_as_destroy(as); - return CMD_SUCCESS; -} - -/* TODO: routing-key */ -DEFUN_ATTR(as_asp, as_asp_cmd, - "asp NAME", - "Specify that a given ASP is part of this AS\n" - "Name of ASP to be added to AS\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_as *as = vty->index; - - if (osmo_ss7_as_add_asp(as, argv[0])) { - vty_out(vty, "cannot find ASP '%s'%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN_ATTR(as_no_asp, as_no_asp_cmd, - "no asp NAME", - NO_STR "Specify ASP to be removed from this AS\n" - "Name of ASP to be removed\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_as *as = vty->index; - - if (osmo_ss7_as_del_asp(as, argv[0])) { - vty_out(vty, "cannot find ASP '%s'%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN_USRATTR(as_traf_mode, as_traf_mode_cmd, - OSMO_SCCP_LIB_ATTR_RSTRT_ASP, - "traffic-mode (broadcast | roundrobin | override)", - "Specifies traffic mode of operation of the ASP within the AS\n" - "Broadcast to all ASP within AS\n" - "Round-Robin between all ASP within AS\n" - "Override\n") -{ - struct osmo_ss7_as *as = vty->index; - - as->cfg.mode = get_string_value(osmo_ss7_as_traffic_mode_vals, argv[0]); - as->cfg.mode_set_by_vty = true; - return CMD_SUCCESS; -} - -DEFUN_USRATTR(as_traf_mode_loadshare, as_traf_mode_loadshare_cmd, - OSMO_SCCP_LIB_ATTR_RSTRT_ASP, - "traffic-mode loadshare [bindings] [sls] [opc-sls] [opc-shift] [<0-2>]", - "Specifies traffic mode of operation of the ASP within the AS\n" - "Share Load among all ASP within AS\n" - "Configure Loadshare parameters\n" - "Configure Loadshare SLS generation parameters\n" - "Generate extended SLS with OPC information\n" - "Shift OPC bits used during routing decision\n" - "How many bits from ITU OPC field (starting from least-significant-bit) to skip (default=0). 6 bits are always used\n" - ) -{ - struct osmo_ss7_as *as = vty->index; - - as->cfg.mode = OSMO_SS7_AS_TMOD_LOADSHARE; - as->cfg.mode_set_by_vty = true; - if (argc < 3) { - as->cfg.loadshare.opc_sls = false; - as->cfg.loadshare.opc_shift = 0; - return CMD_SUCCESS; - } - as->cfg.loadshare.opc_sls = true; - if (argc < 5) { - as->cfg.loadshare.opc_shift = 0; - return CMD_SUCCESS; - } - as->cfg.loadshare.opc_shift = atoi(argv[4]); - return CMD_SUCCESS; -} - -DEFUN_USRATTR(as_no_traf_mode, as_no_traf_mode_cmd, - OSMO_SCCP_LIB_ATTR_RSTRT_ASP, - "no traffic-mode", - NO_STR "Remove explicit traffic mode of operation of this AS\n") -{ - struct osmo_ss7_as *as = vty->index; - - as->cfg.mode = 0; - as->cfg.mode_set_by_vty = false; - - as->cfg.loadshare.sls_shift = 0; - as->cfg.loadshare.opc_sls = false; - as->cfg.loadshare.opc_shift = 0; - return CMD_SUCCESS; -} - -DEFUN_ATTR(as_sls_shift, as_sls_shift_cmd, - "sls-shift <0-3>", - "Shift SLS bits used during routing decision\n" - "How many bits from SLS field (starting from least-significant-bit) to skip\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_as *as = vty->index; - as->cfg.loadshare.sls_shift = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN_ATTR(as_bindingtable_reset, as_bindingtable_reset_cmd, - "binding-table reset", - "AS Loadshare binding table operations\n" - "Reset loadshare binding table\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_as *as = vty->index; - ss7_as_loadshare_binding_table_reset(as); - return CMD_SUCCESS; -} - -DEFUN_ATTR(as_recov_tout, as_recov_tout_cmd, - "recovery-timeout <1-2000>", - "Specifies the recovery timeout value in milliseconds\n" - "Recovery Timeout in Milliseconds\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_as *as = vty->index; - as->cfg.recovery_timeout_msec = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN_ATTR(as_qos_clas, as_qos_class_cmd, - "qos-class " QOS_CLASS_RANGE_STR, - "Specity QoS Class of AS\n" - QOS_CLASS_RANGE_HELP_STR, - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_as *as = vty->index; - as->cfg.qos_class = atoi(argv[0]); - return CMD_SUCCESS; -} - -const struct value_string mtp_si_vals[] = { - { MTP_SI_SCCP, "sccp" }, - { MTP_SI_TUP, "tup" }, - { MTP_SI_ISUP, "isup" }, - { MTP_SI_DUP, "dup" }, - { MTP_SI_TESTING, "testing" }, - { MTP_SI_B_ISUP, "b-isup" }, - { MTP_SI_SAT_ISUP, "sat-isup" }, - { MTP_SI_AAL2_SIG, "aal2" }, - { MTP_SI_BICC, "bicc" }, - { MTP_SI_GCP, "h248" }, - { 0, NULL } -}; - -#define ROUTING_KEY_CMD "routing-key RCONTEXT DPC" -#define ROUTING_KEY_CMD_STRS \ - "Define a routing key\n" \ - "Routing context number\n" \ - "Destination Point Code\n" -#define ROUTING_KEY_SI_ARG " si (aal2|bicc|b-isup|h248|isup|sat-isup|sccp|tup)" -#define ROUTING_KEY_SI_ARG_STRS \ - "Match on Service Indicator\n" \ - "ATM Adaption Layer 2\n" \ - "Bearer Independent Call Control\n" \ - "Broadband ISDN User Part\n" \ - "H.248\n" \ - "ISDN User Part\n" \ - "Sattelite ISDN User Part\n" \ - "Signalling Connection Control Part\n" \ - "Telephony User Part\n" -#define ROUTING_KEY_SSN_ARG " ssn SSN" -#define ROUTING_KEY_SSN_ARG_STRS \ - "Match on Sub-System Number\n" \ - "Sub-System Number to match on\n" - -static int _rout_key(struct vty *vty, - const char *rcontext, const char *dpc, - const char *si, const char *ssn) -{ - struct osmo_ss7_as *as = vty->index; - struct osmo_ss7_routing_key *rkey = &as->cfg.routing_key; - struct osmo_ss7_route *rt; - int pc; - - if (as->cfg.proto == OSMO_SS7_ASP_PROT_IPA && atoi(rcontext) != 0) { - vty_out(vty, "IPA doesn't support routing contexts; only permitted routing context " - "is 0\n"); - return CMD_WARNING; - } - - pc = osmo_ss7_pointcode_parse(as->inst, dpc); - if (pc < 0) { - vty_out(vty, "Invalid point code (%s)%s", dpc, VTY_NEWLINE); - return CMD_WARNING; - } - - /* When libosmo-sigtran is used in ASP role, the VTY routing table node - * (config-cs7-rt) is not available. However, when we add a routing key - * to an AS we still have to put a matching route into the routing - * table. This is done automatically by first removing the old route - * (users may change the routing key via VTY during runtime) and then - * putting a new route (see below). */ - if (cs7_role == CS7_ROLE_ASP) { - rt = ss7_route_table_find_route_by_dpc_mask(as->inst->rtable_system, rkey->pc, 0xffffff); - if (rt) - ss7_route_destroy(rt); - } - - rkey->pc = pc; - - rkey->context = atoi(rcontext); /* FIXME: input validation */ - rkey->si = si ? get_string_value(mtp_si_vals, si) : 0; /* FIXME: input validation */ - rkey->ssn = ssn ? atoi(ssn) : 0; /* FIXME: input validation */ - - /* automatically add new route (see also comment above) */ - if (cs7_role == CS7_ROLE_ASP) { - if (!ss7_route_create(as->inst->rtable_system, rkey->pc, 0xffffff, as->cfg.name)) { - vty_out(vty, "Cannot create route (pc=%s, linkset=%s) to AS %s", dpc, as->cfg.name, VTY_NEWLINE); - return CMD_WARNING; - } - } - - return CMD_SUCCESS; -} - -DEFUN_ATTR(as_rout_key, as_rout_key_cmd, - ROUTING_KEY_CMD, - ROUTING_KEY_CMD_STRS, - CMD_ATTR_IMMEDIATE) -{ - return _rout_key(vty, argv[0], argv[1], NULL, NULL); -} - -DEFUN_ATTR(as_rout_key_si, as_rout_key_si_cmd, - ROUTING_KEY_CMD ROUTING_KEY_SI_ARG, - ROUTING_KEY_CMD_STRS ROUTING_KEY_SI_ARG_STRS, - CMD_ATTR_IMMEDIATE) -{ - return _rout_key(vty, argv[0], argv[1], argv[2], NULL); -} - -DEFUN_ATTR(as_rout_key_ssn, as_rout_key_ssn_cmd, - ROUTING_KEY_CMD ROUTING_KEY_SSN_ARG, - ROUTING_KEY_CMD_STRS ROUTING_KEY_SSN_ARG_STRS, - CMD_ATTR_IMMEDIATE) -{ - return _rout_key(vty, argv[0], argv[1], NULL, argv[2]); -} - -DEFUN_ATTR(as_rout_key_si_ssn, as_rout_key_si_ssn_cmd, - ROUTING_KEY_CMD ROUTING_KEY_SI_ARG ROUTING_KEY_SSN_ARG, - ROUTING_KEY_CMD_STRS ROUTING_KEY_SI_ARG_STRS ROUTING_KEY_SSN_ARG_STRS, - CMD_ATTR_IMMEDIATE) -{ - return _rout_key(vty, argv[0], argv[1], argv[2], argv[3]); -} - -DEFUN_ATTR(as_pc_override, as_pc_override_cmd, - "point-code override dpc PC", - "Point Code Specific Features\n" - "Override (force) a point-code to hard-coded value\n" - "Override Source Point Code\n" - "Override Destination Point Code\n" - "New Point Code\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_as *as = vty->index; - int pc = osmo_ss7_pointcode_parse(as->inst, argv[0]); - if (pc < 0) { - vty_out(vty, "Invalid point code (%s)%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - if (as->cfg.proto != OSMO_SS7_ASP_PROT_IPA) { - vty_out(vty, "Only IPA type AS support point-code override. " - "Be happy that you don't need it!%s", VTY_NEWLINE); - return CMD_WARNING; - } - - as->cfg.pc_override.dpc = pc; - - return CMD_SUCCESS; -} - -DEFUN_ATTR(as_pc_patch_sccp, as_pc_patch_sccp_cmd, - "point-code override patch-sccp (disabled|both)", - "Point Code Specific Features\n" - "Override (force) a point-code to hard-coded value\n" - "Patch point code values into SCCP called/calling address\n" - "Don't patch any point codes into SCCP called/calling address\n" - "Patch both origin and destination point codes into SCCP called/calling address\n", - CMD_ATTR_IMMEDIATE) -{ - struct osmo_ss7_as *as = vty->index; - - if (as->cfg.proto != OSMO_SS7_ASP_PROT_IPA) { - vty_out(vty, "Only IPA type AS support point-code patch-into-sccp. " - "Be happy that you don't need it!%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "disabled")) - as->cfg.pc_override.sccp_mode = OSMO_SS7_PATCH_NONE; - else - as->cfg.pc_override.sccp_mode = OSMO_SS7_PATCH_BOTH; - - return CMD_SUCCESS; -} - -static void write_one_as(struct vty *vty, struct osmo_ss7_as *as, bool show_dyn_config) -{ - struct osmo_ss7_routing_key *rkey; - unsigned int i; - - /* skip any dynamically allocated AS definitions */ - if ((as->rkm_dyn_allocated || as->simple_client_allocated) - && !show_dyn_config) - return; - - vty_out(vty, " as %s %s%s", as->cfg.name, - osmo_ss7_asp_protocol_name(as->cfg.proto), VTY_NEWLINE); - if (as->cfg.description) - vty_out(vty, " description %s%s", as->cfg.description, VTY_NEWLINE); - for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) { - struct osmo_ss7_asp *asp = as->cfg.asps[i]; - if (!asp) - continue; - /* skip any dynamically created ASPs (e.g. auto-created at connect time) */ - if ((asp->dyn_allocated || asp->simple_client_allocated) - && !show_dyn_config) - continue; - vty_out(vty, " asp %s%s", asp->cfg.name, VTY_NEWLINE); - } - if (as->cfg.mode_set_by_vty) { - vty_out(vty, " traffic-mode %s%s", osmo_ss7_as_traffic_mode_name(as->cfg.mode), VTY_NEWLINE); - if (as->cfg.mode == OSMO_SS7_AS_TMOD_LOADSHARE) { - if (as->cfg.loadshare.opc_sls) { - vty_out(vty, " bindings sls opc-sls"); - if (as->cfg.loadshare.opc_shift != 0) - vty_out(vty, " opc-shift %u", as->cfg.loadshare.opc_shift); - } - vty_out(vty, "%s", VTY_NEWLINE); - } - - if (as->cfg.loadshare.sls_shift != 0) - vty_out(vty, " sls-shift %u%s", as->cfg.loadshare.sls_shift, VTY_NEWLINE); - } - - if (as->cfg.recovery_timeout_msec != 2000) { - vty_out(vty, " recovery-timeout %u%s", - as->cfg.recovery_timeout_msec, VTY_NEWLINE); - } - if (as->cfg.qos_class) - vty_out(vty, " qos-class %u%s", as->cfg.qos_class, VTY_NEWLINE); - rkey = &as->cfg.routing_key; - vty_out(vty, " routing-key %u %s", rkey->context, - osmo_ss7_pointcode_print(as->inst, rkey->pc)); - if (rkey->si) - vty_out(vty, " si %s", - get_value_string(mtp_si_vals, rkey->si)); - if (rkey->ssn) - vty_out(vty, " ssn %u", rkey->ssn); - vty_out(vty, "%s", VTY_NEWLINE); - - if (as->cfg.pc_override.dpc) - vty_out(vty, " point-code override dpc %s%s", - osmo_ss7_pointcode_print(as->inst, as->cfg.pc_override.dpc), VTY_NEWLINE); - - if (as->cfg.pc_override.sccp_mode) - vty_out(vty, " point-code override patch-sccp both%s", VTY_NEWLINE); -} - -static void show_one_as(struct vty *vty, struct osmo_ss7_as *as) -{ - vty_out(vty, "%-12s %-12s %-10u %-13s %4s %13s %3s %5s %4s %10s%s", - as->cfg.name, osmo_fsm_inst_state_name(as->fi), as->cfg.routing_key.context, - osmo_ss7_pointcode_print(as->inst, as->cfg.routing_key.pc), - "", "", "", "", "", osmo_ss7_as_traffic_mode_name(as->cfg.mode), - VTY_NEWLINE); -} - -static int show_as(struct vty *vty, int id, const char *as_name, const char *filter) -{ - struct osmo_ss7_instance *inst; - struct osmo_ss7_as *as = NULL; - - inst = osmo_ss7_instance_find(id); - if (!inst) { - vty_out(vty, "%% No SS7 instance %d found%s", id, VTY_NEWLINE); - return CMD_WARNING; - } - - if (as_name) { - as = osmo_ss7_as_find_by_name(inst, as_name); - if (!as) { - vty_out(vty, "%% No AS '%s' found%s", as_name, VTY_NEWLINE); - return CMD_WARNING; - } - } - - vty_out(vty, " Routing Routing Key Cic Cic Traffic%s", VTY_NEWLINE); - vty_out(vty, "AS Name State Context Dpc Si Opc Ssn Min Max Mode%s", VTY_NEWLINE); - vty_out(vty, "------------ ------------ ---------- ------------- ---- ------------- --- ----- ----- -------%s", VTY_NEWLINE); - - if (as) { - show_one_as(vty, as); - return CMD_SUCCESS; - } - - llist_for_each_entry(as, &inst->as_list, list) { - if (filter && !strcmp(filter, "m3ua") && as->cfg.proto != OSMO_SS7_ASP_PROT_M3UA) - continue; - if (filter && !strcmp(filter, "sua") && as->cfg.proto != OSMO_SS7_ASP_PROT_SUA) - continue; - if (filter && !strcmp(filter, "active") && !osmo_ss7_as_active(as)) - continue; - show_one_as(vty, as); - } - return CMD_SUCCESS; -} - -DEFUN(show_cs7_as, show_cs7_as_cmd, - "show cs7 instance <0-15> as (active|all|m3ua|sua)", - SHOW_STR CS7_STR INST_STR INST_STR "Application Server (AS)\n" - "Display all active ASs\n" - "Display all ASs (default)\n" - "Display all m3ua ASs\n" - "Display all SUA ASs\n") -{ - const char *filter = argv[1]; - int id = atoi(argv[0]); - - return show_as(vty, id, NULL, filter); -} - -DEFUN(show_cs7_as_name, show_cs7_as_name_cmd, - "show cs7 instance <0-15> as name AS_NAME", - SHOW_STR CS7_STR INST_STR INST_STR "Application Server (AS)\n" - "Look up AS with a given name\n" - "Name of the Application Server (AS)\n") -{ - int id = atoi(argv[0]); - const char *as_name = argv[1]; - - return show_as(vty, id, as_name, NULL); -} - -DEFUN(show_cs7_as_bindingtable_name, show_cs7_as_bindingtable_name_cmd, - "show cs7 instance <0-15> as binding-table name AS_NAME", - SHOW_STR CS7_STR INST_STR INST_STR "Application Server (AS)\n" - "Display binding table\n" - "Look up AS with a given name\n" - "Name of the Application Server (AS)\n") -{ - int id = atoi(argv[0]); - const char *as_name = argv[1]; - struct osmo_ss7_instance *inst; - struct osmo_ss7_as *as = NULL; - - inst = osmo_ss7_instance_find(id); - if (!inst) { - vty_out(vty, "No SS7 instance %d found%s", id, VTY_NEWLINE); - return CMD_WARNING; - } - - if (as_name) { - as = osmo_ss7_as_find_by_name(inst, as_name); - if (!as) { - vty_out(vty, "No AS %s found%s", as_name, VTY_NEWLINE); - return CMD_WARNING; - } - } - - vty_out(vty, "Loadshare Seed Normal ASP Active Alternative ASP Active%s", VTY_NEWLINE); - vty_out(vty, "-------------- --------------- ------ --------------- ------%s", VTY_NEWLINE); - - for (unsigned int i = 0; i < ARRAY_SIZE(as->aesls_table); i++) { - struct osmo_ss7_as_esls_entry *e = &as->aesls_table[i]; - vty_out(vty, "%-15u %-16s %-7s %-16s %-7s%s", - i, - e->normal_asp ? e->normal_asp->cfg.name : "-", - e->normal_asp ? (osmo_ss7_asp_active(e->normal_asp) ? "Yes" : "No") : "-", - e->alt_asp ? e->alt_asp->cfg.name : "-", - e->alt_asp ? (osmo_ss7_asp_active(e->alt_asp) ? "Yes" : "No") : "-", - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -/*********************************************************************** * SCCP addressbook handling ***********************************************************************/
@@ -3287,11 +1402,11 @@
/* first dump ASPs, as ASs reference them */ llist_for_each_entry(asp, &inst->asp_list, list) - write_one_asp(vty, asp, show_dyn_config); + ss7_vty_write_one_asp(vty, asp, show_dyn_config);
/* then dump ASPs, as routes reference them */ llist_for_each_entry(as, &inst->as_list, list) - write_one_as(vty, as, show_dyn_config); + ss7_vty_write_one_as(vty, as, show_dyn_config);
/* now dump everything that is relevent for the SG role */ if (cs7_role == CS7_ROLE_SG) { @@ -3301,7 +1416,7 @@ write_one_rtable(vty, rtable);
llist_for_each_entry(oxs, &inst->xua_servers, list) - write_one_xua(vty, oxs); + ss7_vty_write_one_oxs(vty, oxs); }
/* Append SCCP Addressbook */ @@ -3313,57 +1428,21 @@
int osmo_ss7_vty_go_parent(struct vty *vty) { - struct osmo_ss7_as *as; - struct osmo_ss7_asp *asp; struct osmo_ss7_route_table *rtbl; - struct osmo_xua_server *oxs; struct osmo_sccp_addr_entry *entry;
switch (vty->node) { case L_CS7_ASP_NODE: - asp = vty->index; - if (asp->cfg.explicit_shutdown_state_by_vty_since_node_enter) { - /* Interactive VTY, inform of new behavior upon use of new '[no] shutdown' commands: */ - if (vty->type != VTY_FILE) - vty_out(vty, "%% NOTE: Skipping automatic restart of ASP since an explicit '[no] shutdown' command was entered%s", VTY_NEWLINE); - asp->cfg.explicit_shutdown_state_by_vty_since_node_enter = false; - } else if (vty->type == VTY_FILE) { - /* Make sure config reading is backward compatible by starting the ASP if no explicit 'no shutdown' is read: */ - vty_out(vty, - "%% VTY node 'asp' without a '[no] shutdown' command at the end is deprecated, " - "please make sure you update your cfg file for future compatibility.%s", - VTY_NEWLINE); - ss7_asp_restart_after_reconfigure(asp); - } else { - /* Interactive VTY without '[no] shutdown' explicit cmd, remind the user that we are no - * longer automatically restarting the ASP when going out of the "asp" node: */ - vty_out(vty, - "%% NOTE: Make sure to use '[no] shutdown' command in 'asp' node " - "in order to restart the ASP for new configs to be applied.%s", - VTY_NEWLINE); - } - vty->node = L_CS7_NODE; - vty->index = asp->inst; - break; + return ss7_vty_node_asp_go_parent(vty); case L_CS7_RTABLE_NODE: rtbl = vty->index; vty->node = L_CS7_NODE; vty->index = rtbl->inst; break; case L_CS7_AS_NODE: - as = vty->index; - vty->node = L_CS7_NODE; - vty->index = as->inst; - break; + return ss7_vty_node_as_go_parent(vty); case L_CS7_XUA_NODE: - oxs = vty->index; - /* If no local addr was set, or erased after _create(): */ - ss7_xua_server_set_default_local_hosts(oxs); - if (ss7_xua_server_bind(oxs) < 0) - vty_out(vty, "%% Unable to bind xUA server to IP(s)%s", VTY_NEWLINE); - vty->node = L_CS7_NODE; - vty->index = oxs->inst; - break; + return ss7_vty_node_oxs_go_parent(vty); case L_CS7_SCCPADDR_NODE: entry = vty->index; vty->node = L_CS7_NODE; @@ -3437,20 +1516,8 @@ { g_ctx = ctx;
- asp_quirk_cmd.string = vty_cmd_string_from_valstr(ctx, asp_quirk_names, - "quirk (", "|", ")", VTY_DO_LOWER); - asp_quirk_cmd.doc = vty_cmd_string_from_valstr(ctx, asp_quirk_descs, - "Enable quirk to work around interop issues\n", - "\n", "\n", 0); - asp_no_quirk_cmd.string = vty_cmd_string_from_valstr(ctx, asp_quirk_names, - "no quirk (", "|", ")", VTY_DO_LOWER); - asp_no_quirk_cmd.doc = vty_cmd_string_from_valstr(ctx, asp_quirk_descs, - NO_STR "Disable quirk to work around interop issues\n", - "\n", "\n", 0); - install_lib_element_ve(&show_cs7_user_cmd); - install_lib_element_ve(&show_cs7_xua_cmd); - install_lib_element_ve(&show_cs7_xua_trans_proto_cmd); + ss7_vty_init_show_oxs(); install_lib_element_ve(&show_cs7_config_cmd); install_lib_element(ENABLE_NODE, &cs7_asp_disconnect_cmd);
@@ -3469,57 +1536,8 @@ install_lib_element(L_CS7_NODE, &cs7_opc_dpc_shift_cmd); install_lib_element(L_CS7_NODE, &cs7_sls_shift_cmd);
- install_node(&asp_node, NULL); - install_lib_element_ve(&show_cs7_asp_cmd); - install_lib_element_ve(&show_cs7_asp_name_cmd); - install_lib_element_ve(&show_cs7_asp_remaddr_cmd); - install_lib_element_ve(&show_cs7_asp_remaddr_name_cmd); - install_lib_element_ve(&show_cs7_asp_assoc_status_cmd); - install_lib_element_ve(&show_cs7_asp_assoc_status_name_cmd); - install_lib_element(L_CS7_NODE, &cs7_asp_cmd); - install_lib_element(L_CS7_NODE, &cs7_asp_trans_proto_cmd); - install_lib_element(L_CS7_NODE, &no_cs7_asp_cmd); - install_lib_element(L_CS7_ASP_NODE, &cfg_description_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_remote_ip_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_no_remote_ip_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_local_ip_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_no_local_ip_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_qos_class_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_role_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_transport_role_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_sctp_role_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_sctp_param_init_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_no_sctp_param_init_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_quirk_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_no_quirk_cmd); - gen_asp_timer_cmd_strs(&asp_timer_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_timer_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_block_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_shutdown_cmd); - install_lib_element(L_CS7_ASP_NODE, &asp_no_shutdown_cmd); - - install_node(&as_node, NULL); - install_lib_element_ve(&show_cs7_as_cmd); - install_lib_element_ve(&show_cs7_as_name_cmd); - install_lib_element_ve(&show_cs7_as_bindingtable_name_cmd); - install_lib_element(L_CS7_NODE, &cs7_as_cmd); - install_lib_element(L_CS7_NODE, &no_cs7_as_cmd); - install_lib_element(L_CS7_AS_NODE, &cfg_description_cmd); - install_lib_element(L_CS7_AS_NODE, &as_asp_cmd); - install_lib_element(L_CS7_AS_NODE, &as_no_asp_cmd); - install_lib_element(L_CS7_AS_NODE, &as_traf_mode_cmd); - install_lib_element(L_CS7_AS_NODE, &as_traf_mode_loadshare_cmd); - install_lib_element(L_CS7_AS_NODE, &as_no_traf_mode_cmd); - install_lib_element(L_CS7_AS_NODE, &as_sls_shift_cmd); - install_lib_element(L_CS7_AS_NODE, &as_bindingtable_reset_cmd); - install_lib_element(L_CS7_AS_NODE, &as_recov_tout_cmd); - install_lib_element(L_CS7_AS_NODE, &as_qos_class_cmd); - install_lib_element(L_CS7_AS_NODE, &as_rout_key_cmd); - install_lib_element(L_CS7_AS_NODE, &as_rout_key_si_cmd); - install_lib_element(L_CS7_AS_NODE, &as_rout_key_ssn_cmd); - install_lib_element(L_CS7_AS_NODE, &as_rout_key_si_ssn_cmd); - install_lib_element(L_CS7_AS_NODE, &as_pc_override_cmd); - install_lib_element(L_CS7_AS_NODE, &as_pc_patch_sccp_cmd); + ss7_vty_init_node_asp(); + ss7_vty_init_node_as();
install_lib_element_ve(&show_cs7_route_cmd); install_lib_element_ve(&show_cs7_route_bindingtable_cmd); @@ -3545,12 +1563,5 @@ install_lib_element(L_CS7_RTABLE_NODE, &cs7_rt_upd_cmd); install_lib_element(L_CS7_RTABLE_NODE, &cs7_rt_rem_cmd);
- install_node(&xua_node, NULL); - install_lib_element(L_CS7_NODE, &cs7_xua_cmd); - install_lib_element(L_CS7_NODE, &no_cs7_xua_cmd); - install_lib_element(L_CS7_XUA_NODE, &xua_local_ip_cmd); - install_lib_element(L_CS7_XUA_NODE, &xua_no_local_ip_cmd); - install_lib_element(L_CS7_XUA_NODE, &xua_accept_dyn_asp_cmd); - install_lib_element(L_CS7_XUA_NODE, &xua_sctp_param_init_cmd); - install_lib_element(L_CS7_XUA_NODE, &xua_no_sctp_param_init_cmd); + ss7_vty_init_node_oxs(); } diff --git a/src/ss7_vty.h b/src/ss7_vty.h new file mode 100644 index 0000000..18dc5a6 --- /dev/null +++ b/src/ss7_vty.h @@ -0,0 +1,64 @@ +#pragma once + +/* Internal header used by libosmo-sccp, not available publicly for lib users */ + +#include <stdbool.h> +#include <stdint.h> + +#include <osmocom/vty/vty.h> + +#include <osmocom/netif/stream.h> +#include <osmocom/sigtran/osmo_ss7.h> + +#include "ss7_instance.h" + +enum cs7_role_t { + CS7_ROLE_SG, + CS7_ROLE_ASP +}; + +extern void *g_ctx; +extern enum cs7_role_t cs7_role; +extern const struct value_string ipproto_vals[]; + +#define CS7_STR "ITU-T Signaling System 7\n" +#define PC_STR "Point Code\n" +#define INST_STR "An instance of the SS7 stack\n" + +#define XUA_VAR_STR "(sua|m3ua|ipa)" + +#define XUA_VAR_HELP_STR \ + "SCCP User Adaptation\n" \ + "MTP3 User Adaptation\n" \ + "IPA Multiplex (SCCP Lite)\n" + +#define IPPROTO_VAR_STR "(sctp|tcp)" +#define IPPROTO_VAR_HELP_STR \ + "SCTP (Stream Control Transmission Protocol)\n" \ + "TCP (Transmission Control Protocol)\n" + +#define QOS_CLASS_RANGE_STR "<0-7>" +#define QOS_CLASS_RANGE_HELP_STR "QoS Class\n" +#define QOS_CLASS_VAR_STR "(" QOS_CLASS_RANGE_STR "|default)" +#define QOS_CLASS_VAR_HELP_STR \ + QOS_CLASS_RANGE_HELP_STR \ + "Default QoS Class (0)\n" + +int parse_trans_proto(const char *protocol); +enum osmo_ss7_asp_protocol parse_asp_proto(const char *protocol); + +/* ss7_asp_vty.c */ +void ss7_vty_init_node_asp(void); +void ss7_vty_write_one_asp(struct vty *vty, struct osmo_ss7_asp *asp, bool show_dyn_config); +int ss7_vty_node_asp_go_parent(struct vty *vty); + +/* ss7_as_vty.c */ +void ss7_vty_init_node_as(void); +void ss7_vty_write_one_as(struct vty *vty, struct osmo_ss7_as *as, bool show_dyn_config); +int ss7_vty_node_as_go_parent(struct vty *vty); + +/* ss7_xua_srv_vty.c */ +void ss7_vty_init_node_oxs(void); +void ss7_vty_init_show_oxs(void); +void ss7_vty_write_one_oxs(struct vty *vty, struct osmo_xua_server *xs); +int ss7_vty_node_oxs_go_parent(struct vty *vty); diff --git a/src/ss7_xua_srv_vty.c b/src/ss7_xua_srv_vty.c new file mode 100644 index 0000000..3a5e4ae --- /dev/null +++ b/src/ss7_xua_srv_vty.c @@ -0,0 +1,347 @@ +/* SS7 xua_srv VTY Interface */ + +/* (C) 2015-2021 by Harald Welte laforge@gnumonks.org + * (C) 2025 by sysmocom s.f.m.c. GmbH info@sysmocom.de + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> + +#include <osmocom/vty/vty.h> +#include <osmocom/vty/command.h> +#include <osmocom/vty/logging.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/misc.h> + +#include <osmocom/netif/stream.h> + +#include <osmocom/sigtran/osmo_ss7.h> + +#include "xua_internal.h" +#include "ss7_asp.h" +#include "ss7_internal.h" +#include "ss7_vty.h" +#include "ss7_xua_srv.h" + +/*********************************************************************** + * xUA Listener Configuration (SG) + ***********************************************************************/ + +static struct cmd_node xua_node = { + L_CS7_XUA_NODE, + "%s(config-cs7-listen)# ", + 1, +}; + +DEFUN_ATTR(cs7_xua, cs7_xua_cmd, + "listen " XUA_VAR_STR " <0-65534> [" IPPROTO_VAR_STR "]", + "Configure/Enable xUA Listener\n" + XUA_VAR_HELP_STR + "Port number\n" + IPPROTO_VAR_HELP_STR, + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_instance *inst = vty->index; + struct osmo_xua_server *xs; + enum osmo_ss7_asp_protocol proto = parse_asp_proto(argv[0]); + uint16_t port = atoi(argv[1]); + int trans_proto; + + if (argc > 2) + trans_proto = parse_trans_proto(argv[2]); + else /* default transport protocol */ + trans_proto = ss7_default_trans_proto_for_asp_proto(proto); + if (trans_proto < 0) + return CMD_WARNING; + + xs = ss7_xua_server_find2(inst, trans_proto, proto, port); + if (!xs) { + xs = ss7_xua_server_create2(inst, trans_proto, proto, port, NULL); + if (!xs) + return CMD_WARNING; + /* Drop first dummy address created automatically by _create(): */ + ss7_xua_server_set_local_hosts(xs, NULL, 0); + } + + vty->node = L_CS7_XUA_NODE; + vty->index = xs; + return CMD_SUCCESS; +} + +DEFUN_ATTR(no_cs7_xua, no_cs7_xua_cmd, + "no listen " XUA_VAR_STR " <0-65534> [" IPPROTO_VAR_STR "]", + NO_STR "Disable xUA Listener on given port\n" + XUA_VAR_HELP_STR + "Port number\n" + IPPROTO_VAR_HELP_STR, + CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_instance *inst = vty->index; + struct osmo_xua_server *xs; + enum osmo_ss7_asp_protocol proto = parse_asp_proto(argv[0]); + uint16_t port = atoi(argv[1]); + int trans_proto; + + if (argc > 2) + trans_proto = parse_trans_proto(argv[2]); + else /* default transport protocol */ + trans_proto = ss7_default_trans_proto_for_asp_proto(proto); + if (trans_proto < 0) + return CMD_WARNING; + + xs = ss7_xua_server_find2(inst, trans_proto, proto, port); + if (!xs) { + vty_out(vty, "No xUA server for port %u found%s", port, VTY_NEWLINE); + return CMD_WARNING; + } + ss7_xua_server_destroy(xs); + return CMD_SUCCESS; +} + +DEFUN_ATTR(xua_local_ip, xua_local_ip_cmd, + "local-ip " VTY_IPV46_CMD, + "Configure the Local IP Address for xUA\n" + "IPv4 Address to use for XUA\n" + "IPv6 Address to use for XUA\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_xua_server *xs = vty->index; + + ss7_xua_server_add_local_host(xs, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN_ATTR(xua_no_local_ip, xua_no_local_ip_cmd, + "no local-ip " VTY_IPV46_CMD, + NO_STR "Configure the Local IP Address for xUA\n" + "IPv4 Address to use for XUA\n" + "IPv6 Address to use for XUA\n", + CMD_ATTR_NODE_EXIT) +{ + struct osmo_xua_server *xs = vty->index; + + if (ss7_xua_server_del_local_host(xs, argv[0]) != 0) { + vty_out(vty, "%% Failed deleting local address '%s' from set%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +DEFUN_ATTR(xua_accept_dyn_asp, xua_accept_dyn_asp_cmd, + "accept-asp-connections (pre-configured|dynamic-permitted)", + "Define what kind of ASP connections to accept\n" + "Accept only pre-configured ASPs (source IP/port)\n" + "Accept any connection and dynamically create an ASP definition\n", + CMD_ATTR_IMMEDIATE) +{ + struct osmo_xua_server *xs = vty->index; + + if (!strcmp(argv[0], "dynamic-permitted")) + xs->cfg.accept_dyn_reg = true; + else + xs->cfg.accept_dyn_reg = false; + + return CMD_SUCCESS; +} + +#define XUA_SRV_SCTP_PARAM_INIT_DESC \ + "Configure SCTP parameters\n" \ + "Configure INIT related parameters\n" \ + "Configure INIT Number of Outbound Streams\n" \ + "Configure INIT Maximum Inboud Streams\n" +#define XUA_SRV_SCTP_PARAM_INIT_FIELDS "(num-ostreams|max-instreams)" + +DEFUN_ATTR(xua_sctp_param_init, xua_sctp_param_init_cmd, + "sctp-param init " XUA_SRV_SCTP_PARAM_INIT_FIELDS " <0-65535>", + XUA_SRV_SCTP_PARAM_INIT_DESC + "Value of the parameter\n", + CMD_ATTR_NODE_EXIT) +{ + struct osmo_xua_server *xs = vty->index; + + uint16_t val = atoi(argv[1]); + + if (strcmp(argv[0], "num-ostreams") == 0) { + xs->cfg.sctp_init.num_ostreams_present = true; + xs->cfg.sctp_init.num_ostreams_value = val; + } else if (strcmp(argv[0], "max-instreams") == 0) { + xs->cfg.sctp_init.max_instreams_present = true; + xs->cfg.sctp_init.max_instreams_value = val; + } else { + OSMO_ASSERT(0); + } + return CMD_SUCCESS; +} + +DEFUN_ATTR(xua_no_sctp_param_init, xua_no_sctp_param_init_cmd, + "no sctp-param init " XUA_SRV_SCTP_PARAM_INIT_FIELDS, + NO_STR XUA_SRV_SCTP_PARAM_INIT_DESC, + CMD_ATTR_NODE_EXIT) +{ + struct osmo_xua_server *xs = vty->index; + + if (strcmp(argv[0], "num-ostreams") == 0) + xs->cfg.sctp_init.num_ostreams_present = false; + else if (strcmp(argv[0], "max-instreams") == 0) + xs->cfg.sctp_init.max_instreams_present = false; + else + OSMO_ASSERT(0); + return CMD_SUCCESS; +} + +void ss7_vty_write_one_oxs(struct vty *vty, struct osmo_xua_server *xs) +{ + int i; + + vty_out(vty, " listen %s %u", + get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto), + xs->cfg.local.port); + if (xs->cfg.trans_proto != ss7_default_trans_proto_for_asp_proto(xs->cfg.proto)) + vty_out(vty, " %s", get_value_string(ipproto_vals, xs->cfg.trans_proto)); + vty_out(vty, "%s", VTY_NEWLINE); + + for (i = 0; i < xs->cfg.local.host_cnt; i++) { + if (xs->cfg.local.host[i]) + vty_out(vty, " local-ip %s%s", xs->cfg.local.host[i], VTY_NEWLINE); + } + if (xs->cfg.accept_dyn_reg) + vty_out(vty, " accept-asp-connections dynamic-permitted%s", VTY_NEWLINE); + if (xs->cfg.sctp_init.num_ostreams_present) + vty_out(vty, " sctp-param init num-ostreams %u%s", xs->cfg.sctp_init.num_ostreams_value, VTY_NEWLINE); + if (xs->cfg.sctp_init.max_instreams_present) + vty_out(vty, " sctp-param init max-instreams %u%s", xs->cfg.sctp_init.max_instreams_value, VTY_NEWLINE); +} + +static void vty_dump_xua_server(struct vty *vty, struct osmo_xua_server *xs) +{ + char buf[OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN]; + const char *proto = get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto); + int fd = xs->server ? osmo_stream_srv_link_get_fd(xs->server) : -1; + + if (fd < 0) { + if (ss7_asp_peer_snprintf(buf, sizeof(buf), &xs->cfg.local) < 0) + snprintf(buf, sizeof(buf), "<error>"); + } else { + char hostbuf[OSMO_SOCK_MAX_ADDRS][INET6_ADDRSTRLEN]; + size_t num_hostbuf = ARRAY_SIZE(hostbuf); + char portbuf[6]; + int rc; + rc = osmo_sock_multiaddr_get_ip_and_port(fd, xs->cfg.trans_proto, + &hostbuf[0][0], &num_hostbuf, sizeof(hostbuf[0]), + portbuf, sizeof(portbuf), true); + if (rc < 0) { + snprintf(buf, sizeof(buf), "<error>"); + } else { + if (num_hostbuf > ARRAY_SIZE(hostbuf)) + num_hostbuf = ARRAY_SIZE(hostbuf); + osmo_multiaddr_ip_and_port_snprintf(buf, sizeof(buf), + &hostbuf[0][0], num_hostbuf, sizeof(hostbuf[0]), + portbuf); + } + } + vty_out(vty, "xUA server for %s/%s on %s is %s%s", + proto, get_value_string(ipproto_vals, xs->cfg.trans_proto), + buf, fd >= 0 ? "listening" : "inactive", VTY_NEWLINE); +} + +static int _show_cs7_xua(struct vty *vty, + enum osmo_ss7_asp_protocol proto, + int trans_proto, int local_port) +{ + const struct osmo_ss7_instance *inst; + + llist_for_each_entry(inst, &osmo_ss7_instances, list) { + struct osmo_xua_server *xs; + + llist_for_each_entry(xs, &inst->xua_servers, list) { + if (xs->cfg.proto != proto) + continue; + if (local_port >= 0 && xs->cfg.local.port != local_port) /* optional */ + continue; + if (trans_proto >= 0 && xs->cfg.trans_proto != trans_proto) /* optional */ + continue; + vty_dump_xua_server(vty, xs); + } + } + + return CMD_SUCCESS; +} + +#define SHOW_CS7_XUA_CMD \ + "show cs7 " XUA_VAR_STR +#define SHOW_CS7_XUA_CMD_HELP \ + SHOW_STR CS7_STR XUA_VAR_HELP_STR + +DEFUN(show_cs7_xua, show_cs7_xua_cmd, + SHOW_CS7_XUA_CMD " [<0-65534>]", + SHOW_CS7_XUA_CMD_HELP "Local Port Number\n") +{ + enum osmo_ss7_asp_protocol proto = parse_asp_proto(argv[0]); + int local_port = (argc > 1) ? atoi(argv[1]) : -1; + + return _show_cs7_xua(vty, proto, -1, local_port); +} + +DEFUN(show_cs7_xua_trans_proto, show_cs7_xua_trans_proto_cmd, + SHOW_CS7_XUA_CMD " " IPPROTO_VAR_STR " [<0-65534>]", + SHOW_CS7_XUA_CMD_HELP IPPROTO_VAR_HELP_STR "Local Port Number\n") +{ + enum osmo_ss7_asp_protocol proto = parse_asp_proto(argv[0]); + int trans_proto = parse_trans_proto(argv[1]); + int local_port = (argc > 2) ? atoi(argv[2]) : -1; + + return _show_cs7_xua(vty, proto, trans_proto, local_port); +} + +int ss7_vty_node_oxs_go_parent(struct vty *vty) +{ + struct osmo_xua_server *oxs = vty->index; + + /* If no local addr was set, or erased after _create(): */ + ss7_xua_server_set_default_local_hosts(oxs); + if (ss7_xua_server_bind(oxs) < 0) + vty_out(vty, "%% Unable to bind xUA server to IP(s)%s", VTY_NEWLINE); + vty->node = L_CS7_NODE; + vty->index = oxs->inst; + + return 0; +} + +void ss7_vty_init_show_oxs(void) +{ + install_lib_element_ve(&show_cs7_xua_cmd); + install_lib_element_ve(&show_cs7_xua_trans_proto_cmd); +} + +void ss7_vty_init_node_oxs(void) +{ + install_node(&xua_node, NULL); + install_lib_element(L_CS7_NODE, &cs7_xua_cmd); + install_lib_element(L_CS7_NODE, &no_cs7_xua_cmd); + install_lib_element(L_CS7_XUA_NODE, &xua_local_ip_cmd); + install_lib_element(L_CS7_XUA_NODE, &xua_no_local_ip_cmd); + install_lib_element(L_CS7_XUA_NODE, &xua_accept_dyn_asp_cmd); + install_lib_element(L_CS7_XUA_NODE, &xua_sctp_param_init_cmd); + install_lib_element(L_CS7_XUA_NODE, &xua_no_sctp_param_init_cmd); +}