From 3c12f005f809542c8c1eab59c2c56def3fc497f7 Mon Sep 17 00:00:00 2001
From: Harald Hoyer <harald@redhat.com>
Date: Wed, 28 Mar 2012 15:42:36 +0200
Subject: [PATCH] iscsi: add support for interface binding

The iscsi root parameter can now have "<iscsi_iface_name>" and
"<netdev_name>".

netroot=iscsi:[<servername>]:[<protocol>]:[<port>]:[<iscsi_iface_name>]:[<netdev_name>]:[<LUN>]:<targetname>
---
 dracut.8                             |  15 ++++-
 modules.d/95iscsi/iscsiroot          | 120 ++++++++++++++---------------------
 modules.d/95iscsi/parse-iscsiroot.sh |  10 ++-
 modules.d/99base/dracut-lib.sh       |  85 +++++++++++++++++++++++++
 4 files changed, 149 insertions(+), 81 deletions(-)

diff --git a/dracut.8 b/dracut.8
index bdd8959..a380203 100644
--- a/dracut.8
+++ b/dracut.8
@@ -341,7 +341,7 @@ Set the NFSv4 domain name. Will overwrite the settings in /etc/idmap.conf.
 .SS iSCSI
 .TP
 .ad l
-.BR root= "\%iscsi:[username:password\:[:reverse:password]@]\:[<servername>]\::[<protocol>]\::[<port>]\::[<LUN>]\::<targetname>"
+.BR root= "\%iscsi:[username:password\:[:reverse:password]@]\:[<servername>]\::[<protocol>]\::[<port>]\:[:[<iscsi_iface_name>]:[<netdev_name>]]\::[<LUN>]\::<targetname>"
 .ad
 protocol defaults to "6", LUN defaults to "0".
 
@@ -356,7 +356,7 @@ http://tools.ietf.org/html/rfc4173
 
 .TP
 .ad l
-.BR root= "\%iscsi:[username:password\:[:reverse:password]@]\:[<servername>]\::[<protocol>]\::[<port>]\::[<LUN>]\::<targetname>"
+.BR root= "\%iscsi:[username:password\:[:reverse:password]@]\:[<servername>]\::[<protocol>]\::[<port>]\:[:[<iscsi_iface_name>]:[<netdev_name>]]\::[<LUN>]\::<targetname>"
 .ad
 e.g. root=iscsi:192.168.50.1::::iqn.2009-06.dracut:target0
 
@@ -366,7 +366,7 @@ If servername is an IPv6 address, it has to be put in brackets.
   root=iscsi:[2001:DB8::1]::::iqn.2009-06.dracut:target0
 .TP
 .ad l
-.BR root= ??? " netroot=" "\%iscsi:[username:password[:reverse:password]@]\:[<servername>]\::[<protocol>]\::[<port>]\::[<LUN>]\::<targetname> ..."
+.BR root= ??? " netroot=" "\%iscsi:[username:password[:reverse:password]@]\:[<servername>]\::[<protocol>]\::[<port>]\:[:[<iscsi_iface_name>]:[<netdev_name>]]\::[<LUN>]\::<targetname> ..."
 .ad
 multiple netroot options allow setting up multiple iscsi disks
  e.g. 
@@ -388,6 +388,15 @@ manually specify all iscsistart parameter (see \fIiscsistart\ --help\fR)
 .BR root= ??? " netroot=iscsi iscsi_firmware"
 will read the iscsi parameter from the BIOS firmware
 
+.TP
+.BR iscsi_param= <param>
+<param> will be passed as "--param <param>" to iscsistart.
+This parameter can be specified multiple times.
+ e.g.:
+   "netroot=iscsi iscsi_firmware iscsi_param=node.session.timeo.replacement_timeout=30"
+   will result in
+   iscsistart -b --param node.session.timeo.replacement_timeout=30
+
 .SS FCoE
 .TP
 .BR fcoe=<edd|interface|MAC>:<dcb|nodcb>
diff --git a/modules.d/95iscsi/iscsiroot b/modules.d/95iscsi/iscsiroot
index f0c969f..dc93f7e 100755
--- a/modules.d/95iscsi/iscsiroot
+++ b/modules.d/95iscsi/iscsiroot
@@ -45,84 +45,56 @@ if getarg iscsi_firmware ; then
         # if root is not specified try to mount the whole iSCSI LUN
 	printf 'ENV{DEVTYPE}!="partition", SYMLINK=="disk/by-path/*-iscsi-*-*", SYMLINK+="root"\n' >> /etc/udev/rules.d/99-iscsi-root.rules
     fi
-    iscsistart -b
-    exit 0
-fi
 
-unset iscsi_initiator iscsi_target_name iscsi_target_ip iscsi_target_port
-unset iscsi_target_group iscsi_protocol iscsirw iscsi_lun
-unset iscsi_username iscsi_password
-unset iscsi_in_username iscsi_in_password
+    for p in $(getargs iscsi_param); do
+	iscsi_param="$iscsi_param --param $p"
+    done
 
