aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRonnie Sahlberg <ronniesahlberg@gmail.com>2012-01-26 09:39:02 +1100
committerKevin Wolf <kwolf@redhat.com>2012-02-09 16:17:50 +0100
commitf9dadc9855ae1554a98c53a10a3a962859992865 (patch)
tree0caa488862f25a75658e28b0b01f636f5143ba29
parent71b58b82dac1e1dc5e08a005a14bbcafecbd9e2a (diff)
iSCSI: add configuration variables for iSCSI
This patch adds configuration variables for iSCSI to set initiator-name to use when logging in to the target, which type of header-digest to negotiate with the target and username and password for CHAP authentication. This allows specifying a initiator-name either from the command line -iscsi initiator-name=iqn.2004-01.com.example:test or from a configuration file included with -readconfig [iscsi] initiator-name = iqn.2004-01.com.example:test header-digest = CRC32C|CRC32C-NONE|NONE-CRC32C|NONE user = CHAP username password = CHAP password If you use several different targets, you can also configure this on a per target basis by using a group name: [iscsi "iqn.target.name"] ... The configuration file can be read using -readconfig. Example : qemu-system-i386 -drive file=iscsi://127.0.0.1/iqn.ronnie.test/1 -readconfig iscsi.conf Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
-rw-r--r--block/iscsi.c139
-rw-r--r--qemu-config.c27
-rw-r--r--qemu-doc.texi54
-rw-r--r--qemu-options.hx16
-rw-r--r--vl.c8
5 files changed, 229 insertions, 15 deletions
diff --git a/block/iscsi.c b/block/iscsi.c
index 938c56807..bd3ca11b2 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -455,6 +455,109 @@ iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
}
}
+static int parse_chap(struct iscsi_context *iscsi, const char *target)
+{
+ QemuOptsList *list;
+ QemuOpts *opts;
+ const char *user = NULL;
+ const char *password = NULL;
+
+ list = qemu_find_opts("iscsi");
+ if (!list) {
+ return 0;
+ }
+
+ opts = qemu_opts_find(list, target);
+ if (opts == NULL) {
+ opts = QTAILQ_FIRST(&list->head);
+ if (!opts) {
+ return 0;
+ }
+ }
+
+ user = qemu_opt_get(opts, "user");
+ if (!user) {
+ return 0;
+ }
+
+ password = qemu_opt_get(opts, "password");
+ if (!password) {
+ error_report("CHAP username specified but no password was given");
+ return -1;
+ }
+
+ if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
+ error_report("Failed to set initiator username and password");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
+{
+ QemuOptsList *list;
+ QemuOpts *opts;
+ const char *digest = NULL;
+
+ list = qemu_find_opts("iscsi");
+ if (!list) {
+ return;
+ }
+
+ opts = qemu_opts_find(list, target);
+ if (opts == NULL) {
+ opts = QTAILQ_FIRST(&list->head);
+ if (!opts) {
+ return;
+ }
+ }
+
+ digest = qemu_opt_get(opts, "header-digest");
+ if (!digest) {
+ return;
+ }
+
+ if (!strcmp(digest, "CRC32C")) {
+ iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C);
+ } else if (!strcmp(digest, "NONE")) {
+ iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE);
+ } else if (!strcmp(digest, "CRC32C-NONE")) {
+ iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE);
+ } else if (!strcmp(digest, "NONE-CRC32C")) {
+ iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
+ } else {
+ error_report("Invalid header-digest setting : %s", digest);
+ }
+}
+
+static char *parse_initiator_name(const char *target)
+{
+ QemuOptsList *list;
+ QemuOpts *opts;
+ const char *name = NULL;
+
+ list = qemu_find_opts("iscsi");
+ if (!list) {
+ return g_strdup("iqn.2008-11.org.linux-kvm");
+ }
+
+ opts = qemu_opts_find(list, target);
+ if (opts == NULL) {
+ opts = QTAILQ_FIRST(&list->head);
+ if (!opts) {
+ return g_strdup("iqn.2008-11.org.linux-kvm");
+ }
+ }
+
+ name = qemu_opt_get(opts, "initiator-name");
+ if (!name) {
+ return g_strdup("iqn.2008-11.org.linux-kvm");
+ }
+
+ return g_strdup(name);
+}
+
/*
* We support iscsi url's on the form
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
@@ -465,6 +568,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
struct iscsi_context *iscsi = NULL;
struct iscsi_url *iscsi_url = NULL;
struct IscsiTask task;
+ char *initiator_name = NULL;
int ret;
if ((BDRV_SECTOR_SIZE % 512) != 0) {
@@ -474,16 +578,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
return -EINVAL;
}
- memset(iscsilun, 0, sizeof(IscsiLun));
-
- /* Should really append the KVM name after the ':' here */
- iscsi = iscsi_create_context("iqn.2008-11.org.linux-kvm:");
- if (iscsi == NULL) {
- error_report("iSCSI: Failed to create iSCSI context.");
- ret = -ENOMEM;
- goto failed;
- }
-
iscsi_url = iscsi_parse_full_url(iscsi, filename);
if (iscsi_url == NULL) {
error_report("Failed to parse URL : %s %s", filename,
@@ -492,6 +586,17 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
goto failed;
}
+ memset(iscsilun, 0, sizeof(IscsiLun));
+
+ initiator_name = parse_initiator_name(iscsi_url->target);
+
+ iscsi = iscsi_create_context(initiator_name);
+ if (iscsi == NULL) {
+ error_report("iSCSI: Failed to create iSCSI context.");
+ ret = -ENOMEM;
+ goto failed;
+ }
+
if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
error_report("iSCSI: Failed to set target name.");
ret = -EINVAL;
@@ -507,6 +612,14 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
goto failed;
}
}
+
+ /* check if we got CHAP username/password via the options */
+ if (parse_chap(iscsi, iscsi_url->target) != 0) {
+ error_report("iSCSI: Failed to set CHAP user/password");
+ ret = -EINVAL;
+ goto failed;
+ }
+
if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
error_report("iSCSI: Failed to set session type to normal.");
ret = -EINVAL;
@@ -515,6 +628,9 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
+ /* check if we got HEADER_DIGEST via the options */
+ parse_header_digest(iscsi, iscsi_url->target);
+
task.iscsilun = iscsilun;
task.status = 0;
task.complete = 0;
@@ -548,6 +664,9 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
return 0;
failed:
+ if (initiator_name != NULL) {
+ g_free(initiator_name);
+ }
if (iscsi_url != NULL) {
iscsi_destroy_url(iscsi_url);
}
diff --git a/qemu-config.c b/qemu-config.c
index b030205e2..e980fd68d 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -118,6 +118,32 @@ static QemuOptsList qemu_drive_opts = {
},
};
+static QemuOptsList qemu_iscsi_opts = {
+ .name = "iscsi",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head),
+ .desc = {
+ {
+ .name = "user",
+ .type = QEMU_OPT_STRING,
+ .help = "username for CHAP authentication to target",
+ },{
+ .name = "password",
+ .type = QEMU_OPT_STRING,
+ .help = "password for CHAP authentication to target",
+ },{
+ .name = "header-digest",
+ .type = QEMU_OPT_STRING,
+ .help = "HeaderDigest setting. "
+ "{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}",
+ },{
+ .name = "initiator-name",
+ .type = QEMU_OPT_STRING,
+ .help = "Initiator iqn name to use when connecting",
+ },
+ { /* end of list */ }
+ },
+};
+
static QemuOptsList qemu_chardev_opts = {
.name = "chardev",
.implied_opt_name = "backend",
@@ -580,6 +606,7 @@ static QemuOptsList *vm_config_groups[32] = {
&qemu_option_rom_opts,
&qemu_machine_opts,
&qemu_boot_opts,
+ &qemu_iscsi_opts,
NULL,
};
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 11f416699..83b2ad523 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -730,6 +730,57 @@ export LIBISCSI_CHAP_PASSWORD=<password>
iscsi://<host>/<target-iqn-name>/<lun>
@end example
+Various session related parameters can be set via special options, either
+in a configuration file provided via '-readconfig' or directly on the
+command line.
+
+@example
+Setting a specific initiator name to use when logging in to the target
+-iscsi initiator-name=iqn.qemu.test:my-initiator
+@end example
+
+@example
+Controlling which type of header digest to negotiate with the target
+-iscsi header-digest=CRC32C|CRC32C-NONE|NONE-CRC32C|NONE
+@end example
+
+These can also be set via a configuration file
+@example
+[iscsi]
+ user = "CHAP username"
+ password = "CHAP password"
+ initiator-name = "iqn.qemu.test:my-initiator"
+ # header digest is one of CRC32C|CRC32C-NONE|NONE-CRC32C|NONE
+ header-digest = "CRC32C"
+@end example
+
+
+Setting the target name allows different options for different targets
+@example
+[iscsi "iqn.target.name"]
+ user = "CHAP username"
+ password = "CHAP password"
+ initiator-name = "iqn.qemu.test:my-initiator"
+ # header digest is one of CRC32C|CRC32C-NONE|NONE-CRC32C|NONE
+ header-digest = "CRC32C"
+@end example
+
+
+Howto use a configuration file to set iSCSI configuration options:
+@example
+cat >iscsi.conf <<EOF
+[iscsi]
+ user = "me"
+ password = "my password"
+ initiator-name = "iqn.qemu.test:my-initiator"
+ header-digest = "CRC32C"
+EOF
+
+qemu-system-i386 -drive file=iscsi://127.0.0.1/iqn.qemu.test/1 \
+ -readconfig iscsi.conf
+@end example
+
+
Howto set up a simple iSCSI target on loopback and accessing it via QEMU:
@example
This example shows how to set up an iSCSI target with one CDROM and one DISK
@@ -744,7 +795,8 @@ tgtadm --lld iscsi --mode logicalunit --op new --tid 1 --lun 2 \
-b /IMAGES/cd.iso --device-type=cd
tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL
-qemu-system-i386 -boot d -drive file=iscsi://127.0.0.1/iqn.qemu.test/1 \
+qemu-system-i386 -iscsi initiator-name=iqn.qemu.test:my-initiator \
+ -boot d -drive file=iscsi://127.0.0.1/iqn.qemu.test/1 \
-cdrom iscsi://127.0.0.1/iqn.qemu.test/2
@end example
diff --git a/qemu-options.hx b/qemu-options.hx
index f577cc847..42d22aebf 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1829,24 +1829,32 @@ Syntax for specifying iSCSI LUNs is
Example (without authentication):
@example
-qemu -cdrom iscsi://192.0.2.1/iqn.2001-04.com.example/2 \
---drive file=iscsi://192.0.2.1/iqn.2001-04.com.example/1
+qemu -iscsi initiator-name=iqn.2001-04.com.example:my-initiator \
+-cdrom iscsi://192.0.2.1/iqn.2001-04.com.example/2 \
+-drive file=iscsi://192.0.2.1/iqn.2001-04.com.example/1
@end example
Example (CHAP username/password via URL):
@example
-qemu --drive file=iscsi://user%password@@192.0.2.1/iqn.2001-04.com.example/1
+qemu -drive file=iscsi://user%password@@192.0.2.1/iqn.2001-04.com.example/1
@end example
Example (CHAP username/password via environment variables):
@example
LIBISCSI_CHAP_USERNAME="user" \
LIBISCSI_CHAP_PASSWORD="password" \
-qemu --drive file=iscsi://192.0.2.1/iqn.2001-04.com.example/1
+qemu -drive file=iscsi://192.0.2.1/iqn.2001-04.com.example/1
@end example
iSCSI support is an optional feature of QEMU and only available when
compiled and linked against libiscsi.
+ETEXI
+DEF("iscsi", HAS_ARG, QEMU_OPTION_iscsi,
+ "-iscsi [user=user][,password=password]\n"
+ " [,header-digest=CRC32C|CR32C-NONE|NONE-CRC32C|NONE\n"
+ " [,initiator-name=iqn]\n"
+ " iSCSI session parameters\n", QEMU_ARCH_ALL)
+STEXI
@item NBD
QEMU supports NBD (Network Block Devices) both using TCP protocol as well
diff --git a/vl.c b/vl.c
index 63dd72521..0c660719f 100644
--- a/vl.c
+++ b/vl.c
@@ -2529,6 +2529,14 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
break;
+#ifdef CONFIG_LIBISCSI
+ case QEMU_OPTION_iscsi:
+ opts = qemu_opts_parse(qemu_find_opts("iscsi"), optarg, 0);
+ if (!opts) {
+ exit(1);
+ }
+ break;
+#endif
#ifdef CONFIG_SLIRP
case QEMU_OPTION_tftp:
legacy_tftp_prefix = optarg;