aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2018-06-15 22:04:28 +0200
committerVadim Yanitskiy <axilirator@gmail.com>2018-07-29 23:17:59 +0700
commite6f695e0e6e591e4c6376accbc6dab87f2c6e385 (patch)
treecd1e918dc3398b3a04dcff3e2bce8027970fc211
parent35d23428502d205dd2876ad12941c7fd4160b099 (diff)
SS/USSD: implement basic USSE routing configuration for VTYfixeria/ussd
This change introduces basic Unstructured Supplementary Services processing Entity (USSE) configuration for VTY by analogy with ESME and SMPP. The configuration is done by allocating USSE nodes, so each one is bound to a particular GSUP-connection, and represents a single processing entity. Then, one or more USSD-code matching patterns (e.g. code, prefix, regexp) can be assigned to a particular USSE node, so all matched USSD-requests will be handled by this node. Among all other USSE nodes, which are basically External USSEs, there is a special one called IUSSE - internal USSD handler. It can be optionally configured to handle and answer simple requests, like *#100#. Also, it's possible to set the default USSE, which will handle all USSD-requests unmatched by other USSEs. Otherwise, unmatched requests are being rejected. Change-Id: I3cfd7cd401ea32b7e92f1124d129099d9f7dc6e6
-rw-r--r--src/Makefile.am2
-rw-r--r--src/hlr.c5
-rw-r--r--src/hlr.h7
-rw-r--r--src/hlr_ss_ussd.c129
-rw-r--r--src/hlr_ss_ussd.h51
-rw-r--r--src/hlr_vty.c293
-rw-r--r--src/hlr_vty.h2
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/test_nodes.vty38
-rw-r--r--tests/test_usse.vty246
10 files changed, 773 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ea5023d..646fd34 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,6 +30,7 @@ noinst_HEADERS = \
ctrl.h \
hlr_vty.h \
hlr_vty_subscr.h \
+ hlr_ss_ussd.h \
db_bootstrap.h \
$(NULL)
@@ -53,6 +54,7 @@ osmo_hlr_SOURCES = \
hlr_vty.c \
hlr_vty_subscr.c \
gsup_send.c \
+ hlr_ss_ussd.c \
$(NULL)
osmo_hlr_LDADD = \
diff --git a/src/hlr.c b/src/hlr.c
index 41566be..fb05973 100644
--- a/src/hlr.c
+++ b/src/hlr.c
@@ -42,6 +42,7 @@
#include "rand.h"
#include "luop.h"
#include "hlr_vty.h"
+#include "hlr_ss_ussd.h"
static struct hlr *g_hlr;
@@ -533,6 +534,7 @@ static void signal_hdlr(int signal)
case SIGINT:
LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
osmo_gsup_server_destroy(g_hlr->gs);
+ hlr_usse_clean_up(g_hlr);
db_close(g_hlr->dbc);
log_fini();
talloc_report_full(hlr_ctx, stderr);
@@ -569,6 +571,9 @@ int main(int argc, char **argv)
g_hlr = talloc_zero(hlr_ctx, struct hlr);
+ INIT_LLIST_HEAD(&g_hlr->usse_list);
+ g_hlr->usse_default = NULL;
+
rc = osmo_init_logging2(hlr_ctx, &hlr_log_info);
if (rc < 0) {
fprintf(stderr, "Error initializing logging\n");
diff --git a/src/hlr.h b/src/hlr.h
index 368a052..d9bdc87 100644
--- a/src/hlr.h
+++ b/src/hlr.h
@@ -23,6 +23,9 @@
#pragma once
#include <stdbool.h>
+#include <osmocom/core/linuxlist.h>
+
+struct hlr_usse;
struct hlr {
/* GSUP server pointer */
@@ -37,6 +40,10 @@ struct hlr {
/* Local bind addr */
char *gsup_bind_addr;
+
+ /* SS/USSD processing entities */
+ struct llist_head usse_list;
+ struct hlr_usse *usse_default;
};
struct hlr_subscriber;
diff --git a/src/hlr_ss_ussd.c b/src/hlr_ss_ussd.c
new file mode 100644
index 0000000..027b33f
--- /dev/null
+++ b/src/hlr_ss_ussd.c
@@ -0,0 +1,129 @@
+/* Supplementary Services signalling implementation */
+
+/* (C) 2018 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/linuxlist.h>
+
+#include "hlr.h"
+#include "hlr_ss_ussd.h"
+
+struct hlr_usse *hlr_usse_find(struct hlr *hlr, const char *name)
+{
+ struct hlr_usse *usse;
+
+ llist_for_each_entry(usse, &hlr->usse_list, list) {
+ if (!strcmp(usse->name, name))
+ return usse;
+ }
+
+ return NULL;
+}
+
+struct hlr_usse *hlr_usse_alloc(struct hlr *hlr, const char *name)
+{
+ struct hlr_usse *usse;
+
+ usse = hlr_usse_find(hlr, name);
+ if (usse)
+ return NULL;
+
+ usse = talloc(hlr, struct hlr_usse);
+ usse->name = talloc_strdup(usse, name);
+ usse->description = NULL;
+ usse->hlr = hlr;
+
+ INIT_LLIST_HEAD(&usse->patterns);
+ llist_add_tail(&usse->list, &hlr->usse_list);
+
+ return usse;
+}
+
+void hlr_usse_del(struct hlr_usse *usse)
+{
+ struct hlr_usse_pattern *pt, *_pt;
+
+ /* Release linked patterns */
+ llist_for_each_entry_safe(pt, _pt, &usse->patterns, list)
+ hlr_usse_pattern_del(pt);
+
+ /* Unlink from the HLR's USSE list */
+ llist_del(&usse->list);
+
+ /* Release memory */
+ talloc_free(usse);
+}
+
+struct hlr_usse_pattern *hlr_usse_pattern_find(struct hlr_usse *usse,
+ enum hlr_usse_pattern_type type, const char *pattern)
+{
+ struct hlr_usse_pattern *pt;
+
+ llist_for_each_entry(pt, &usse->patterns, list) {
+ if (pt->type != type)
+ continue;
+ if (strcmp(pt->pattern, pattern))
+ continue;
+
+ return pt;
+ }
+
+ return NULL;
+}
+
+struct hlr_usse_pattern *hlr_usse_pattern_add(struct hlr_usse *usse,
+ enum hlr_usse_pattern_type type, const char *pattern)
+{
+ struct hlr_usse_pattern *pt;
+
+ pt = hlr_usse_pattern_find(usse, type, pattern);
+ if (pt)
+ return NULL;
+
+ pt = talloc(usse, struct hlr_usse_pattern);
+ pt->pattern = talloc_strdup(pt, pattern);
+ pt->rsp_fmt = NULL;
+ pt->type = type;
+ pt->usse = usse;
+
+ llist_add_tail(&pt->list, &usse->patterns);
+ return pt;
+}
+
+void hlr_usse_pattern_del(struct hlr_usse_pattern *pt)
+{
+ /* Unlink from the USSE's list */
+ llist_del(&pt->list);
+
+ /* Release memory */
+ talloc_free(pt);
+}
+
+void hlr_usse_clean_up(struct hlr *hlr)
+{
+ struct hlr_usse *usse, *_usse;
+
+ llist_for_each_entry_safe(usse, _usse, &hlr->usse_list, list)
+ hlr_usse_del(usse);
+}
diff --git a/src/hlr_ss_ussd.h b/src/hlr_ss_ussd.h
new file mode 100644
index 0000000..68becd3
--- /dev/null
+++ b/src/hlr_ss_ussd.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/gsm/gsm0480.h>
+
+/* Represents a single USSE (either the internal, or an external) */
+struct hlr_usse {
+ /* list in the per-HLR list of USSEs */
+ struct llist_head list;
+ /* back-pointer to the HLR instance */
+ struct hlr *hlr;
+ /* human-readable description */
+ const char *description;
+ /* name (must match the IPA ID tag) */
+ const char *name;
+ /* list of USSD-code matching patterns */
+ struct llist_head patterns;
+};
+
+/* Matching pattern types sorted by priority */
+enum hlr_usse_pattern_type {
+ HLR_USSE_PATTERN_CODE = 0, /* higher priority */
+ HLR_USSE_PATTERN_REGEXP,
+ HLR_USSE_PATTERN_PREFIX,
+};
+
+/* Represents a USSD-code matching pattern */
+struct hlr_usse_pattern {
+ /* link to the parent USSE */
+ struct llist_head list;
+ /* back-pointer to the parent USSE */
+ struct hlr_usse *usse;
+ /* Patter type, e.g. code, regexp or prefix */
+ enum hlr_usse_pattern_type type;
+ /* Mathing pattern, e.g. '*110*' for prefix */
+ const char *pattern;
+ /* Response format string, e.g. 'Your MSISDN is %m' */
+ char *rsp_fmt;
+};
+
+struct hlr_usse *hlr_usse_find(struct hlr *hlr, const char *name);
+struct hlr_usse *hlr_usse_alloc(struct hlr *hlr, const char *name);
+void hlr_usse_del(struct hlr_usse *usse);
+
+struct hlr_usse_pattern *hlr_usse_pattern_find(struct hlr_usse *usse,
+ enum hlr_usse_pattern_type type, const char *pattern);
+struct hlr_usse_pattern *hlr_usse_pattern_add(struct hlr_usse *usse,
+ enum hlr_usse_pattern_type type, const char *pattern);
+void hlr_usse_pattern_del(struct hlr_usse_pattern *pt);
+
+void hlr_usse_clean_up(struct hlr *hlr);
diff --git a/src/hlr_vty.c b/src/hlr_vty.c
index ecc2f5c..4e8e60b 100644
--- a/src/hlr_vty.c
+++ b/src/hlr_vty.c
@@ -3,6 +3,7 @@
/* (C) 2016 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
* (C) 2018 Harald Welte <laforge@gnumonks.org>
+ * (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
*
* All Rights Reserved
*
@@ -29,6 +30,7 @@
#include <osmocom/abis/ipa.h>
#include "hlr_vty.h"
+#include "hlr_ss_ussd.h"
#include "hlr_vty_subscr.h"
#include "gsup_server.h"
@@ -94,7 +96,7 @@ static void show_one_conn(struct vty *vty, const struct osmo_gsup_conn *conn)
DEFUN(show_gsup_conn, show_gsup_conn_cmd,
"show gsup-connections",
- SHOW_STR "GSUP Connections from VLRs, SGSNs, EUSEs\n")
+ SHOW_STR "GSUP Connections from VLRs, SGSNs, EUSSEs\n")
{
struct osmo_gsup_server *gs = g_hlr->gs;
struct osmo_gsup_conn *conn;
@@ -119,12 +121,290 @@ DEFUN(cfg_hlr_gsup_bind_ip,
return CMD_SUCCESS;
}
+/***********************************************************************
+ * Unstructured Supplementary Services processing Entity configuration
+ ***********************************************************************/
+
+static struct cmd_node iusse_node = {
+ IUSSE_NODE,
+ "%s(config-hlr-iusse)# ",
+ 1,
+};
+
+static struct cmd_node eusse_node = {
+ EUSSE_NODE,
+ "%s(config-hlr-eusse)# ",
+ 1,
+};
+
+#define VTY_USSE_NAME_DESC \
+ "Internal USSD processing Entity\n" \
+ "Alphanumeric name of an External USSE\n"
+
+DEFUN(cfg_usse, cfg_usse_cmd,
+ "usse (internal|NAME)",
+ "Configure a particular USSE (USSD processing Entity)\n"
+ VTY_USSE_NAME_DESC)
+{
+ const char *name = argv[0];
+ struct hlr_usse *usse;
+
+ usse = hlr_usse_find(g_hlr, name);
+ if (!usse) {
+ usse = hlr_usse_alloc(g_hlr, name);
+ if (!usse)
+ return CMD_WARNING;
+ }
+
+ vty->index_sub = &usse->description;
+ vty->index = usse;
+
+ /* IUSSE or EUSSE? */
+ if (!strcmp(usse->name, "internal"))
+ vty->node = IUSSE_NODE;
+ else
+ vty->node = EUSSE_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_usse, cfg_no_usse_cmd,
+ "no usse (internal|NAME)",
+ NO_STR "Remove a particular USSE (USSD processing Entity)\n"
+ VTY_USSE_NAME_DESC)
+{
+ const char *name = argv[0];
+ struct hlr_usse *usse;
+
+ usse = hlr_usse_find(g_hlr, name);
+ if (!usse) {
+ vty_out(vty, "%% Cannot remove non-existent "
+ "USSE '%s'%s", name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (g_hlr->usse_default == usse) {
+ vty_out(vty, "%% Cannot remove USSE '%s', "
+ "it is the default route%s", name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ hlr_usse_del(usse);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_usse_default, cfg_usse_default_cmd,
+ "usse-default (internal|NAME)",
+ "Default USSD processing Entity\n"
+ VTY_USSE_NAME_DESC)
+{
+ const char *name = argv[0];
+ struct hlr_usse *usse;
+
+ usse = hlr_usse_find(g_hlr, name);
+ if (!usse) {
+ vty_out(vty, "%% Cannot find USSE '%s'%s", name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (g_hlr->usse_default == usse) {
+ vty_out(vty, "%% USSE '%s' is already "
+ "used by default%s", usse->name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ g_hlr->usse_default = usse;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_usse_default, cfg_no_usse_default_cmd,
+ "no usse-default",
+ NO_STR "No default USSD processing Entity "
+ "(drop all unmatched requests)\n")
+{
+ g_hlr->usse_default = NULL;
+ return CMD_SUCCESS;
+}
+
+#define VTY_USSE_PATTERN_CMD \
+ "pattern (code|regexp|prefix) PATTERN"
+
+#define VTY_USSE_PATTERN_DESC \
+ "Match USSD-request codes by exact value (e.g. '*#100#')\n" \
+ "Match USSD-request codes by regular expression (e.g. '^\\*[5-7]+'\\*)\n" \
+ "Match USSD-request codes by prefix (e.g. '*#' or '*110*')\n" \
+ "Matching pattern\n"
+
+static int _cfg_usse_pattern(struct vty *vty, int argc, const char **argv)
+{
+ struct hlr_usse *usse = vty->index;
+ enum hlr_usse_pattern_type type;
+ struct hlr_usse_pattern *pt;
+ bool is_iusse;
+
+ /* Determine which kind of matching pattern required */
+ switch (argv[0][0]) {
+ case 'c':
+ type = HLR_USSE_PATTERN_CODE;
+ break;
+ case 'r':
+ type = HLR_USSE_PATTERN_REGEXP;
+ break;
+ case 'p':
+ type = HLR_USSE_PATTERN_PREFIX;
+ break;
+ default:
+ /* Shouldn't happen, but let's make sure */
+ return CMD_WARNING;
+ }
+
+ /* IUSSE or EUSSE? */
+ is_iusse = !strcmp(usse->name, "internal");
+
+ /* Attempt to find pattern */
+ pt = hlr_usse_pattern_find(usse, type, argv[1]);
+ if (pt && !is_iusse) {
+ /* Response modification is only actual for IUSSE */
+ vty_out(vty, "%% Pattern already exists!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Allocate if required */
+ if (!pt) {
+ pt = hlr_usse_pattern_add(usse, type, argv[1]);
+ if (!pt) {
+ vty_out(vty, "%% Cannot add pattern '%s' of type '%s'%s",
+ argv[1], argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ /* Response string for IUSSE */
+ if (is_iusse) {
+ if (pt->rsp_fmt)
+ talloc_free(pt->rsp_fmt);
+ pt->rsp_fmt = talloc_strdup(pt, argv_concat(argv, argc, 2));
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_iusse_pattern, cfg_iusse_pattern_cmd,
+ VTY_USSE_PATTERN_CMD " response .RESPONSE",
+ "Add or modify a USSD-code matching pattern\n"
+ VTY_USSE_PATTERN_DESC
+ "Response format string (e.g. 'Your MSISDN is %m')\n")
+{
+ return _cfg_usse_pattern(vty, argc, argv);
+}
+
+DEFUN(cfg_eusse_pattern, cfg_eusse_pattern_cmd,
+ VTY_USSE_PATTERN_CMD,
+ "Add a new USSD-code matching pattern\n"
+ VTY_USSE_PATTERN_DESC)
+{
+ return _cfg_usse_pattern(vty, argc, argv);
+}
+
+DEFUN(cfg_usse_no_pattern, cfg_usse_no_pattern_cmd,
+ "no " VTY_USSE_PATTERN_CMD,
+ NO_STR "Remove an existing USSD-code matching pattern\n"
+ VTY_USSE_PATTERN_DESC)
+{
+ struct hlr_usse *usse = vty->index;
+ enum hlr_usse_pattern_type type;
+ struct hlr_usse_pattern *pt;
+
+ /* Determine which kind of matching pattern required */
+ switch (argv[0][0]) {
+ case 'c':
+ type = HLR_USSE_PATTERN_CODE;
+ break;
+ case 'r':
+ type = HLR_USSE_PATTERN_REGEXP;
+ break;
+ case 'p':
+ type = HLR_USSE_PATTERN_PREFIX;
+ break;
+ default:
+ /* Shouldn't happen, but let's make sure */
+ return CMD_WARNING;
+ }
+
+ pt = hlr_usse_pattern_find(usse, type, argv[1]);
+ if (!pt) {
+ vty_out(vty, "%% Cannot remove non-existent pattern '%s' "
+ "of type '%s'%s", argv[1], argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ hlr_usse_pattern_del(pt);
+ return CMD_SUCCESS;
+}
+
+static void dump_one_usse(struct vty *vty, struct hlr_usse *usse)
+{
+ struct hlr_usse_pattern *pt;
+ const char *pt_type;
+
+ vty_out(vty, " usse %s%s", usse->name, VTY_NEWLINE);
+ // FIXME: what about usse->description?
+
+ llist_for_each_entry(pt, &usse->patterns, list) {
+ /* Stringify pattern type */
+ switch (pt->type) {
+ case HLR_USSE_PATTERN_CODE:
+ pt_type = "code";
+ break;
+ case HLR_USSE_PATTERN_REGEXP:
+ pt_type = "regexp";
+ break;
+ case HLR_USSE_PATTERN_PREFIX:
+ pt_type = "prefix";
+ break;
+ default:
+ /* Should not happen */
+ OSMO_ASSERT(0);
+ }
+
+ if (pt->rsp_fmt != NULL)
+ vty_out(vty, " pattern %s %s response %s%s", pt_type,
+ pt->pattern, pt->rsp_fmt, VTY_NEWLINE);
+ else
+ vty_out(vty, " pattern %s %s%s", pt_type,
+ pt->pattern, VTY_NEWLINE);
+ }
+}
+
+static int config_write_usse(struct vty *vty)
+{
+ struct hlr_usse *usse;
+
+ if (g_hlr->usse_default == NULL)
+ vty_out(vty, " no usse-default%s", VTY_NEWLINE);
+ else
+ vty_out(vty, " usse-default %s%s",
+ g_hlr->usse_default->name, VTY_NEWLINE);
+
+ llist_for_each_entry(usse, &g_hlr->usse_list, list)
+ dump_one_usse(vty, usse);
+
+ return CMD_SUCCESS;
+}
+
+/***********************************************************************
+ * Common Code
+ ***********************************************************************/
+
int hlr_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
case GSUP_NODE:
+ case IUSSE_NODE:
+ case EUSSE_NODE:
vty->node = HLR_NODE;
vty->index = NULL;
+ vty->index_sub = NULL;
break;
default:
case HLR_NODE:
@@ -169,5 +449,16 @@ void hlr_vty_init(struct hlr *hlr, const struct log_info *cat)
install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
+ install_element(HLR_NODE, &cfg_usse_cmd);
+ install_element(HLR_NODE, &cfg_no_usse_cmd);
+ install_element(HLR_NODE, &cfg_usse_default_cmd);
+ install_element(HLR_NODE, &cfg_no_usse_default_cmd);
+ install_node(&eusse_node, config_write_usse);
+ install_element(EUSSE_NODE, &cfg_eusse_pattern_cmd);
+ install_element(EUSSE_NODE, &cfg_usse_no_pattern_cmd);
+ install_node(&iusse_node, NULL);
+ install_element(IUSSE_NODE, &cfg_iusse_pattern_cmd);
+ install_element(IUSSE_NODE, &cfg_usse_no_pattern_cmd);
+
hlr_vty_subscriber_init(hlr);
}
diff --git a/src/hlr_vty.h b/src/hlr_vty.h
index cd2ff73..411077a 100644
--- a/src/hlr_vty.h
+++ b/src/hlr_vty.h
@@ -30,6 +30,8 @@
enum hlr_vty_node {
HLR_NODE = _LAST_OSMOVTY_NODE + 1,
GSUP_NODE,
+ IUSSE_NODE,
+ EUSSE_NODE,
};
int hlr_vty_is_config_node(struct vty *vty, int node);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 58679ed..c931efa 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -31,6 +31,7 @@ EXTRA_DIST = \
test_subscriber.vty \
test_subscriber.sql \
test_subscriber.ctrl \
+ test_sse.vty \
$(NULL)
TESTSUITE = $(srcdir)/testsuite
diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty
index 21809c8..6969626 100644
--- a/tests/test_nodes.vty
+++ b/tests/test_nodes.vty
@@ -70,6 +70,10 @@ OsmoHLR(config-hlr)# list
exit
end
gsup
+ usse (internal|NAME)
+ no usse (internal|NAME)
+ usse-default (internal|NAME)
+ no usse-default
OsmoHLR(config-hlr)# gsup
OsmoHLR(config-hlr-gsup)# list
@@ -85,6 +89,39 @@ OsmoHLR(config-hlr-gsup)# list
bind ip A.B.C.D
OsmoHLR(config-hlr-gsup)# exit
+OsmoHLR(config-hlr)# usse test
+OsmoHLR(config-hlr-eusse)# list
+ help
+ list
+ write terminal
+ write file
+ write memory
+ write
+ show running-config
+ exit
+ end
+ pattern (code|regexp|prefix) PATTERN
+ no pattern (code|regexp|prefix) PATTERN
+
+OsmoHLR(config-hlr-eusse)# exit
+OsmoHLR(config-hlr)# usse internal
+OsmoHLR(config-hlr-iusse)# list
+ help
+ list
+ write terminal
+ write file
+ write memory
+ write
+ show running-config
+ exit
+ end
+ pattern (code|regexp|prefix) PATTERN response .RESPONSE
+ no pattern (code|regexp|prefix) PATTERN
+
+OsmoHLR(config-hlr-iusse)# exit
+OsmoHLR(config-hlr)# no usse test
+OsmoHLR(config-hlr)# no usse internal
+
OsmoHLR(config-hlr)# exit
OsmoHLR(config)# exit
OsmoHLR# configure terminal
@@ -119,4 +156,5 @@ ctrl
hlr
gsup
bind ip 127.0.0.1
+ no usse-default
end
diff --git a/tests/test_usse.vty b/tests/test_usse.vty
new file mode 100644
index 0000000..498e8d5
--- /dev/null
+++ b/tests/test_usse.vty
@@ -0,0 +1,246 @@
+OsmoHLR> enable
+OsmoHLR# configure terminal
+OsmoHLR(config)# show running-config
+
+Current configuration:
+!
+!
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category 1
+ logging print extended-timestamp 1
+ logging print file 1
+ logging level all debug
+ logging level main notice
+ logging level db notice
+ logging level auc notice
+...
+!
+line vty
+ no login
+!
+ctrl
+ bind 127.0.0.1
+hlr
+ gsup
+ bind ip 127.0.0.1
+ no usse-default
+end
+
+OsmoHLR(config)# hlr
+OsmoHLR(config-hlr)# no usse test
+% Cannot remove non-existent USSE 'test'
+
+OsmoHLR(config-hlr)# usse gw1
+OsmoHLR(config-hlr-eusse)# no pattern prefix *111*
+% Cannot remove non-existent pattern '*111*' of type 'prefix'
+
+OsmoHLR(config-hlr-eusse)# pattern prefix *110*
+OsmoHLR(config-hlr-eusse)# pattern prefix *181*
+OsmoHLR(config-hlr-eusse)# pattern prefix *110*
+% Pattern already exists!
+
+OsmoHLR(config-hlr-eusse)# pattern code *110*
+OsmoHLR(config-hlr-eusse)# pattern code *888#
+OsmoHLR(config-hlr-eusse)# pattern code *110*
+% Pattern already exists!
+
+OsmoHLR(config-hlr-eusse)# pattern regexp ^*[1-3]{3}*[0-9]+#$
+OsmoHLR(config-hlr-eusse)# pattern regexp ^*[5-7]{3}*[0-9]+#$
+OsmoHLR(config-hlr-eusse)# pattern regexp ^*[1-3]{3}*[0-9]+#$
+% Pattern already exists!
+
+OsmoHLR(config-hlr-eusse)# show running-config
+
+Current configuration:
+!
+!
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category 1
+ logging print extended-timestamp 1
+ logging print file 1
+ logging level all debug
+ logging level main notice
+ logging level db notice
+ logging level auc notice
+...
+!
+line vty
+ no login
+!
+ctrl
+ bind 127.0.0.1
+hlr
+ gsup
+ bind ip 127.0.0.1
+ no usse-default
+ usse gw1
+ pattern prefix *110*
+ pattern prefix *181*
+ pattern code *110*
+ pattern code *888#
+ pattern regexp ^*[1-3]{3}*[0-9]+#$
+ pattern regexp ^*[5-7]{3}*[0-9]+#$
+end
+
+OsmoHLR(config-hlr-eusse)# no pattern prefix *181*
+OsmoHLR(config-hlr-eusse)# no pattern code *110*
+OsmoHLR(config-hlr-eusse)# no pattern regexp ^*[1-3]{3}*[0-9]+#$
+OsmoHLR(config-hlr-eusse)# show running-config
+
+Current configuration:
+!
+!
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category 1
+ logging print extended-timestamp 1
+ logging print file 1
+ logging level all debug
+ logging level main notice
+ logging level db notice
+ logging level auc notice
+...
+!
+line vty
+ no login
+!
+ctrl
+ bind 127.0.0.1
+hlr
+ gsup
+ bind ip 127.0.0.1
+ no usse-default
+ usse gw1
+ pattern prefix *110*
+ pattern code *888#
+ pattern regexp ^*[5-7]{3}*[0-9]+#$
+end
+
+OsmoHLR(config-hlr-eusse)# exit
+OsmoHLR(config-hlr)# usse internal
+
+OsmoHLR(config-hlr-iusse)# pattern code *#100#
+% Command incomplete.
+
+OsmoHLR(config-hlr-iusse)# pattern code *#100# response Your extension is %m
+OsmoHLR(config-hlr-iusse)# pattern regexp ^*999*[0-3]+#$ response Mahlzeit!
+
+OsmoHLR(config-hlr-iusse)# no pattern code *888#
+% Cannot remove non-existent pattern '*888#' of type 'code'
+
+OsmoHLR(config-hlr-iusse)# show running-config
+
+Current configuration:
+!
+!
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category 1
+ logging print extended-timestamp 1
+ logging print file 1
+ logging level all debug
+ logging level main notice
+ logging level db notice
+ logging level auc notice
+...
+!
+line vty
+ no login
+!
+ctrl
+ bind 127.0.0.1
+hlr
+ gsup
+ bind ip 127.0.0.1
+ no usse-default
+ usse gw1
+ pattern prefix *110*
+ pattern code *888#
+ pattern regexp ^*[5-7]{3}*[0-9]+#$
+ usse internal
+ pattern code *#100# response Your extension is %m
+ pattern regexp ^*999*[0-3]+#$ response Mahlzeit!
+end
+
+OsmoHLR(config-hlr-iusse)# exit
+OsmoHLR(config-hlr)# usse-default test
+% Cannot find USSE 'test'
+
+OsmoHLR(config-hlr)# usse-default internal
+OsmoHLR(config-hlr)# usse-default gw1
+OsmoHLR(config-hlr)# show running-config
+
+Current configuration:
+!
+!
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category 1
+ logging print extended-timestamp 1
+ logging print file 1
+ logging level all debug
+ logging level main notice
+ logging level db notice
+ logging level auc notice
+...
+!
+line vty
+ no login
+!
+ctrl
+ bind 127.0.0.1
+hlr
+ gsup
+ bind ip 127.0.0.1
+ usse-default gw1
+ usse gw1
+ pattern prefix *110*
+ pattern code *888#
+ pattern regexp ^*[5-7]{3}*[0-9]+#$
+ usse internal
+ pattern code *#100# response Your extension is %m
+ pattern regexp ^*999*[0-3]+#$ response Mahlzeit!
+end
+
+OsmoHLR(config-hlr)# no usse gw1
+% Cannot remove USSE 'gw1', it is the default route
+
+OsmoHLR(config-hlr)# no usse-default
+OsmoHLR(config-hlr)# no usse gw1
+OsmoHLR(config-hlr)# show running-config
+
+Current configuration:
+!
+!
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category 1
+ logging print extended-timestamp 1
+ logging print file 1
+ logging level all debug
+ logging level main notice
+ logging level db notice
+ logging level auc notice
+...
+!
+line vty
+ no login
+!
+ctrl
+ bind 127.0.0.1
+hlr
+ gsup
+ bind ip 127.0.0.1
+ no usse-default
+ usse internal
+ pattern code *#100# response Your extension is %m
+ pattern regexp ^*999*[0-3]+#$ response Mahlzeit!
+end