-# override conf settings by command line options
-arg=$(getarg iscsi_initiator)
-[ -n "$arg" ] && iscsi_initiator=$arg
-arg=$(getarg iscsi_target_name)
-[ -n "$arg" ] && iscsi_target_name=$arg
-arg=$(getarg iscsi_target_ip)
-[ -n "$arg" ] && iscsi_target_ip=$arg
-arg=$(getarg iscsi_target_port)
-[ -n "$arg" ] && iscsi_target_port=$arg
-arg=$(getarg iscsi_target_group)
-[ -n "$arg" ] && iscsi_target_group=$arg
-arg=$(getarg iscsi_username)
-[ -n "$arg" ] && iscsi_username=$arg
-arg=$(getarg iscsi_password)
-[ -n "$arg" ] && iscsi_password=$arg
-arg=$(getarg iscsi_in_username)
-[ -n "$arg" ] && iscsi_in_username=$arg
-arg=$(getarg iscsi_in_password)
-[ -n "$arg" ] && iscsi_in_password=$arg
+    iscsistart -b $iscsi_param
+    exit 0
+fi
 
 handle_netroot()
 {
-    iroot=$1
-    # override conf/commandline options by dhcp root_path
-    # FIXME this assumes that all values have been provided
-    OLDIFS="$IFS"
-    IFS=@
-    set $iroot
-    if [ $# -gt 1 ]; then
-	authinfo=$1; shift
-	iroot=$*
-    # allow empty authinfo to allow having an @ in iscsi_target_name like this:
-    # netroot=iscsi:@192.168.1.100::3260::iqn.2009-01.com.example:testdi@sk
-	if [ -n "$authinfo" ]; then
-            IFS=:
-            set $authinfo
-            iscsi_username=$1
-            iscsi_password=$2
-            if [ $# -gt 2 ]; then
-		iscsi_in_username=$3
-		iscsi_in_password=$4
-            fi
-	fi
+    local iscsi_initiator iscsi_target_name iscsi_target_ip iscsi_target_port
+    local iscsi_target_group iscsi_protocol iscsirw iscsi_lun
+    local iscsi_username iscsi_password
+    local iscsi_in_username iscsi_in_password
+    local iscsi_iface_name iscsi_netdev_name
+    local iscsi_param
+    local p
+# override conf settings by command line options
+    arg=$(getarg iscsi_initiator)
+    [ -n "$arg" ] && iscsi_initiator=$arg
+    arg=$(getarg iscsi_target_name)
+    [ -n "$arg" ] && iscsi_target_name=$arg
+    arg=$(getarg iscsi_target_ip)
+    [ -n "$arg" ] && iscsi_target_ip=$arg
+    arg=$(getarg iscsi_target_port)
+    [ -n "$arg" ] && iscsi_target_port=$arg
+    arg=$(getarg iscsi_target_group)
+    [ -n "$arg" ] && iscsi_target_group=$arg
+    arg=$(getarg iscsi_username)
+    [ -n "$arg" ] && iscsi_username=$arg
+    arg=$(getarg iscsi_password)
+    [ -n "$arg" ] && iscsi_password=$arg
+    arg=$(getarg iscsi_in_username)
+    [ -n "$arg" ] && iscsi_in_username=$arg
+    arg=$(getarg iscsi_in_password)
+    [ -n "$arg" ] && iscsi_in_password=$arg
+
+    for p in $(getargs iscsi_param); do
+	iscsi_param="$iscsi_param --param $p"
+    done
+
+    parse_iscsi_root "$1" || return 1
+
+    if [ -n "$iscsi_netdev_name" ] && [ "$netif" != "$iscsi_netdev_name" ]; then
+        # skip iscsistart, if the interface is not the required one
+    	return 1
     fi
 
-    IFS="$OLDIFS"
-
-    local v=${iroot}:
-    local i
-    set --
-    while [ -n "$v" ]; do
-	if [ "${v#\[*:*:*\]:}" != "$v" ]; then
-	# handle IPv6 address
-	    i="${v%%\]:*}"
-	    i="${i##\[}"
-	    set -- "$@" "$i"
-	    v=${v#\[$i\]:}
-	else
-	    set -- "$@" "${v%%:*}"
-	    v=${v#*:}
-	fi
-    done
-    iscsi_target_ip=$1; shift
-    iscsi_protocol=$1; shift # ignored
-    iscsi_target_port=$1; shift
-    iscsi_lun=$1; shift
-    IFS=:
-    iscsi_target_name=$*
-    IFS="$OLDIFS"
+
 # XXX is this needed?
     getarg ro && iscsirw=ro
     getarg rw && iscsirw=rw
@@ -182,7 +154,11 @@ handle_netroot()
 	${iscsi_username+-u $iscsi_username} \
 	${iscsi_password+-w $iscsi_password} \
 	${iscsi_in_username+-U $iscsi_in_username} \
-	${iscsi_in_password+-W $iscsi_in_password} || :
+	${iscsi_in_password+-W $iscsi_in_password} \
+	${iscsi_iface_name+--param iface.iscsiname=$iscsi_iface_name} \
+	${iscsi_netdev_name+--param iface.net_ifacename=$iscsi_netdev_name} \
+	${iscsi_param} \
+	|| :
 
 # install mount script
     if [ -n "${root%%block:*}" ]; then
diff --git a/modules.d/95iscsi/parse-iscsiroot.sh b/modules.d/95iscsi/parse-iscsiroot.sh
index 66bd268..5b5ac92 100755
--- a/modules.d/95iscsi/parse-iscsiroot.sh
+++ b/modules.d/95iscsi/parse-iscsiroot.sh
@@ -4,6 +4,9 @@
 #	root=iscsi:[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
 #	[root=*] netroot=iscsi:[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
 #
+#       or netroot=iscsi:[<servername>]:[<protocol>]:[<port>]:[<iscsi_iface_name>]:[<netdev_name>]:[<LUN>]:<targetname>
+#
+#
 # Legacy formats:
 #	[net]root=[iscsi] iscsiroot=[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
 # 	[net]root=[iscsi] iscsi_firmware
@@ -66,12 +69,7 @@ fi
 # If it's not iscsi we don't continue
 [ "${netroot%%:*}" = "iscsi" ] || return
 
-# Check required arguments. there's only one, but it's at the end
-if [ -z "$iscsi_firmware" ] ; then
-    case "${netroot##iscsi:*:*:*:*:}" in
-	$netroot|'') die "Argument targetname for iscsiroot is missing";;
-    esac
-fi
+parse_iscsi_root "$netroot" || return
 
 # ISCSI actually supported?
 if ! [ -e /sys/module/iscsi_tcp ]; then
diff --git a/modules.d/99base/dracut-lib.sh b/modules.d/99base/dracut-lib.sh
index 42d4dce..f29e719 100644
--- a/modules.d/99base/dracut-lib.sh
+++ b/modules.d/99base/dracut-lib.sh
@@ -256,3 +256,88 @@ killproc() {
         fi
     done
 }
+
+parse_iscsi_root()
+{
+    local v
+    v=${1#iscsi:}
+
+# extract authentication info
+    case "$v" in
+	*@*:*:*:*:*)
+	    authinfo=${v%%@*}
+	    v=${v#*@}
+    # allow empty authinfo to allow having an @ in iscsi_target_name like this:
+    # netroot=iscsi:@192.168.1.100::3260::iqn.2009-01.com.example:testdi@sk
+	    if [ -n "$authinfo" ]; then
+		OLDIFS="$IFS"
+		IFS=:
+		set $authinfo
+		IFS="$OLDIFS"
+		if [ $# -gt 4 ]; then
+		    warn "Wrong authentication info in iscsi: parameter!"
+		    return 1
+		fi
+		iscsi_username=$1
+		iscsi_password=$2
+		if [ $# -gt 2 ]; then
+		    iscsi_in_username=$3
+		    iscsi_in_password=$4
+		fi
+	    fi
+	    ;;
+    esac
+
+# extract target ip
+    case "$v" in
+	[[]*[]]:*)
+	    iscsi_target_ip=${v#[[]}
+		iscsi_target_ip=${iscsi_target_ip%%[]]*}
+	    v=${v#[[]$iscsi_target_ip[]]:}
+	    ;;
+	*)
+	    iscsi_target_ip=${v%%[:]*}
+	    v=${v#$iscsi_target_ip:}
+	    ;;
+    esac
+
+# extract target name
+    case "$v" in
+	*:iqn.*)
+	    iscsi_target_name=iqn.${v##*:iqn.}
+	    v=${v%:iqn.*}:
+	    ;;
+	*:eui.*)
+	    iscsi_target_name=iqn.${v##*:eui.}
+	    v=${v%:iqn.*}:
+	    ;;
+	*:naa.*)
+	    iscsi_target_name=iqn.${v##*:naa.}
+	    v=${v%:iqn.*}:
+	    ;;
+	*)
+	    warn "Invalid iscii target name, should begin with 'iqn.' or 'eui.' or 'naa.'"
+	    return 1
+	    ;;
+    esac
+
+# parse the rest
+    OLDIFS="$IFS"
+    IFS=:
+    set $v
+    IFS="$OLDIFS"
+
+    iscsi_protocol=$1; shift # ignored
+    iscsi_target_port=$1; shift
+    if [ $# -eq 3 ]; then
+	iscsi_iface_name=$1; shift
+    fi
+    if [ $# -eq 2 ]; then
+	iscsi_netdev_name=$1; shift
+    fi
+    iscsi_lun=$1; shift
+    if [ $# -ne 0 ]; then
+	warn "Invalid parameter in iscsi: parameter!"
+	return 1
+    fi
+}
-- 
1.8.3.1

