[PATCH] osmo-hlr[master]: implement subscriber vty interface, tests

Neels Hofmeyr gerrit-no-reply at lists.osmocom.org
Sun Oct 15 04:11:06 UTC 2017


Hello Jenkins Builder,

I'd like you to reexamine a change.  Please visit

    https://gerrit.osmocom.org/4273

to look at the new patch set (#2).

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
---
M src/Makefile.am
M src/hlr_vty.c
A src/hlr_vty_subscr.c
A src/hlr_vty_subscr.h
M tests/Makefile.am
A tests/test_subscriber.vty
6 files changed, 859 insertions(+), 2 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/osmo-hlr refs/changes/73/4273/2

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 @@
 	rand.h \
 	ctrl.h \
 	hlr_vty.h \
+	hlr_vty_subscr.h \
 	$(NULL)
 
 bin_PROGRAMS = \
@@ -46,6 +47,7 @@
 	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 @@
 	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..018872a
--- /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 at 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 osmo_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",
+			osmo_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", osmo_hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
+		vty_out(vty, "             %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
+			osmo_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 @@
 	testsuite.at \
 	$(srcdir)/package.m4 \
 	$(TESTSUITE) \
+	test_subscriber.vty \
 	ctrl_test_runner.py \
 	$(NULL)
 
@@ -36,10 +37,26 @@
 	$(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'

-- 
To view, visit https://gerrit.osmocom.org/4273
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newpatchset
Gerrit-Change-Id: I42b3b70a0439a8f2e4964d7cc31e593c1f0d7537
Gerrit-PatchSet: 2
Gerrit-Project: osmo-hlr
Gerrit-Branch: master
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder


More information about the gerrit-log mailing list