summaryrefslogtreecommitdiffstats
path: root/test/TEST-30-ISCSI
diff options
context:
space:
mode:
Diffstat (limited to 'test/TEST-30-ISCSI')
-rw-r--r--test/TEST-30-ISCSI/Makefile7
-rwxr-xr-xtest/TEST-30-ISCSI/client-init.sh24
-rw-r--r--test/TEST-30-ISCSI/client.link6
-rwxr-xr-xtest/TEST-30-ISCSI/create-client-root.sh33
-rwxr-xr-xtest/TEST-30-ISCSI/create-server-root.sh20
-rw-r--r--test/TEST-30-ISCSI/dhcpd.conf48
-rw-r--r--test/TEST-30-ISCSI/hosts8
-rw-r--r--test/TEST-30-ISCSI/ibft.pl458
-rw-r--r--test/TEST-30-ISCSI/ibft.tablebin0 -> 381 bytes
-rwxr-xr-xtest/TEST-30-ISCSI/server-init.sh88
-rw-r--r--test/TEST-30-ISCSI/server.link6
-rwxr-xr-xtest/TEST-30-ISCSI/test.sh240
-rwxr-xr-xtest/TEST-30-ISCSI/wait-if-server.sh4
13 files changed, 942 insertions, 0 deletions
diff --git a/test/TEST-30-ISCSI/Makefile b/test/TEST-30-ISCSI/Makefile
new file mode 100644
index 0000000..88db701
--- /dev/null
+++ b/test/TEST-30-ISCSI/Makefile
@@ -0,0 +1,7 @@
+-include ../Makefile.testdir
+
+ibft.table: Makefile ibft.pl
+ perl ibft.pl \
+ --initiator iqn=iqn.1994-05.com.redhat:633114aacf2 \
+ --nic ip=192.168.50.101,prefix=24,gw=192.168.50.1,dns1=192.168.50.1,dhcp=192.168.50.1,mac=52:54:00:12:34:00,pci=00:03.0 \
+ --target nic=0,ip=192.168.50.1,port=3260,lun=1,name=iqn.2009-06.dracut:target0 >$@
diff --git a/test/TEST-30-ISCSI/client-init.sh b/test/TEST-30-ISCSI/client-init.sh
new file mode 100755
index 0000000..46a5e3f
--- /dev/null
+++ b/test/TEST-30-ISCSI/client-init.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+stty sane
+echo "made it to the rootfs! Powering down."
+while read -r dev _ fstype opts rest || [ -n "$dev" ]; do
+ [ "$fstype" != "ext4" ] && continue
+ echo "iscsi-OK $dev $fstype $opts" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+ break
+done < /proc/mounts
+
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+
+sync
+poweroff -f
diff --git a/test/TEST-30-ISCSI/client.link b/test/TEST-30-ISCSI/client.link
new file mode 100644
index 0000000..b992bfd
--- /dev/null
+++ b/test/TEST-30-ISCSI/client.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+MACAddressPolicy=keep
diff --git a/test/TEST-30-ISCSI/create-client-root.sh b/test/TEST-30-ISCSI/create-client-root.sh
new file mode 100755
index 0000000..267c93a
--- /dev/null
+++ b/test/TEST-30-ISCSI/create-client-root.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+set -ex
+
+mkfs.ext4 -j -L singleroot -F /dev/disk/by-id/ata-disk_singleroot
+mkdir -p /sysroot
+mount -t ext4 /dev/disk/by-id/ata-disk_singleroot /sysroot
+cp -a -t /sysroot /source/*
+umount /sysroot
+mdadm --create /dev/md0 --run --auto=yes --level=stripe --raid-devices=2 /dev/disk/by-id/ata-disk_raid0-1 /dev/disk/by-id/ata-disk_raid0-2
+mdadm -W /dev/md0 || :
+lvm pvcreate -ff -y /dev/md0
+lvm vgcreate dracut /dev/md0
+lvm lvcreate -l 100%FREE -n root dracut
+lvm vgchange -ay
+mkfs.ext4 -j -L sysroot /dev/dracut/root
+mount -t ext4 /dev/dracut/root /sysroot
+cp -a -t /sysroot /source/*
+umount /sysroot
+lvm lvchange -a n /dev/dracut/root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+sync
+poweroff -f
diff --git a/test/TEST-30-ISCSI/create-server-root.sh b/test/TEST-30-ISCSI/create-server-root.sh
new file mode 100755
index 0000000..2dbc2da
--- /dev/null
+++ b/test/TEST-30-ISCSI/create-server-root.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+mkfs.ext4 -L dracut /dev/disk/by-id/ata-disk_root
+mkdir -p /root
+mount -t ext4 /dev/disk/by-id/ata-disk_root /root
+cp -a -t /root /source/*
+mkdir -p /root/run
+umount /root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+poweroff -f
diff --git a/test/TEST-30-ISCSI/dhcpd.conf b/test/TEST-30-ISCSI/dhcpd.conf
new file mode 100644
index 0000000..fd306ea
--- /dev/null
+++ b/test/TEST-30-ISCSI/dhcpd.conf
@@ -0,0 +1,48 @@
+ddns-update-style none;
+
+use-host-decl-names true;
+
+subnet 192.168.50.0 netmask 255.255.255.0 {
+ option subnet-mask 255.255.255.0;
+ option routers 192.168.50.1;
+ next-server 192.168.50.1;
+ server-identifier 192.168.50.1;
+ option domain-name-servers 192.168.50.1;
+ option domain-search "example.com";
+ option domain-name "other.com";
+
+ # MAC numbering scheme:
+ # NFSv3: last octet starts at 0x00 and works up
+
+ group {
+ option root-path "iscsi:192.168.50.1:::1:iqn.2009-06.dracut:target0";
+
+ host iscsi-1 {
+ hardware ethernet 52:54:00:12:34:00;
+ fixed-address 192.168.50.101;
+ }
+ }
+}
+
+subnet 192.168.51.0 netmask 255.255.255.0 {
+ option subnet-mask 255.255.255.0;
+ option routers 192.168.51.1;
+ next-server 192.168.51.1;
+ server-identifier 192.168.51.1;
+ option domain-name-servers 192.168.51.1;
+ option domain-search "example.com";
+ option domain-name "other.com";
+
+ # MAC numbering scheme:
+ # NFSv3: last octet starts at 0x00 and works up
+
+ group {
+ #option root-path "iscsi:192.168.51.1:::1:iqn.2009-06.dracut:target1";
+
+ host iscsi-2 {
+ hardware ethernet 52:54:00:12:34:01;
+ fixed-address 192.168.51.101;
+ }
+
+ }
+}
diff --git a/test/TEST-30-ISCSI/hosts b/test/TEST-30-ISCSI/hosts
new file mode 100644
index 0000000..f8c18b6
--- /dev/null
+++ b/test/TEST-30-ISCSI/hosts
@@ -0,0 +1,8 @@
+127.0.0.1 localhost
+192.168.50.1 server
+192.168.50.2 server-ip
+192.168.50.3 server-proto-ip
+192.168.50.100 workstation1
+192.168.50.101 workstation2
+192.168.50.102 workstation3
+192.168.50.103 workstation4
diff --git a/test/TEST-30-ISCSI/ibft.pl b/test/TEST-30-ISCSI/ibft.pl
new file mode 100644
index 0000000..c612951
--- /dev/null
+++ b/test/TEST-30-ISCSI/ibft.pl
@@ -0,0 +1,458 @@
+#!/usr/bin/perl
+# SPDX-License-Identifier: GPL-2.0+
+#
+# iBFT ACPI table generator
+# $ perldoc ibft.pl if you'd like to read the manual, poor you:
+
+=head1 NAME
+
+ibft.pl - Generate iBFT ACPI table
+
+=head1 SYNOPSIS
+
+ibft.pl
+[--oemid <oemid>]
+[--tableid <tableid>
+[--initiator isns=<ip>,slp=<ip>,radius1=<ip>,radius2=<ip>,iqn=<iqn>]
+[--nic ip=<ip>[,prefix=<prefix>][,gw=<ip>][,dns1=<ip>][,dns2=<ip>][,dhcp=<ip>][,vlan=<id>][,mac=<mac>][,pci=<pci>][,hostname=<hostname>] ...]
+[--target ip=<ip>[,port=<port>][,lun=<lun>][,name=<iqn> ...]
+
+=head1 DESCRIPTION
+
+B<ibft.pl> creates an image of iBFT ACPI table similar to what a real network
+boot firmware would do. This is mainly useful for testing.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<< --oemid <oemid> >>
+
+Create a table with a particular OEM ID, limited to 6 characters.
+It generally doesn't matter.
+
+Defaults to I<DRACUT>.
+
+=item B<< --tableid <tableid> >>
+
+Create a table with a particular OEM Table ID.
+
+Defaults to I<TEST>, but any four-letter word would do. Any.
+
+=item B<< --initiator >>
+
+Configure the Initiator Structure.
+Following parameters are supported:
+
+=over 4
+
+=item B<< isns=<ip> >>
+
+iSNS server address.
+
+=item B<< slp=<ip> >>
+
+SLP server address.
+
+=item B<< radius1=<ip> >>, B<< radius2=<ip> >>
+
+Primary and secondary Radius server addresses.
+
+=item B<< iqn=<iqn> >>
+
+Override the IQN, which defaults to I<iqn.2009-06.dracut:initiator0>.
+
+=back
+
+=item B<< --nic >>
+
+Configure a NIC Structure. This option can be used up multiple times.
+
+Following parameters are supported:
+
+=over 4
+
+=item B<< ip=<ip> >>
+
+Set the IP address. Both I<AF_INET> and I<AF_INET6> families are supported.
+This parameter is mandatory.
+
+=item B<< prefix=<prefix> >>
+
+Set the IP address prefix. You generally also want to set this in order to
+get a sensible iBFT.
+
+=item B<< gw=<ip> >>
+
+Set the gateway IP address.
+
+=item B<< dns1=<ip> >>, B<< dns2=<ip> >>
+
+Set the domain service server addresses.
+
+=item B<< dhcp=<ip> >>
+
+Specify the address of the DHCP server in case dynamic configuration is used.
+
+=item B<< vlan=<id> >>
+
+The VLAN Id. Duh.
+
+=item B<< mac=<mac> >>
+
+Specify the ethernet hardware address, in form of six colon-delimited
+hexadecimal octets.
+
+=item B<< pci=<pci> >>
+
+Specify the ethernet hardware's PCI bus location, in form of
+B<< <bus> >>:B<< <device> >>.B<< <function> >> where the numbers are in
+hexadecimal.
+
+=item B<< hostname=<hostname> >>
+
+The host name. Defaults to B<client>.
+
+=back
+
+=item B<< --target >>
+
+Configure a Target Structure. This option can be used multiple times.
+
+Following parameters are supported:
+
+=over 4
+
+=item B<< ip=<ip> >>
+
+The iSCSI target IP address.
+
+=item B<< port=<port> >>
+
+The iSCSI TCP port, in case the default of I<3260> is not good enough for
+you.
+
+=item B<< lun=<1> >>
+
+The LUN number. Defaults to I<1> no less.
+
+=item B<< name=<iqn> >>
+
+The iSCSI volume name. Defaults to I<iqn.2009-06.dracut:target0> for the first
+target, I<iqn.2009-06.dracut:target1> for the second one.
+
+=back
+
+=back
+
+=cut
+
+use strict;
+use warnings;
+
+sub ip4 {
+ shift =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
+ or die 'not an INET address';
+ return (map { 0x00 } 0..9), 0xff, 0xff, $1, $2, $3, $4;
+}
+
+sub ip6
+{
+ my ($beg, $end) = map { [ map { /^([0-9a-fA-F]{0,2}?)([0-9a-fA-F]{1,2})$/
+ ? (hex $1, hex $2)
+ : die "'$_' not valid in a INET6 address"
+ } split /:/ ] } split /::/, shift;
+
+ $beg ||= [];
+ $end ||= [];
+
+ my $fill = 16 - scalar @$beg + scalar@$end;
+ die 'INET6 address too long' if $fill < 0;
+
+ @$beg, (map { 0 } 1..$fill), @$end;
+}
+
+sub ip
+{
+ my @val;
+ @val = eval { @val = ip6 ($_[0]) };
+ @val = eval { @val = ip4 ($_[0]) } unless @val;
+ die "Saatana: $_[0] is not an INET or INET6 address" unless @val;
+
+ return pack 'C16', @val;
+}
+
+sub mac
+{
+ return pack 'C8', map { hex $_ } split /:/, shift;
+}
+
+sub pci
+{
+ shift =~ /^([0-9a-fA-F]{1,2}?):([0-9a-fA-F]{1,2})\.([0-9a-fA-F]+)$/
+ or die 'Not a PCI address';
+ return (hex $1) << 8 | (hex $2) << 3 | (hex $3);
+}
+
+sub lun
+{
+ return pack 'C8', 0, shift, 0, 0, 0, 0, 0, 0;
+}
+
+# signature, length, revision, checksum, oem_id, oem_table_id, reserved
+sub pack_table_hdr { pack 'a4 V C C a6 a8 a24 x![C8]', @_ }
+
+# id, version, length, index, flags
+# extensions, initiator_off, nic0_off, tgt0_off, nic1_off, tgt1_off, ext*
+sub pack_control { pack 'C C S C C S S S S S S S* x![C8]', @_ }
+
+# id, version, length, index, flags
+# isns_adr, slp_adr, radius1_adr, radius2_adr, iqn_len, iqn_off
+sub pack_initiator { pack 'C C S C C a16 a16 a16 a16 SS x![C8]', @_ }
+
+# id, version, length, index, flags
+# adr, prefix, origin, gw, dns1, dns2, dhcp, vlan_id, mac, pci_bdf, hostname_len, hostname_off
+sub pack_nic { $_[5] ? pack 'C C S C C a16 C C a16 a16 a16 a16 S a6 S SS x![C8]', @_ : '' }
+
+# id, version, length, index, flags
+# tgt_adr, tgt_port, tgt_lun, chap_type, nic_id, tgt_len, tgt_off,
+# chap_name_len, chap_name_off, chap_secret_len, chap_secret_off
+# rchap_name_len, rchap_name_off, rchap_secret_len, rchap_secret_off
+sub pack_tgt { $_[5] ? pack 'C C S C C a16 S a8 C C SS SS SS SS SS x![C8]', @_ : '' };
+
+# str
+sub pack_str { pack 'Z*', @_ }
+
+# Initialize some defaults
+my @table_hdr = ('iBFT', 0000, 1, 0000, 'DRACUT', 'TEST', '');
+my @control = (1, 1, 18, 0, 0, 0000, 0000, 0000, 0000, 0000, 0000);
+my @initiator = (2, 1, 74, 0, 0x03, '', '', '', '', (0000, 0000));
+my @nics;
+my @tgts;
+my $iqn = 'iqn.2009-06.dracut:initiator0';
+my @hostnames;
+my @tgt_names;
+
+while (@ARGV) {
+ my $arg = shift @ARGV;
+ die "Saatana: $arg is missing an argument" unless @ARGV;
+
+ if ($arg eq '--oemid') {
+ $table_hdr[4] = shift @ARGV;
+ } elsif ($arg eq '--tableid') {
+ $table_hdr[5] = shift @ARGV;
+ } elsif ($arg eq '--initiator') {
+ my %val = split /[,=]/, shift @ARGV;
+ $initiator[5] = ip (delete $val{isns}) if exists $val{isns};
+ $initiator[6] = ip (delete $val{slp}) if exists $val{slp};
+ $initiator[7] = ip (delete $val{radius1}) if exists $val{radius1};
+ $initiator[8] = ip (delete $val{radius2}) if exists $val{radius2};
+ $iqn = delete $val{iqn} if exists $val{iqn};
+ die "Saatana: Extra arguments to --initiator: ".join (', ', %val) if %val;
+ } elsif ($arg eq '--nic') {
+ my @nic = (3, 1, 102, 0, 0x03,
+ undef, 0, 0x01, '', '', '', '', 0, '', 0, (0000, 0000));
+ push @nics, \@nic;
+
+ my %val = split /[,=]/, shift @ARGV;
+ die 'Saatana: --nic needs an ip' unless exists $val{ip};
+ $nic[3] = $#nics;
+ $nic[5] = ip (delete $val{ip});
+ $nic[6] = delete $val{prefix} if exists $val{prefix};
+ $nic[7] = 0x03 if exists $val{dhcp};
+ $nic[8] = ip (delete $val{gw}) if exists $val{gw};
+ $nic[9] = ip (delete $val{dns1}) if exists $val{dns1};
+ $nic[10] = ip (delete $val{dns2}) if exists $val{dns2};
+ $nic[11] = ip (delete $val{dhcp}) if exists $val{dhcp};
+ $nic[12] = delete $val{vlan} if exists $val{vlan};
+ $nic[13] = mac (delete $val{mac}) if exists $val{mac};
+ $nic[14] = pci (delete $val{pci}) if exists $val{pci};
+ $hostnames[$#nics] = exists $val{hostname} ? delete $val{hostname} : 'client';
+ $hostnames[$#nics] = pack_str $hostnames[$#nics];
+ die "Saatana: Extra arguments to --nic: ".join (', ', %val) if %val;
+
+ # Allocate an control expansion entry
+ if ($#nics > 1) {
+ $control[2] += 2;
+ push @control, (0x4444);
+ }
+ } elsif ($arg eq '--target') {
+ my @tgt = (4, 1, 54, 0, 0x03,
+ undef, 3260, lun (1), 0, 0,
+ (0000, 0000),
+ (0000, 0000),
+ (0000, 0000),
+ (0000, 0000),
+ (0000, 0000));
+ push @tgts, \@tgt;
+
+ my %val = split /[,=]/, shift @ARGV;
+ die 'Saatana: --target needs an ip' unless exists $val{ip};
+ $tgt[3] = $#tgts;
+ $tgt[5] = ip (delete $val{ip}) if exists $val{ip};
+ $tgt[6] = delete $val{port} if exists $val{port};
+ $tgt[7] = lun (delete $val{lun}) if exists $val{lun};
+ $tgt[9] = delete $val{nic} if exists $val{nic};
+ $tgt_names[$#tgts] = exists $val{name} ? delete $val{name}
+ : 'iqn.2009-06.dracut:target'.$#tgts;
+ $tgt_names[$#tgts] = pack_str $tgt_names[$#tgts];
+ die "Saatana: Extra arguments to --target: ".join (', ', %val) if %val;
+
+ # Allocate an control expansion entry if necessary
+ if ($#tgts > 1) {
+ $control[2] += 2;
+ push @control, (0x1111);
+ }
+ } else {
+ die "Saatana: Unknown argument: $arg";
+ }
+}
+
+# Pass 1
+my $table_hdr = pack_table_hdr @table_hdr;
+my $control = pack_control @control;
+my $initiator = pack_initiator @initiator;
+my @packed_nics = map { pack_nic @$_ } @nics;
+my @packed_tgts = map { pack_tgt @$_ } @tgts;
+$iqn = pack_str $iqn;
+
+
+# Resolve the offsets
+my $len = 0;
+$len += length $table_hdr;
+$len += length $control;
+$control[6] = $len;
+$len += length $initiator;
+
+for my $i (0..$#packed_nics) {
+ if ($i == 0) {
+ # NIC 0
+ $control[7] = $len;
+ } elsif ($i == 1) {
+ # NIC 1
+ $control[9] = $len;
+ } else {
+ # Expansion
+ $control[11 + $i - 2] = $len;
+ }
+ $len += length $packed_nics[$i];
+}
+
+for my $i (0..$#packed_tgts) {
+ if ($i == 0) {
+ # Target 0
+ $control[8] = $len;
+ } elsif ($i == 1) {
+ # Target 1
+ $control[10] = $len;
+ } else {
+ # Expansion
+ $control[11 + scalar @packed_nics - 2 + $i - 2] = $len;
+ }
+ $len += length $packed_tgts[$i];
+}
+
+$initiator[9] = -1 + length $iqn;
+$initiator[10] = $len;
+$len += length $iqn;
+
+for my $i (0..$#hostnames) {
+ $nics[$i]->[15] = -1 + length $hostnames[$i];
+ $nics[$i]->[16] = $len;
+ $len += length $hostnames[$i];
+}
+
+for my $i (0..$#tgt_names) {
+ $tgts[$i]->[10] = -1 + length $tgt_names[$i];
+ $tgts[$i]->[11] = $len;
+ $len += length $tgt_names[$i];
+}
+
+@table_hdr[1] = $len;
+
+# Pass 2, with the offsets resolved
+$table_hdr = pack_table_hdr @table_hdr;
+$control = pack_control @control;
+$initiator = pack_initiator @initiator;
+@packed_nics = map { pack_nic @$_ } @nics;
+@packed_tgts = map { pack_tgt @$_ } @tgts;
+
+# Pass 3, calculate checksum
+my $cksum = 0xff;
+$cksum += ord $_ foreach split //, join '', $table_hdr, $control, $initiator,
+ @packed_nics, @packed_tgts, $iqn, @hostnames, @tgt_names;
+$cksum = ~$cksum & 0xff;
+$table_hdr[3] = $cksum;
+$table_hdr = pack_table_hdr @table_hdr;
+
+# Puke stuff out
+print $table_hdr;
+print $control;
+print $initiator;
+print @packed_nics;
+print @packed_tgts;
+print $iqn;
+print @hostnames;
+print @tgt_names;
+
+=head1 EXAMPLES
+
+=over
+
+=item B<< perl ibft.pl --oemid FENSYS --tableid iPXE --nic ip=192.168.50.101,prefix=24,gw=192.168.50.1,dns1=192.168.50.1,dhcp=192.168.50.1,vlan=0,mac=52:54:00:12:34:00,pci=00:02.0,hostname=iscsi-1 --target ip=192.168.50.1 >ibft.img >>
+
+Generate an iBFT image with a single NIC while pretending we're iPXE for
+no good reason.
+
+=item B<<perl ibft.pl --initiator iqn=iqn.1994-05.com.redhat:633114aacf2 --nic ip=192.168.50.101,prefix=24,gw=192.168.50.1,dns1=192.168.50.1,dhcp=192.168.50.1,mac=52:54:00:12:34:00,pci=00:03.0 --nic ip=192.168.51.101,prefix=24,gw=192.168.51.1,dns1=192.168.51.1,dhcp=192.168.51.1,mac=52:54:00:12:34:01,pci=00:04.0 --target ip=192.168.50.1,port=3260,lun=1,name=iqn.2009-06.dracut:target0 --target ip=192.168.51.1,port=3260,lun=2,name=iqn.2009-06.dracut:target1 >ibft.img >>
+
+Generate an iBFT image for two NICs while being slightly more expressive
+than necessary.
+
+=item B<qemy-system-x86_64 -acpitable file=ibft.img>
+
+Use the image with QEMU.
+
+=back
+
+=head1 BUGS
+
+No support for CHAP secrets.
+
+=head1 SEE ALSO
+
+=over 4
+
+=item L<qemu(1)>,
+
+=item L<iSCSI Boot Firmware Table (iBFT)|ftp://ftp.software.ibm.com/systems/support/bladecenter/iscsi_boot_firmware_table_v1.03.pdf>,
+
+=item L<NL_PREFIX_ORIGIN Enumeration|https://docs.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_prefix_origin>
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2019 Lubomir Rintel
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=head1 AUTHOR
+
+Lubomir Rintel C<lkundrak@v3.sk>
+
+=cut
+
+# Forgive me.
+# This would have been much easier with FORTH.
diff --git a/test/TEST-30-ISCSI/ibft.table b/test/TEST-30-ISCSI/ibft.table
new file mode 100644
index 0000000..0837940
--- /dev/null
+++ b/test/TEST-30-ISCSI/ibft.table
Binary files differ
diff --git a/test/TEST-30-ISCSI/server-init.sh b/test/TEST-30-ISCSI/server-init.sh
new file mode 100755
index 0000000..a1c3b7e
--- /dev/null
+++ b/test/TEST-30-ISCSI/server-init.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+exec < /dev/console > /dev/console 2>&1
+set -x
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+export TERM=linux
+export PS1='server:\w\$ '
+stty sane
+echo "made it to the rootfs!"
+echo server > /proc/sys/kernel/hostname
+
+wait_for_if_link() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 600 ]; do
+ li=$(ip -o link show dev "$1" 2> /dev/null)
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_if_up() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 200 ]; do
+ li=$(ip -o link show up dev "$1")
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_route_ok() {
+ local cnt=0
+ while [ $cnt -lt 200 ]; do
+ li=$(ip route show)
+ [ -n "$li" ] && [ -z "${li##*"$1"*}" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+linkup() {
+ wait_for_if_link "$1" 2> /dev/null && ip link set "$1" up 2> /dev/null && wait_for_if_up "$1" 2> /dev/null
+}
+
+wait_for_if_link enx525400123456
+wait_for_if_link enx525400123457
+
+ip addr add 127.0.0.1/8 dev lo
+ip link set lo up
+
+ip addr add 192.168.50.1/24 dev enx525400123456
+linkup enx525400123456
+
+ip addr add 192.168.51.1/24 dev enx525400123457
+linkup enx525400123457
+
+modprobe af_packet
+
+: > /var/lib/dhcpd/dhcpd.leases
+chmod 777 /var/lib/dhcpd/dhcpd.leases
+dhcpd -d -cf /etc/dhcpd.conf -lf /var/lib/dhcpd/dhcpd.leases &
+
+tgtd
+tgtadm --lld iscsi --mode target --op new --tid 1 --targetname iqn.2009-06.dracut:target0
+tgtadm --lld iscsi --mode target --op new --tid 2 --targetname iqn.2009-06.dracut:target1
+tgtadm --lld iscsi --mode target --op new --tid 3 --targetname iqn.2009-06.dracut:target2
+tgtadm --lld iscsi --mode logicalunit --op new --tid 1 --lun 1 -b /dev/disk/by-id/ata-disk_singleroot
+tgtadm --lld iscsi --mode logicalunit --op new --tid 2 --lun 2 -b /dev/disk/by-id/ata-disk_raid0-1
+tgtadm --lld iscsi --mode logicalunit --op new --tid 3 --lun 3 -b /dev/disk/by-id/ata-disk_raid0-2
+tgtadm --lld iscsi --mode target --op bind --tid 1 -I 192.168.50.101
+tgtadm --lld iscsi --mode target --op bind --tid 2 -I 192.168.51.101
+tgtadm --lld iscsi --mode target --op bind --tid 3 -I 192.168.50.101
+
+# Wait forever for the VM to die
+echo "Serving iSCSI"
+while pidof tgtd > /dev/null; do
+ : > /dev/watchdog
+ dmesg -c
+ sleep 1
+done
+dmesg -c
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-30-ISCSI/server.link b/test/TEST-30-ISCSI/server.link
new file mode 100644
index 0000000..1d21856
--- /dev/null
+++ b/test/TEST-30-ISCSI/server.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=mac
+MACAddressPolicy=keep
diff --git a/test/TEST-30-ISCSI/test.sh b/test/TEST-30-ISCSI/test.sh
new file mode 100755
index 0000000..ac9f096
--- /dev/null
+++ b/test/TEST-30-ISCSI/test.sh
@@ -0,0 +1,240 @@
+#!/bin/bash
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem over iSCSI with $USE_NETWORK"
+
+#DEBUGFAIL="rd.shell rd.break rd.debug loglevel=7 "
+#SERVER_DEBUG="rd.debug loglevel=7"
+#SERIAL="tcp:127.0.0.1:9999"
+
+run_server() {
+ # Start server first
+ echo "iSCSI TEST SETUP: Starting DHCP/iSCSI server"
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.img serverroot 0 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/singleroot.img singleroot
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid0-1.img raid0-1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid0-2.img raid0-2
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -serial "${SERIAL:-"file:$TESTDIR/server.log"}" \
+ -net nic,macaddr=52:54:00:12:34:56,model=e1000 \
+ -net nic,macaddr=52:54:00:12:34:57,model=e1000 \
+ -net socket,listen=127.0.0.1:12330 \
+ -append "panic=1 oops=panic softlockup_panic=1 quiet root=/dev/disk/by-id/ata-disk_serverroot rootfstype=ext4 rw console=ttyS0,115200n81 selinux=0 $SERVER_DEBUG" \
+ -initrd "$TESTDIR"/initramfs.server \
+ -pidfile "$TESTDIR"/server.pid -daemonize || return 1
+ chmod 644 "$TESTDIR"/server.pid || return 1
+
+ # Cleanup the terminal if we have one
+ tty -s && stty sane
+
+ if ! [[ $SERIAL ]]; then
+ while :; do
+ grep Serving "$TESTDIR"/server.log && break
+ echo "Waiting for the server to startup"
+ tail "$TESTDIR"/server.log
+ sleep 1
+ done
+ else
+ echo Sleeping 10 seconds to give the server a head start
+ sleep 10
+ fi
+
+}
+
+run_client() {
+ local test_name=$1
+ shift
+ echo "CLIENT TEST START: $test_name"
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -net nic,macaddr=52:54:00:12:34:00,model=e1000 \
+ -net nic,macaddr=52:54:00:12:34:01,model=e1000 \
+ -net socket,connect=127.0.0.1:12330 \
+ -acpitable file=ibft.table \
+ -append "$*" \
+ -initrd "$TESTDIR"/initramfs.testing
+
+ # shellcheck disable=SC2181
+ if [[ $? -ne 0 ]] || ! test_marker_check iscsi-OK; then
+ echo "CLIENT TEST END: $test_name [FAILED - BAD EXIT]"
+ return 1
+ fi
+
+ echo "CLIENT TEST END: $test_name [OK]"
+ return 0
+}
+
+do_test_run() {
+ initiator=$(iscsi-iname)
+
+ run_client "root=dhcp" \
+ "root=/dev/root netroot=dhcp ip=enp0s1:dhcp" \
+ "rd.iscsi.initiator=$initiator" \
+ || return 1
+
+ run_client "netroot=iscsi target0" \
+ "root=LABEL=singleroot netroot=iscsi:192.168.50.1::::iqn.2009-06.dracut:target0" \
+ "ip=192.168.50.101::192.168.50.1:255.255.255.0:iscsi-1:enp0s1:off" \
+ "rd.iscsi.initiator=$initiator" \
+ || return 1
+
+ run_client "netroot=iscsi target1 target2" \
+ "root=LABEL=sysroot" \
+ "ip=dhcp" \
+ "netroot=iscsi:192.168.51.1::::iqn.2009-06.dracut:target1" \
+ "netroot=iscsi:192.168.50.1::::iqn.2009-06.dracut:target2" \
+ "rd.iscsi.initiator=$initiator" \
+ || return 1
+
+ run_client "root=ibft" \
+ "root=LABEL=singleroot" \
+ "rd.iscsi.ibft=1" \
+ "rd.iscsi.firmware=1" \
+ || return 1
+
+ echo "All tests passed [OK]"
+ return 0
+}
+
+test_run() {
+ if ! run_server; then
+ echo "Failed to start server" 1>&2
+ return 1
+ fi
+ do_test_run
+ ret=$?
+ if [[ -s $TESTDIR/server.pid ]]; then
+ kill -TERM "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+ return $ret
+}
+
+test_check() {
+ if ! command -v tgtd &> /dev/null || ! command -v tgtadm &> /dev/null; then
+ echo "Need tgtd and tgtadm from scsi-target-utils"
+ return 1
+ fi
+}
+
+test_setup() {
+ # Create what will eventually be the client root filesystem onto an overlay
+ "$DRACUT" -l --keep --tmpdir "$TESTDIR" \
+ -m "test-root" \
+ -i ./client-init.sh /sbin/init \
+ -I "ip ping grep setsid" \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh" \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh" \
+ --no-hostonly --no-hostonly-cmdline --nohardlink \
+ -f "$TESTDIR"/initramfs.root "$KVERSION" || return 1
+ mkdir -p "$TESTDIR"/overlay/source && mv "$TESTDIR"/dracut.*/initramfs/* "$TESTDIR"/overlay/source && rm -rf "$TESTDIR"/dracut.*
+
+ mkdir -p -- "$TESTDIR"/overlay/source/var/lib/nfs/rpc_pipefs
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "test-makeroot crypt lvm mdraid" \
+ -I "mkfs.ext4 setsid blockdev" \
+ -i ./create-client-root.sh /lib/dracut/hooks/initqueue/01-create-client-root.sh \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/singleroot.img singleroot 200
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid0-1.img raid0-1 100
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid0-2.img raid0-2 100
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/fakeroot rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ rm -- "$TESTDIR"/marker.img
+
+ # Create what will eventually be the server root filesystem onto an overlay
+ "$DRACUT" -l --keep --tmpdir "$TESTDIR" \
+ -m "test-root network network-legacy" \
+ -d "iscsi_tcp crc32c ipv6" \
+ -i ./server-init.sh /sbin/init \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh" \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh" \
+ -I "modprobe chmod ip ping tcpdump setsid pidof tgtd tgtadm /etc/passwd" \
+ --install-optional "/etc/netconfig dhcpd /etc/group /etc/nsswitch.conf /etc/rpc /etc/protocols /etc/services /usr/etc/nsswitch.conf /usr/etc/rpc /usr/etc/protocols /usr/etc/services" \
+ -i "./hosts" "/etc/hosts" \
+ -i "./dhcpd.conf" "/etc/dhcpd.conf" \
+ --no-hostonly --no-hostonly-cmdline --nohardlink \
+ -f "$TESTDIR"/initramfs.root "$KVERSION" || return 1
+ mkdir -p "$TESTDIR"/overlay/source && mv "$TESTDIR"/dracut.*/initramfs/* "$TESTDIR"/overlay/source && rm -rf "$TESTDIR"/dracut.*
+
+ mkdir -p "$TESTDIR"/overlay/source/var/lib/dhcpd
+
+ # second, install the files needed to make the root filesystem
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "test-makeroot" \
+ -I "mkfs.ext4" \
+ -i ./create-server-root.sh /lib/dracut/hooks/initqueue/01-create-server-root.sh \
+ --nomdadmconf \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.img root 240
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/dracut/root rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ rm -- "$TESTDIR"/marker.img
+
+ # Make server's dracut image
+ "$DRACUT" -l \
+ -a "dash rootfs-block test kernel-modules network network-legacy" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod e1000 drbg" \
+ -i "./server.link" "/etc/systemd/network/01-server.link" \
+ -i ./wait-if-server.sh /lib/dracut/hooks/pre-mount/99-wait-if-server.sh \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.server "$KVERSION" || return 1
+
+ # Make client's dracut image
+ test_dracut \
+ --add "$USE_NETWORK" \
+ --include "./client.link" "/etc/systemd/network/01-client.link" \
+ --kernel-cmdline "rw rd.auto rd.retry=50" \
+ "$TESTDIR"/initramfs.testing
+}
+
+test_cleanup() {
+ if [[ -s $TESTDIR/server.pid ]]; then
+ kill -TERM "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-30-ISCSI/wait-if-server.sh b/test/TEST-30-ISCSI/wait-if-server.sh
new file mode 100755
index 0000000..b53e41f
--- /dev/null
+++ b/test/TEST-30-ISCSI/wait-if-server.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+. /lib/net-lib.sh
+wait_for_if_link enx525400123456
+wait_for_if_link enx525400123457