aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2017-10-06 02:59:54 +0200
committerNeels Hofmeyr <neels@hofmeyr.de>2017-10-17 00:59:00 +0200
commit183e7009afc8577f0d89c99f92a5720697040494 (patch)
tree3c774fa05e32f09d3805845751b48d9554177153
parentb6837e36a34a1e9e7fafea822516e61285e3c09c (diff)
implement subscriber vty interface, tests
Implement VTY commands for subscriber manipulation: - create / delete subscriber - modify MSISDN - add/edit/remove 2G and 3G authentication data - show by IMSI, MSISDN or DB ID. (enable/disable CS/PS and purge/unpurge to follow later.) Implement VTY unit tests for the new commands using new osmo_verify_transcript_vty.py from osmo-python-tests. Depends: libosmocore I1e94f5b0717b947d2a7a7d36bacdf04a75cb3522 osmo-python-tests Id47331009910e651372b9c9c76e12f2e8964cc2c Change-Id: I42b3b70a0439a8f2e4964d7cc31e593c1f0d7537
-rw-r--r--src/Makefile.am2
-rw-r--r--src/hlr_vty.c3
-rw-r--r--src/hlr_vty_subscr.c484
-rw-r--r--src/hlr_vty_subscr.h3
-rw-r--r--tests/Makefile.am21
-rw-r--r--tests/test_subscriber.vty348
6 files changed, 859 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index b410ff3..fc7c653 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,6 +23,7 @@ noinst_HEADERS = \
rand.h \
ctrl.h \
hlr_vty.h \
+ hlr_vty_subscr.h \
$(NULL)
bin_PROGRAMS = \
@@ -46,6 +47,7 @@ osmo_hlr_SOURCES = \
logging.c \
rand_urandom.c \
hlr_vty.c \
+ hlr_vty_subscr.c \
$(NULL)
osmo_hlr_LDADD = \
diff --git a/src/hlr_vty.c b/src/hlr_vty.c
index 946117e..a5eb26f 100644
--- a/src/hlr_vty.c
+++ b/src/hlr_vty.c
@@ -26,6 +26,7 @@
#include <osmocom/vty/logging.h>
#include "hlr_vty.h"
+#include "hlr_vty_subscr.h"
static struct hlr *g_hlr = NULL;
@@ -135,4 +136,6 @@ void hlr_vty_init(struct hlr *hlr, const struct log_info *cat)
install_default(GSUP_NODE);
install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
+
+ hlr_vty_subscriber_init(hlr);
}
diff --git a/src/hlr_vty_subscr.c b/src/hlr_vty_subscr.c
new file mode 100644
index 0000000..5704922
--- /dev/null
+++ b/src/hlr_vty_subscr.c
@@ -0,0 +1,484 @@
+/* OsmoHLR subscriber management VTY implementation */
+/* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * 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 <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/gsm/gsm23003.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/core/utils.h>
+
+#include "hlr.h"
+#include "db.h"
+
+struct vty;
+
+#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
+
+static struct hlr *g_hlr = NULL;
+
+static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
+{
+ int rc;
+ struct osmo_sub_auth_data aud2g;
+ struct osmo_sub_auth_data aud3g;
+
+ vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
+
+ vty_out(vty, " IMSI: %s%s", subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
+ vty_out(vty, " MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
+ if (*subscr->vlr_number)
+ vty_out(vty, " VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
+ if (*subscr->sgsn_number)
+ vty_out(vty, " SGSN number: %s%s", subscr->sgsn_number, VTY_NEWLINE);
+ if (*subscr->sgsn_address)
+ vty_out(vty, " SGSN address: %s%s", subscr->sgsn_address, VTY_NEWLINE);
+ if (subscr->periodic_lu_timer)
+ vty_out(vty, " Periodic LU timer: %u%s", subscr->periodic_lu_timer, VTY_NEWLINE);
+ if (subscr->periodic_rau_tau_timer)
+ vty_out(vty, " Periodic RAU/TAU timer: %u%s", subscr->periodic_rau_tau_timer, VTY_NEWLINE);
+ if (subscr->lmsi)
+ vty_out(vty, " LMSI: %x%s", subscr->lmsi, VTY_NEWLINE);
+ if (!subscr->nam_cs)
+ vty_out(vty, " CS disabled%s", VTY_NEWLINE);
+ if (subscr->ms_purged_cs)
+ vty_out(vty, " CS purged%s", VTY_NEWLINE);
+ if (!subscr->nam_ps)
+ vty_out(vty, " PS disabled%s", VTY_NEWLINE);
+ if (subscr->ms_purged_ps)
+ vty_out(vty, " PS purged%s", VTY_NEWLINE);
+
+ if (!*subscr->imsi)
+ return;
+
+ OSMO_ASSERT(g_hlr);
+ rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL);
+
+ if (rc) {
+ if (rc == -ENOENT) {
+ aud2g.algo = OSMO_AUTH_ALG_NONE;
+ aud3g.algo = OSMO_AUTH_ALG_NONE;
+ } else {
+ vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
+ return;
+ }
+ }
+
+ if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
+ vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE);
+ aud2g = (struct osmo_sub_auth_data){};
+ }
+
+ if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) {
+ vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE);
+ aud3g = (struct osmo_sub_auth_data){};
+ }
+
+ if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) {
+ vty_out(vty, " 2G auth: %s%s",
+ osmo_auth_alg_name(aud2g.algo), VTY_NEWLINE);
+ vty_out(vty, " KI=%s%s",
+ hexdump_buf(aud2g.u.gsm.ki), VTY_NEWLINE);
+ }
+
+ if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) {
+ vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE);
+ vty_out(vty, " K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
+ vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
+ hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE);
+ vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen);
+ if (aud3g.u.umts.sqn)
+ vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn);
+ vty_out(vty, VTY_NEWLINE);
+ }
+}
+
+static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
+{
+ int rc = -1;
+ if (strcmp(type, "imsi") == 0)
+ rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
+ else if (strcmp(type, "msisdn") == 0)
+ rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
+ else if (strcmp(type, "id") == 0)
+ rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
+ if (rc)
+ vty_out(vty, "%% No subscriber for %s = '%s'%s",
+ type, id, VTY_NEWLINE);
+ return rc;
+}
+
+#define SUBSCR_CMD "subscriber "
+#define SUBSCR_CMD_HELP "Subscriber management commands\n"
+
+#define SUBSCR_ID "(imsi|msisdn|id) IDENT "
+#define SUBSCR_ID_HELP \
+ "Identify subscriber by IMSI\n" \
+ "Identify subscriber by MSISDN (phone number)\n" \
+ "Identify subscriber by database ID\n" \
+ "IMSI/MSISDN/ID of the subscriber\n"
+
+#define SUBSCR SUBSCR_CMD SUBSCR_ID
+#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
+
+#define SUBSCR_UPDATE SUBSCR "update "
+#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
+
+DEFUN(subscriber_show,
+ subscriber_show_cmd,
+ SUBSCR "show",
+ SUBSCR_HELP "Show subscriber information\n")
+{
+ struct hlr_subscriber subscr;
+ const char *id_type = argv[0];
+ const char *id = argv[1];
+
+ if (get_subscr_by_argv(vty, id_type, id, &subscr))
+ return CMD_WARNING;
+
+ subscr_dump_full_vty(vty, &subscr);
+ return CMD_SUCCESS;
+}
+
+DEFUN(subscriber_create,
+ subscriber_create_cmd,
+ SUBSCR_CMD "imsi IDENT create",
+ SUBSCR_CMD_HELP
+ "Create subscriber by IMSI\n"
+ "IMSI/MSISDN/ID of the subscriber\n")
+{
+ int rc;
+ struct hlr_subscriber subscr;
+ const char *imsi = argv[0];
+
+ if (!osmo_imsi_str_valid(imsi)) {
+ vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ rc = db_subscr_create(g_hlr->dbc, imsi);
+
+ if (rc) {
+ if (rc == -EEXIST)
+ vty_out(vty, "%% Subscriber already exists for IMSI = %s%s",
+ imsi, VTY_NEWLINE);
+ else
+ vty_out(vty, "%% Error (rc=%d): cannot create subscriber for IMSI = %s%s",
+ rc, imsi, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ rc = db_subscr_get_by_imsi(g_hlr->dbc, imsi, &subscr);
+ vty_out(vty, "%% Created subscriber %s%s", imsi, VTY_NEWLINE);
+
+ subscr_dump_full_vty(vty, &subscr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(subscriber_delete,
+ subscriber_delete_cmd,
+ SUBSCR "delete",
+ SUBSCR_HELP "Delete subscriber from database\n")
+{
+ struct hlr_subscriber subscr;
+ int rc;
+ const char *id_type = argv[0];
+ const char *id = argv[1];
+
+ /* Find out the IMSI regardless of which way the caller decided to
+ * identify the subscriber by. */
+ if (get_subscr_by_argv(vty, id_type, id, &subscr))
+ return CMD_WARNING;
+
+ rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
+ if (rc) {
+ vty_out(vty, "%% Error: Failed to remove subscriber for IMSI '%s'%s",
+ subscr.imsi, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "%% Deleted subscriber for IMSI '%s'%s", subscr.imsi, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+DEFUN(subscriber_msisdn,
+ subscriber_msisdn_cmd,
+ SUBSCR_UPDATE "msisdn MSISDN",
+ SUBSCR_UPDATE_HELP
+ "Set MSISDN (phone number) of the subscriber\n"
+ "New MSISDN (phone number)\n")
+{
+ struct hlr_subscriber subscr;
+ const char *id_type = argv[0];
+ const char *id = argv[1];
+ const char *msisdn = argv[2];
+
+ if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
+ vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
+ sizeof(subscr.msisdn)-1, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!osmo_msisdn_str_valid(msisdn)) {
+ vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (get_subscr_by_argv(vty, id_type, id, &subscr))
+ return CMD_WARNING;
+
+ if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
+ vty_out(vty, "%% Error: cannot update MSISDN for subscriber IMSI='%s'%s",
+ subscr.imsi, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
+ subscr.imsi, msisdn, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+static bool is_hexkey_valid(struct vty *vty, const char *label,
+ const char *hex_str, int minlen, int maxlen)
+{
+ if (osmo_is_hexstr(hex_str, minlen * 2, maxlen * 2, true))
+ return true;
+ vty_out(vty, "%% Invalid value for %s: '%s'%s", label, hex_str, VTY_NEWLINE);
+ return false;
+}
+
+#define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)"
+#define AUTH_ALG_TYPES_2G_HELP \
+ "Use COMP128v1 algorithm\n" \
+ "Use COMP128v2 algorithm\n" \
+ "Use COMP128v3 algorithm\n" \
+ "Use XOR algorithm\n"
+
+#define AUTH_ALG_TYPES_3G "milenage"
+#define AUTH_ALG_TYPES_3G_HELP \
+ "Use Milenage algorithm\n"
+
+#define A38_XOR_MIN_KEY_LEN 12
+#define A38_XOR_MAX_KEY_LEN 16
+#define A38_COMP128_KEY_LEN 16
+
+#define MILENAGE_KEY_LEN 16
+
+static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
+ int *minlen, int *maxlen)
+{
+ if (!strcasecmp(alg_str, "none")) {
+ *algo = OSMO_AUTH_ALG_NONE;
+ *minlen = *maxlen = 0;
+ } else if (!strcasecmp(alg_str, "comp128v1")) {
+ *algo = OSMO_AUTH_ALG_COMP128v1;
+ *minlen = *maxlen = A38_COMP128_KEY_LEN;
+ } else if (!strcasecmp(alg_str, "comp128v2")) {
+ *algo = OSMO_AUTH_ALG_COMP128v2;
+ *minlen = *maxlen = A38_COMP128_KEY_LEN;
+ } else if (!strcasecmp(alg_str, "comp128v3")) {
+ *algo = OSMO_AUTH_ALG_COMP128v3;
+ *minlen = *maxlen = A38_COMP128_KEY_LEN;
+ } else if (!strcasecmp(alg_str, "xor")) {
+ *algo = OSMO_AUTH_ALG_XOR;
+ *minlen = A38_XOR_MIN_KEY_LEN;
+ *maxlen = A38_XOR_MAX_KEY_LEN;
+ } else if (!strcasecmp(alg_str, "milenage")) {
+ *algo = OSMO_AUTH_ALG_MILENAGE;
+ *minlen = *maxlen = MILENAGE_KEY_LEN;
+ } else
+ return false;
+ return true;
+}
+
+DEFUN(subscriber_no_aud2g,
+ subscriber_no_aud2g_cmd,
+ SUBSCR_UPDATE "aud2g none",
+ SUBSCR_UPDATE_HELP
+ "Set 2G authentication data\n"
+ "Delete 2G authentication data\n")
+{
+ struct hlr_subscriber subscr;
+ int rc;
+ const char *id_type = argv[0];
+ const char *id = argv[1];
+ struct sub_auth_data_str aud = {
+ .type = OSMO_AUTH_TYPE_GSM,
+ .algo = OSMO_AUTH_ALG_NONE,
+ };
+
+ if (get_subscr_by_argv(vty, id_type, id, &subscr))
+ return CMD_WARNING;
+
+ rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
+
+ if (rc) {
+ vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s",
+ subscr.imsi, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(subscriber_aud2g,
+ subscriber_aud2g_cmd,
+ SUBSCR_UPDATE "aud2g " AUTH_ALG_TYPES_2G " ki KI",
+ SUBSCR_UPDATE_HELP
+ "Set 2G authentication data\n"
+ AUTH_ALG_TYPES_2G_HELP
+ "Set Ki Encryption Key\n" "Ki as 32 hexadecimal characters\n")
+{
+ struct hlr_subscriber subscr;
+ int rc;
+ int minlen = 0;
+ int maxlen = 0;
+ const char *id_type = argv[0];
+ const char *id = argv[1];
+ const char *alg_type = argv[2];
+ const char *ki = argv[3];
+ struct sub_auth_data_str aud2g = {
+ .type = OSMO_AUTH_TYPE_GSM,
+ .u.gsm.ki = ki,
+ };
+
+ if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) {
+ vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!is_hexkey_valid(vty, "KI", aud2g.u.gsm.ki, minlen, maxlen))
+ return CMD_WARNING;
+
+ if (get_subscr_by_argv(vty, id_type, id, &subscr))
+ return CMD_WARNING;
+
+ rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g);
+
+ if (rc) {
+ vty_out(vty, "%% Error: cannot set 2G auth data for IMSI='%s'%s",
+ subscr.imsi, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(subscriber_no_aud3g,
+ subscriber_no_aud3g_cmd,
+ SUBSCR_UPDATE "aud3g none",
+ SUBSCR_UPDATE_HELP
+ "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
+ "Delete 3G authentication data\n")
+{
+ struct hlr_subscriber subscr;
+ int rc;
+ const char *id_type = argv[0];
+ const char *id = argv[1];
+ struct sub_auth_data_str aud = {
+ .type = OSMO_AUTH_TYPE_UMTS,
+ .algo = OSMO_AUTH_ALG_NONE,
+ };
+
+ if (get_subscr_by_argv(vty, id_type, id, &subscr))
+ return CMD_WARNING;
+
+ rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
+
+ if (rc) {
+ vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s",
+ subscr.imsi, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(subscriber_aud3g,
+ subscriber_aud3g_cmd,
+ SUBSCR_UPDATE "aud3g " AUTH_ALG_TYPES_3G
+ " k K"
+ " (op|opc) OP_C"
+ " [ind-bitlen] [<0-28>]",
+ SUBSCR_UPDATE_HELP
+ "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
+ AUTH_ALG_TYPES_3G_HELP
+ "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
+ "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n"
+ "Set IND bit length\n" "IND bit length value (default: 5)\n")
+{
+ struct hlr_subscriber subscr;
+ int minlen = 0;
+ int maxlen = 0;
+ int rc;
+ const char *id_type = argv[0];
+ const char *id = argv[1];
+ const char *alg_type = AUTH_ALG_TYPES_3G;
+ const char *k = argv[2];
+ bool opc_is_op = (strcasecmp("op", argv[3]) == 0);
+ const char *op_opc = argv[4];
+ int ind_bitlen = argc > 6? atoi(argv[6]) : 5;
+ struct sub_auth_data_str aud3g = {
+ .type = OSMO_AUTH_TYPE_UMTS,
+ .u.umts = {
+ .k = k,
+ .opc_is_op = opc_is_op,
+ .opc = op_opc,
+ .ind_bitlen = ind_bitlen,
+ },
+ };
+
+ if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
+ vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
+ return CMD_WARNING;
+
+ if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc,
+ MILENAGE_KEY_LEN, MILENAGE_KEY_LEN))
+ return CMD_WARNING;
+
+ if (get_subscr_by_argv(vty, id_type, id, &subscr))
+ return CMD_WARNING;
+
+ rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
+
+ if (rc) {
+ vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
+ subscr.imsi, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+void hlr_vty_subscriber_init(struct hlr *hlr)
+{
+ g_hlr = hlr;
+
+ install_element_ve(&subscriber_show_cmd);
+ install_element(ENABLE_NODE, &subscriber_create_cmd);
+ install_element(ENABLE_NODE, &subscriber_delete_cmd);
+ install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
+ install_element(ENABLE_NODE, &subscriber_no_aud2g_cmd);
+ install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
+ install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
+ install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
+}
diff --git a/src/hlr_vty_subscr.h b/src/hlr_vty_subscr.h
new file mode 100644
index 0000000..841db5a
--- /dev/null
+++ b/src/hlr_vty_subscr.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void hlr_vty_subscriber_init(struct hlr *hlr);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0b625f5..8f1826d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -26,6 +26,7 @@ EXTRA_DIST = \
testsuite.at \
$(srcdir)/package.m4 \
$(TESTSUITE) \
+ test_subscriber.vty \
ctrl_test_runner.py \
$(NULL)
@@ -36,10 +37,26 @@ DISTCLEANFILES = \
$(NULL)
if ENABLE_EXT_TESTS
-python-tests: $(BUILT_SOURCES)
+python-tests:
+# don't run vty and ctrl tests concurrently so that the ports don't conflict
+ $(MAKE) vty-test
$(PYTHON) $(srcdir)/ctrl_test_runner.py -w $(abs_top_builddir) -v
+
+VTY_TEST_DB = hlr_vty_test.db
+
+# To update the VTY script from current application behavior,
+# pass -u to vty_script_runner.py by doing:
+# make vty-test U=-u
+vty-test:
+ -rm -f $(VTY_TEST_DB)
+ sqlite3 $(VTY_TEST_DB) < $(top_srcdir)/sql/hlr.sql
+ osmo_verify_transcript_vty.py -v \
+ -n OsmoHLR -p 4258 \
+ -r "$(top_builddir)/src/osmo-hlr -c $(top_srcdir)/doc/examples/osmo-hlr.cfg -l hlr_vty_test.db" \
+ $(U) $(srcdir)/*.vty
+ -rm -f $(VTY_TEST_DB)
else
-python-tests: $(BUILT_SOURCES)
+python-tests:
echo "Not running python-based tests (determined at configure-time)"
endif
diff --git a/tests/test_subscriber.vty b/tests/test_subscriber.vty
new file mode 100644
index 0000000..2e0bdce
--- /dev/null
+++ b/tests/test_subscriber.vty
@@ -0,0 +1,348 @@
+OsmoHLR> enable
+
+OsmoHLR# list
+...
+ subscriber (imsi|msisdn|id) IDENT show
+ subscriber imsi IDENT create
+ subscriber (imsi|msisdn|id) IDENT delete
+ subscriber (imsi|msisdn|id) IDENT update msisdn MSISDN
+ subscriber (imsi|msisdn|id) IDENT update aud2g none
+ subscriber (imsi|msisdn|id) IDENT update aud2g (comp128v1|comp128v2|comp128v3|xor) ki KI
+ subscriber (imsi|msisdn|id) IDENT update aud3g none
+ subscriber (imsi|msisdn|id) IDENT update aud3g milenage k K (op|opc) OP_C [ind-bitlen] [<0-28>]
+
+OsmoHLR# subscriber?
+ subscriber Subscriber management commands
+
+OsmoHLR# subscriber ?
+ imsi Identify subscriber by IMSI
+ msisdn Identify subscriber by MSISDN (phone number)
+ id Identify subscriber by database ID
+
+OsmoHLR# subscriber imsi ?
+ IDENT IMSI/MSISDN/ID of the subscriber
+OsmoHLR# subscriber msisdn ?
+ IDENT IMSI/MSISDN/ID of the subscriber
+OsmoHLR# subscriber id ?
+ IDENT IMSI/MSISDN/ID of the subscriber
+
+OsmoHLR# subscriber imsi 123456789023000 show
+% No subscriber for imsi = '123456789023000'
+OsmoHLR# subscriber id 1 show
+% No subscriber for id = '1'
+OsmoHLR# subscriber msisdn 12345 show
+% No subscriber for msisdn = '12345'
+
+OsmoHLR# subscriber imsi 1234567890230001 create
+% Not a valid IMSI: 1234567890230001
+OsmoHLR# subscriber imsi 12345678902300x create
+% Not a valid IMSI: 12345678902300x
+OsmoHLR# subscriber imsi 12345 create
+% Not a valid IMSI: 12345
+
+OsmoHLR# subscriber imsi 123456789023000 create
+% Created subscriber 123456789023000
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: none
+
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: none
+OsmoHLR# subscriber id 1 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: none
+OsmoHLR# subscriber msisdn 12345 show
+% No subscriber for msisdn = '12345'
+
+OsmoHLR# subscriber imsi 123456789023000 update msisdn 12345
+% Updated subscriber IMSI='123456789023000' to MSISDN='12345'
+
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 12345
+OsmoHLR# subscriber id 1 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 12345
+OsmoHLR# subscriber msisdn 12345 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 12345
+
+OsmoHLR# subscriber msisdn 12345 update msisdn 423
+% Updated subscriber IMSI='123456789023000' to MSISDN='423'
+OsmoHLR# subscriber msisdn 12345 show
+% No subscriber for msisdn = '12345'
+
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+OsmoHLR# subscriber id 1 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+OsmoHLR# subscriber msisdn 423 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+
+OsmoHLR# subscriber imsi 123456789023000 update ?
+ msisdn Set MSISDN (phone number) of the subscriber
+ aud2g Set 2G authentication data
+ aud3g Set UMTS authentication data (3G, and 2G with UMTS AKA)
+
+OsmoHLR# subscriber imsi 123456789023000 update aud2g ?
+ none Delete 2G authentication data
+ comp128v1 Use COMP128v1 algorithm
+ comp128v2 Use COMP128v2 algorithm
+ comp128v3 Use COMP128v3 algorithm
+ xor Use XOR algorithm
+
+OsmoHLR# subscriber imsi 123456789023000 update aud2g comp128v1 ?
+ ki Set Ki Encryption Key
+
+OsmoHLR# subscriber imsi 123456789023000 update aud2g comp128v1 ki ?
+ KI Ki as 32 hexadecimal characters
+
+OsmoHLR# subscriber imsi 123456789023000 update aud2g comp128v1 ki val ?
+ <cr>
+
+OsmoHLR# subscriber imsi 123456789023000 update aud2g xor ki Deaf0ff1ceD0d0DabbedD1ced1ceF00d
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: XOR
+ KI=deaf0ff1ced0d0dabbedd1ced1cef00d
+
+OsmoHLR# subscriber imsi 123456789023000 update aud2g comp128v1 ki BeefedCafeFaceAcedAddedDecadeFee
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v1
+ KI=beefedcafefaceacedaddeddecadefee
+OsmoHLR# subscriber id 1 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v1
+ KI=beefedcafefaceacedaddeddecadefee
+OsmoHLR# subscriber msisdn 423 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v1
+ KI=beefedcafefaceacedaddeddecadefee
+
+OsmoHLR# subscriber id 1 update aud2g comp128v2 ki CededEffacedAceFacedBadFadedBeef
+OsmoHLR# subscriber id 1 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v2
+ KI=cededeffacedacefacedbadfadedbeef
+OsmoHLR# subscriber msisdn 423 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v2
+ KI=cededeffacedacefacedbadfadedbeef
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v2
+ KI=cededeffacedacefacedbadfadedbeef
+
+OsmoHLR# subscriber msisdn 423 update aud2g comp128v3 ki C01ffedC1cadaeAc1d1f1edAcac1aB0a
+OsmoHLR# subscriber msisdn 423 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v3
+ KI=c01ffedc1cadaeac1d1f1edacac1ab0a
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v3
+ KI=c01ffedc1cadaeac1d1f1edacac1ab0a
+OsmoHLR# subscriber id 1 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v3
+ KI=c01ffedc1cadaeac1d1f1edacac1ab0a
+
+OsmoHLR# subscriber id 1 update aud2g nonsense ki BeefedCafeFaceAcedAddedDecadeFee
+% Unknown command.
+OsmoHLR# subscriber id 1 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v3
+ KI=c01ffedc1cadaeac1d1f1edacac1ab0a
+
+OsmoHLR# subscriber id 1 update aud2g milenage ki BeefedCafeFaceAcedAddedDecadeFee
+% Unknown command.
+OsmoHLR# subscriber id 1 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v3
+ KI=c01ffedc1cadaeac1d1f1edacac1ab0a
+
+OsmoHLR# subscriber id 1 update aud2g xor ki CoiffedCicadaeAcidifiedAcaciaBoa
+% Invalid value for KI: 'CoiffedCicadaeAcidifiedAcaciaBoa'
+OsmoHLR# subscriber id 1 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v3
+ KI=c01ffedc1cadaeac1d1f1edacac1ab0a
+
+OsmoHLR# subscriber id 1 update aud2g xor ki C01ffedC1cadaeAc1d1f1edAcac1aB0aX
+% Invalid value for KI: 'C01ffedC1cadaeAc1d1f1edAcac1aB0aX'
+OsmoHLR# subscriber id 1 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v3
+ KI=c01ffedc1cadaeac1d1f1edacac1ab0a
+
+OsmoHLR# subscriber id 1 update aud2g none
+OsmoHLR# subscriber id 1 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g ?
+ none Delete 3G authentication data
+ milenage Use Milenage algorithm
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage ?
+ k Set Encryption Key K
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k ?
+ K K as 32 hexadecimal characters
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d ?
+ op Set OP key
+ opc Set OPC key
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc ?
+ OP_C OP or OPC as 32 hexadecimal characters
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc CededEffacedAceFacedBadFadedBeef ?
+ [ind-bitlen] Set IND bit length
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc CededEffacedAceFacedBadFadedBeef ind-bitlen ?
+ [<0-28>] IND bit length value (default: 5)
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc CededEffacedAceFacedBadFadedBeef
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 3G auth: MILENAGE
+ K=deaf0ff1ced0d0dabbedd1ced1cef00d
+ OPC=cededeffacedacefacedbadfadedbeef
+ IND-bitlen=5
+
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d op DeafBeddedBabeAcceededFadedDecaf
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 3G auth: MILENAGE
+ K=deaf0ff1ced0d0dabbedd1ced1cef00d
+ OP=deafbeddedbabeacceededfadeddecaf
+ IND-bitlen=5
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g none
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc CededEffacedAceFacedBadFadedBeef ind-bitlen 23
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 3G auth: MILENAGE
+ K=deaf0ff1ced0d0dabbedd1ced1cef00d
+ OPC=cededeffacedacefacedbadfadedbeef
+ IND-bitlen=23
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k CoiffedCicadaeAcidifiedAcaciaBoa opc CededEffacedAceFacedBadFadedBeef
+% Invalid value for K: 'CoiffedCicadaeAcidifiedAcaciaBoa'
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 3G auth: MILENAGE
+ K=deaf0ff1ced0d0dabbedd1ced1cef00d
+ OPC=cededeffacedacefacedbadfadedbeef
+ IND-bitlen=23
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc CoiffedCicadaeAcidifiedAcaciaBoa
+% Invalid value for OPC: 'CoiffedCicadaeAcidifiedAcaciaBoa'
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 3G auth: MILENAGE
+ K=deaf0ff1ced0d0dabbedd1ced1cef00d
+ OPC=cededeffacedacefacedbadfadedbeef
+ IND-bitlen=23
+
+OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d op CoiffedCicadaeAcidifiedAcaciaBoa
+% Invalid value for OP: 'CoiffedCicadaeAcidifiedAcaciaBoa'
+OsmoHLR# subscriber imsi 123456789023000 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 3G auth: MILENAGE
+ K=deaf0ff1ced0d0dabbedd1ced1cef00d
+ OPC=cededeffacedacefacedbadfadedbeef
+ IND-bitlen=23
+
+OsmoHLR# subscriber id 1 update aud2g comp128v2 ki CededEffacedAceFacedBadFadedBeef
+OsmoHLR# subscriber id 1 show
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: 423
+ 2G auth: COMP128v2
+ KI=cededeffacedacefacedbadfadedbeef
+ 3G auth: MILENAGE
+ K=deaf0ff1ced0d0dabbedd1ced1cef00d
+ OPC=cededeffacedacefacedbadfadedbeef
+ IND-bitlen=23
+
+OsmoHLR# subscriber imsi 123456789023000 delete
+% Deleted subscriber for IMSI '123456789023000'
+
+OsmoHLR# subscriber imsi 123456789023000 show
+% No subscriber for imsi = '123456789023000'
+OsmoHLR# subscriber id 1 show
+% No subscriber for id = '1'
+OsmoHLR# subscriber msisdn 423 show
+% No subscriber for msisdn = '423'
+
+OsmoHLR# subscriber imsi 123456789023000 create
+% Created subscriber 123456789023000
+ ID: 1
+ IMSI: 123456789023000
+ MSISDN: none
+
+OsmoHLR# subscriber imsi 123456789023000 delete
+% Deleted subscriber for IMSI '123456789023000'