File fcoe-utils-git-update of Package open-fcoe

diff --git a/.gitignore b/.gitignore
index 974800d..a15894c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,7 @@ fcoeplumb
 *.o
 fcoeadm
 fcoemon
+fipvlan
 etc/initd/fcoe
 
 # build.sh generated files
diff --git a/INSTALL b/INSTALL
index 9974167..c974282 100644
--- a/INSTALL
+++ b/INSTALL
@@ -17,7 +17,23 @@ DEPENDENCIES
 * automake
 * libtool
 
-1) Bootstrap, configure, make and make install
+1) Ensure correct header files
+   - fcping requires certain kernel header files to be exported
+     or it cannot be compiled. These header files were included
+     in the 2.6.31 Linux kernel. If you are using a distribution
+     kernel that is >= 2.6.31 then you shouldn't have to take
+     this step. If you are building your own kernel or are using
+     a pre-2.6.31 kernel you will need to take this step to
+     export the appropriate header files.
+
+   # pushd <kernel-src>
+   # make headers_install
+   # PWD=`pwd`
+   # ln -s $PWD/usr/include/scsi/scsi_bsg_fc.h /usr/include/scsi/
+   # ln -s $PWD/usr/include/scsi/fc /usr/include/scsi/fc
+   # popd
+
+2) Bootstrap, configure, make and make install
    # ./bootstrap.sh
    # rpm --eval "%configure" | sh
    # make
diff --git a/Makefile.am b/Makefile.am
index 5d896bf..3f8ac7f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,11 +1,11 @@
 ## target programs, to be built and installed in $(prefix)/sbin
-sbin_PROGRAMS = fcoeadm
+sbin_PROGRAMS = fcoeadm fcping fipvlan fcnsq
 if WITH_DCB
 sbin_PROGRAMS += fcoemon
 endif
 
 ## all targets should look for headers in the include directory
-AM_CPPFLAGS = -I${srcdir}/include
+AM_CPPFLAGS = -Wall -I${srcdir}/include
 
 ## pass the sysconfdir into the C proprocessor
 AM_CFLAGS = -DSYSCONFDIR="\"${sysconfdir}\""
@@ -19,6 +19,14 @@ include/fc_scsi.h include/fc_types.h include/net_types.h
 fcoeadm_CFLAGS = $(HBAAPI_CFLAGS)
 fcoeadm_LDFLAGS = $(HBAAPI_LIBS)
 
+## rules for building fcping
+## only listed sources get packaged, so must list all headers too
+fcping_SOURCES = fcping.c
+
+## fcping uses HBAAPI, so get the right flags for compiling and linking
+fcping_CFLAGS = $(HBAAPI_CFLAGS)
+fcping_LDFLAGS = $(HBAAPI_LIBS) -lrt
+
 ## rules for building fcoemon
 ## only listed sources get packaged, so must list all headers too
 fcoemon_SOURCES = fcoemon_utils.c fcoemon.c fcoemon.h fcoemon_utils.h \
@@ -28,6 +36,10 @@ include/fc_scsi.h include/fc_types.h include/net_types.h
 fcoemon_CFLAGS = $(DCBD_CFLAGS)
 fcoemon_LDFLAGS = -lrt
 
+## rules for building fipvlan
+## only listed sources get packaged, so must list all headers too
+fipvlan_SOURCES = fipvlan.c include/fip.h log.c include/log.h include/list.h
+
 ## install configuration file in $(prefix)/etc/fcoe
 fcoe_configdir = ${sysconfdir}/fcoe
 dist_fcoe_config_DATA = etc/config etc/cfg-ethx
diff --git a/QUICKSTART b/QUICKSTART
index a711390..e2f91c2 100644
--- a/QUICKSTART
+++ b/QUICKSTART
@@ -117,7 +117,23 @@ DEPENDENCIES
 
 PROCESS
 
-1) Bootstrap, configure, make and make install
+1) Ensure correct header files
+   - fcping requires certain kernel header files to be exported
+     or it cannot be compiled. These header files were included
+     in the 2.6.31 Linux kernel. If you are using a distribution
+     kernel that is >= 2.6.31 then you shouldn't have to take
+     this step. If you are building your own kernel or are using
+     a pre-2.6.31 kernel you will need to take this step to
+     export the appropriate header files.
+
+   # pushd <kernel-src>
+   # make headers_install
+   # PWD=`pwd`
+   # ln -s $PWD/usr/include/scsi/scsi_bsg_fc.h /usr/include/scsi/
+   # ln -s $PWD/usr/include/scsi/fc /usr/include/scsi/fc
+   # popd
+
+2) Bootstrap, configure, make and make install
    # ./bootstrap.sh
    # rpm --eval "%configure" | sh
    # make
diff --git a/debug/dcbcheck.sh b/debug/dcbcheck.sh
index 05bc88a..5f30bd0 100755
--- a/debug/dcbcheck.sh
+++ b/debug/dcbcheck.sh
@@ -4,6 +4,13 @@
 # return 1 on failure
 
 IFNAME=$1
+
+if [ -r /proc/net/vlan/$IFNAME ] ; then
+	PHYSDEV=$(grep '^Device:' /proc/net/vlan/$IFNAME | awk '{print $2}')
+else
+	PHYSDEV=$IFNAME
+fi
+
 ret=0
 
 # check for DCB netlink symbols
@@ -26,29 +33,29 @@ if ifconfig ${IFNAME} 2>&1 | grep -q "Device not found" ; then
 fi
 
 # Determine if we can communicate with DCBD
-if dcbtool gc ${IFNAME} dcb | grep Status | grep -q Failed ; then
+if dcbtool gc ${PHYSDEV} dcb | grep Status | grep -q Failed ; then
     echo "Unable to communicate with the DCB daemon (dcbd) or DCB capable driver." >&2
     exit 1
 fi
 
 # Determine if DCB is on
-if dcbtool gc ${IFNAME} dcb | grep 'DCB State' | grep -q off ; then
+if dcbtool gc ${PHYSDEV} dcb | grep 'DCB State' | grep -q off ; then
     echo "DCB is not on, execute the following command to turn it on" >&2
-    echo "dcbtool sc ${IFNAME} dcb on" >&2
+    echo "dcbtool sc ${PHYSDEV} dcb on" >&2
     ret=1
 fi
 
 # Determine if PFC is enabled
-if dcbtool gc ${IFNAME} pfc | grep Enable | grep -q false ; then
+if dcbtool gc ${PHYSDEV} pfc | grep Enable | grep -q false ; then
     echo "PFC is not enabled, execute the following command to turn it on" >&2
-    echo "dcbtool sc ${IFNAME} pfc e:1" >&2
+    echo "dcbtool sc ${PHYSDEV} pfc e:1" >&2
     ret=1
 fi
 
 # Determine if the FCoE APP TLV is enabled
-if dcbtool gc ${IFNAME} app:fcoe | grep Enable | grep -q false ; then
+if dcbtool gc ${PHYSDEV} app:fcoe | grep Enable | grep -q false ; then
     echo "The FCoE APP TLV is not enabled, execute the following command to turn it on" >&2
-    echo "dcbtool sc ${IFNAME} app:fcoe e:1" >&2
+    echo "dcbtool sc ${PHYSDEV} app:fcoe e:1" >&2
     ret=1
 fi
 
diff --git a/debug/fcc.sh b/debug/fcc.sh
new file mode 100755
index 0000000..053e3bd
--- /dev/null
+++ b/debug/fcc.sh
@@ -0,0 +1,499 @@
+#! /bin/bash
+#
+# Copyright 2008-2009 Cisco Systems, Inc.  All rights reserved.
+#
+# This program is free software; you may redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# Author: Joe Eykholt (jeykholt at cisco dot com)
+#
+# Please send comments and changes to jeykholt at cisco dot com
+
+VERSION="fcc v1.0.2 10/07/2009"
+
+fcoe_dir=/sys/module/fcoe
+fdir=/sys/class/fc_host
+sdir=/sys/class/scsi_host
+cmdname=`basename $0`
+
+usage() {
+cat <<USAGE
+usage: $cmdname '[<cmd> [<hba> ...]]'
+
+cmd:
+	create 		Start FCoE on an ethernet interface.
+	delete / del	Delete an FCoE instance
+	destroy 	Same as destroy
+	enable / en	Same as create
+	help		Show this usage message
+	info		Show HBA detailed info
+	list		List the HBAs with remote port and LUN status
+	luns		Show LUN list and status
+	stats		Show HBA statistics
+	reset		Reset the HBA
+	version		Show version
+USAGE
+}
+
+verify_hba() {
+	local x=$1
+
+	if [ ! -d $fdir/$x ]
+	then
+		echo "$cmdname: invalid HBA name $x" >&2
+		exit
+	fi
+}
+
+hba_stats() {
+	local x=$1
+	local y
+
+	verify_hba $x
+
+	printf "\n$x Statistics:\n"
+	(
+		cd $fdir/$x/statistics
+		for y in *
+		do
+			#
+			# avoid the write-only files.
+			# test -r doesn't help if we're root
+			#
+			if [ "$y" == "reset_statistics" ]
+			then
+				continue
+			fi
+			val=`cat $y`
+			if [ "$val" != 0xffffffffffffffff ]
+			then
+				printf "%-30s%8lld\n" $y: $val
+			fi
+		done
+	)
+}
+
+
+#
+# format a /sys file containing a hex WWN or FC_ID
+# from 0x123456\n to 12:34:56 or from 0xffffffff to - (unknown WWN or FC_ID)
+#
+fmt_hex() {
+	sed -e 's/^0xff*$/-/' -e 's/0x//' -e 's/../&:/g' -e 's/:$//' < $1
+}
+
+rport_list() {
+	local x
+	local hba=$1
+
+	rdir=/sys/class/fc_remote_ports
+	host=`echo $hba | sed -e 's/host//'`
+	rports=`ls -d $rdir/rport-$host:* 2>/dev/null`
+	if [ -z "$rports" ]
+	then
+		return
+	fi
+
+	printf "\n$hba Remote Ports:\n"
+
+
+	fmt="%-8s  %-23s  %-8s  %-8s  %-15s\n"
+
+	printf "$fmt" Path "Port Name" "Port ID" State Roles
+	for x in $rports
+	do
+		btl="`echo $x | sed -e 's/.*-\(.*-.*\)/\1/'`"
+		printf "$fmt" "$btl" \
+			"`fmt_hex $x/port_name`" \
+			"`fmt_hex $x/port_id`" \
+			"`cat $x/port_state`" \
+			"`cat $x/roles`"
+	done
+}
+
+#
+# Translate capacity to readable units.
+# compute sizes in base-10 marketing units
+#
+lun_cap() {
+	local sizek=0 sizem=0 sizeg=0 sizet=0
+	local size
+	local onek=1000
+	local cap
+
+	size=$1
+	let sizek="$size * 512 / $onek"
+	let sizem="$sizek / $onek"
+	let sizeg="$sizem / $onek"
+	let sizet="$sizeg / $onek"
+	if [ "$sizet" -gt 0 ]
+	then
+		cap="$sizet TB"
+	elif [ "$sizeg" -gt 0 ]
+	then
+		cap="$sizeg GB"
+	elif [ "$sizem" -gt 0 ]
+	then
+		cap="$sizem MB"
+	else
+		cap="$sizek KB"
+	fi
+	echo $cap
+}
+
+lun_list() {
+	local x
+	local lun
+	local hba=$1
+	local ddir=/sys/class/scsi_device
+
+	host=`echo $hba | sed -e 's/host//'`
+
+	local luns=`(cd $ddir && ls -d $host:*) 2>/dev/null`
+
+	if [ -z "$luns" ]
+	then
+		return
+	fi
+
+	printf "\n$hba LUNs:\n"
+
+	fmt="%-10s %-8s %6s   %-15s %-20s %-8s\n"
+
+	printf "$fmt" Path Device Size Vendor Model State
+
+	for lun in $luns
+	do
+		(
+			local size=0
+			cap=-
+
+			cd $ddir/$lun/device
+			if [ -d block ]
+			then
+				dev=`ls block | tail -1`
+				size=`cat block/$dev/size`
+				cap=`lun_cap $size`
+			elif [ -d char ]
+			then
+				dev=`ls char | tail -1`
+				cap=-
+			elif [ -d scsi_tape ]
+			then
+				dev=`ls scsi_tape | egrep '^st[0-9]*$'`
+				cap=-
+			elif [ -d scsi_generic ]
+			then
+				dev=`ls scsi_generic | tail -1`
+				cap=-
+			else
+				dev="`ls -d block:* char:* 2>/dev/null |
+					sed -e 's/.*\://'`"
+				if [ -L block:$dev -o -d block:$dev ]
+				then
+					size=`cat block:$dev/size`
+					cap=`lun_cap $size`
+				fi
+			fi
+
+			printf "$fmt" "$lun" "$dev" "$cap"\
+				"`cat vendor`" \
+				"`cat model`" \
+				"`cat state`"
+		)
+	 done
+}
+
+sym_name() {
+	local hba=$1
+	local file
+
+	file=$fdir/$hba/symbolic_name
+	if [ -f "$file" ]
+	then
+		sed -e 's/.*over //' < $file
+	else
+		exit 1
+	fi
+}
+
+#
+# Lookup the host name for a given symbolic name
+#
+hba_name() {
+	local sym=$1
+	local hba
+
+	if [ -d "$fdir/$sym" ]
+	then
+		echo $sym
+		exit
+	fi
+
+	for hba in $all_hbas
+	do
+		if [ "`sym_name $hba`" = "$sym" ]
+		then
+			echo $hba
+			exit
+		fi
+	done
+	exit 1
+}
+
+hba_state() {
+	local x
+
+	echo "FC HBAs:"
+	fmt="%-8s  %-23s  %-8s  %-8s  %-15s\n"
+	printf "$fmt" HBA "Port Name" "Port ID" State Device
+	for x in $hbas
+	do
+		(
+			cd $fdir/$x
+			printf "$fmt" "$x" \
+				"`fmt_hex $fdir/$x/port_name`" \
+				"`fmt_hex $fdir/$x/port_id`" \
+				"`cat $fdir/$x/port_state`" \
+				"`sym_name $x`"
+		) 2>/dev/null
+	done
+}
+
+hba_info() {
+	local x=`hba_name $1`
+	local fmt="\t%-20s %s\n"
+
+	verify_hba $x
+	printf "\n$x Info:\n"
+	(
+		cd $fdir/$x
+
+		printf "$fmt" "Symbolic Name" "`cat symbolic_name`"
+		printf "$fmt" "Port Name" "`fmt_hex port_name`"
+		printf "$fmt" "Node Name" "`fmt_hex node_name`"
+		printf "$fmt" "Port Type" "`cat port_type`"
+		echo
+		printf "$fmt" "Port State" "`cat port_state`"
+		printf "$fmt" "Port ID" "`fmt_hex port_id`"
+		printf "$fmt" "Fabric Name" "`fmt_hex fabric_name`"
+		echo
+		printf "$fmt" "Max Frame Size" "`cat maxframe_size`"
+		printf "$fmt" "Speed" "`cat speed`"
+		echo
+	)
+}
+
+scsi_state() {
+	local x
+	local dev
+
+	printf "\nSCSI States:\n"
+	fmt="%-8s  %-10s  %-15s  %-10s  %8s\n"
+	printf "$fmt" HBA Device Mode State Busy
+	for x in $scsi_hbas
+	do
+		(
+			cd $fdir/$x
+			dev="`cat $sdir/$x/proc_name``cat $sdir/$x/unique_id`"
+			printf "$fmt" "$x" "$dev" \
+				"`cat $sdir/$x/active_mode`" \
+				"`cat $sdir/$x/state`" \
+				"`cat $sdir/$x/host_busy`"
+		) 2>/dev/null
+	done
+}
+
+hba_list() {
+	local x
+
+	hba_state
+	#  scsi_state
+
+	for x in $hbas
+	do
+		rport_list $x
+		lun_list $x
+	done
+}
+
+#
+# Do a command for a list of arguments
+#
+repeat() {
+	local cmd=$1
+	local x
+	shift
+
+	for x
+	do
+		$cmd $x
+	done
+}
+
+fcoe_ctl() {
+	local hba=$1
+	local cmd=$2
+	local file=$fcoe_dir/parameters/$2
+	local old_file=$fcoe_dir/$2
+
+	if [ -w "$old_file" ]
+	then
+		file=$old_file
+	fi
+
+	if [ -w "$file" ]
+	then
+		echo $hba > $file
+	elif [ -f "$file" ]
+	then
+		echo "$cmdname: no permission to $cmd $hba" >&2
+	else
+		echo "$cmdname: file $file doesn't exist.  " \
+			"Check for fcoe module." >&2
+	fi
+}
+
+fc_host_ctl() {
+	local hba=$1
+	local cmd=$2
+	local value=$3
+	local file
+	local dir
+
+	dir=$fdir/$hba
+	if [ ! -d "$dir" ]
+	then
+		dir=$fdir/`hba_name $hba`
+		if [ $? != 0 ]
+		then
+			echo "$cmdname: hba $hba not found" >&2
+			exit 1
+		fi
+	fi
+	file=$dir/$cmd
+
+	if [ -w "$file" ]
+	then
+		echo $value > $file
+	elif [ -f "$file" ]
+	then
+		echo "$cmdname: no permission to $cmd $hba" 1>&2
+		exit 2
+	else
+		echo "$cmdname: $cmd not supported for $hba" 1>&2
+		exit 2
+	fi
+}
+
+#
+# Start of main script code.
+#
+scsi_hbas=
+if [ -d "$sdir" ]
+then
+	scsi_hbas=`ls $sdir 2>/dev/null`
+fi
+if [ -d "$fdir" ]
+then
+	all_hbas=`ls $fdir 2>/dev/null`
+fi
+hbas="$all_hbas"
+
+if [ $# -lt 1 ]
+then
+	hba_list
+	exit 0
+fi
+
+if [ $# -lt 2 ]
+then
+	cmd=$1
+	hba_spec=no
+else
+	cmd=$1
+	hba=$2
+	shift
+	if [ $# -eq 0 ]
+	then
+		hba_spec=n
+	elif [ $# -eq 1 -a "$1" = all ]
+	then
+		hba_spec=y
+	else
+		hba_spec=y
+		hbas="$@"
+		scsi_hbas="$@"
+	fi
+fi
+
+case "$cmd" in
+	create | enable | en)
+		if [ ! -d $fcoe_dir ]
+		then
+			modprobe fcoe
+			echo "$cmdname: loading fcoe module" >&2
+			sleep 1
+			if [ ! -d $fcoe_dir ]
+			then
+				echo "$cmdname: $fcoe_dir not found" >&2
+				exit 2
+			fi
+		fi
+		fcoe_ctl $hba create
+		;;
+	delete | del | destroy)
+		if [ ! -d $fcoe_dir ]
+		then
+			echo "$cmdname: $fcoe_dir not found" >&2
+			exit 2
+		fi
+		fcoe_ctl $hba destroy
+		;;
+	info)
+		repeat hba_info $hbas
+		;;
+	list)
+		hba_list
+		;;
+	lun*)
+		repeat lun_list $hbas
+		;;
+	names)
+		repeat echo $hbas
+		;;
+	stat*)
+		repeat hba_stats $hbas
+		;;
+	realname)
+		hba_name $hba
+		;;
+	reset)
+		if [ "$hba_spec" != y ]
+		then
+			echo "$cmdname: reset requires hba name" >&2
+			exit 2
+		fi
+		for hba in $hbas
+		do
+			(fc_host_ctl $hba issue_lip 1 &&
+				 echo "reset $hba") || break
+		done
+		;;
+	version)
+		echo $VERSION
+		;;
+	*)
+		usage
+		;;
+esac
diff --git a/debug/fcoedump.sh b/debug/fcoedump.sh
index 972f1cd..3c4bac6 100755
--- a/debug/fcoedump.sh
+++ b/debug/fcoedump.sh
@@ -9,6 +9,14 @@ if [ -z $1 ]; then
     exit 1;
 fi
 
+DEVICE=$1
+
+if [ -r /proc/net/vlan/$DEVICE ] ; then
+	PHYSDEV=$(grep '^Device:' /proc/net/vlan/$DEVICE | awk '{print $2}')
+else
+	PHYSDEV=$DEVICE
+fi
+
 kernel_info () {
 	echo -e "\n###KERNEL INFO###"
 	uname -a
@@ -27,55 +35,68 @@ system_info () {
 }
 
 adapter_info () {
-	echo -e "\n###Adapter INFO"
+	if [ $DEVICE != $PHYSDEV ]
+	then
+		echo -e "\n###Adapter INFO VLAN $DEVICE"
+		echo -e "#ethtool:"
+		ethtool $DEVICE
+		echo -e "#ethtool interface:"
+		ethtool -i $DEVICE
+		echo -e "#ethtool offloads:"
+		ethtool -k $DEVICE
+		echo -e "#ifconfig:"
+		ifconfig $DEVICE
+	fi
+
+	echo -e "\n###Adapter INFO $PHYSDEV"
 	echo -e "#ethtool:"
-	ethtool $1
+	ethtool $PHYSDEV
 	echo -e "#ethtool interface:"
-	ethtool -i $1
+	ethtool -i $PHYSDEV
 	echo -e "#ethtool pause:"
-	ethtool -a $1
+	ethtool -a $PHYSDEV
 	echo -e "#ethtool offloads:"
-	ethtool -k $1
+	ethtool -k $PHYSDEV
 	echo -e "#ethtool stats:"
-	ethtool -S $1
+	ethtool -S $PHYSDEV
 	echo -e "#ifconfig:"
-	ifconfig $1
+	ifconfig $PHYSDEV
 }
 
 dcb_info () {
 	echo -e "\n###DCB INFO"
 	echo -e "#tc config"
 	tc qdisc
-	tc filter show dev $1 | grep -v filter
+	tc filter show dev $PHYSDEV | grep -v filter
 	echo -e "#service dcbd status:"
 	service dcbd status
-	echo -e "\n########## Showing dcb for $1"
+	echo -e "\n########## Showing dcb for $PHYSDEV"
 	dcbtool -v
-	dcbtool gc $1 dcb
-	echo -e "\n########## Getting dcb config for $1"
-	dcbtool gc $1 pg
+	dcbtool gc $PHYSDEV dcb
+	echo -e "\n########## Getting dcb config for $PHYSDEV"
+	dcbtool gc $PHYSDEV pg
 	echo
-	dcbtool gc $1 pfc
+	dcbtool gc $PHYSDEV pfc
 	echo
-	dcbtool gc $1 app:0
+	dcbtool gc $PHYSDEV app:0
 	echo
-	dcbtool gc $1 ll:0
-	echo -e "\n########## Getting dcb oper for $1"
-	dcbtool go $1 pg
+	dcbtool gc $PHYSDEV ll:0
+	echo -e "\n########## Getting dcb oper for $PHYSDEV"
+	dcbtool go $PHYSDEV pg
 	echo
-	dcbtool go $1 pfc
+	dcbtool go $PHYSDEV pfc
 	echo
-	dcbtool go $1 app:0
+	dcbtool go $PHYSDEV app:0
 	echo
-	dcbtool go $1 ll:0
-	echo -e "\n########## Getting dcb peer for $1"
-	dcbtool gp $1 pg
+	dcbtool go $PHYSDEV ll:0
+	echo -e "\n########## Getting dcb peer for $PHYSDEV"
+	dcbtool gp $PHYSDEV pg
 	echo
-	dcbtool gp $1 pfc
+	dcbtool gp $PHYSDEV pfc
 	echo
-	dcbtool gp $1 app:0
+	dcbtool gp $PHYSDEV app:0
 	echo
-	dcbtool gp $1 ll:0
+	dcbtool gp $PHYSDEV ll:0
 }
 
 fcoe_info () {
@@ -102,15 +123,25 @@ sysfs_dump () {
 	find /sys/class/fc_vports/*/ -type f -print -exec cat '{}' \;
 }
 
+logfile_dump() {
+	echo "###LOGFILES"
+	echo "#/var/log/messages"
+	cat /var/log/messages
+	echo
+	echo "#dmesg"
+	dmesg
+}
+
 fcoe_debug () {
-	kernel_info $1
-	system_info $1
-	adapter_info $1
-	dcb_info $1
-	fcoe_info $1
-	sysfs_dump $1
+	kernel_info
+	system_info
+	adapter_info
+	dcb_info
+	fcoe_info
+	sysfs_dump
+	logfile_dump
 }
 
-fcoe_debug $1
+fcoe_debug
 
 
diff --git a/doc/fcoemon.8 b/doc/fcoemon.8
index 2516635..556e067 100644
--- a/doc/fcoemon.8
+++ b/doc/fcoemon.8
@@ -1,7 +1,7 @@
 .TH "FCOEMON" "8" "December 22, 2008" "Open-FCoE Applications" "Open-FCoE Tools"
 .SH "NAME"
 \fBfcoemon\fR \- The Fibre Channel over Ethernet (FCoE) administration tool
-          for monitoring and processing events from DCB daemon.
+          for monitoring and processing events from the DCB daemon (\fBdcbd\fR).
 .SH "SYNOPSIS"
 \fBfcoemon\fR [\fB\-h\fR | \fB\-\-help\fR]
 .P
@@ -11,21 +11,19 @@
 .P
 \fBfcoemon\fR [\fB\-d\fR | \fB\-\-debug\fR]
 .P
+\fBfcoemon\fR [\fB\-s\fR | \fB\-\-syslog\fR]
+.P
 \fBfcoemon\fR [\fB\-e\fR | \fB\-\-exec\fR \fI<path\-to\-fcoeplumb\-script>\fR]
 .SH "DESCRIPTION"
-The \fBfcoemon\fR command is a FCoE management tool provided by the Open\-FCoE package.
+The \fBfcoemon\fR command is a FCoE management tool provided by the \fBfcoe-utils\fR package.
 \fBfcoemon\fR is the daemon of the \fBfcoe\fR system service.  When \fBfcoemon\fR starts, it establishes a socket
-connection with the \fBDCB\fR daemon. It then sends commands to, and receives responses
-and event from the \fBDCB\fR daemon. The \fBfcoemon\fR  will process the received responses
-and events and invoke the \fBfcoeplumb\fR script to create and destroy the FCoE interfaces.
-Since \fBfcoemon\fR depends on the existence of \fBDCB service\fR, there
-are settings required for \fBDCB\fR before \fBfcoemon\fR can be started. See
-the \fIDCB Settings\fR section below.
+connection with the DCB daemon (\fBdcbd\fR). It then sends commands to, and receives responses
+and events from \fBdcbd\fR.  \fBfcoemon\fR  will process the responses
+and events and will invoke the \fBfcoeplumb\fR script to create and destroy the FCoE interfaces as needed.
+Since \fBfcoemon\fR can depend on \fBdcbd\fR, there may be settings required for \fBdcbd\fR before \fBfcoemon\fR can be started. See the \fIDCB Settings\fR section below.
 
-Notice that the fcoe system service does not depend on dcbd service. However, the fcoemon daemon will be
-started by the fcoe service only if any one of the Ethernet port requires DCB service. In this case, fcoe
-service depends on dcbd service.  If none of the Ethernet ports requires DCB service, the fcoemon will not
-be started and, in this case, the fcoe service does not depend on dcbd service.
+The \fBfcoe\fR system may or may not depend on the DCB service. \fBfcoemon\fR will be
+started by the \fBfcoe\fR service only if one of the Ethernet ports requires DCB, as specified by the \fBfcoe\fR per-interface configuration files. If no Ethernet ports require DCB then \fBfcoemon\fR will not be started and the \fBfcoe\fR service will not depend on \fBdcbd\fR.
 
 .SH "OPTIONS"
 .TP
@@ -38,6 +36,9 @@ Run \fBfcoemon\fR in the foreground.
 \fB\-d | \-\-debug\fR
 Enable debugging messages.
 .TP
+\fB\-s | \-\-syslog\fR
+Use syslogd for logging. The default behavior is to log to stdout and stderr.
+.TP
 \fB\-e | \-\-exec\fR \fI<path\-of\-fcoeplumb\-script>\fR
 Specify the location of the \fBfcoeplumb\fR script.
 .SH "TERMINOLOGY"
@@ -48,33 +49,32 @@ Specify the location of the \fBfcoeplumb\fR script.
 .TP
 \fBLLINK\fR \- The DCB Logical Link TLV (or Logical Link) feature.
 .TP
-\fBmultiq\fR \- See Documentation/networking/multiqueue.txt of linux kernel 2.6.28 or higher.
+\fBmultiq\fR \- See Documentation/networking/multiqueue.txt of linux kernel source, v2.6.28 or higher.
 .TP
-\fBskbedit\fR \- See Documentation/networking/multiqueue.txt of linux kernel 2.6.28 or higher.
+\fBskbedit\fR \- See Documentation/networking/multiqueue.txt of linux kernel source v2.6.28 or higher.
 .SH "INSTALLATION REQUIREMENTS"
-The DCB and FCoE kernel configuration options must be enabled, these were introduced in v2.6.29. Both the linux kernel and iproute2 must support multiq and skbedit.  The DCB must be installed with version 0.9.4 and higher.
+The DCB and FCoE kernel configuration options must be enabled, these were introduced in the kernel version v2.6.29. Both the linux kernel and iproute2 must support multiq and skbedit.  The DCB package must be installed with version 0.9.4 and higher.
 .SH "SUPPORTED DCB EVENTS"
-In response to each supported event from the DCB daemon, the fcoemon collects the current
-settings from the DCB daemon and decide whether to delete and re-add the multiq queue discipline
-and skbedit filter. The \fBfcoemon\fR does not destroy, reset, or create FCoE interfaces during
-the DCB event processing.
+In response to each supported event from \fBdcbd\fR, \fBfcoemon\fR collects the current
+settings from \fBdcbd\fR to decide whether to delete and/or re-add the multiq queue discipline
+and skbedit filter.
 .TP
 \fBFEATURE_APP\fR
 If an event message is received from dcbd and if the feature code in the event message
 is FEATURE_APP (5), and if the subtype field is APP_FCOE_STYPE (0), we got a mode or
-configuration change event of the FCoE application.  The fcoemon will then issue queries
+configuration change event of the FCoE application.  \fBfcoemon\fR will then issue queries
 to the DCB daemon to collect the current mode and configuration information.
 .TP
 \fBFEATURE_PFC\fR
 If an event message is received from dcbd and if the feature code in the event message
 is FEATURE_PFC (3), we got a mode or configuration change event of the Priority Flow
-Control (PFC) feature.  The fcoemon will then issue queries to the DCB daemon to collect
+Control (PFC) feature.  The \fBfcoemon\fR will then issue queries to the DCB daemon to collect
 the current mode and configuration information.
 .TP
 \fBFEATURE_LLINK\fR
 If an event message is received from dcbd and if the feature code in the event message
 is FEATURE_LLINK (6), and if the subtype field is LLINK_FCOE_STYPE (0), we got a mode
-or configuration change event of the Logical Link TLV feature.  The fcoemon will then
+or configuration change event of the Logical Link TLV feature.  The \fBfcoemon\fR will then
 issue queries to the DCB daemon to collect the current mode and configuration information.
 .SH "CRITERIA OF CREATING, RESETTING  AND DESTROYING FCOE INTERFACE"
 In this section the \fBdcbtool\fR is used to describe the conditions of the DCB feture status
@@ -120,61 +120,50 @@ The Logical Link TLV feature is configured correctly if
 .RE
 .TP
 \fBCriteria to create FCoE interface\fR
-If DCB is required at the Ethernet port, a FCoE interface may be created only if
-the DCB and the Logical Link TLV feature are configured correctly. If DCB is not
-required at the Ethernet port, the FCoE interface may be created. FCoE interfaces
-are normally created by the \fBfcoe\fR system service.
+If DCB is required at the Ethernet port, an FCoE interface may be created only if
+the DCB is configured correctly. If DCB is not
+required at the Ethernet port, the FCoE interface may be created.
 .TP
 \fBCriteria to Destroy FCoE Interface\fR
 An FCoE interface will only be destroyed when the \fBfcoe\fR system service is stopped.
 .TP
-\fBCriteria to reset a FCoE interface\fR
-The \fBfcoe\fR system service does not reset any FCoE interfaces.
-.TP
 \fBChanging DCB Configuration, Qdisc and Filters\fR
 Changing the DCB configuration, qdisc, and filter are considered to be administrative actions.
 When the \fBfcoe\fR system service starts up, it sets up the default DCB configuration, qdisc, and filter
 for reliable FCoE operations. Administrators may alter the configuration while the service is running.
-Changing the DCB parameters may cause the fcoemon daemon to delete the existing multiq queue discipline,
-skbedit filter and re-add; but the fcoe service will not touch (e.g. destroy or reset) the FCoE interface.
+Changing the DCB parameters may cause the \fBfcoemon\fR daemon to delete the existing multiq queue discipline,
+skbedit filter and re-add; but the \fBfcoe\fR service will not touch (e.g. destroy or reset) the FCoE interface.
 Changing the DCB configuration, qdisc, and filter should be avoided while I/O traffic are in progress.
 .SH "FILES"
-The Installation of the Open-FCoE management tools include the following files:
-.TP
-\fB/etc/fcoe/config\fR
-This is the common configuration file for the \fBfcoe\fR system service. This file will
-be read by the \fI/etc/init.d/fcoe\fR" script, the \fI/etc/fcoe/scripts/fcoeplumb\fR script,
-and the \fIfcoemon\fR daemon. The default options in this file are:
-\fBDEBUG="yes"\fR and \fBUSE_SYSLOG="yes"\fR. The former is used to enable (select yes) or disable (select no)
-debugging messages of fcoemon, the \fIfcoe\fR service script, and the fcoeplumb script. The latter is
-to indicate if the log messages of fcoemon, the \fIfcoe\fR service script, and the fcoeplumb script are
-to be output to the system log.  Use editor to set the desired \fByes/no\fR values.
-.TP
-\fB/etc/fcoe/cfg-ethX\fR
-There is one of this file for each Ethernet interface \fBethX\fR found in the output of
-\fBcat /proc/net/dev\fR at the time of installation. This file will be read by the
-\fI/etc/init.d/fcoe\fR script and the \fIfcoemon\fR daemon.  The default options in this file are:
-\fBFCOE_ENABLE="no"\fR and \fBDCB_REQUIRED="yes"\fR.  The former is used to enable (select yes) or
-disable (select no) the FCoE service at the ethX port. The latter is to indicate if the DCB service
+Installation of the fcoe-utils management tools include the following files:
+.TP
+\fI/etc/fcoe/config\fR
+This is the primary configuration file for the \fBfcoe\fR system service. The default options in this file are: \fBDEBUG="no"\fR and \fBUSE_SYSLOG="yes"\fR. The former is used to enable
+debugging messages from the \fBfcoe\fR service script, \fBfcoemon\fR, and the \fBfcoeplumb\fR script. The latter is
+to indicate if the log messages are to be output to the system log.  Any changes to this file will require a restart of the \fBfcoe\fR service.
+.TP
+\fI/etc/fcoe/cfg-<ifname>\fR
+This file will be read by the \fI/etc/init.d/fcoe\fR script and the \fBfcoemon\fR daemon.  The default options in this file are:
+\fBFCOE_ENABLE="no"\fR and \fBDCB_REQUIRED="yes"\fR.  The former is used to enable/disable the FCoE service at the ethX port. The latter is to indicate if the DCB service
 is required (select yes) or not required (select no) at the ethX port. If the former is set to no, the
 latter is ignored. The selection of the settings should match the settings of the FCoE switch port connected
-to the local Ethernet ethX port. Use editor to set the desired \fByes/no\fR
+to the local Ethernet ethX port. Use an editor to set the desired \fByes/no\fR
 values for the \fBethX\fR interfaces.
 .TP
-\fB/etc/init.d/fcoe\fR
-This is the \fIfcoe\fR system service shell script. This script is invoked by the \fBinit\fR process
+\fI/etc/init.d/fcoe\fR
+This is the \fBfcoe\fR system service shell script. This script is invoked by the \fBinit\fR process
 or by the \fBservice\fR command.
 .TP
-\fB/sbin/fcoemon\fR
-This is the \fIfcoemon\fR daemon only invoked by the \fIfcoe\fR system service script.
+\fI/sbin/fcoemon\fR
+This is the \fBfcoemon\fR daemon only invoked by the \fBfcoe\fR system service script.
 .TP
-\fB/sbin/fcoeadm\fR
-This is the program used by the \fIfcoe\fR system service to create or destroy FCoE interfaces.
+\fI/sbin/fcoeadm\fR
+This is the program used by the \fBfcoe\fR system service to create or destroy FCoE interfaces.
 .TP
-\fB/etc/fcoe/scripts/fcoeplumb\fR
+\fI/etc/fcoe/scripts/fcoeplumb\fR
 This is a script only used by the \fBfcoemon\fR daemon.
 .SH "REPORTING BUGS"
-If you have identified a defect please either file a bug or engage the
+If you have identified a defect please file a bug with your distribution or engage the
 development mailing list at <http://www.Open\-FCoE.org>.
 .SH "SUPPORT"
 Open\-FCoE is maintained at <http://www.Open\-FCoE.org>. There are resources
diff --git a/etc/initd/initd.fedora b/etc/initd/initd.fedora
index e0b97f6..629fc76 100755
--- a/etc/initd/initd.fedora
+++ b/etc/initd/initd.fedora
@@ -33,7 +33,7 @@ LOG_FILE="/var/log/fcoemon.log"
 FCOEMON=/usr/sbin/fcoemon
 FCOEADM=/usr/sbin/fcoeadm
 DCBD=dcbd
-LOGGER="logger -t fcoe -s"
+LOGGER="echo"
 FCOEMON_OPTS=
 
 . /etc/init.d/functions
@@ -85,8 +85,9 @@ if [ $? -ne 0 ]; then
     failure
 fi
 
-if [ "$USE_SYSLOG" != "yes" ] && [ "$USE_SYSLOG" != "YES" ]; then
-	LOGGER="echo"
+if [ "$USE_SYSLOG" = "yes" ] || [ "$USE_SYSLOG" = "YES" ]; then
+    LOGGER="logger -t fcoe"
+    FCOEMON_OPTS+=" --syslog"
 fi
 
 if [ "$DEBUG" = "yes" ] || [ "$DEBUG" = "YES" ]; then
diff --git a/etc/initd/initd.suse b/etc/initd/initd.suse
index 398ed7b..6840d44 100755
--- a/etc/initd/initd.suse
+++ b/etc/initd/initd.suse
@@ -46,7 +46,7 @@ LOG_FILE="/var/log/fcoemon.log"
 FCOEMON=/usr/sbin/fcoemon
 FCOEADM=/usr/sbin/fcoeadm
 DCBD=dcbd
-LOGGER="logger -t fcoe -s"
+LOGGER="echo"
 FCOEMON_OPTS=
 
 . /etc/rc.status
@@ -110,9 +110,12 @@ if [ $? -ne 0 ]; then
 	rc_status -v
 	rc_exit
 fi
-if [ "$USE_SYSLOG" != "yes" ] && [ "$USE_SYSLOG" != "YES" ]; then
-	LOGGER="echo"
+
+if [ "$USE_SYSLOG" = "yes" ] || [ "$USE_SYSLOG" = "YES" ]; then
+    LOGGER="logger -t fcoe"
+    FCOEMON_OPTS+=" --syslog"
 fi
+
 if [ "$DEBUG" = "yes" ] || [ "$DEBUG" = "YES" ]; then
 	FCOEMON_OPTS+=" --debug"
 fi
diff --git a/fcnsq.c b/fcnsq.c
new file mode 100644
index 0000000..1c78901
--- /dev/null
+++ b/fcnsq.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <linux/types.h>
+typedef __u8 u8;
+typedef __u16 u16;
+typedef __u32 u32;
+typedef __u64 u64;
+#include <linux/bsg.h>
+#include <scsi/sg.h>
+#include <scsi/fc/fc_gs.h>
+#include <scsi/fc/fc_ns.h>
+#include <scsi/scsi_bsg_fc.h>
+
+static bool quiet = false;
+
+static int print_result(const char *prefix, const char *format, ...)
+{
+	va_list ap;
+	int rc;
+
+	va_start(ap, format);
+	if (!quiet)
+		printf("%s: ", prefix);
+	rc = vprintf(format, ap);
+	va_end(ap);
+	return rc;
+}
+
+static int print_err(const char *format, ...)
+{
+	va_list ap;
+	int rc;
+
+	if (quiet)
+		return 0;
+	va_start(ap, format);
+	rc = vprintf(format, ap);
+	va_end(ap);
+	return rc;
+}
+
+#define ntoh24(n) (u32) ((n)[0] << 16 | (n)[1] << 8  | (n)[2])
+#define hton24(h) { (h) >> 16 & 0xff, (h) >> 8 & 0xff, (h) & 0xff }
+
+static u64 ntohll(u64 netll)
+{
+	u8 *_netll = (u8 *)&netll;
+	return	(u64) _netll[0] << (7 * 8) |
+		(u64) _netll[1] << (6 * 8) |
+		(u64) _netll[2] << (5 * 8) |
+		(u64) _netll[3] << (4 * 8) |
+		(u64) _netll[4] << (3 * 8) |
+		(u64) _netll[5] << (2 * 8) |
+		(u64) _netll[6] << (1 * 8) |
+		(u64) _netll[7];
+}
+
+static u64 htonll(u64 hostll)
+{
+	u64 netll;
+	u8 *_netll = (u8 *)&netll;
+	_netll[0] = hostll >> (7 * 8) & 0xff;
+	_netll[1] = hostll >> (6 * 8) & 0xff;
+	_netll[2] = hostll >> (5 * 8) & 0xff;
+	_netll[3] = hostll >> (4 * 8) & 0xff;
+	_netll[4] = hostll >> (3 * 8) & 0xff;
+	_netll[5] = hostll >> (2 * 8) & 0xff;
+	_netll[6] = hostll >> (1 * 8) & 0xff;
+	_netll[7] = hostll & 0xff;
+	return netll;
+}
+
+static char* rjt_reason[] = {
+	[1] = "Invalid command code",
+	[2] = "Invalid version level",
+	[3] = "Logical error",
+	[4] = "Invalid CT_IU size",
+	[5] = "Logical busy",
+	[7] = "Protocol error",
+	[9] = "Unable to perform command request",
+	[0xB] = "Command not supported",
+	[0xD] = "Server not available",
+	[0xE] = "Session could not be established",
+};
+
+static char* rjt_explan[] = {
+	[0] = "No additional explanation",
+	[1] = "Port Identifier not registered",
+	[2] = "Port Name not registered",
+	[3] = "Node Name not registered",
+	[4] = "Class of Service not registered",
+	[6] = "Initial Process Associator not registered",
+	[7] = "FC-4 Types not registered",
+	[8] = "Symbolic Port Name not registered",
+	[9] = "Symbolic Node Name not registered",
+	[0xA] = "Port Type not registered",
+	[0xC] = "Fabric Port Name not registered",
+	[0xD] = "Hard Address not registered",
+	[0xF] = "FC-4 Features not registered",
+	[0x10] = "Access denied",
+	[0x11] = "Unacceptable Port Identifier",
+	[0x12] = "Data base empty",
+	[0x13] = "No object registered in the specified scope",
+	[0x14] = "Domain ID not present",
+	[0x15] = "Port number not present",
+	[0x16] = "No device attached",
+	[0xf0] = "Authorization Exception",
+	[0xf1] = "Authentication Exception",
+	[0xf2] = "Data base full",
+	[0xf3] = "Data base empty",
+	[0xf4] = "Processing request",
+	[0xf5] = "Unable to verify connection",
+	[0xf6] = "Devices not in a common zone",
+};
+
+static u16 ct_rjt(u8 reason, u8 explan) {
+	return (u16) reason << 8 | explan;
+}
+
+static u8 ct_rjt_reason(u16 rjt) {
+	return (u8) rjt >> 8;
+}
+
+static u8 ct_rjt_explan(u16 rjt) {
+	return (u8) rjt & 0xff;
+}
+
+static int ns_query(int bsg, void *req, int req_len, void *resp, int resp_len)
+{
+	char sense[96];
+
+	struct fc_bsg_request cdb = {
+		.msgcode 	= FC_BSG_HST_CT,
+		.rqst_data.h_ct	= {
+			.port_id = hton24(0xfffffc),
+		}
+	};
+
+	struct sg_io_v4 sgio = {
+		.guard			= 'Q',
+		.protocol		= BSG_PROTOCOL_SCSI,
+		.subprotocol		= BSG_SUB_PROTOCOL_SCSI_TRANSPORT,
+		.request_len		= sizeof(cdb),
+		.request		= (u64) &cdb,
+		.dout_xfer_len		= req_len,
+		.dout_xferp		= (u64) req,
+		.din_xfer_len		= resp_len,
+		.din_xferp		= (u64) resp,
+		.max_response_len	= sizeof(sense),
+		.response		= (u64) &sense,
+		.timeout		= 1000,
+	};
+
+	return ioctl(bsg, SG_IO, &sgio);
+}
+
+static u16 gn_id(int bsg, u32 fcid, u16 cmd, u64 *wwn)
+{
+	struct {
+		struct fc_ct_hdr hdr;
+		u64 wwn;
+	} __attribute__((__packed__)) gn_resp;
+
+	struct {
+		struct fc_ct_hdr hdr;
+		u8 resv;
+		u8 port_id[3];
+	} __attribute__((__packed__)) gn = {
+		.hdr = {
+			.ct_rev		= FC_CT_REV,
+			.ct_fs_type	= FC_FST_DIR,
+			.ct_fs_subtype	= FC_NS_SUBTYPE,
+			.ct_cmd		= htons(cmd),
+			.ct_mr_size	= htons(sizeof(gn_resp)),
+		},
+		.port_id = hton24(fcid),
+	};
+
+	if (ns_query(bsg, &gn, sizeof(gn), &gn_resp, sizeof(gn_resp)) < 0)
+		return ~0;
+	if (gn_resp.hdr.ct_cmd != htons(FC_FS_ACC))
+		return ct_rjt(gn_resp.hdr.ct_reason, gn_resp.hdr.ct_explan);
+	*wwn = ntohll(gn_resp.wwn);
+	return 0;
+}
+
+#define FC_NS_GPN_ID	0x0112
+static int gpn_id(int bsg, u32 fcid)
+{
+	u64 wwpn;
+	u16 rjt;
+
+	rjt = gn_id(bsg, fcid, FC_NS_GPN_ID, &wwpn);
+	if (rjt)
+		goto fail;
+	print_result("Port Name", "%16.16llx\n", wwpn);
+	return 0;
+fail:
+	if (rjt == ~0)
+		print_err("%s ioctl failed: %s\n", __func__, strerror(errno));
+	else
+		print_err("%s command failed: %s, %s\n", __func__,
+				rjt_reason[ct_rjt_reason(rjt)],
+				rjt_explan[ct_rjt_explan(rjt)]);
+	return rjt;
+}
+
+#define FC_NS_GNN_ID	0x0113
+static int gnn_id(int bsg, u32 fcid)
+{
+	u64 wwnn;
+	u16 rjt;
+
+	rjt = gn_id(bsg, fcid, FC_NS_GNN_ID, &wwnn);
+	if (rjt)
+		goto fail;
+	print_result("Node Name", "%16.16llx\n", wwnn);
+	return 0;
+fail:
+	if (rjt == ~0)
+		print_err("%s ioctl failed: %s\n", __func__, strerror(errno));
+	else
+		print_err("%s command failed: %s, %s\n", __func__,
+				rjt_reason[ct_rjt_reason(rjt)],
+				rjt_explan[ct_rjt_explan(rjt)]);
+	return rjt;
+}
+
+#define FC_NS_GSPN_ID	0x0118
+static int gspn_id(int bsg, u32 fcid)
+{
+	struct {
+		struct fc_ct_hdr hdr;
+		u8 len;
+		char name[255];
+	} __attribute__((__packed__)) gspn_resp;
+
+	struct {
+		struct fc_ct_hdr hdr;
+		u8 resv;
+		u8 port_id[3];
+	} __attribute__((__packed__)) gspn = {
+		.hdr = {
+			.ct_rev		= FC_CT_REV,
+			.ct_fs_type	= FC_FST_DIR,
+			.ct_fs_subtype	= FC_NS_SUBTYPE,
+			.ct_cmd		= htons(FC_NS_GSPN_ID),
+			.ct_mr_size	= htons(sizeof(gspn_resp)),
+		},
+		.port_id = hton24(fcid),
+	};
+
+	if (ns_query(bsg, &gspn, sizeof(gspn), &gspn_resp, sizeof(gspn_resp)) < 0) {
+		print_err("%s ioctl failed: %s\n", __func__, strerror(errno));
+		return ~0;
+	}
+	if (gspn_resp.hdr.ct_cmd != htons(FC_FS_ACC)) {
+		print_err("%s command failed: %s, %s\n", __func__,
+				rjt_reason[gspn_resp.hdr.ct_reason],
+				rjt_explan[gspn_resp.hdr.ct_explan]);
+		return ct_rjt(gspn_resp.hdr.ct_reason, gspn_resp.hdr.ct_explan);
+	}
+	print_result("Symbolic Port Name", "%s\n", gspn_resp.name);
+	return 0;
+}
+
+#define FC_NS_GSNN_NN	0x0139
+static int gsnn_nn(int bsg, u64 wwnn)
+{
+	struct {
+		struct fc_ct_hdr hdr;
+		u8 len;
+		char name[255];
+	} __attribute__((__packed__)) gsnn_resp;
+
+	struct {
+		struct fc_ct_hdr hdr;
+		u64 wwnn;
+	} __attribute__((__packed__)) gsnn = {
+		.hdr = {
+			.ct_rev		= FC_CT_REV,
+			.ct_fs_type	= FC_FST_DIR,
+			.ct_fs_subtype	= FC_NS_SUBTYPE,
+			.ct_cmd		= htons(FC_NS_GSNN_NN),
+			.ct_mr_size	= htons(sizeof(gsnn_resp)),
+		},
+		.wwnn = htonll(wwnn),
+	};
+
+	if (ns_query(bsg, &gsnn, sizeof(gsnn), &gsnn_resp, sizeof(gsnn_resp)) < 0) {
+		print_err("%s ioctl failed: %s\n", __func__, strerror(errno));
+		return ~0;
+	}
+	if (gsnn_resp.hdr.ct_cmd != htons(FC_FS_ACC)) {
+		print_err("%s command failed: %s, %s\n", __func__,
+				rjt_reason[gsnn_resp.hdr.ct_reason],
+				rjt_explan[gsnn_resp.hdr.ct_explan]);
+		return ct_rjt(gsnn_resp.hdr.ct_reason, gsnn_resp.hdr.ct_explan);
+	}
+	print_result("Symbolic Node Name", "%s\n", gsnn_resp.name);
+	return 0;
+}
+
+enum commands {
+	NONE = 0,
+	GPN_ID,
+	GNN_ID,
+	GSPN_ID,
+	GSNN_NN,
+};
+
+static const struct option options[] = {
+	{ "gpn", required_argument, NULL, GPN_ID },
+	{ "gnn", required_argument, NULL, GNN_ID },
+	{ "gspn", required_argument, NULL, GSPN_ID },
+	{ "gsnn", required_argument, NULL, GSNN_NN },
+	{ "quiet", no_argument, NULL, 'q' },
+	{ NULL, 0, NULL, 0 },
+};
+
+static void help(int status)
+{
+	printf(
+"Usage: fcnsq <host#> <command> [options]\n"
+"Commands:\n"
+"  --gpn  <port id>\n"
+"  --gnn  <port id>\n"
+"  --gspn <port id>\n"
+"  --gsnn <world wide node name>\n"
+"Options:\n"
+"  --quiet	print minimal results on success, and no error messages\n"
+"\n"
+"Port IDs and World Wide Names must be specified in hexadecimal.\n"
+      );
+	exit(status);
+}
+
+int main(int argc, char *argv[])
+{
+	char *bsg;
+	int bsg_dev;
+	u32 port_id;
+	u64 wwnn;
+	int rc;
+	enum commands cmd = 0;
+	char c;
+
+	while(1) {
+		c = getopt_long_only(argc, argv, "", options, NULL);
+		if (c < 0)
+			break;
+		switch(c) {
+		case '?':
+			help(-1);
+			break;
+		case 'q':
+			quiet = true;
+			break;
+		case GPN_ID:
+		case GNN_ID:
+		case GSPN_ID:
+			if (cmd)
+				help(-1);
+			cmd = c;
+			sscanf(optarg, "%x", &port_id);
+			break;
+		case GSNN_NN:
+			if (cmd)
+				help(-1);
+			cmd = c;
+			sscanf(optarg, "%llx", &wwnn);
+			break;
+		}
+	}
+
+	if (cmd == NONE)
+		help(-1);
+
+	if (asprintf(&bsg, "/dev/bsg/fc_%s", argv[optind]) < 0) {
+		if (!quiet)
+			perror(NULL);
+		return -1;
+	}
+	bsg_dev = open(bsg, O_RDWR);
+	if (bsg_dev < 0) {
+		if (!quiet)
+			perror(bsg);
+		return -1;
+	}
+	switch (cmd) {
+	case GPN_ID:
+		rc = gpn_id(bsg_dev, port_id);
+		break;
+	case GNN_ID:
+		rc = gnn_id(bsg_dev, port_id);
+		break;
+	case GSPN_ID:
+		rc = gspn_id(bsg_dev, port_id);
+		break;
+	case GSNN_NN:
+		rc = gsnn_nn(bsg_dev, wwnn);
+		break;
+	default:
+		help(-1);
+		break;
+	};
+	close(bsg_dev);
+	free(bsg);
+	return rc;
+}
+
diff --git a/fcoemon.c b/fcoemon.c
index d42c39b..96c59e6 100644
--- a/fcoemon.c
+++ b/fcoemon.c
@@ -37,6 +37,7 @@
 #include <sys/socket.h>
 #include <sys/queue.h>
 #include <sys/un.h>
+#include <sys/wait.h>
 #include <linux/sockios.h>
 #include <linux/if.h>
 #include <linux/if_arp.h>
@@ -68,6 +69,8 @@
 #define FCM_DEFAULT_QOS_MASK        (1 << 3)
 #define FILE_NAME_LEN               (NAME_MAX + 1)
 
+#define VLAN_DIR                "/proc/net/vlan"
+
 #define CLIF_NAME_PATH          _PATH_VARRUN "dcbd/clif"
 #define CLIF_PID_FILE           _PATH_VARRUN "fcoemon.pid"
 #define CLIF_LOCAL_SUN_PATH     _PATH_TMP "fcoemon.dcbd.%d"
@@ -93,12 +96,6 @@ struct fcoe_port_config {
 	int dcb_app_0_willing;
 };
 
-struct fcoe_config {
-	int debug;
-	int use_syslog;
-	struct fcoe_port_config *port;
-} fcoe_config;
-
 enum fcoeadm_action {
 	ADM_DESTROY = 0,
 	ADM_CREATE,
@@ -136,8 +133,6 @@ static struct fcm_clif *fcm_clif = &fcm_clif_st;
 static struct sa_timer fcm_dcbd_timer;
 
 char *fcm_dcbd_cmd = CONFIG_DIR "/scripts/fcoeplumb";
-int fcm_debug;
-int fcm_use_syslog;
 
 /* Debugging routine */
 static void print_errors(char *buf, int errors);
@@ -155,6 +150,7 @@ static int fcm_link_buf_check(size_t);
  */
 static struct option fcm_options[] = {
 	{"debug", 0, NULL, 'd'},
+	{"syslog", 0, NULL, 's'},
 	{"exec", 1, NULL, 'e'},
 	{"foreground", 0, NULL, 'f'},
 	{"version", 0, NULL, 'v'},
@@ -244,9 +240,9 @@ static size_t fcm_read_config_variable(char *file, char *val_buf, size_t len,
 		*s = '\0';
 		n = snprintf(val_buf, len, "%s", val);
 		if (fcm_remove_quotes(val_buf, n) < 0) {
-			SA_LOG("Invalid format in config file"
-			       " %s: %s=%s\n",
-			       file, var_name, val);
+			FCM_LOG("Invalid format in config file"
+				" %s: %s=%s\n",
+				file, var_name, val);
 			/* error */
 			return -1;
 		}
@@ -268,34 +264,9 @@ static int fcm_read_config_files(void)
 	struct fcoe_port_config *next;
 	int rc;
 
-	memset(&fcoe_config, 0, sizeof(fcoe_config));
-
-	strncpy(file, CONFIG_DIR "/" "config", sizeof(file));
-	fp = fopen(file, "r");
-	if (!fp) {
-		SA_LOG_ERR(errno, "Failed reading %s\n", file);
-		exit(1);
-	}
-
-	rc = fcm_read_config_variable(file, val,
-				      sizeof(val), fp, "USE_SYSLOG");
-	if (rc < 0) {
-		fclose(fp);
-		return -1;
-	}
-
-	/* if not found, default to "yes" */
-	if (!strncasecmp(val, "yes", 3) || !rc) {
-		fcoe_config.use_syslog = 1;
-		fcm_use_syslog = 1;
-	}
-
-	fclose(fp);
-
 	dir = opendir(CONFIG_DIR);
 	if (dir == NULL) {
-		SA_LOG_ERR(errno,
-			   "Failed reading directory %s\n", CONFIG_DIR);
+		FCM_LOG_ERR(errno, "Failed reading directory %s\n", CONFIG_DIR);
 		return -1;
 	}
 	for (;;) {
@@ -323,7 +294,7 @@ static int fcm_read_config_files(void)
 		strncat(file, dp->d_name, sizeof(file) - strlen(file));
 		fp = fopen(file, "r");
 		if (!fp) {
-			SA_LOG_ERR(errno, "Failed reading %s\n", file);
+			FCM_LOG_ERR(errno, "Failed reading %s\n", file);
 			exit(1);
 		}
 
@@ -383,7 +354,7 @@ static int fcm_link_init(void)
 
 	fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
 	if (fd < 0) {
-		SA_LOG_ERR(errno, "socket error");
+		FCM_LOG_ERR(errno, "socket error");
 		return fd;
 	}
 	memset(&l_local, 0, sizeof(l_local));
@@ -392,7 +363,7 @@ static int fcm_link_init(void)
 	l_local.nl_pid = 0;
 	rc = bind(fd, (struct sockaddr *)&l_local, sizeof(l_local));
 	if (rc == -1) {
-		SA_LOG_ERR(errno, "bind error");
+		FCM_LOG_ERR(errno, "bind error");
 		return rc;
 	}
 	fcm_link_socket = fd;
@@ -405,49 +376,195 @@ static int fcm_link_init(void)
 	return 0;
 }
 
+
+/* fcm_read_vlan - parse file for "Device" string
+ * @val_buf - copy string to val_buf
+ * @len - length of val_buf buffer
+ * @fp - file pointer to parse
+ */
+static void read_vlan(char *val_buf, size_t len,  FILE *fp)
+{
+	char *s;
+	char buf[FILE_NAME_LEN];
+
+	val_buf[0] = '\0';
+	buf[sizeof(buf) - 1] = '\0';
+	while ((s = fgets(buf, sizeof(buf) - 1, fp)) != NULL) {
+		while (isspace(*s))
+			s++;
+
+		if (*s == '\0' || *s == '#')
+			continue;
+
+		s = strstr(s, "Device:");
+		if (s == NULL)
+			continue;
+
+		while (!isspace(*s))
+			s++;
+
+		while (isspace(*s))
+			s++;
+
+		s = strncpy(val_buf, s, len);
+		while (isalnum(*s))
+			s++;
+
+		*s = '\0';
+		return;
+	}
+}
+
+
+
+
+/* fcm_vlan_dev_real_dev - parse vlan real_dev from /proc
+ * @vlan_dev - vlan_dev to find real interface name for
+ * @real_dev - pointer to copy real_dev to
+ *
+ * This parses the /proc/net/vlan/ directory for the vlan_dev.
+ * If the file exists it will parse for the real device and
+ * copy it to real_dev parameter.
+ */
+static void fcm_vlan_dev_real_dev(char *vlan_ifname, char *real_ifname)
+{
+	FILE *fp;
+	char  file[80];
+
+	if (opendir(VLAN_DIR)) {
+		strncpy(file, VLAN_DIR "/", sizeof(file));
+		strncat(file, vlan_ifname, sizeof(file) - strlen(file));
+
+		fp = fopen(file, "r");
+		if (fp) {
+			read_vlan(real_ifname, sizeof(real_ifname), fp);
+			fclose(fp);
+			return;
+		}
+	}
+
+	strncpy(real_ifname, vlan_ifname, sizeof(real_ifname));
+}
+
+/* fcm_is_linkinfo_vlan - parse nlmsg linkinfo rtattr for vlan kind
+ * @ap: pointer to the linkinfo rtattr
+ *
+ * This function parses the linkinfo rtattr and returns
+ * 1 if it is kind vlan otherwise returns 0.
+ */
+int fcm_is_linkinfo_vlan(struct rtattr *ap)
+{
+	struct rtattr *info;
+	int len;
+
+	info = (struct rtattr *) (RTA_DATA(ap));
+
+	for (len = ap->rta_len; RTA_OK(info, len); info = RTA_NEXT(info, len)) {
+		if (info->rta_type != IFLA_INFO_KIND)
+			continue;
+
+		if (strncmp("vlan", RTA_DATA(info), sizeof("vlan")))
+			return 0;
+		else
+			return 1;
+	}
+
+	return 0;
+}
+
+static struct sa_nameval fcm_dcbd_states[] = FCM_DCBD_STATES;
+
+static void fcm_dcbd_state_set(struct fcm_fcoe *ff,
+			       enum fcm_dcbd_state new_state)
+{
+	if (fcoe_config.debug) {
+		char old[32];
+		char new[32];
+
+		FCM_LOG_DEV_DBG(ff, "%s -> %s",
+				sa_enum_decode(old, sizeof(old),
+					       fcm_dcbd_states,
+					       ff->ff_dcbd_state),
+				sa_enum_decode(new, sizeof(new),
+					       fcm_dcbd_states, new_state));
+	}
+	ff->ff_dcbd_state = new_state;
+}
+
 void fcm_parse_link_msg(struct ifinfomsg *ip, int len)
 {
 	struct fcm_fcoe *ff;
+	struct fcm_vfcoe *fv;
 	struct rtattr *ap;
 	char ifname[IFNAMSIZ];
+	char real_dev[IFNAMSIZ];
+	u_int8_t operstate;
+	u_int64_t mac;
+	int is_vlan;
 
-	if (ip->ifi_type != ARPHRD_ETHER)
-		return;
+	mac = is_vlan = 0;
+	operstate = IF_OPER_UNKNOWN;
 
-	ff = fcm_fcoe_lookup_create_ifindex(ip->ifi_index);
-	if (!ff)
+	if (ip->ifi_type != ARPHRD_ETHER)
 		return;
 
-	ff->ff_flags = ip->ifi_flags;
-
 	len -= sizeof(*ip);
 	for (ap = (struct rtattr *)(ip + 1); RTA_OK(ap, len);
 	     ap = RTA_NEXT(ap, len)) {
 		switch (ap->rta_type) {
 		case IFLA_ADDRESS:
 			if (RTA_PAYLOAD(ap) == 6)
-				ff->ff_mac =
-					net48_get(RTA_DATA(ap));
+				mac = net48_get(RTA_DATA(ap));
 			break;
 
 		case IFLA_IFNAME:
 			sa_strncpy_safe(ifname, sizeof(ifname),
 					RTA_DATA(ap),
 					RTA_PAYLOAD(ap));
-			if (fcm_debug)
-				SA_LOG("ifname %s", ifname);
-			fcm_fcoe_set_name(ff, ifname);
+			FCM_LOG_DBG("ifname %s", ifname);
 			break;
 
 		case IFLA_OPERSTATE:
-			ff->ff_operstate =
-				*(uint8_t *) RTA_DATA(ap);
+			operstate = *(uint8_t *) RTA_DATA(ap);
+			break;
+
+		case IFLA_LINKINFO:
+			if (!fcm_is_linkinfo_vlan(ap))
+				break;
+			fcm_vlan_dev_real_dev(ifname, real_dev);
+			is_vlan = 1;
+			FCM_LOG_DBG("vlan ifname %s:%s", real_dev, ifname);
 			break;
 
 		default:
 			break;
 		}
 	}
+
+	if (is_vlan) {
+		fv = fcm_vfcoe_lookup_create_ifname(ifname, real_dev);
+		if (!fv)
+			return;
+		ff = fcm_fcoe_lookup_name(real_dev);
+		fv->fv_active = 1;
+	} else {
+		ff = fcm_fcoe_lookup_create_ifname(ifname);
+		if (!ff)
+			return;
+		ff->ff_active = 1;
+	}
+
+	/* Set FCD_INIT when netif is enabled */
+	if (!(ff->ff_flags & IFF_LOWER_UP) && (ip->ifi_flags & IFF_LOWER_UP))
+		fcm_dcbd_state_set(ff, FCD_INIT);
+
+	/* Set FCD_ERROR when netif is disabled */
+	if ((ff->ff_flags & IFF_LOWER_UP) && !(ip->ifi_flags & IFF_LOWER_UP))
+		fcm_dcbd_state_set(ff, FCD_ERROR);
+
+	ff->ff_flags = ip->ifi_flags;
+	ff->ff_mac = mac;
+	ff->ff_operstate = operstate;
 }
 
 static void fcm_link_recv(void *arg)
@@ -456,7 +573,6 @@ static void fcm_link_recv(void *arg)
 	char *buf;
 	struct nlmsghdr *hp;
 	struct ifinfomsg *ip;
-	struct fcm_fcoe *ff;
 	unsigned type;
 	int plen;
 	int rlen;
@@ -465,7 +581,7 @@ static void fcm_link_recv(void *arg)
 	rc = read(fcm_link_socket, buf, fcm_link_buf_size);
 	if (rc <= 0) {
 		if (rc < 0)
-			SA_LOG_ERR(errno, "read error");
+			FCM_LOG_ERR(errno, "read error");
 		return;
 	}
 
@@ -484,14 +600,14 @@ static void fcm_link_recv(void *arg)
 			break;
 
 		if (hp->nlmsg_type == NLMSG_ERROR) {
-			SA_LOG("nlmsg error");
+			FCM_LOG("nlmsg error");
 			break;
 		}
 
 		plen = NLMSG_PAYLOAD(hp, 0);
 		ip = (struct ifinfomsg *)NLMSG_DATA(hp);
 		if (plen < sizeof(*ip)) {
-			SA_LOG("too short (%d) to be a LINK message", rc);
+			FCM_LOG("too short (%d) to be a LINK message", rc);
 			break;
 		}
 
@@ -499,9 +615,8 @@ static void fcm_link_recv(void *arg)
 		case RTM_NEWLINK:
 		case RTM_DELLINK:
 		case RTM_GETLINK:
-			if (fcm_debug)
-				SA_LOG("Link event %d for index %d", type,
-				       ip->ifi_index);
+			FCM_LOG_DBG("Link event: %d flags %05X index %d\n",
+				    type, ip->ifi_flags, ip->ifi_index);
 
 			fcm_parse_link_msg(ip, plen);
 			break;
@@ -530,12 +645,12 @@ static void fcm_link_getlink(void)
 	msg.nl.nlmsg_type = RTM_GETLINK;
 	msg.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_ATOMIC;
 	msg.nl.nlmsg_seq = ++fcm_link_seq;
-	/* msg.nl.nlmsg_pid = getpid(); */
+	msg.nl.nlmsg_pid = getpid();
 	msg.ifi.ifi_family = AF_UNSPEC;
 	msg.ifi.ifi_type = ARPHRD_ETHER;
 	rc = write(fcm_link_socket, &msg, sizeof(msg));
 	if (rc < 0)
-		SA_LOG_ERR(errno, "write error");
+		FCM_LOG_ERR(errno, "write error");
 }
 
 /*
@@ -568,6 +683,18 @@ static void fcm_fcoe_init(void)
 }
 
 /*
+ * Allocate a virtual FCoE interface state structure.
+ */
+static struct fcm_vfcoe *fcm_vfcoe_alloc(struct fcm_fcoe *ff)
+{
+	struct fcm_vfcoe *fv;
+
+	fv = calloc(1, sizeof(*fv));
+	if (fv)
+		TAILQ_INSERT_TAIL(&ff->ff_vfcoe_head, fv, fv_list);
+	return fv;
+}
+/*
  * Allocate an FCoE interface state structure.
  */
 static struct fcm_fcoe *fcm_fcoe_alloc(void)
@@ -580,50 +707,127 @@ static struct fcm_fcoe *fcm_fcoe_alloc(void)
 		ff->ff_ifindex = ~0;
 		ff->ff_operstate = IF_OPER_UNKNOWN;
 		TAILQ_INSERT_TAIL(&fcm_fcoe_head, ff, ff_list);
+		TAILQ_INIT(&(ff->ff_vfcoe_head));
 	}
 	return ff;
 }
 
 /*
+ * Find a virtual FCoE interface by ifname
+ */
+static struct fcm_vfcoe *fcm_vfcoe_lookup_create_ifname(char *ifname,
+							char *real_name)
+{
+	struct fcm_fcoe *ff;
+	struct fcm_vfcoe *fv;
+	struct fcoe_port_config *p;
+
+	p = fcm_find_port_config(ifname);
+	if (!p)
+		return NULL;
+
+	ff = fcm_fcoe_lookup_name(real_name);
+	if (!ff) {
+		ff = fcm_fcoe_alloc();
+		if (!ff)
+			return NULL;
+		fcm_fcoe_set_name(ff, real_name);
+		ff->ff_app_info.enable = DCB_APP_0_DEFAULT_ENABLE;
+		ff->ff_app_info.willing = DCB_APP_0_DEFAULT_WILLING;
+		ff->ff_pfc_saved.u.pfcup = 0xffff;
+		sa_timer_init(&ff->ff_event_timer, fcm_event_timeout, ff);
+	}
+
+	fv = fcm_vfcoe_lookup_name(ff, ifname);
+	if (fv)
+		return fv;
+
+	fv = fcm_vfcoe_alloc(ff);
+	if (!fv) {
+		TAILQ_REMOVE(&fcm_fcoe_head, ff, ff_list);
+		free(ff);
+		return NULL;
+	}
+
+	snprintf(fv->fv_name, sizeof(fv->fv_name), "%s", ifname);
+	return fv;
+}
+
+/*
  * Find an FCoE interface by ifindex.
+ * @ifname - interface name to create
+ * @check - check for port config file before creating
+ *
+ * This create a fcoe interface structure with interface name,
+ * or if one already exists returns the existing one.  If check
+ * is set it verifies the ifname has a valid config file before
+ * creating interface
  */
-static struct fcm_fcoe *fcm_fcoe_lookup_create_ifindex(u_int32_t ifindex)
+static struct fcm_fcoe *fcm_fcoe_lookup_create_ifname(char *ifname)
 {
 	struct fcm_fcoe *ff;
+	struct fcoe_port_config *p;
+
+	p = fcm_find_port_config(ifname);
+	if (!p)
+		return NULL;
 
 	TAILQ_FOREACH(ff, &fcm_fcoe_head, ff_list) {
-		if (ff->ff_ifindex == ifindex)
+		if (!strncmp(ifname, ff->ff_name, IFNAMSIZ))
 			return ff;
 	}
+
 	ff = fcm_fcoe_alloc();
 	if (ff != NULL) {
-		ff->ff_ifindex = ifindex;
+		fcm_fcoe_set_name(ff, ifname);
 		ff->ff_pfc_saved.u.pfcup = 0xffff;
 		sa_timer_init(&ff->ff_event_timer, fcm_event_timeout, ff);
 	}
+
+	FCM_LOG_DEV_DBG(ff, "Monitoring port %s\n", ifname);
 	return ff;
 }
 
 /*
+ * Find an virtual FCoE interface by name.
+ */
+static struct fcm_vfcoe *fcm_vfcoe_lookup_name(struct fcm_fcoe *ff, char *name)
+{
+	struct fcm_vfcoe *fv, *curr;
+
+	TAILQ_FOREACH(curr, &(ff->ff_vfcoe_head), fv_list) {
+		if (!strncmp(curr->fv_name, name, sizeof(name))) {
+			fv = curr;
+			break;
+		}
+	}
+
+	return fv;
+}
+
+/*
  * Find an FCoE interface by name.
- * What about VLAN instances?  They can't use DCB, perhaps.
  */
 static struct fcm_fcoe *fcm_fcoe_lookup_name(char *name)
 {
-	struct fcm_fcoe *ff;
+	struct fcm_fcoe *ff, *curr;
 
-	TAILQ_FOREACH(ff, &fcm_fcoe_head, ff_list) {
-		if (strcmp(ff->ff_name, name) == 0)
+	ff = NULL;
+
+	TAILQ_FOREACH(curr, &fcm_fcoe_head, ff_list) {
+		if (strcmp(curr->ff_name, name) == 0) {
+			ff = curr;
 			break;
+		}
 	}
+
 	return ff;
 }
 
 static void fcm_fcoe_get_dcb_settings(struct fcm_fcoe *ff)
 {
-	fc_wwn_t wwpn;
-	int vlan = ff->ff_vlan;
 	struct fcoe_port_config *p;
+	struct fcm_vfcoe *fv;
 
 	if (ff->ff_mac == 0)
 		return;		/* loopback or other non-eligible interface */
@@ -631,8 +835,6 @@ static void fcm_fcoe_get_dcb_settings(struct fcm_fcoe *ff)
 	/*
 	 * Get DCB config from file if possible.
 	 */
-	wwpn = fc_wwn_from_mac(ff->ff_mac, 2, vlan >= 0 ? vlan : 0);
-
 	p = fcoe_config.port;
 	while (p) {
 		if (!strncmp(ff->ff_name, p->ifname, IFNAMSIZ)) {
@@ -640,6 +842,15 @@ static void fcm_fcoe_get_dcb_settings(struct fcm_fcoe *ff)
 			ff->ff_app_info.willing = p->dcb_app_0_willing;
 			break;
 		}
+
+		TAILQ_FOREACH(fv, &(ff->ff_vfcoe_head), fv_list) {
+			if (!strncmp(fv->fv_name, p->ifname, IFNAMSIZ)) {
+				ff->ff_app_info.enable = p->dcb_app_0_enable;
+				ff->ff_app_info.willing = p->dcb_app_0_willing;
+				break;
+			}
+		}
+
 		p = p->next;
 	}
 }
@@ -678,7 +889,7 @@ static int fcm_dcbd_connect(void)
 	ASSERT(fcm_clif->cl_fd < 0);
 	fd = socket(PF_UNIX, SOCK_DGRAM, 0);
 	if (fd < 0) {
-		SA_LOG_ERR(errno, "clif socket open failed");	/* XXX */
+		FCM_LOG_ERR(errno, "clif socket open failed");	/* XXX */
 		return 0;
 	}
 
@@ -688,7 +899,7 @@ static int fcm_dcbd_connect(void)
 		 CLIF_LOCAL_SUN_PATH, getpid());
 	rc = bind(fd, (struct sockaddr *)lp, sizeof(*lp));
 	if (rc < 0) {
-		SA_LOG_ERR(errno, "clif bind failed");
+		FCM_LOG_ERR(errno, "clif bind failed");
 		close(fd);
 		return 0;
 	}
@@ -699,15 +910,14 @@ static int fcm_dcbd_connect(void)
 		 CLIF_NAME_PATH);
 	rc = connect(fd, (struct sockaddr *)&dest, sizeof(dest));
 	if (rc < 0) {
-		SA_LOG_ERR(errno, "clif connect failed");
+		FCM_LOG_ERR(errno, "clif connect failed");
 		unlink(lp->sun_path);
 		close(fd);
 		return 0;
 	}
 	fcm_clif->cl_fd = fd;
 	sa_select_add_fd(fd, fcm_dcbd_rx, NULL, fcm_dcbd_ex, fcm_clif);
-	if (fcm_debug)
-		SA_LOG("connected to dcbd");
+	FCM_LOG_DBG("connected to dcbd");
 	return 1;
 }
 
@@ -725,22 +935,13 @@ static int is_query_in_progress(void)
 
 static void fcm_fcoe_config_reset(void)
 {
-	struct fcoe_port_config *p;
 	struct fcm_fcoe *ff;
 
-	p = fcoe_config.port;
-	while (p) {
-		if (p->fcoe_enable && p->dcb_required) {
-			ff = fcm_fcoe_lookup_name(p->ifname);
-			if (ff) {
-				fcm_dcbd_setup(ff, ADM_DESTROY);
-				ff->ff_qos_mask = fcm_def_qos_mask;
-				ff->ff_pfc_saved.u.pfcup = 0xffff;
-			}
-			if (fcm_debug)
-				SA_LOG("Port %s config reset\n", p->ifname);
-		}
-		p = p->next;
+	TAILQ_FOREACH(ff, &fcm_fcoe_head, ff_list) {
+		fcm_dcbd_setup(ff, ADM_DESTROY);
+		ff->ff_qos_mask = fcm_def_qos_mask;
+		ff->ff_pfc_saved.u.pfcup = 0xffff;
+		FCM_LOG_DEV_DBG(ff, "Port config reset\n");
 	}
 }
 
@@ -773,21 +974,53 @@ static void fcm_dcbd_disconnect(void)
 		fcm_clif->cl_busy = 0;
 		fcm_clif->cl_ping_pending = 0;
 		fcm_fcoe_config_reset();
-		if (fcm_debug)
-			SA_LOG("disconnected from dcbd");
+		FCM_LOG_DBG("Disconnected from dcbd");
 	}
 }
 
 static void fcm_dcbd_shutdown(void)
 {
-	if (fcm_debug)
-		SA_LOG("Shut down dcbd connection\n");
+	FCM_LOG_DBG("Shutdown dcbd connection\n");
 	fcm_dcbd_request("D");	/* DETACH_CMD */
 	fcm_dcbd_disconnect();
 	unlink(fcm_pidfile);
 	closelog();
 }
 
+static void fcm_vfcoe_cleanup(struct fcm_fcoe *ff)
+{
+	struct fcm_vfcoe *fv, *head;
+	struct fcm_vfcoe_head *list;
+
+	list = &(ff->ff_vfcoe_head);
+
+	for (head = TAILQ_FIRST(list); head; head = fv) {
+		fv = TAILQ_NEXT(head, fv_list);
+		TAILQ_REMOVE(list, head, fv_list);
+		free(head);
+	}
+}
+
+static void fcm_cleanup(void)
+{
+	struct fcoe_port_config *curr, *next;
+	struct fcm_fcoe *ff, *head;
+
+	for (curr = fcoe_config.port; curr; curr = next) {
+		next = curr->next;
+		free(curr);
+	}
+
+	for (head = TAILQ_FIRST(&fcm_fcoe_head); head; head = ff) {
+		ff = TAILQ_NEXT(head, ff_list);
+		TAILQ_REMOVE(&fcm_fcoe_head, head, ff_list);
+		fcm_vfcoe_cleanup(head);
+		free(head);
+	}
+
+	free(fcm_link_buf);
+}
+
 static u_int32_t fcm_get_hex(char *cp, u_int32_t len, char **endptr)
 {
 	u_int32_t hex = 0;
@@ -808,25 +1041,6 @@ static u_int32_t fcm_get_hex(char *cp, u_int32_t len, char **endptr)
 	return hex;
 }
 
-static struct sa_nameval fcm_dcbd_states[] = FCM_DCBD_STATES;
-
-static void fcm_dcbd_state_set(struct fcm_fcoe *ff,
-			       enum fcm_dcbd_state new_state)
-{
-	if (fcm_debug) {
-		char old[32];
-		char new[32];
-
-		SA_LOG("%s: %s -> %s",
-		       ff->ff_name,
-		       sa_enum_decode(old, sizeof(old),
-				      fcm_dcbd_states, ff->ff_dcbd_state),
-		       sa_enum_decode(new, sizeof(new),
-				      fcm_dcbd_states, new_state));
-	}
-	ff->ff_dcbd_state = new_state;
-}
-
 static void fcm_dcbd_rx(void *arg)
 {
 	struct fcm_clif *clif = arg;
@@ -839,27 +1053,27 @@ static void fcm_dcbd_rx(void *arg)
 	len = sizeof(buf);
 	rc = read(clif->cl_fd, buf, sizeof(buf) - 1);
 	if (rc < 0)
-		SA_LOG_ERR(errno, "read");
+		FCM_LOG_ERR(errno, "read");
 	else if ((rc > 0) && (rc < sizeof(buf))) {
 		ASSERT(rc < sizeof(buf));
 		buf[rc] = '\0';
 		len = strlen(buf);
 		ASSERT(len <= rc);
-		if (fcm_debug && len > FCM_PING_RSP_LEN)
-			SA_LOG("received len %d buf '%s'", len, buf);
+		if (len > FCM_PING_RSP_LEN)
+			FCM_LOG_DBG("received len %d buf '%s'", len, buf);
 
 		switch (buf[CLIF_RSP_MSG_OFF]) {
 		case CMD_RESPONSE:
 			st = fcm_get_hex(buf + CLIF_STAT_OFF, CLIF_STAT_LEN,
 					 &ep);
 			if (ep != NULL)
-				SA_LOG("unexpected response code from dcbd: "
-				       "len %d buf %s rc %d", len, buf, rc);
+				FCM_LOG("unexpected response code from dcbd: "
+					"len %d buf %s rc %d", len, buf, rc);
 			else if (st != cmd_success &&
 				 st != cmd_device_not_found) {
-				SA_LOG("error response from dcbd: "
-				       "error %d len %d %s",
-				       st, len, buf);
+				FCM_LOG("error response from dcbd: "
+					"error %d len %d %s",
+					st, len, buf);
 			}
 			fcm_clif->cl_busy = 0;
 
@@ -878,9 +1092,9 @@ static void fcm_dcbd_rx(void *arg)
 			case LEVEL_CMD:
 				break;
 			default:
-				SA_LOG("Unexpected cmd in response "
-				       "from dcbd: len %d %s",
-				       len, buf);
+				FCM_LOG("Unexpected cmd in response "
+					"from dcbd: len %d %s",
+					len, buf);
 				break;
 			}
 			fcm_dcbd_next();	/* advance ports if possible */
@@ -890,8 +1104,8 @@ static void fcm_dcbd_rx(void *arg)
 			fcm_dcbd_event(buf, len);
 			break;
 		default:
-			SA_LOG("Unexpected message from dcbd: len %d buf %s",
-			       len, buf);
+			FCM_LOG("Unexpected message from dcbd: len %d buf %s",
+				len, buf);
 			break;
 		}
 	}
@@ -899,8 +1113,7 @@ static void fcm_dcbd_rx(void *arg)
 
 static void fcm_dcbd_ex(void *arg)
 {
-	if (fcm_debug)
-		SA_LOG("called");
+	FCM_LOG_DBG("called");
 }
 
 static void fcm_dcbd_request(char *req)
@@ -915,15 +1128,15 @@ static void fcm_dcbd_request(char *req)
 	fcm_clif->cl_busy = 1;
 	rc = write(fcm_clif->cl_fd, req, len);
 	if (rc < 0) {
-		SA_LOG_ERR(errno, "Failed write req %s len %d", req, len);
+		FCM_LOG_ERR(errno, "Failed write req %s len %d", req, len);
 		fcm_clif->cl_busy = 0;
 		fcm_dcbd_disconnect();
 		fcm_dcbd_connect();
 		return;
 	}
 
-	if (fcm_debug && rc > FCM_PING_REQ_LEN)
-		SA_LOG("sent '%s', rc=%d bytes succeeded", req, rc);
+	if (rc > FCM_PING_REQ_LEN)
+		FCM_LOG_DBG("sent '%s', rc=%d bytes succeeded", req, rc);
 	return;
 }
 
@@ -949,12 +1162,12 @@ static struct fcm_fcoe *fcm_dcbd_get_port(char **msgp, size_t len_off,
 
 	if_len = fcm_get_hex(msg + len_off, len_len, &ep);
 	if (ep != NULL) {
-		SA_LOG("Parse error on port len: msg %s", msg);
+		FCM_LOG("Parse error on port len: msg %s", msg);
 		return NULL;
 	}
 
 	if (len_off + len_len + if_len > len) {
-		SA_LOG("Invalid port len %d msg %s", if_len, msg);
+		FCM_LOG("Invalid port len %d msg %s", if_len, msg);
 		return NULL;
 	}
 	msg += len_off + len_len;
@@ -962,7 +1175,7 @@ static struct fcm_fcoe *fcm_dcbd_get_port(char **msgp, size_t len_off,
 	*msgp = msg + if_len;
 	ff = fcm_fcoe_lookup_name(ifname);
 	if (ff == NULL) {
-		SA_LOG("ifname '%s' not found", ifname);
+		FCM_LOG("ifname '%s' not found", ifname);
 		exit(1);	/* XXX */
 	}
 	return ff;
@@ -994,7 +1207,8 @@ static int dcb_rsp_parser(struct fcm_fcoe *ff, char *rsp, cmd_status st)
 	if (feature != FEATURE_DCB &&
 	    feature != FEATURE_PFC &&
 	    feature != FEATURE_APP) {
-		SA_LOG("WARNING: Unexpected DCB feature %d\n", feature);
+		FCM_LOG_DEV(ff, "WARNING: Unexpected DCB feature %d\n",
+			    feature);
 		return -1;
 	}
 
@@ -1002,13 +1216,14 @@ static int dcb_rsp_parser(struct fcm_fcoe *ff, char *rsp, cmd_status st)
 	if (dcb_cmd != CMD_GET_CONFIG &&
 	    dcb_cmd != CMD_GET_OPER &&
 	    dcb_cmd != CMD_GET_PEER) {
-		SA_LOG("WARNING: Unexpected DCB cmd %d\n", dcb_cmd);
+		FCM_LOG_DEV(ff, "WARNING: Unexpected DCB cmd %d\n", dcb_cmd);
 		return -1;
 	}
 
 	version = rsp[DCB_VER_OFF] & 0x0f;
 	if (version != CLIF_MSG_VERSION) {
-		SA_LOG("WARNING: Unexpected rsp version %d\n", version);
+		FCM_LOG_DEV(ff, "WARNING: Unexpected rsp version %d\n",
+			    version);
 		return -1;
 	}
 
@@ -1020,7 +1235,7 @@ static int dcb_rsp_parser(struct fcm_fcoe *ff, char *rsp, cmd_status st)
 	case FEATURE_DCB:
 		ff->ff_dcb_state = (*(rsp+doff+CFG_ENABLE) == '1');
 		if (!ff->ff_dcb_state) {
-			SA_LOG("WARNING: DCB state is off\n");
+			FCM_LOG_DEV(ff, "WARNING: DCB state is off\n");
 			return -1;
 		}
 		return 0;
@@ -1086,43 +1301,42 @@ static int validating_dcb_app_pfc(struct fcm_fcoe *ff)
 	int error = 0;
 
 	if (!ff->ff_dcb_state) {
-		SA_LOG("WARNING: DCB state is off\n");
+		FCM_LOG_DEV(ff, "WARNING: DCB state is off\n");
 		error++;
 	}
 	if (!ff->ff_app_info.willing) {
-		SA_LOG("WARNING: APP:0 willing mode is false\n");
+		FCM_LOG_DEV(ff, "WARNING: APP:0 willing mode is false\n");
 		error++;
 	}
 	if (!ff->ff_app_info.advertise) {
-		SA_LOG("WARNING: APP:0 advertise mode is false\n");
+		FCM_LOG_DEV(ff, "WARNING: APP:0 advertise mode is false\n");
 		error++;
 	}
 	if (!ff->ff_app_info.enable) {
-		SA_LOG("WARNING: APP:0 enable mode is false\n");
+		FCM_LOG_DEV(ff, "WARNING: APP:0 enable mode is false\n");
 		error++;
 	}
 	if (!ff->ff_app_info.op_mode) {
-		SA_LOG("WARNING: APP:0 operational mode is false\n");
+		FCM_LOG_DEV(ff, "WARNING: APP:0 operational mode is false\n");
 		error++;
 	}
 	if (!ff->ff_pfc_info.op_mode) {
-		SA_LOG("WARNING: PFC operational mode is false\n");
+		FCM_LOG_DEV(ff, "WARNING: PFC operational mode is false\n");
 		error++;
 	}
 	if ((ff->ff_pfc_info.u.pfcup & ff->ff_app_info.u.appcfg) \
 	    != ff->ff_app_info.u.appcfg) {
-		SA_LOG("WARNING: APP:0 priority (0x%02x) doesn't "
-		       "match PFC priority (0x%02x)\n",
-		       ff->ff_app_info.u.appcfg,
-		       ff->ff_pfc_info.u.pfcup);
+		FCM_LOG_DEV(ff, "WARNING: APP:0 priority (0x%02x) doesn't "
+			    "match PFC priority (0x%02x)\n",
+			    ff->ff_app_info.u.appcfg,
+			    ff->ff_pfc_info.u.pfcup);
 		error++;
 	}
 	if (error) {
-		SA_LOG("WARNING: DCB is configured incorrectly\n");
+		FCM_LOG_DEV(ff, "WARNING: DCB is configured incorrectly\n");
 		return 0;
 	}
-	if (fcm_debug)
-		SA_LOG("DCB is configured correctly\n");
+	FCM_LOG_DEV_DBG(ff, "DCB is configured correctly\n");
 
 	return 1;
 }
@@ -1171,7 +1385,7 @@ static void update_saved_pfcup(struct fcm_fcoe *ff)
 }
 
 /*
- * clear_dcbd_info - lear dcbd info to unknown values
+ * clear_dcbd_info - clear dcbd info to unknown values
  *
  */
 static void clear_dcbd_info(struct fcm_fcoe *ff)
@@ -1277,15 +1491,17 @@ static void fcm_dcbd_get_oper(struct fcm_fcoe *ff, char *resp,
 	val = fcm_get_hex(cp + OPER_ERROR, 2, &ep);
 
 	if (ep) {
-		SA_LOG("invalid get oper response parse error byte %d."
-		       "  resp %s", ep - cp, cp);
+		FCM_LOG_DEV(ff, "Invalid get oper response "
+			    "parse error byte %d, resp %s",
+			    ep - cp, cp);
 		fcm_dcbd_state_set(ff, FCD_ERROR);
 	} else {
 		if (val != 0) {
-			if (fcm_debug) {
-				SA_LOG("val=0x%x resp:%s\n", val, resp);
+			FCM_LOG_DEV_DBG(ff, "val=0x%x resp:%s\n", val,
+					resp);
+			if (fcoe_config.debug)
 				print_errors("", val);
-			}
+
 			fcm_dcbd_setup(ff, ADM_DESTROY);
 			fcm_dcbd_state_set(ff, FCD_DONE);
 			return;
@@ -1300,14 +1516,12 @@ static void fcm_dcbd_get_oper(struct fcm_fcoe *ff, char *resp,
 
 		switch (ff->ff_dcbd_state) {
 		case FCD_GET_PFC_OPER:
-			if (fcm_debug) {
-				SA_LOG("%s PFC feature is %ssynced",
-				       ff->ff_name,
-				       cp[OPER_SYNCD] == '1' ? "" : "not ");
-				SA_LOG("%s PFC operating mode is %s",
-				       ff->ff_name, cp[OPER_OPER_MODE] == '1'
-				       ? "on" : "off ");
-			}
+			FCM_LOG_DEV_DBG(ff, "PFC feature is %ssynced",
+					cp[OPER_SYNCD] == '1' ? "" : "not ");
+
+			FCM_LOG_DEV_DBG(ff, "PFC operating mode is %s",
+					cp[OPER_OPER_MODE] == '1'
+					? "on" : "off ");
 			ff->ff_pfc_info.enable = enable;
 			rc = dcb_rsp_parser(ff, resp, st);
 			if (!rc)
@@ -1317,15 +1531,11 @@ static void fcm_dcbd_get_oper(struct fcm_fcoe *ff, char *resp,
 			break;
 
 		case FCD_GET_APP_OPER:
-			if (fcm_debug) {
-				SA_LOG("%s FCoE feature is %ssynced",
-				       ff->ff_name,
-				       cp[OPER_SYNCD] == '1' ? "" : "not ");
-				SA_LOG("%s FCoE operating mode is %s",
-				       ff->ff_name,
-				       cp[OPER_OPER_MODE] == '1' ?
-				       "on" : "off ");
-			}
+			FCM_LOG_DEV_DBG(ff, "FCoE feature is %ssynced",
+					cp[OPER_SYNCD] == '1' ? "" : "not ");
+			FCM_LOG_DEV_DBG(ff, "FCoE operating mode is %s",
+					cp[OPER_OPER_MODE] == '1'
+					? "on" : "off ");
 			rc = dcb_rsp_parser(ff, resp, st);
 			if (rc) {
 				fcm_dcbd_state_set(ff, FCD_ERROR);
@@ -1335,7 +1545,8 @@ static void fcm_dcbd_get_oper(struct fcm_fcoe *ff, char *resp,
 			parm_len = fcm_get_hex(cp + OPER_LEN, 2, &ep);
 			cp += OPER_LEN + 2;
 			if (ep != NULL || parm_len > strlen(cp)) {
-				SA_LOG("invalid peer parm_len %d", parm_len);
+				FCM_LOG_DEV_DBG(ff, "Invalid peer parm_len %d",
+						parm_len);
 				fcm_dcbd_state_set(ff, FCD_ERROR);
 				break;
 			}
@@ -1343,52 +1554,42 @@ static void fcm_dcbd_get_oper(struct fcm_fcoe *ff, char *resp,
 			if (parm_len > 0) {
 				parm = fcm_get_hex(cp, parm_len, &ep);
 				if (ep != NULL) {
-					SA_LOG("invalid parameter %s", cp);
+					FCM_LOG_DEV_DBG(ff, "Invalid parameter "
+							"%s", cp);
 					fcm_dcbd_state_set(ff, FCD_ERROR);
 					break;
 				}
 			}
 			ff->ff_qos_mask = parm;
 			if (validating_dcbd_info(ff)) {
-				if (fcm_debug)
-					SA_LOG("DCB settings of %s "
-					       "qualified for creating "
-					       "FCoE interface\n",
-					       ff->ff_name);
+				FCM_LOG_DEV_DBG(ff, "DCB settings "
+						"qualified for creating "
+						"FCoE interface\n");
+
 				rc = is_pfcup_changed(ff);
 				if (rc == 1) {
-					if (fcm_debug)
-						SA_LOG("%s: Initial "
-						       "QOS = 0x%x\n",
-						       ff->ff_name,
-						       ff->ff_qos_mask);
+					FCM_LOG_DEV_DBG(ff, "Initial "
+							"QOS = 0x%x\n",
+							ff->ff_qos_mask);
 					fcm_dcbd_setup(ff, ADM_CREATE);
 				} else if (rc == 2) {
-					if (fcm_debug)
-						SA_LOG("%s: QOS changed"
-						       " to 0x%x\n",
-						       ff->ff_name,
-						       ff->ff_qos_mask);
+					FCM_LOG_DEV_DBG(ff, "QOS changed"
+							" to 0x%x\n",
+							ff->ff_qos_mask);
 					fcm_dcbd_setup(ff, ADM_RESET);
 				} else if (!ff->ff_enabled) {
-					if (fcm_debug)
-						SA_LOG("%s: Re-create "
-						       "QOS = 0x%x\n",
-						       ff->ff_name,
-						       ff->ff_qos_mask);
+					FCM_LOG_DEV_DBG(ff, "Re-create "
+							"QOS = 0x%x\n",
+							ff->ff_qos_mask);
 					fcm_dcbd_setup(ff, ADM_CREATE);
 				} else {
-					if (fcm_debug)
-						SA_LOG("%s: No action will "
-						       "be taken\n",
-						       ff->ff_name);
+					FCM_LOG_DEV_DBG(ff, "No action will "
+							"be taken\n");
 				}
 			} else {
-				if (fcm_debug)
-					SA_LOG("DCB settings of %s not "
-					       "qualified for FCoE "
-					       "operations.",
-					       ff->ff_name);
+				FCM_LOG_DEV_DBG(ff, "DCB settings of %s not "
+						"qualified for FCoE "
+						"operations.");
 				fcm_dcbd_setup(ff, ADM_DESTROY);
 				clear_dcbd_info(ff);
 			}
@@ -1416,21 +1617,20 @@ static void fcm_dcbd_get_peer(struct fcm_fcoe *ff, char *resp,
 {
 	char *ep = NULL;
 	u_int32_t val;
-	int rc;
 
 	val = fcm_get_hex(cp + OPER_ERROR, 2, &ep);
 	if (ep) {
-		SA_LOG("invalid get oper response parse error byte %d."
-		       "  resp %s", ep - cp, cp);
+		FCM_LOG_DEV_DBG(ff, "Invalid get oper response "
+				"parse error byte %d. resp %s",
+				ep - cp, cp);
 		fcm_dcbd_state_set(ff, FCD_ERROR);
 		return;
 	}
 
 	if (val != 0) {
-		if (fcm_debug) {
-			SA_LOG("val=0x%x resp:%s\n", val, resp);
+		FCM_LOG_DEV_DBG(ff, "val=0x%x resp:%s\n", val, resp);
+		if (fcoe_config.debug)
 			print_errors("", val);
-		}
 		fcm_dcbd_setup(ff, ADM_DESTROY);
 		fcm_dcbd_state_set(ff, FCD_DONE);
 		return;
@@ -1453,44 +1653,46 @@ static void fcm_dcbd_cmd_resp(char *resp, cmd_status st)
 	struct fcm_fcoe *ff;
 	u_int32_t ver;
 	u_int32_t cmd;
-	u_int32_t val;
 	u_int32_t feature;
 	u_int32_t subtype;
 	char *ep;
 	char *cp;
 	size_t len;
-	u_int32_t enable;
-	int rc;
 
 	resp += CLIF_RSP_OFF;
 	len = strlen(resp);
 	ver = fcm_get_hex(resp + DCB_VER_OFF, DCB_VER_LEN, &ep);
 	if (ep != NULL) {
-		SA_LOG("parse error: resp %s", resp);
+		FCM_LOG("parse error: resp %s", resp);
 		return;
 	} else	if (ver != CLIF_RSP_VERSION) {
-		SA_LOG("unexpected version %d resp %s", ver, resp);
+		FCM_LOG("unexpected version %d resp %s", ver, resp);
 		return;
 	}
 	cmd = fcm_get_hex(resp + DCB_CMD_OFF, DCB_CMD_LEN, &ep);
 	if (ep != NULL) {
-		SA_LOG("parse error on resp cmd: resp %s", resp);
+		FCM_LOG("parse error on resp cmd: resp %s", resp);
 		return;
 	}
 	feature = fcm_get_hex(resp + DCB_FEATURE_OFF, DCB_FEATURE_LEN, &ep);
 	if (ep != NULL) {
-		SA_LOG("parse error on resp feature: resp %s", resp);
+		FCM_LOG("parse error on resp feature: resp %s", resp);
 		return;
 	}
 	subtype = fcm_get_hex(resp + DCB_SUBTYPE_OFF, DCB_SUBTYPE_LEN, &ep);
 	if (ep != NULL) {
-		SA_LOG("parse error on resp subtype: resp %s", resp);
+		FCM_LOG("parse error on resp subtype: resp %s", resp);
 		return;
 	}
 	cp = resp;
 	ff = fcm_dcbd_get_port(&cp, DCB_PORTLEN_OFF, DCB_PORTLEN_LEN, len);
 	if (ff == NULL) {
-		SA_LOG("port not found. resp %s", resp);
+		FCM_LOG("port not found. resp %s", resp);
+		return;
+	}
+
+	if (!(ff->ff_flags & IFF_LOWER_UP)) {
+		FCM_LOG_DEV_DBG(ff, "Port state netif carrier down\n");
 		return;
 	}
 
@@ -1512,8 +1714,8 @@ static void fcm_dcbd_cmd_resp(char *resp, cmd_status st)
 		break;
 
 	default:
-		SA_LOG("Unknown cmd 0x%x in response: resp %s",
-		       cmd, resp);
+		FCM_LOG_DEV_DBG(ff, "Unknown cmd 0x%x in response: resp %s",
+				cmd, resp);
 		break;
 	}
 }
@@ -1522,9 +1724,8 @@ static void fcm_event_timeout(void *arg)
 {
 	struct fcm_fcoe *ff = (struct fcm_fcoe *)arg;
 
-	if (fcm_debug)
-		SA_LOG("%s: %d milliseconds timeout!\n",
-		       ff->ff_name, FCM_EVENT_TIMEOUT_USEC/1000);
+	FCM_LOG_DEV_DBG(ff, "%d milliseconds timeout!\n",
+			FCM_EVENT_TIMEOUT_USEC/1000);
 
 	if (!is_query_in_progress()) {
 		fcm_clif->cl_ping_pending++;
@@ -1548,81 +1749,82 @@ static void fcm_dcbd_event(char *msg, size_t len)
 	if (msg[EV_LEVEL_OFF] != MSG_DCB + '0' || len <= EV_PORT_ID_OFF)
 		return;
 	if (msg[EV_VERSION_OFF] != CLIF_EV_VERSION + '0') {
-		SA_LOG("Unexpected version in event msg %s", msg);
+		FCM_LOG("Unexpected version in event msg %s", msg);
 		return;
 	}
 	cp = msg;
 	ff = fcm_dcbd_get_port(&cp, EV_PORT_LEN_OFF, EV_PORT_LEN_LEN, len);
 	if (ff == NULL)
 		return;
+
+	if (!(ff->ff_flags & IFF_LOWER_UP)) {
+		FCM_LOG_DEV_DBG(ff, "Port state netif carrier down\n");
+		return;
+	}
+
 	feature = fcm_get_hex(cp + EV_FEATURE_OFF, 2, &ep);
 	if (ep != NULL) {
-		SA_LOG("%s: Invalid feature code in event msg %s",
-		       ff->ff_name, msg);
+		FCM_LOG_DEV_DBG(ff, "Invalid feature code in event msg %s",
+				msg);
 		return;
 	}
 
 	switch (feature) {
 	case FEATURE_DCB:
-		if (fcm_debug)
-			SA_LOG("<%s: Got DCB Event>\n", ff->ff_name);
+		FCM_LOG_DEV_DBG(ff, "<%s: Got DCB Event>\n");
 		goto ignore_event;
 	case FEATURE_PG:     /* 'E5204eth2020001' */
-		if (fcm_debug)
-			SA_LOG("<%s: Got PG Event>\n", ff->ff_name);
+		FCM_LOG_DEV_DBG(ff, "<%s: Got PG Event>\n");
 		goto ignore_event;
 	case FEATURE_BCN:    /* 'E5204eth2040001' */
-		if (fcm_debug)
-			SA_LOG("<%s: Got BCN Event>\n", ff->ff_name);
+		FCM_LOG_DEV_DBG(ff, "<%s: Got BCN Event>\n");
 		goto ignore_event;
 	case FEATURE_PG_DESC:
-		if (fcm_debug)
-			SA_LOG("<%s: Got PG_DESC Event>\n", ff->ff_name);
+		FCM_LOG_DEV_DBG(ff, "<%s: Got PG_DESC Event>\n");
 		goto ignore_event;
 	case FEATURE_PFC:    /* 'E5204eth2030011' */
-		if (fcm_debug)
-			SA_LOG("<%s: Got PFC Event>\n", ff->ff_name);
+		FCM_LOG_DEV_DBG(ff, "<%s: Got PFC Event>\n");
 		goto handle_event;
 	case FEATURE_APP:    /* 'E5204eth2050011' */
-		if (fcm_debug)
-			SA_LOG("<%s: Got APP Event>\n", ff->ff_name);
+		FCM_LOG_DEV_DBG(ff, "<%s: Got APP Event>\n");
 		goto handle_event;
 	default:
-		SA_LOG("%s: Unknown feature 0x%x in msg %s",
-		       ff->ff_name, feature, msg);
+		FCM_LOG_DEV_DBG(ff, "Unknown feature 0x%x in msg %s",
+				feature, msg);
+		goto ignore_event;
+	}
 
 handle_event:
-		subtype = fcm_get_hex(cp + EV_SUBTYPE_OFF, 2, &ep);
-		if (ep != NULL || subtype != APP_FCOE_STYPE) {
-			SA_LOG("%s: Unknown application subtype in msg %s",
-			       ff->ff_name, msg);
-			break;
-		}
-		if (fcm_debug) {
-			if (cp[EV_OP_MODE_CHG_OFF] == '1')
-				SA_LOG("%s: operational mode changed",
-				       ff->ff_name);
-			if (cp[EV_OP_CFG_CHG_OFF] == '1')
-				SA_LOG("%s: operational config changed",
-				       ff->ff_name);
-		}
-		if (ff->ff_dcbd_state == FCD_DONE ||
-		    ff->ff_dcbd_state == FCD_ERROR) {
-			if (cp[EV_OP_MODE_CHG_OFF] == '1' ||
-			    cp[EV_OP_CFG_CHG_OFF] == '1') {
-				/* Cancel timer if it is active */
-				sa_timer_cancel(&ff->ff_event_timer);
-				/* Reset the timer */
-				sa_timer_set(&ff->ff_event_timer,
-					     FCM_EVENT_TIMEOUT_USEC);
-			}
-			if (fcm_clif->cl_busy == 0)
-				fcm_dcbd_port_advance(ff);
+	subtype = fcm_get_hex(cp + EV_SUBTYPE_OFF, 2, &ep);
+	if (subtype != APP_FCOE_STYPE) {
+		FCM_LOG_DEV_DBG(ff, "Unknown application subtype "
+				"in msg %s", msg);
+		return;
+	}
+	if (fcoe_config.debug) {
+		if (cp[EV_OP_MODE_CHG_OFF] == '1')
+			FCM_LOG_DEV_DBG(ff,
+					"Operational mode changed");
+		if (cp[EV_OP_CFG_CHG_OFF] == '1')
+			FCM_LOG_DEV_DBG(ff,
+					"Operational config changed");
+	}
+
+	if (ff->ff_dcbd_state == FCD_DONE ||
+	    ff->ff_dcbd_state == FCD_ERROR) {
+		if (cp[EV_OP_MODE_CHG_OFF] == '1' ||
+		    cp[EV_OP_CFG_CHG_OFF] == '1') {
+			/* Cancel timer if it is active */
+			sa_timer_cancel(&ff->ff_event_timer);
+			/* Reset the timer */
+			sa_timer_set(&ff->ff_event_timer,
+				     FCM_EVENT_TIMEOUT_USEC);
 		}
-
-ignore_event:
-		break;
+		if (fcm_clif->cl_busy == 0)
+			fcm_dcbd_port_advance(ff);
 	}
+ignore_event:
+	return;
 }
 
 /*
@@ -1634,7 +1836,8 @@ ignore_event:
  */
 static void fcm_dcbd_setup(struct fcm_fcoe *ff, enum fcoeadm_action action)
 {
-	char *op, *debug = NULL;
+	struct fcm_vfcoe *fv;
+	char *op, *debug, *syslog = NULL;
 	char *qos_arg;
 	char qos[64];
 	u_int32_t mask;
@@ -1642,17 +1845,18 @@ static void fcm_dcbd_setup(struct fcm_fcoe *ff, enum fcoeadm_action action)
 	int fd;
 
 	if (action == 0)
-		op = "--disable";
+		op = "--destroy";
 	else if (action == 1)
-		op = "--enable";
+		op = "--create";
 	else
 		op = "--reset";
 	if (action && !ff->ff_qos_mask)
 		return;
 	if (fcm_dcbd_cmd == NULL) {
-		SA_LOG("Should %s %s per op state", op, ff->ff_name);
+		FCM_LOG_DEV_DBG(ff, "Should %s per op state", op);
 		return;
 	}
+
 	/*
 	 * XXX should wait for child status
 	 */
@@ -1660,11 +1864,12 @@ static void fcm_dcbd_setup(struct fcm_fcoe *ff, enum fcoeadm_action action)
 
 	rc = fork();
 	if (rc < 0) {
-		SA_LOG_ERR(errno, "fork error");
+		FCM_LOG_ERR(errno, "fork error");
 	} else if (rc == 0) {	/* child process */
 		for (fd = ulimit(4 /* __UL_GETOPENMAX */ , 0); fd > 2; fd--)
 			close(fd);
-		qos_arg = NULL;
+		qos_arg = "--qos-disable";
+		snprintf(qos, sizeof(qos), "%s", "0");
 		if (action) {
 			mask = ff->ff_qos_mask;
 			if (mask) {
@@ -1682,28 +1887,59 @@ static void fcm_dcbd_setup(struct fcm_fcoe *ff, enum fcoeadm_action action)
 					mask &= ~(1 << bit);
 					sep = ",";
 				}
-				qos_arg = "--qos";
+				qos_arg = "--qos-enable";
 			}
 		}
 
-		if (fcm_debug) {
+		if (fcoe_config.use_syslog)
+			syslog = "--syslog";
+
+		if (fcoe_config.debug) {
 			debug = "--debug";
 
 			if (!action)
-				SA_LOG("%s %s %s\n",
-				       fcm_dcbd_cmd, ff->ff_name, op);
+				FCM_LOG_DEV_DBG(ff, "%s %s %s\n", fcm_dcbd_cmd,
+						op, syslog);
+			else
+				FCM_LOG_DEV_DBG(ff, "%s %s %s %s %s\n",
+						fcm_dcbd_cmd, op, qos_arg, qos,
+						syslog);
+		}
+
+		rc = fork();
+		if (rc < 0)
+			FCM_LOG_ERR(errno, "fork error");
+		else if (rc == 0) {     /* child process */
+			if (ff->ff_active)
+				execlp(fcm_dcbd_cmd, fcm_dcbd_cmd, ff->ff_name,
+				       op, qos_arg, qos, debug, syslog,
+				       (char *)NULL);
 			else
-				SA_LOG("%s %s %s %s %s\n",
-				       fcm_dcbd_cmd, ff->ff_name, op,
-				       qos_arg, qos);
+				execlp(fcm_dcbd_cmd, fcm_dcbd_cmd, ff->ff_name,
+				       qos_arg, qos, debug, syslog,
+				       (char *)NULL);
 		}
 
-		execlp(fcm_dcbd_cmd, fcm_dcbd_cmd, ff->ff_name,
-		       op, qos_arg, qos, debug, (char *)NULL);
+		/* VLAN interfaces only enable and disable */
+		if (action < 0  || action > 1)
+			exit(1);
+
+		TAILQ_FOREACH(fv, &(ff->ff_vfcoe_head), fv_list) {
+			if (!fv->fv_active)
+				continue;
+			FCM_LOG_DEV_DBG(ff, "%s %s %s %s\n", fcm_dcbd_cmd,
+					fv->fv_name, op, syslog);
+			rc = fork();
+			if (rc < 0)
+				FCM_LOG_ERR(errno, "fork error");
+			else if (rc == 0)       /* child process */
+				execlp(fcm_dcbd_cmd, fcm_dcbd_cmd, fv->fv_name,
+				       op, debug, syslog, (char *)NULL);
+		}
 
-		SA_LOG_ERR(errno, "exec '%s' failed", fcm_dcbd_cmd);
 		exit(1);
-	}
+	} else
+		wait(NULL);
 }
 
 /*
@@ -1713,15 +1949,10 @@ static void fcm_dcbd_setup(struct fcm_fcoe *ff, enum fcoeadm_action action)
 static void fcm_dcbd_port_advance(struct fcm_fcoe *ff)
 {
 	char buf[80], params[30];
-	struct fcoe_port_config *p;
 
 	ASSERT(ff);
 	ASSERT(fcm_clif);
 
-	p = fcm_find_port_config(ff->ff_name);
-	if (!p)
-		return;
-
 	if (fcm_clif->cl_busy)
 		return;
 
@@ -1799,7 +2030,9 @@ static void fcm_dcbd_next(void)
 	TAILQ_FOREACH(ff, &fcm_fcoe_head, ff_list) {
 		if (fcm_clif->cl_busy)
 			break;
-		fcm_dcbd_port_advance(ff);
+
+		if (ff->ff_flags & IFF_LOWER_UP)
+			fcm_dcbd_port_advance(ff);
 	}
 }
 
@@ -1810,6 +2043,7 @@ static void fcm_usage(void)
 	       "\t [-e|--exec <exec>]\n"
 	       "\t [-f|--foreground]\n"
 	       "\t [-d|--debug]\n"
+	       "\t [-s|--syslog]\n"
 	       "\t [-v|--version]\n"
 	       "\t [-h|--help]\n\n", progname);
 	exit(1);
@@ -1835,9 +2069,9 @@ static void fcm_pidfile_create(void)
 		pid = atoi(sp);
 		rc = kill(pid, 0);
 		if (sp && (pid > 0) && !rc) {
-			SA_LOG("Another instance"
-			       " (pid %d) is running - exiting\n",
-			       pid);
+			FCM_LOG("Another instance"
+				" (pid %d) is running - exiting\n",
+				pid);
 			exit(1);
 		}
 		fclose(fp);
@@ -1856,6 +2090,8 @@ int main(int argc, char **argv)
 	int rc;
 	int c;
 
+	memset(&fcoe_config, 0, sizeof(fcoe_config));
+
 	strncpy(progname, basename(argv[0]), sizeof(progname));
 	sa_log_prefix = progname;
 	sa_log_flags = 0;
@@ -1867,7 +2103,11 @@ int main(int argc, char **argv)
 		case 'f':
 			fcm_fg = 1;
 		case 'd':
-			fcm_debug = 1;
+			fcoe_config.debug = 1;
+			break;
+		case 's':
+			fcoe_config.use_syslog = 1;
+			enable_syslog(1);
 			break;
 		case 'e':
 			fcm_dcbd_cmd = optarg;
@@ -1889,7 +2129,7 @@ int main(int argc, char **argv)
 
 		pid = fork();
 		if (pid < 0) {
-			SA_LOG("Starting daemon failed");
+			FCM_LOG("Starting daemon failed");
 			exit(EXIT_FAILURE);
 		} else if (pid)
 			exit(EXIT_SUCCESS);
@@ -1912,21 +2152,32 @@ int main(int argc, char **argv)
 	memset(&sig, 0, sizeof(sig));
 	sig.sa_handler = fcm_sig;
 	rc = sigaction(SIGINT, &sig, NULL);
-	if (rc < 0)
-		SA_LOG_ERR_EXIT(errno, "sigaction failed");
+	if (rc < 0) {
+		FCM_LOG_ERR(errno, "sigaction failed");
+		exit(1);
+	}
 	rc = sigaction(SIGTERM, &sig, NULL);
-	if (rc < 0)
-		SA_LOG_ERR_EXIT(errno, "sigaction failed");
+	if (rc < 0) {
+		FCM_LOG_ERR(errno, "sigaction failed");
+		exit(1);
+	}
 	rc = sigaction(SIGHUP, &sig, NULL);
-	if (rc < 0)
-		SA_LOG_ERR_EXIT(errno, "sigaction failed");
+	if (rc < 0) {
+		FCM_LOG_ERR(errno, "sigaction failed");
+		exit(1);
+	}
 	fcm_pidfile_create();
 	fcm_fcoe_init();
 	fcm_link_init();	/* NETLINK_ROUTE protocol */
 	fcm_dcbd_init();
 
-	sa_select_loop();
+	rc = sa_select_loop();
+	if (rc < 0) {
+		FCM_LOG_ERR(rc, "select error\n");
+		exit(EXIT_FAILURE);
+	}
 	fcm_dcbd_shutdown();
+	fcm_cleanup();
 	return 0;
 }
 
@@ -1945,7 +2196,7 @@ static void print_errors(char *buf, int errors)
 
 	if (!errors) {
 		j = sprintf(msg + len, "none\n");
-		SA_LOG("%s %s", buf, msg);
+		FCM_LOG("%s %s", buf, msg);
 		return;
 	}
 
@@ -1989,5 +2240,5 @@ static void print_errors(char *buf, int errors)
 		sprintf(msg + j, "peer feature not present");
 	}
 
-	SA_LOG("%s %s\n", buf, msg);
+	FCM_LOG("%s %s\n", buf, msg);
 }
diff --git a/fcoemon.h b/fcoemon.h
index 57425e8..11cb4fb 100644
--- a/fcoemon.h
+++ b/fcoemon.h
@@ -20,6 +20,42 @@
 #ifndef _FCOEMON_H_
 #define _FCOEMON_H_
 
+struct fcoe_config {
+	int debug;
+	int use_syslog;
+	struct fcoe_port_config *port;
+} fcoe_config;
+
+/*
+ * Log message.
+ */
+#define FCM_LOG(...)							\
+	do {								\
+		sa_log(__VA_ARGS__);					\
+	} while (0)
+
+#define FCM_LOG_ERR(error, ...) \
+	do {								\
+		sa_log_err(error, NULL, __VA_ARGS__);			\
+	} while (0)
+
+#define FCM_LOG_DBG(fmt, args...)					\
+	do {								\
+		if (fcoe_config.debug)					\
+			sa_log(fmt, ##args);				\
+	} while (0)
+
+#define FCM_LOG_DEV_DBG(fcm, fmt, args...)				\
+	do {								\
+		if (fcoe_config.debug)					\
+			sa_log("%s, " fmt, fcm->ff_name, ##args);	\
+	} while (0)
+
+#define FCM_LOG_DEV(fcm, fmt, args...)				\
+	do {							\
+		sa_log("%s, " fmt, fcm->ff_name, ##args);	\
+	} while (0)
+
 /*
  * States for HBAs relative to the DCB daemon.
  * States advance sequentially if conditions are right.
@@ -29,11 +65,8 @@ enum fcm_dcbd_state {
    FCD_GET_DCB_STATE,   /* getting DCB state */
    FCD_SEND_CONF,       /* set proposed configuration */
    FCD_GET_PFC_CONFIG,  /* getting PFC configuration */
-   FCD_GET_LLINK_CONFIG,/* getting LLINK configuration */
    FCD_GET_APP_CONFIG,  /* getting APP configuration */
    FCD_GET_PFC_OPER,    /* getting PFC operational mode */
-   FCD_GET_LLINK_OPER,  /* getting LLINK operational mode */
-   FCD_GET_LLINK_PEER,  /* getting LLINK peer configuration */
    FCD_GET_APP_OPER,    /* getting operational mode */
    FCD_GET_PEER,        /* getting peer configuration */
    FCD_DONE,            /* DCB exchanges complete */
@@ -45,11 +78,8 @@ enum fcm_dcbd_state {
     { "GET_DCB_STATE",    FCD_GET_DCB_STATE },    \
     { "SEND_CONF",        FCD_SEND_CONF },        \
     { "GET_PFC_CONFIG",   FCD_GET_PFC_CONFIG },   \
-    { "GET_LLINK_CONFIG", FCD_GET_LLINK_CONFIG }, \
     { "GET_APP_CONFIG",   FCD_GET_APP_CONFIG },   \
     { "GET_PFC_OPER",     FCD_GET_PFC_OPER },     \
-    { "GET_LLINK_OPER",   FCD_GET_LLINK_OPER },   \
-    { "GET_LLINK_PEER",   FCD_GET_LLINK_PEER },   \
     { "GET_APP_OPER",     FCD_GET_APP_OPER },     \
     { "GET_PEER",         FCD_GET_PEER },         \
     { "DONE",             FCD_DONE },             \
@@ -73,10 +103,22 @@ struct feature_info {
 };
 
 /*
+ * Description of FCoE VLAN interfaces
+ */
+struct fcm_vfcoe {
+   TAILQ_ENTRY(fcm_vfcoe) fv_list;
+   char			  fv_name[IFNAMSIZ];
+   int			  fv_active;
+};
+
+TAILQ_HEAD(fcm_vfcoe_head, fcm_vfcoe);
+
+/*
  * Description of potential FCoE interface.
  */
 struct fcm_fcoe {
    TAILQ_ENTRY(fcm_fcoe) ff_list;          /* list linkage */
+   struct fcm_vfcoe_head ff_vfcoe_head;    /* list of fcm_vfcoe */
    u_int32_t             ff_ifindex;       /* kernel interface index */
    u_int32_t             ff_flags;         /* kernel interface flags */
    u_int32_t             ff_last_flags;    /* previously known flags */
@@ -89,6 +131,7 @@ struct fcm_fcoe {
    u_int32_t             ff_llink_status;  /* LLink status */
    u_int64_t             ff_mac;           /* MAC address */
    int                   ff_vlan;          /* VLAN ID or -1 if none */
+   int                   ff_active;	   /* active device */
    u_int8_t              ff_operstate;     /* RFC 2863 operational status */
    u_int8_t              ff_qos_mask;      /* 801.p priority mask */
    enum fcm_dcbd_state   ff_dcbd_state;    /* DCB daemon state */
@@ -109,10 +152,11 @@ static struct fcm_fcoe *fcm_fcoe_lookup_mac(u_int64_t ff_mac, int vlan);
 static struct fcm_fcoe *fcm_fcoe_lookup_create_mac(u_int64_t ff_mac, int vlan);
 #endif
 static struct fcm_fcoe *fcm_fcoe_lookup_name(char *name);
-static struct fcm_fcoe *fcm_fcoe_lookup_create_ifindex(u_int32_t ifindex);
+static struct fcm_vfcoe *fcm_vfcoe_lookup_name(struct fcm_fcoe *, char *);
+static struct fcm_fcoe *fcm_fcoe_lookup_create_ifname(char *);
+static struct fcm_vfcoe *fcm_vfcoe_lookup_create_ifname(char *, char *);
 static void fcm_fcoe_set_name(struct fcm_fcoe *, char *);
 static void fcm_fcoe_get_dcb_settings(struct fcm_fcoe *);
-static int fcm_fcoe_port_ready(struct fcm_fcoe *);
 static int fcm_link_init(void);
 
 #endif /* _FCOEMON_H_ */
diff --git a/fcoemon_utils.c b/fcoemon_utils.c
index e98d259..74353e0 100644
--- a/fcoemon_utils.c
+++ b/fcoemon_utils.c
@@ -23,12 +23,19 @@
 
 u_char libsa_lock_hier;		/* for lock debugging non-log related */
 
+int use_syslog;
+
 /*
  * Size of on-stack line buffers.
  * These shouldn't be to large for a kernel stack frame.
  */
 #define SA_LOG_BUF_LEN  200	/* on-stack line buffer size */
 
+void enable_syslog(int enable)
+{
+	use_syslog = enable;
+}
+
 /*
  * log with a variable argument list.
  */
@@ -78,19 +85,6 @@ sa_log(const char *format, ...)
 }
 
 /*
- * log with function name.
- */
-void
-sa_log_func(const char *func, const char *format, ...)
-{
-	va_list arg;
-
-	va_start(arg, format);
-	sa_log_va(func, format, arg);
-	va_end(arg);
-}
-
-/*
  * log with error number.
  */
 void
@@ -182,7 +176,7 @@ sa_log_timestamp(void)
 void
 sa_log_output(const char *buf)
 {
-	if (fcm_use_syslog) {
+	if (use_syslog) {
 		syslog(LOG_INFO, "%s", buf);
 		return;
 	}
@@ -198,12 +192,6 @@ sa_log_abort(const char *buf)
 	abort();
 }
 
-void
-sa_log_output_exit(const char *buf)
-{
-	exit(1);
-}
-
 /*
  * Make a printable NUL-terminated copy of the string.
  * The source buffer might not be NUL-terminated.
@@ -498,8 +486,7 @@ sa_select_call_deferred_funcs(void)
 	}
 }
 
-void
-sa_select_loop(void)
+int sa_select_loop(void)
 {
 	struct sa_sel_state *ss = &sa_sel_state;
 	struct sa_sel_fd *fp;
@@ -533,7 +520,7 @@ sa_select_loop(void)
 		if (rv == -1) {
 			if (errno == EINTR)
 				continue;
-			SA_LOG_ERR_EXIT(errno, "select error");
+			return errno;
 		}
 
 		fp = ss->ts_fd;
@@ -568,6 +555,7 @@ sa_select_loop(void)
 		if (ss->ts_defer_list.tqh_first != NULL)
 			sa_select_call_deferred_funcs();
 	}
+	return 0;
 }
 
 void
diff --git a/fcoemon_utils.h b/fcoemon_utils.h
index e8c5de9..4a4546f 100644
--- a/fcoemon_utils.h
+++ b/fcoemon_utils.h
@@ -40,7 +40,6 @@
 #include "fc_types.h"
 
 void sa_log(const char *format, ...);
-void sa_log_func(const char *func, const char *format, ...);
 void sa_log_err(int, const char *func, const char *format, ...);
 
 /*
@@ -49,43 +48,10 @@ void sa_log_err(int, const char *func, const char *format, ...);
  */
 void sa_log_output(const char *);	/* log message */
 void sa_log_abort(const char *);	/* log message and abort */
-void sa_log_output_exit(const char *);	/* log message and exit */
 
 #define __SA_STRING(x)  #x
 
 /*
- * Log message.
- */
-#define SA_LOG(...) \
-	do {								\
-		sa_log_func(__func__, __VA_ARGS__);			\
-	} while (0)
-
-#define SA_LOG_ERR(error, ...) \
-	do {								\
-		sa_log_err(error, NULL, __VA_ARGS__);			\
-	} while (0)
-
-/*
- * Logging exits.
- */
-#define SA_LOG_EXIT(...) \
-	do {								\
-		sa_log_func(__func__, __VA_ARGS__);			\
-		sa_log_func(__func__, "exiting at %s:%d",		\
-			__FILE__, __LINE__);				\
-		sa_log_output_exit(__func__);				\
-	} while (0)
-
-#define SA_LOG_ERR_EXIT(error, ...) \
-	do {								\
-		sa_log_func(__func__, __VA_ARGS__);			\
-		sa_log_err(error, __func__, "exiting at %s:%d",		\
-			__FILE__, __LINE__);				\
-		sa_log_output_exit(__func__);           		\
-	} while (0)
-
-/*
  * Logging options.
  */
 #define SA_LOGF_TIME    0x0001      /* include timestamp in message */
@@ -256,7 +222,7 @@ u_int sa_timer_get_secs(void);
 /*
  * Enter the polling loop which never exits.
  */
-void sa_select_loop(void);
+int sa_select_loop(void);
 
 /*
  * Set callback for every time through the select loop.
@@ -310,6 +276,8 @@ extern fc_wwn_t
 fc_wwn_from_mac(u_int64_t, u_int32_t scheme, u_int32_t port);
 
 extern int hex2int(char *b);
-extern int fcm_use_syslog;
+
+int use_syslog;
+void enable_syslog(int);
 
 #endif /* _FCOEMON_UTILS_H_ */
diff --git a/fcoeplumb.in b/fcoeplumb.in
index 50573cd..0445999 100755
--- a/fcoeplumb.in
+++ b/fcoeplumb.in
@@ -22,8 +22,8 @@ cmdname=`basename $0`
 usage()
 {
 	echo usage: $cmdname \
-		'<ethX> [--reset | --enable | --disable] [--debug]' \
-		'[--qos <pri>[,<pri>]...]]' >&2
+		'<ethX> [--reset | --create | --destroy] [--debug] [--syslog]' \
+		'[--qos-disable | --qos-enable  <pri>[,<pri>]...]' >&2
 	exit 1
 }
 
@@ -50,17 +50,10 @@ sysconfdir=@sysconfdir@
 # make sure there's a sane path to find basic commands and system tools (tc)
 PATH=$PATH:/sbin:/usr/sbin:/bin:/usr/bin
 FCOEADM=${sbindir}/fcoeadm	# command to create/destroy FCoE instances
-LOGGER="logger -s -t fcoeplumb"
 CONFIG_DIR=${sysconfdir}/fcoe
+LOGGER=
 DEBUG_LOGGING=
 
-. $CONFIG_DIR/config
-if [ "$USE_SYSLOG" != "yes" ] && [ "$USE_SYSLOG" != "YES" ]; then
-	LOGGER="echo"
-else
-	USE_SYSLOG="yes"
-fi
-
 find_multiq_qdisc()
 {
 	ifname=$1
@@ -204,8 +197,8 @@ remove_fcoe_interface()
 	ifname=$1
 
 	STATUS=`$FCOEADM -i $ifname 2>&1 | \
-		awk '/Symbolic Name:/{print $6}'`
-	if [ "$STATUS" = "$ifname" ]; then
+		awk '/Symbolic Name:/ && /'$ifname'$/{print $6}'`
+	if [ "$STATUS" == "$ifname" ]; then
 		[ ${DEBUG_LOGGING} ] && $LOGGER "$FCOEADM -d $ifname"
 		$FCOEADM -d $ifname
 	else
@@ -219,7 +212,7 @@ create_fcoe_interface()
 	ifname=$1
 
 	STATUS=`$FCOEADM -i $ifname 2>&1 | \
-		awk '/Symbolic Name:/{print $6}'`
+		awk '/Symbolic Name:/ && /'$ifname'$/{print $6}'`
 	if [ -z "$STATUS" ]; then
 		[ ${DEBUG_LOGGING} ] && $LOGGER "$FCOEADM -c $ifname"
 		$FCOEADM -c $ifname
@@ -229,6 +222,19 @@ create_fcoe_interface()
 	fi
 }
 
+config_logging()
+{
+    if [ $DEBUG_LOGGING ] ; then
+	if [ $USE_SYSLOG ] ; then
+	    LOGGER="logger -t fcoeplumb"
+	else
+	    LOGGER="echo"
+	fi
+    else
+	LOGGER=
+    fi
+}
+
 [ "$#" -lt 1 ] && usage
 
 [ ${DEBUG_LOGGING} ] && $LOGGER "fcoeplumb arguments: ($*)"
@@ -239,25 +245,33 @@ shift
 while [ "$#" -ge 1 ]
 do
 	case "$1" in
-	--reset | -r)
+	    --reset | -r)
 		cmd=reset
 		;;
-	--enable | -e)
-		cmd=enable
+	    --create | -c)
+		cmd=create
 		;;
-	--disable | -d)
-		cmd=disable
+	    --destroy | -d)
+		cmd=destroy
 		;;
-	--debug)
-		LOGGER="logger -t fcoeplumb -s"
+	    --debug)
 		DEBUG_LOGGING=1
+		config_logging
 		;;
-	--qos | -q)
+            --syslog)
+		USE_SYSLOG=1
+		config_logging
+		;;
+	    --qos-enable)
 		[ "$#" -lt 2 ] && usage
 		qos_list=$2
 		shift
 		;;
-	*)
+	    --qos-disable)
+		[[ "$2" =~ ^[0-9]+$ ]] && shift
+		qos_list="disable"
+		;;
+	    *)
 		echo "$cmdname: unknown parameter '$1'" >&2
 		usage
 		;;
@@ -271,8 +285,14 @@ done
 fcoe_filter_id=`get_filter_id $ifname $FCOE_FILTER_KEY`
 fip_filter_id=`get_filter_id $ifname $FIP_FILTER_KEY`
 
-if [ "$cmd" == "disable" ]; then
+if [ "$cmd" == "destroy" ]; then
 	remove_fcoe_interface $ifname
+
+elif [ "$cmd" == "create" ]; then
+	create_fcoe_interface $ifname
+fi
+
+if [ "$qos_list" == "disable" ]; then
 	# Remove the FCoE filters
 	find_skbedit_filter $ifname $FCOE_ETHERTYPE $fcoe_filter_id
 	found_filter=$?
@@ -283,7 +303,7 @@ if [ "$cmd" == "disable" ]; then
 	found_filter=$?
 	[ $found_filter -le 7 ] && delete_skbedit_filter $ifname $found_filter $FIP_ETHERTYPE $fip_filter_id
 
-else
+elif [ -n $qos_list ]; then
 	#
 	# Choose the best QOS to use for FCoE out of the listed choices.
 	#
@@ -315,11 +335,11 @@ else
 		done
 	fi
 
-	[ ${DEBUG_LOGGING} ] && $LOGGER "$ifname - Choosing QOS '$QOS_BEST'"
-
 	# If the best QOS is not found, do nothing.
 	[ -z "$QOS_BEST" ] && exit 0
 
+	[ ${DEBUG_LOGGING} ] && $LOGGER "$ifname - Choosing QOS '$QOS_BEST'"
+
 	#
 	# Setup the traffic classifier for FCoE
 	# First see if it is already set up.
@@ -366,10 +386,6 @@ else
 		add_skbedit_filter $ifname $qdisc_id $qos_queue $FCOE_ETHERTYPE $fcoe_filter_id
 		add_skbedit_filter $ifname $qdisc_id $qos_queue $FIP_ETHERTYPE $fip_filter_id
 	fi
-
-	if [ "$cmd" = "enable" ]; then
-		create_fcoe_interface $ifname
-	fi
 fi
 
 exit 0
diff --git a/fcping.c b/fcping.c
new file mode 100644
index 0000000..e75bc01
--- /dev/null
+++ b/fcping.c
@@ -0,0 +1,932 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * FCPing - FC fabric diagnostic.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <limits.h>
+#include <signal.h>
+#include <libgen.h>
+#include <assert.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+#include <hbaapi.h>
+#include <vendorhbaapi.h>
+#include <linux/types.h>
+#include <linux/bsg.h>
+#include "net_types.h"
+#include "fc_types.h"
+typedef uint8_t u8;
+#include <scsi/sg.h>
+#include <scsi/fc/fc_ns.h>
+#include <scsi/fc/fc_gs.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/scsi_bsg_fc.h>
+
+static const char *cmdname;
+
+#define FC_MAX_PAYLOAD  (2112U - sizeof(net32_t))
+#define MAX_SENSE_LEN	96	/* SCSI_SENSE_BUFFERSIZE */
+#define MAX_HBA_COUNT	128
+#define FP_LEN_DEF	32	/* default ping payload length */
+#define FP_LEN_PAD	32	/* extra length for response */
+#define FP_MIN_INTVL	0.001	/* minimum interval in seconds */
+#define FP_DEF_INTVL	1.000	/* default sending interval in seconds */
+#define SYSFS_HBA_DIR   "/sys/class/net"
+
+static void
+fp_usage()
+{
+	fprintf(stderr,
+	"Usage: %s [-fqx] -i <interval> -c <count> -h <hba> -s <size> \\\n"
+	"                [ -F <FC-ID> | -P <WWPN> | -N <WWNN>]\n"
+	"  flags: \n"
+	"     -f:            Flood ping\n"
+	"     -q:            Quiet! just print summary\n"
+	"     -x:            Hex dump of responses\n"
+	"     -i <interval>: Wait <interval> seconds between each ping\n"
+	"     -c <count>:    Stop after sending <count> pings\n"
+	"     -h <hba>:      eth<n>, MAC address, WWPN, or FC-ID of the HBA\n"
+	"     -s <size>:     Byte-length of ping request payload (max %ld)\n"
+	"     -F <FC-ID>:    Destination port ID\n"
+	"     -P <WWPN>:     Destination world-wide port name\n"
+	"     -N <WWNN>:     Destination world-wide node name\n",
+	cmdname, FC_MAX_PAYLOAD);
+	exit(1);
+}
+
+static fc_fid_t fp_did;
+static fc_wwn_t fp_port_wwn;
+static fc_wwn_t fp_node_wwn;
+static int fp_count = -1;	/* send indefinitely by default */
+static uint32_t fp_len = FP_LEN_DEF;
+static int fp_flood;			/* send as fast as possible */
+static uint32_t fp_interval = FP_DEF_INTVL * 1000; /* in milliseconds */
+static int fp_quiet;
+static int fp_hex;
+static char *fp_hba;	/* name of interface to be used */
+static int fp_hba_type;
+#define FP_HBA_FCID_TYPE	1
+#define FP_HBA_WWPN_TYPE	2
+#define FP_HBA_HOST_TYPE	3
+#define FP_HBA_ETH_TYPE		4
+static char fp_dev[64];
+static int fp_fd;	/* file descriptor for openfc ioctls */
+static void *fp_buf;	/* sending buffer */
+static int fp_debug;
+
+struct fp_stats {
+	uint32_t fp_tx_frames;
+	uint32_t fp_rx_frames;
+	uint32_t fp_rx_errors;
+	uint64_t fp_transit_time_us; /* total transit time in microseconds */
+	uint32_t fp_rx_times;        /* valid times on receive */
+};
+static struct fp_stats fp_stats;
+
+#define hton24(p, v) \
+do { \
+	p[0] = (((v) >> 16) & 0xFF);	\
+	p[1] = (((v) >> 8) & 0xFF);	\
+	p[2] = ((v) & 0xFF);		\
+} while (0)
+
+#define hton64(p, v) \
+do { \
+	p[0] = (u_char) ((v) >> 56) & 0xFF;	\
+	p[1] = (u_char) ((v) >> 48) & 0xFF;	\
+	p[2] = (u_char) ((v) >> 40) & 0xFF;	\
+	p[3] = (u_char) ((v) >> 32) & 0xFF;	\
+	p[4] = (u_char) ((v) >> 24) & 0xFF;	\
+	p[5] = (u_char) ((v) >> 16) & 0xFF;	\
+	p[6] = (u_char) ((v) >> 8) & 0xFF;	\
+	p[7] = (u_char) (v) & 0xFF;		\
+} while (0)
+
+static void sa_log_func(const char *func, const char *format, ...);
+static void sa_log_err(int, const char *func, const char *format, ...);
+static void sa_log_output(const char *buf);
+
+/*
+ * Log message.
+ */
+#define SA_LOG(...) \
+	do { sa_log_func(__func__, __VA_ARGS__); } while (0)
+
+#define SA_LOG_ERR(error, ...) \
+	do { sa_log_err(error, NULL, __VA_ARGS__); } while (0)
+
+/*
+ * Logging exits.
+ */
+#define SA_LOG_EXIT(...) \
+	do {	sa_log_func(__func__, __VA_ARGS__); \
+		if (fp_debug) \
+			sa_log_func(__func__, \
+			"Exiting at %s:%d", __FILE__, __LINE__); \
+		exit(1); \
+	} while (0)
+
+#define SA_LOG_ERR_EXIT(error, ...) \
+	do {	sa_log_func(__func__, __VA_ARGS__); \
+		if (fp_debug) \
+			sa_log_err(error, __func__, \
+			"Exiting at %s:%d", __FILE__, __LINE__); \
+		else \
+			sa_log_err(error, NULL, NULL); \
+		exit(1); \
+	} while (0)
+
+#define SA_LOG_BUF_LEN  200     /* on-stack line buffer size */
+
+/*
+ * log with a variable argument list.
+ */
+static void
+sa_log_va(const char *func, const char *format, va_list arg)
+{
+	size_t len;
+	size_t flen;
+	int add_newline;
+	char sa_buf[SA_LOG_BUF_LEN];
+	char *bp;
+
+	/*
+	 * If the caller didn't provide a newline at the end, we will.
+	 */
+	len = strlen(format);
+	add_newline = 0;
+	if (!len || format[len - 1] != '\n')
+		add_newline = 1;
+	bp = sa_buf;
+	len = sizeof(sa_buf);
+	if (func) {
+		flen = snprintf(bp, len, "%s: ", func);
+		len -= flen;
+		bp += flen;
+	}
+	flen = vsnprintf(bp, len, format, arg);
+	if (add_newline && flen < len) {
+		bp += flen;
+		*bp++ = '\n';
+		*bp = '\0';
+	}
+	sa_log_output(sa_buf);
+}
+
+/*
+ * log with function name.
+ */
+static void
+sa_log_func(const char *func, const char *format, ...)
+{
+	va_list arg;
+
+	va_start(arg, format);
+	if (fp_debug)
+		sa_log_va(func, format, arg);
+	else
+		sa_log_va(NULL, format, arg);
+	va_end(arg);
+}
+
+/*
+ * log with error number.
+ */
+static void
+sa_log_err(int error, const char *func, const char *format, ...)
+{
+	va_list arg;
+	char buf[SA_LOG_BUF_LEN];
+
+	strerror_r(error, buf, sizeof(buf));
+	sa_log_func(func, "errno=%d %s", error, buf);
+	if (format) {
+		va_start(arg, format);
+		sa_log_va(func, format, arg);
+		va_end(arg);
+	}
+}
+
+static void
+sa_log_output(const char *buf)
+{
+	fprintf(stderr, "%s", buf);
+	fflush(stderr);
+}
+
+static char *
+sa_hex_format(char *buf, size_t buflen,
+		const unsigned char *data, size_t data_len,
+		unsigned int group_len, char *inter_group_sep)
+{
+	size_t rlen, tlen;
+	char *bp, *sep;
+	unsigned int i;
+
+	rlen = buflen;
+	bp = buf;
+	sep = "";
+	for (i = 0; rlen > 0 && i < data_len; ) {
+		tlen = snprintf(bp, rlen, "%s%2.2x", sep, data[i]);
+		rlen -= tlen;
+		bp += tlen;
+		i++;
+		sep = (i % group_len) ? "" : inter_group_sep;
+	}
+	return buf;
+}
+
+/*
+ * Hex dump buffer to file.
+ */
+static void sa_hex_dump(unsigned char *bp, size_t len, FILE *fp)
+{
+	char lbuf[120];
+	size_t tlen;
+	uint32_t offset = 0;
+
+	while (len > 0) {
+		tlen = 16;  /* bytes per line */
+		if (tlen > len)
+			tlen = len;
+		sa_hex_format(lbuf, sizeof(lbuf), bp, tlen, 4, " ");
+		fprintf(fp, "%6x %s\n", offset, lbuf);
+		offset += tlen;
+		len -= tlen;
+		bp += tlen;
+	}
+}
+
+/*
+ * Convert 48-bit IEEE MAC address to 64-bit FC WWN.
+ */
+fc_wwn_t
+fc_wwn_from_mac(uint64_t mac, uint32_t scheme, uint32_t port)
+{
+	fc_wwn_t wwn;
+
+	assert(mac < (1ULL << 48));
+	wwn = mac | ((fc_wwn_t) scheme << 60);
+	switch (scheme) {
+	case 1:
+		assert(port == 0);
+		break;
+	case 2:
+		assert(port < 0xfff);
+		wwn |= (fc_wwn_t) port << 48;
+		break;
+	default:
+		assert(1);
+		break;
+	}
+	return wwn;
+}
+
+/*
+ * Handle WWN/MAC arguments
+ */
+static fc_wwn_t
+fp_parse_wwn(const char *arg, char *msg, uint32_t scheme, uint32_t port)
+{
+	char *endptr;
+	fc_wwn_t wwn;
+	fc_wwn_t oui;
+	struct ether_addr mac;
+
+	wwn = strtoull(arg, &endptr, 16);
+	if (*endptr != '\0') {
+		if (ether_aton_r(arg, &mac) == NULL &&
+		    ether_hostton(arg, &mac) != 0) {
+			SA_LOG_EXIT("invalid %s WWN or MAC addr %s", msg, arg);
+		}
+		oui = net48_get((net48_t *)mac.ether_addr_octet);
+		wwn = fc_wwn_from_mac(oui, scheme, port);
+	}
+	return wwn;
+}
+
+/*
+ * Handle options.
+ */
+static void
+fp_options(int argc, char *argv[])
+{
+	int opt;
+	char *endptr;
+	float sec;
+	int targ_spec = 0;
+
+	cmdname = basename(argv[0]);
+	if (argc <= 1)
+		fp_usage();
+
+	while ((opt = getopt(argc, argv, "c:fi:h:qs:xF:P:N:")) != -1) {
+		switch (opt) {
+		case 'c':
+			fp_count = (int) strtoul(optarg, &endptr, 10);
+			if (*endptr != '\0')
+				SA_LOG_EXIT("bad count %s\n", optarg);
+			break;
+		case 'f':
+			fp_flood = 1;
+			break;
+		case 'i':
+			if (sscanf(optarg, "%f", &sec) != 1 ||
+					sec < FP_MIN_INTVL)
+				SA_LOG_EXIT("bad interval %s\n", optarg);
+			fp_interval = sec * 1000;
+			break;
+		case 'h':
+			fp_hba = optarg;
+			break;
+		case 'q':
+			fp_quiet = 1;
+			break;
+		case 's':
+			fp_len = strtoul(optarg, &endptr, 0);
+			if (*endptr != '\0' || fp_len > FC_MAX_PAYLOAD)
+				SA_LOG_EXIT("bad size %s max %d\n",
+					optarg, FC_MAX_PAYLOAD);
+			if (fp_len < 4)
+				SA_LOG_EXIT("bad size %s min %d\n",
+					optarg, 4);
+			break;
+		case 'x':
+			fp_hex = 1;
+			break;
+
+		/*
+		 * -F specifies the target FC_ID.
+		 */
+		case 'F':
+			fp_did = strtoull(optarg, &endptr, 16);
+			if (*endptr != '\0')
+				SA_LOG_EXIT("bad target FC_ID %s\n", optarg);
+			targ_spec++;
+			break;
+
+		/*
+		 * The -P and -N flags take a world-wide name in hex,
+		 * or an ethernet addr, or an etherhost entry from /etc/ethers.
+		 */
+		case 'N':
+			fp_node_wwn = fp_parse_wwn(optarg, "Node", 1, 0);
+			targ_spec++;
+			break;
+
+		case 'P':
+			fp_port_wwn = fp_parse_wwn(optarg, "Port", 2, 0);
+			targ_spec++;
+			break;
+
+		case '?':
+		default:
+			fprintf(stderr, "FC_MAX_PAYLOAD=%lu\n", FC_MAX_PAYLOAD);
+			fp_usage();	/* exits */
+			break;
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (fp_hba == NULL)
+		SA_LOG_EXIT("FCoE interface not specified");
+
+	if (targ_spec == 0)
+		SA_LOG_EXIT("no target specified");
+
+	if (targ_spec > 1)
+		SA_LOG_EXIT("too many targets specified;"
+			    " only one is allowed.");
+
+	return;
+}
+
+/*
+ * Lookup specified adapter using HBAAPI.
+ */
+static int
+fp_find_hba(void)
+{
+	HBA_STATUS retval;
+	HBA_UINT32 hba_cnt;
+	HBA_HANDLE hba_handle = 0;
+	HBA_ADAPTERATTRIBUTES hba_attrs;
+	HBA_PORTATTRIBUTES port_attrs;
+	HBA_UINT32 fcid = 0;
+	struct stat statbuf;
+	char namebuf[1028];
+	char hba_dir[256];
+	fc_wwn_t wwn = 0;
+	HBA_WWN wwpn;
+	char *endptr;
+	int i, found = 0;
+
+	/*
+	 * Parse HBA spec. if there is one.
+	 * These formats are tried:
+	 *    If pass in an interface name, it does not need
+	 *    to be validated here. The interface name can be
+	 *    anything. It will have to be found via HBAAPI
+	 *    library. It fails if not found.
+	 *    host<n> = match the index <n>.
+	 *    mac address xx:xx:xx:xx:xx:xx
+	 *    otherwise, try parsing as a wwn and match that.
+	 */
+
+	snprintf(hba_dir, sizeof(hba_dir), SYSFS_HBA_DIR "/%s", fp_hba);
+	if (!stat(hba_dir, &statbuf)) {
+		fp_hba_type = FP_HBA_ETH_TYPE;
+	} else if (strstr(fp_hba, "host") == fp_hba) {
+		i = strtoul(fp_hba + 4, &endptr, 10);
+		if (*endptr != '\0')
+			SA_LOG_EXIT("invalid hba name %s", fp_hba);
+		fp_hba_type = FP_HBA_HOST_TYPE;
+	} else if (strstr(fp_hba, ":")) {
+		wwn = fp_parse_wwn(fp_hba, "HBA", 2, 0);
+		hton64(wwpn.wwn, wwn);
+		fp_hba_type = FP_HBA_WWPN_TYPE;
+	} else {
+		wwn = strtoull(fp_hba, &endptr, 16);
+		if (wwn < 0x1000000) {
+			fcid = wwn;
+			fp_hba_type = FP_HBA_FCID_TYPE;
+		} else {
+			if (*endptr != '\0')
+				SA_LOG_EXIT("unsupported hba name");
+			wwn = fp_parse_wwn(fp_hba, "HBA", 2, 0);
+			hton64(wwpn.wwn, wwn);
+			fp_hba_type = FP_HBA_WWPN_TYPE;
+		}
+	}
+
+	hba_cnt = HBA_GetNumberOfAdapters();
+	if (!hba_cnt)
+		SA_LOG_EXIT("No FCoE interfaces created");
+
+	for (i = 0; i < hba_cnt; i++) {
+		retval = HBA_GetAdapterName(i, namebuf);
+		if (retval != HBA_STATUS_OK) {
+			SA_LOG("HBA_GetAdapterName"
+				" failed, retval=%d", retval);
+			continue;
+		}
+
+		hba_handle = HBA_OpenAdapter(namebuf);
+		if (!hba_handle) {
+			SA_LOG("HBA_OpenAdapter failed");
+			continue;
+		}
+
+		retval = HBA_GetAdapterAttributes(hba_handle, &hba_attrs);
+		if (retval != HBA_STATUS_OK) {
+			SA_LOG("HBA_GetAdapterAttributes"
+				" failed, retval=%d", retval);
+			HBA_CloseAdapter(hba_handle);
+			continue;
+		}
+
+		retval = HBA_GetAdapterPortAttributes(
+				hba_handle, 0, &port_attrs);
+		if (retval != HBA_STATUS_OK) {
+			SA_LOG("HBA_GetAdapterPortAttributes"
+				" failed, retval=%d", retval);
+			HBA_CloseAdapter(hba_handle);
+			continue;
+		}
+
+		switch (fp_hba_type) {
+		case FP_HBA_FCID_TYPE:
+			if (port_attrs.PortFcId != fcid) {
+				HBA_CloseAdapter(hba_handle);
+				continue;
+			}
+			break;
+		case FP_HBA_WWPN_TYPE:
+			if (memcmp(&port_attrs.PortWWN, &wwpn, sizeof(wwpn))) {
+				HBA_CloseAdapter(hba_handle);
+				continue;
+			}
+			break;
+		case FP_HBA_HOST_TYPE:
+			if (!strstr(port_attrs.OSDeviceName, fp_hba)) {
+				HBA_CloseAdapter(hba_handle);
+				continue;
+			}
+			break;
+		default:
+			if (!strstr(port_attrs.PortSymbolicName, fp_hba)) {
+				HBA_CloseAdapter(hba_handle);
+				continue;
+			}
+			break;
+		}
+
+		snprintf(fp_dev, sizeof(fp_dev),
+			"fc_%s", port_attrs.OSDeviceName);
+		found = 1;
+		break;
+	}
+	if (!found)
+		SA_LOG("FCoE interface %s not found", fp_hba);
+
+	return found;
+}
+
+static void
+fp_report(void)
+{
+	double loss;
+	struct fp_stats *sp = &fp_stats;
+
+	loss = 100.0 * (sp->fp_tx_frames - sp->fp_rx_frames) / sp->fp_tx_frames;
+	printf("%d frames sent, %d received %d errors, %.3f%% loss, "
+		"avg. rt time %.3f ms\n",
+		sp->fp_tx_frames, sp->fp_rx_frames, sp->fp_rx_errors, loss,
+		sp->fp_rx_times ?  sp->fp_transit_time_us * 1.0 /
+		(1000.0 * sp->fp_rx_times) : 0.0);
+}
+
+/*
+ * Lookup ID from port name or node name.
+ */
+static int
+fp_ns_get_id(uint32_t op, fc_wwn_t wwn, char *response, size_t *resp_len)
+{
+	struct ct_get_id {
+		struct fc_ct_hdr hdr;
+		net64_t	 wwn;
+	} ct;
+	struct fc_bsg_request cdb;
+	struct fc_bsg_reply reply;
+	struct sg_io_v4 sg_io;
+	size_t actual_len;
+	int cmd, rc = 0;
+
+	memset((char *)&cdb, 0, sizeof(cdb));
+	memset(&ct, 0, sizeof(ct));
+	ct.hdr.ct_rev = FC_CT_REV;
+	hton24(ct.hdr.ct_in_id, 0xfffffc);
+	ct.hdr.ct_fs_type = FC_FST_DIR;
+	ct.hdr.ct_fs_subtype = FC_NS_SUBTYPE;
+	ct.hdr.ct_options = 0;
+	ct.hdr.ct_cmd = htons(op);
+	ct.hdr.ct_mr_size = *resp_len;
+	net64_put(&ct.wwn, wwn);
+
+	cdb.msgcode = FC_BSG_HST_CT;
+	hton24(cdb.rqst_data.h_ct.port_id, 0xfffffc);
+	memcpy(&cdb.rqst_data.h_ct.preamble_word0, &ct.hdr,
+	       3 * sizeof(uint32_t));
+
+	sg_io.guard = 'Q';
+	sg_io.protocol = BSG_PROTOCOL_SCSI;
+	sg_io.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT;
+	sg_io.request_len = sizeof(cdb);
+	sg_io.request = (__u64)&cdb;
+	sg_io.dout_xfer_len = sizeof(ct);
+	sg_io.dout_xferp = (unsigned long)&ct;
+	sg_io.din_xfer_len = *resp_len;
+	sg_io.din_xferp = (__u64)response;
+	sg_io.max_response_len = sizeof(reply);
+	sg_io.response = (__u64)&reply;
+	sg_io.timeout = 1000;	/* millisecond */
+	memset(&reply, 0, sizeof(reply));
+	memset(response, 0, *resp_len);
+
+	rc = ioctl(fp_fd, SG_IO, &sg_io);
+	if (rc < 0) {
+		if (op == FC_NS_GID_PN)
+			printf("GID_PN error: %s\n", strerror(errno));
+		if (op == FC_NS_GID_NN)
+			printf("GID_NN error: %s\n", strerror(errno));
+		return rc;
+	}
+
+	cmd = ((response[8]<<8) | response[9]) & 0xffff;
+	if (cmd != FC_FS_ACC)
+		return -1;
+
+	actual_len = reply.reply_payload_rcv_len;
+	if (actual_len < *resp_len)
+		*resp_len = actual_len;
+
+	return 0;
+}
+
+static int
+fp_lookup_target()
+{
+	char response[32];
+	size_t resp_len;
+	int rc;
+
+	if (fp_did != 0)
+		return 0;
+
+	if (fp_port_wwn != 0) {
+		resp_len = sizeof(response);
+		memset(&response, 0, sizeof(response));
+		rc = fp_ns_get_id(FC_NS_GID_PN, fp_port_wwn,
+				response, &resp_len);
+		if (rc == 0) {
+			fp_did = ((response[17] << 16) & 0xff0000) |
+				 ((response[18] << 8) & 0x00ff00) |
+				 (response[19] & 0x0000ff);
+			return 0;
+		}
+		SA_LOG("cannot find fcid of destination @ wwpn 0x%llX",
+			fp_port_wwn);
+	}
+	if (fp_node_wwn != 0) {
+		resp_len = sizeof(response);
+		memset(&response, 0, sizeof(response));
+		rc = fp_ns_get_id(FC_NS_GID_NN, fp_node_wwn,
+				response, &resp_len);
+		if (rc == 0) {
+			fp_did = ((response[17] << 16) & 0xff0000) |
+				 ((response[18] << 8) & 0x00ff00) |
+				 (response[19] & 0x0000ff);
+			return 0;
+		}
+		SA_LOG("cannot find fcid of destination @ wwnn 0x%llX",
+			fp_node_wwn);
+	}
+	return 1;
+}
+
+/*
+ * ELS_ECHO request format being used.
+ * Put a sequence number in the payload, followed by the pattern.
+ */
+struct fcping_echo {
+	net8_t      fe_op;              /* opcode */
+	net24_t     fe_resvd;           /* reserved, must be zero */
+	net32_t     fe_seq;             /* sequence number */
+};
+
+/*
+ * Setup buffer to be sent.
+ */
+static void
+fp_buf_setup(void)
+{
+	struct fcping_echo *ep;
+	net8_t *pp;
+	int len;
+	int i;
+
+	/*
+	 * Alloc extra in case of odd len or shorter than minimum.
+	 */
+	len = fp_len + sizeof(*ep) + sizeof(net32_t);
+	ep = calloc(1, len);
+	if (ep == NULL)
+		SA_LOG_ERR_EXIT(errno, "calloc %d bytes failed", len);
+	ep->fe_op = ELS_ECHO;
+	net32_put(&ep->fe_seq, 1);      /* starting sequence number */
+	i = 0;
+	for (pp = (net8_t *) (ep + 1); pp < (net8_t *) ep + fp_len; pp++)
+		*pp = i++;
+	fp_buf = ep;
+}
+
+static unsigned long long
+fp_get_time_usec(void)
+{
+#ifdef _POSIX_TIMERS
+	struct timespec ts;
+	int rc;
+
+	rc = clock_gettime(CLOCK_MONOTONIC, &ts);
+	if (rc)
+		SA_LOG_ERR_EXIT(errno, "clock_gettime error");
+	return ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000;
+#else
+#warning no _POSIX_TIMERS
+	struct timeval ts;
+
+	gettimeofday(&ts, NULL);
+	return ts.tv_sec * 1000000ULL + ts.tv_usec;
+#endif /* _POSIX_TIMERS */
+}
+
+static int
+send_els_echo(int fp_fd, void *fp_buf, uint32_t fp_len,
+		unsigned char *resp, uint32_t *resp_len, fc_fid_t fp_did)
+{
+	struct fc_bsg_request cdb;
+	char sense[MAX_SENSE_LEN];
+	struct sg_io_v4 sg_io;
+	int rc;
+
+	cdb.msgcode = FC_BSG_HST_ELS_NOLOGIN;
+	cdb.rqst_data.h_els.command_code = ELS_ECHO;
+	hton24(cdb.rqst_data.h_els.port_id, fp_did);
+
+	sg_io.guard = 'Q';
+	sg_io.protocol = BSG_PROTOCOL_SCSI;
+	sg_io.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT;
+	sg_io.request_len = sizeof(cdb);
+	sg_io.request = (unsigned long)&cdb;
+	sg_io.dout_xfer_len = fp_len;
+	sg_io.dout_xferp = (unsigned long)fp_buf;
+	sg_io.din_xfer_len = *resp_len;
+	sg_io.din_xferp = (unsigned long)resp;
+	sg_io.max_response_len = sizeof(sense);
+	sg_io.response = (unsigned long)sense;
+	sg_io.timeout = 20000;
+	memset(sense, 0, sizeof(sense));
+
+	rc = ioctl(fp_fd, SG_IO, &sg_io);
+	if (rc < 0)
+		return 1;
+
+	*resp_len = sg_io.din_xfer_len - sg_io.din_resid;
+	return 0;
+}
+
+/*
+ * Send ELS ECHO.
+ */
+static int fp_send_ping(void)
+{
+	struct fp_stats *sp = &fp_stats;
+	struct fcping_echo *ep;
+	int rc;
+	uint32_t resp_len;
+	unsigned char *resp;
+	unsigned long long tx_time;
+	unsigned long long usec;
+	char msg[80];
+	char time_msg[80];
+
+	resp_len = fp_len + FP_LEN_PAD; /* for odd-byte padding and then some */
+	resp = calloc(1, resp_len);
+	if (resp == NULL)
+		SA_LOG_EXIT("calloc %d bytes failed", resp_len);
+
+	sp->fp_tx_frames++;
+	if (fp_len >= sizeof(*ep)) {
+		ep = (struct fcping_echo *) fp_buf;
+		net32_put(&ep->fe_seq, sp->fp_tx_frames);
+	}
+	tx_time = fp_get_time_usec();
+
+	/* send ELS ECHO frame and receive */
+	rc = send_els_echo(fp_fd, fp_buf, fp_len, resp, &resp_len, fp_did);
+	if (rc) {
+		sp->fp_rx_errors++;
+		printf("echo %4d error: %s\n",
+			sp->fp_tx_frames, strerror(errno));
+	} else {
+		usec = fp_get_time_usec();
+		sp->fp_rx_frames++;
+		ep = (struct fcping_echo *) resp;
+		if (usec < tx_time) {
+			snprintf(time_msg, sizeof(time_msg),
+				"time unknown now %llx old %llx",
+				usec, tx_time);
+			usec = 0;	/* as if time went backwards */
+		} else {
+			usec = usec - tx_time;
+			snprintf(time_msg, sizeof(time_msg),
+				"%6.3f ms", usec / 1000.0);
+			sp->fp_transit_time_us += usec;
+			sp->fp_rx_times++;
+		}
+		if (ep->fe_op == ELS_LS_ACC) {
+			if (memcmp((char *) ep + 1,
+					(char *) fp_buf + 1, fp_len - 1) == 0)
+				snprintf(msg, sizeof(msg), "accepted");
+			else {
+				sp->fp_rx_errors++;
+				snprintf(msg, sizeof(msg),
+					"accept data mismatches");
+			}
+		} else if (ep->fe_op == ELS_LS_RJT) {
+			sp->fp_rx_errors++;
+			snprintf(msg, sizeof(msg), "REJECT received");
+		} else {
+			sp->fp_rx_errors++;
+			snprintf(msg, sizeof(msg),
+				"op %x received", ep->fe_op);
+		}
+		if (fp_quiet == 0)
+			printf("echo %4d %-30s %s\n",
+				sp->fp_tx_frames, msg, time_msg);
+	}
+	if (fp_hex) {
+		printf("response length %u\n", resp_len);
+		sa_hex_dump(resp, resp_len, stdout);
+		printf("\n");
+	}
+	free(resp);
+	return rc;
+}
+
+static void
+fp_signal_handler(int sig)
+{
+	/*
+	 * Allow graceful termination of the
+	 * for loop in fp_start.
+	 */
+	fp_count = 0;
+}
+
+/*
+ * Main loop.
+ */
+static void fp_start(void)
+{
+	struct sigaction act;
+	int i;
+	int rc;
+
+	memset(&act, 0, sizeof(act));
+	act.sa_handler = fp_signal_handler;
+	act.sa_flags = 0;
+
+	sigaction(SIGTERM, &act, NULL);		/* Signal 15: kill <pid> */
+	sigaction(SIGQUIT, &act, NULL);		/* Signal 3: Ctrl-\ */
+	sigaction(SIGINT,  &act, NULL);		/* Signal 2: Ctrl-C */
+
+	printf("sending echo to 0x%X\n", fp_did);
+	for (i = 0; fp_count == -1 || i < fp_count; i++) {
+		rc = fp_send_ping();
+		if (rc != 0 && errno == EMSGSIZE)
+			break;
+		if (rc != 0 && errno == ECONNABORTED)
+			break;
+		if (fp_flood == 0)
+			usleep(fp_interval * 1000);
+		if (!fp_count)
+			break;
+	}
+}
+
+/*
+ * Main.
+ */
+int main(int argc, char *argv[])
+{
+	char bsg_dev[80];
+	int rc = 1;
+
+	fp_options(argc, argv);
+
+	if (HBA_LoadLibrary() != HBA_STATUS_OK)
+		SA_LOG_ERR_EXIT(errno, "HBA_LoadLibrary failed");
+
+	if (fp_find_hba()) {
+		sprintf(bsg_dev, "/dev/bsg/%s", fp_dev);
+		fp_fd = open(bsg_dev, O_RDWR);
+		if (fp_fd < 0)
+			SA_LOG_ERR_EXIT(errno,
+				"open of %s failed", bsg_dev);
+
+		if (!fp_lookup_target()) {
+			fp_buf_setup();
+			fp_start();
+			fp_report();
+			rc = 0;
+		}
+		close(fp_fd);
+	}
+
+	HBA_FreeLibrary();
+	return rc;
+}
diff --git a/fipvlan.c b/fipvlan.c
new file mode 100644
index 0000000..639d5ec
--- /dev/null
+++ b/fipvlan.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <netpacket/packet.h>
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include "fip.h"
+#include "log.h"
+#include "list.h"
+
+#define ARRAY_SIZE(a)	(sizeof(a) / sizeof((a)[0]))
+
+/* global configuration */
+
+char *exe;
+#define VERSION_STR	"0.5"
+
+struct iff {
+	int ifindex;
+	char *ifname;
+	unsigned char mac_addr[ETHER_ADDR_LEN];
+	struct list_head list;
+};
+
+LIST_HEAD(interfaces);
+
+struct fcf {
+	struct iff *interface;
+	uint16_t vlan;
+	unsigned char mac_addr[ETHER_ADDR_LEN];
+	struct list_head list;
+};
+
+LIST_HEAD(fcfs);
+
+/**
+ * packet_socket - create a packet socket bound to the FIP ethertype
+ */
+int packet_socket(void)
+{
+	int s;
+
+	log_debug(1, "creating ETH_P_FIP packet socket");
+	s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_FIP));
+	if (s < 0)
+		log_errno("packet socket error");
+
+	return s;
+}
+
+/**
+ * fip_send_vlan_request - send a FIP VLAN request
+ * @s: ETH_P_FIP packet socket
+ * @iff: network interface to send from
+ *
+ * Note: sends to FIP_ALL_FCF_MACS
+ */
+ssize_t fip_send_vlan_request(int s, struct iff *iff)
+{
+	struct sockaddr_ll sa = {
+		.sll_family = AF_PACKET,
+		.sll_protocol = htons(ETH_P_FIP),
+		.sll_ifindex = iff->ifindex,
+		.sll_hatype = ARPHRD_ETHER,
+		.sll_pkttype = PACKET_MULTICAST,
+		.sll_halen = ETHER_ADDR_LEN,
+		.sll_addr = FIP_ALL_FCF_MACS,
+	};
+	struct fiphdr fh = {
+		.fip_version = FIP_VERSION(1),
+		.fip_proto = htons(FIP_PROTO_VLAN),
+		.fip_subcode = FIP_VLAN_REQ,
+		.fip_desc_len = htons(2),
+		.fip_flags = 0,
+	};
+	struct {
+		struct fip_tlv_mac_addr mac;
+	} tlvs = {
+		.mac = {
+			.hdr.tlv_type = FIP_TLV_MAC_ADDR,
+			.hdr.tlv_len = 2,
+		},
+	};
+	struct iovec iov[] = {
+		{ .iov_base = &fh, .iov_len = sizeof(fh), },
+		{ .iov_base = &tlvs, .iov_len = sizeof(tlvs), },
+	};
+	struct msghdr msg = {
+		.msg_name = &sa,
+		.msg_namelen = sizeof(sa),
+		.msg_iov = iov,
+		.msg_iovlen = ARRAY_SIZE(iov),
+	};
+	int rc;
+
+	memcpy(tlvs.mac.mac_addr, iff->mac_addr, ETHER_ADDR_LEN);
+
+	log_debug(1, "sending FIP VLAN request");
+	rc = sendmsg(s, &msg, 0);
+	if (rc < 0)
+		log_errno("sendmsg error");
+
+	return rc;
+}
+
+struct fip_tlv_ptrs {
+	struct fip_tlv_mac_addr		*mac;
+	struct fip_tlv_vlan		*vlan[370];
+	unsigned int 			vlanc;
+};
+
+#define SET_BIT(b, n)	((b) |= (1 << (n)))
+
+#define TLV_LEN_CHECK(t, l) ({ \
+	int _tlc = ((t)->tlv_len != (l)) ? 1 : 0; \
+	if (_tlc) \
+		log_warn("bad length for TLV of type %d", (t)->tlv_type); \
+	_tlc; \
+})
+
+/**
+ * fip_parse_tlvs - parse type/length/value encoded FIP descriptors
+ * @ptr: pointer to beginning of FIP TLV payload, the first descriptor
+ * @len: total length of all TLVs, in double words
+ * @tlv_ptrs: pointers to type specific structures to fill out
+ */
+unsigned int fip_parse_tlvs(void *ptr, int len, struct fip_tlv_ptrs *tlv_ptrs)
+{
+	struct fip_tlv_hdr *tlv = ptr;
+	unsigned int bitmap = 0;
+
+	tlv_ptrs->vlanc = 0;
+	while (len > 0) {
+		switch (tlv->tlv_type) {
+		case FIP_TLV_MAC_ADDR:
+			if (TLV_LEN_CHECK(tlv, 2))
+				break;
+			SET_BIT(bitmap, FIP_TLV_MAC_ADDR);
+			tlv_ptrs->mac = (struct fip_tlv_mac_addr *) tlv;
+			break;
+		case FIP_TLV_VLAN:
+			if (TLV_LEN_CHECK(tlv, 1))
+				break;
+			SET_BIT(bitmap, FIP_TLV_VLAN);
+			tlv_ptrs->vlan[tlv_ptrs->vlanc++] = (void *) tlv;
+			break;
+		default:
+			/* unexpected or unrecognized descriptor */
+			log_warn("unrecognized TLV type %d", tlv->tlv_type);
+			break;
+		}
+		len -= tlv->tlv_len;
+		tlv = ((void *) tlv) + (tlv->tlv_len << 2);
+	};
+	return bitmap;
+}
+
+/**
+ * fip_recv_vlan_note - parse a FIP VLAN Notification
+ * @fh: FIP header, the beginning of the received FIP frame
+ * @len: total length of the received FIP frame
+ * @iff: interface this notification was received on
+ */
+int fip_recv_vlan_note(struct fiphdr *fh, ssize_t len, struct iff *iff)
+{
+	struct fip_tlv_ptrs tlvs;
+	struct fcf *fcf;
+	unsigned int bitmap, required_tlvs;
+	int desc_len;
+	int i;
+
+	log_debug(1, "received FIP VLAN Notification");
+
+	desc_len = ntohs(fh->fip_desc_len);
+	if (len < (sizeof(*fh) + (desc_len << 2)))
+		return -1;
+
+	required_tlvs = (1 << FIP_TLV_MAC_ADDR) | (1 << FIP_TLV_VLAN);
+
+	bitmap = fip_parse_tlvs((fh + 1), desc_len, &tlvs);
+	if ((bitmap & required_tlvs) != required_tlvs)
+		return -1;
+
+	for (i = 0; i < tlvs.vlanc; i++) {
+		fcf = malloc(sizeof(*fcf));
+		if (!fcf) {
+			log_errno("malloc failed");
+			break;
+		}
+		memset(fcf, 0, sizeof(*fcf));
+		fcf->interface = iff;
+		fcf->vlan = ntohs(tlvs.vlan[i]->vlan);
+		memcpy(fcf->mac_addr, tlvs.mac->mac_addr, ETHER_ADDR_LEN);
+		list_add_tail(&fcf->list, &fcfs);
+	}
+
+	return 0;
+}
+
+/**
+ * fip_recv - receive from a FIP packet socket
+ * @s: packet socket with data ready to be received
+ */
+int fip_recv(int s)
+{
+	char buf[4096];
+	struct sockaddr_ll sa;
+	struct iovec iov[] = {
+		{ .iov_base = &buf[0], .iov_len = sizeof(buf), },
+	};
+	struct msghdr msg = {
+		.msg_name = &sa,
+		.msg_namelen = sizeof(sa),
+		.msg_iov = iov,
+		.msg_iovlen = ARRAY_SIZE(iov),
+	};
+	struct fiphdr *fh;
+	struct iff *iff;
+	ssize_t len;
+
+	log_debug(1, "%s", __func__);
+
+	len = recvmsg(s, &msg, 0);
+	if (len < 0) {
+		log_errno("packet socket recv error");
+		return len;
+	}
+
+	if (len < sizeof(*fh)) {
+		log_err("received packed smaller that FIP header length");
+		return -1;
+	}
+
+	fh = (struct fiphdr *) buf;
+
+	/* We only care about VLAN Notifications */
+	if (ntohs(fh->fip_proto) != FIP_PROTO_VLAN) {
+		log_debug(1, "ignoring FIP packet, protocol %d",
+			  ntohs(fh->fip_proto));
+		return -1;
+	}
+
+	list_for_each_entry(iff, &interfaces, list) {
+		if (iff->ifindex == sa.sll_ifindex)
+			break;
+	}
+	if (&iff->list == &interfaces) {
+		log_warn("received packet on unexpected interface");
+		return -1;
+	}
+
+	switch (fh->fip_subcode) {
+	case FIP_VLAN_NOTE:
+		fip_recv_vlan_note(fh, len, iff);
+		break;
+	default:
+		log_warn("FIP packet with unknown subcode %d", fh->fip_subcode);
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * rtnl_socket - create and bind a routing netlink socket
+ */
+int rtnl_socket(void)
+{
+	struct sockaddr_nl sa = {
+		.nl_family = AF_NETLINK,
+		.nl_pid = getpid(),
+		.nl_groups = RTMGRP_LINK,
+	};
+	int s;
+	int rc;
+
+	log_debug(1, "creating netlink socket");
+	s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (s < 0) {
+		log_errno("netlink socket error");
+		return s;
+	}
+
+	rc = bind(s, (struct sockaddr *) &sa, sizeof(sa));
+	if (rc < 0) {
+		log_errno("netlink bind error");
+		close(s);
+		return rc;
+	}
+
+	return s;
+}
+
+/**
+ * send_getlink_dump - send an RTM_GETLINK dump request to list all interfaces
+ * @s: routing netlink socket to use
+ */
+ssize_t send_getlink_dump(int s)
+{
+	struct sockaddr_nl sa = {
+		.nl_family = AF_NETLINK,
+		.nl_pid = 0,
+	};
+	struct {
+		struct nlmsghdr nh;
+		struct ifinfomsg ifm;
+	} req = {
+		.nh = {
+			.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+			.nlmsg_type = RTM_GETLINK,
+			.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
+			.nlmsg_pid = 0,
+		},
+		.ifm = {
+			.ifi_family = AF_UNSPEC,
+			.ifi_type = ARPHRD_ETHER,
+		},
+	};
+	struct iovec iov[] = {
+		{ .iov_base = &req, .iov_len = sizeof(req), },
+	};
+	struct msghdr msg = {
+		.msg_name = &sa,
+		.msg_namelen = sizeof(sa),
+		.msg_iov = iov,
+		.msg_iovlen = ARRAY_SIZE(iov),
+	};
+	int rc;
+
+	log_debug(1, "sending RTM_GETLINK dump request");
+	rc = sendmsg(s, &msg, 0);
+	if (rc < 0)
+		log_errno("netlink sendmsg error");
+
+	return rc;
+}
+
+/**
+ * rtnl_recv_newlink - parse response to RTM_GETLINK, or an RTM_NEWLINK event
+ * @nh: netlink message header, beginning of received netlink frame
+ */
+void rtnl_recv_newlink(struct nlmsghdr *nh)
+{
+	struct ifinfomsg *ifm;
+	struct rtattr *rta;
+	struct iff *iff;
+	unsigned int len;
+
+	log_debug(1, "RTM_NEWLINK");
+
+	ifm = NLMSG_DATA(nh);
+	log_debug(1, "ifindex %d, type %d", ifm->ifi_index, ifm->ifi_type);
+
+	/* We only deal with Ethernet interfaces */
+	if (ifm->ifi_type != ARPHRD_ETHER)
+		return;
+
+	/* if there's no link, we're not going to wait for it */
+	if ((ifm->ifi_flags & IFF_RUNNING) != IFF_RUNNING)
+		return;
+
+	iff = malloc(sizeof(*iff));
+	if (!iff) {
+		log_errno("malloc failed");
+		return;
+	}
+	memset(iff, 0, sizeof(*iff));
+
+	iff->ifindex = ifm->ifi_index;
+
+	len = IFLA_PAYLOAD(nh);
+	for (rta = IFLA_RTA(ifm); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
+		switch (rta->rta_type) {
+		case IFLA_ADDRESS:
+			memcpy(iff->mac_addr, RTA_DATA(rta), ETHER_ADDR_LEN);
+			log_debug(1, "\tIFLA_ADDRESS\t%x:%x:%x:%x:%x:%x",
+					iff->mac_addr[0], iff->mac_addr[1],
+					iff->mac_addr[2], iff->mac_addr[3],
+					iff->mac_addr[4], iff->mac_addr[5]);
+			break;
+		case IFLA_IFNAME:
+			iff->ifname = strdup(RTA_DATA(rta));
+			log_debug(1, "\tIFLA_IFNAME\t%s", iff->ifname);
+			break;
+		default:
+			/* other attributes don't matter */
+			break;
+		}
+	}
+
+	list_add_tail(&iff->list, &interfaces);
+}
+
+#define NLMSG(c) ((struct nlmsghdr *) (c))
+
+/**
+ * rtnl_recv - receive from a routing netlink socket
+ * @s: routing netlink socket with data ready to be received
+ *
+ * Returns:	0 when NLMSG_DONE is received
+ * 		<0 on error
+ * 		>0 when more data is expected
+ */
+int rtnl_recv(int s)
+{
+	char buf[8192];
+	struct sockaddr_nl sa;
+	struct iovec iov[] = {
+		[0] = { .iov_base = buf, .iov_len = sizeof(buf), },
+	};
+	struct msghdr msg = {
+		.msg_name = &sa,
+		.msg_namelen = sizeof(sa),
+		.msg_iov = iov,
+		.msg_iovlen = ARRAY_SIZE(iov),
+	};
+	struct nlmsghdr *nh;
+	int len;
+	int rc;
+
+	log_debug(1, "%s", __func__);
+
+	len = recvmsg(s, &msg, 0);
+	if (len < 0) {
+		log_errno("netlink recvmsg error");
+		return len;
+	}
+
+	rc = 1;
+	for (nh = NLMSG(buf); NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
+		switch (nh->nlmsg_type) {
+		case RTM_NEWLINK:
+			rtnl_recv_newlink(nh);
+			break;
+		case NLMSG_DONE:
+			log_debug(1, "NLMSG_DONE");
+			break;
+		case NLMSG_ERROR:
+			log_debug(1, "NLMSG_ERROR");
+			break;
+		default:
+			log_warn("unexpected netlink message type %d",
+				 nh->nlmsg_type);
+			break;
+		}
+
+		if (nh->nlmsg_type == NLMSG_DONE) {
+			rc = 0;
+			break;
+		}
+		if (!(nh->nlmsg_flags & NLM_F_MULTI))
+			break;
+	}
+	return rc;
+}
+
+/* command line arguments */
+
+#define GETOPT_STR "ahv"
+
+static const struct option long_options[] = {
+	{ "auto", no_argument, NULL, 'a' },
+	{ "help", no_argument, NULL, 'h' },
+	{ "version", no_argument, NULL, 'v' },
+	{ NULL, 0, NULL, 0 }
+};
+
+static void help(int status)
+{
+	printf(
+"Usage: %s [ options ] [ network interfaces ]\n"
+"Options:\n"
+"  -a, --auto           Auto select Ethernet interfaces\n"
+"  -h, --help           Display this help and exit\n"
+"  -v, --version        Display version information and exit\n",
+	exe);
+
+	exit(status);
+}
+
+/* array of interface names to use */
+char **namev;
+/* length of namev */
+int namec;
+
+int parse_cmdline(int argc, char **argv)
+{
+	char c;
+	int automode = 0;
+
+	while (1) {
+		c = getopt_long(argc, argv, GETOPT_STR, long_options, NULL);
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'a':
+			automode = 1;
+			break;
+		case 'h':
+			help(0);
+			break;
+		case 'v':
+			printf("%s version %s\n", exe, VERSION_STR);
+			exit(0);
+			break;
+		default:
+			fprintf(stderr, "Try '%s --help' "
+					"for more information\n", exe);
+			exit(1);
+		}
+	}
+
+	if ((optind == argc) && (!automode))
+		help(1);
+
+	namev = &argv[optind];
+	namec = argc - optind;
+	return automode;
+}
+
+/* exit after waiting 2 seconds without receiving anything */
+#define TIMEOUT 2000
+
+int autodetect()
+{
+	struct pollfd pfd[1];
+	int ns;
+	int rc;
+
+	ns = rtnl_socket();
+	if (ns < 0)
+		return ns;
+
+	send_getlink_dump(ns);
+	pfd[0].fd = ns;
+	pfd[0].events = POLLIN;
+
+	while (1) {
+		rc = poll(pfd, ARRAY_SIZE(pfd), TIMEOUT);
+		log_debug(1, "return from poll %d", rc);
+		if (rc == 0) /* timeout */
+			break;
+		if (rc == -1) {
+			log_errno("poll error");
+			break;
+		}
+		if (pfd[0].revents) {
+			rc = rtnl_recv(pfd[0].fd);
+			if (rc == 0)
+				break;
+		}
+		pfd[0].revents = 0;
+	}
+	close(ns);
+	return 0;
+}
+
+int check_interface(char *name, int ps)
+{
+	struct ifreq ifr;
+	struct iff *iff;
+
+	iff = malloc(sizeof(*iff));
+	if (!iff) {
+		log_errno("malloc failed");
+		return -1;
+	}
+	memset(iff, 0, sizeof(*iff));
+
+	strncpy(ifr.ifr_name, name, IFNAMSIZ);
+	if (ioctl(ps, SIOCGIFINDEX, &ifr) != 0) {
+		log_errno("SIOCGIFINDEX");
+		goto err;
+	}
+	iff->ifname = strdup(ifr.ifr_name);
+	iff->ifindex = ifr.ifr_ifindex;
+
+	if (ioctl(ps, SIOCGIFHWADDR, &ifr) != 0) {
+		log_errno("SIOCGIFHWADDR");
+		goto err;
+	}
+	if (ifr.ifr_addr.sa_family != ARPHRD_ETHER) {
+		log_err("%s is not an Ethernet interface", name);
+		goto err;
+	}
+	memcpy(iff->mac_addr, ifr.ifr_addr.sa_data, ETHER_ADDR_LEN);
+
+	list_add_tail(&iff->list, &interfaces);
+	return 0;
+err:
+	free(iff);
+	return -1;
+}
+
+void print_results()
+{
+	struct fcf *fcf;
+
+	if (list_empty(&fcfs)) {
+		printf("No Fibre Channel Forwarders Found\n");
+		return;
+	}
+
+	printf("Fibre Channel Forwarders Discovered\n");
+	printf("%-10.10s| %-5.5s| %-10.10s\n", "interface", "VLAN", "FCF MAC");
+	printf("------------------------------------\n");
+	list_for_each_entry(fcf, &fcfs, list) {
+		printf("%-10.10s| %-5d| %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+			fcf->interface->ifname, fcf->vlan,
+			fcf->mac_addr[0], fcf->mac_addr[1], fcf->mac_addr[2],
+			fcf->mac_addr[3], fcf->mac_addr[4], fcf->mac_addr[5]);
+	}
+}
+
+void recv_loop(int ps)
+{
+	struct pollfd pfd[1] = {
+		[0].fd = ps,
+		[0].events = POLLIN,
+	};
+	int rc;
+
+	while (1) {
+		rc = poll(pfd, ARRAY_SIZE(pfd), TIMEOUT);
+		log_debug(1, "return from poll %d", rc);
+		if (rc == 0) /* timeout */
+			break;
+		if (rc == -1) {
+			log_errno("poll error");
+			break;
+		}
+		if (pfd[0].revents)
+			fip_recv(pfd[0].fd);
+		pfd[0].revents = 0;
+	}
+}
+
+int main(int argc, char **argv)
+{
+	int ps;
+	struct iff *iff;
+	int i;
+	int automode;
+
+	exe = strrchr(argv[0], '/');
+	if (exe)
+		exe++;
+	else
+		exe = argv[0];
+
+	automode = parse_cmdline(argc, argv);
+	log_start(exe, 0, 0);
+
+	ps = packet_socket();
+
+	if (automode) {
+		autodetect();
+	} else {
+		for (i = 0; i < namec; i++)
+			check_interface(namev[i], ps);
+	}
+
+	if (list_empty(&interfaces)) {
+		log_err("no interfaces to perform discovery on");
+		close(ps);
+		log_stop();
+		exit(1);
+	}
+
+	list_for_each_entry(iff, &interfaces, list)
+		fip_send_vlan_request(ps, iff);
+
+	recv_loop(ps);
+	print_results();
+
+	close(ps);
+	log_stop();
+	exit(0);
+}
+
diff --git a/include/fip.h b/include/fip.h
new file mode 100644
index 0000000..91a017f
--- /dev/null
+++ b/include/fip.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef FIP_H
+#define FIP_H
+
+#define ETH_P_FCOE	0x8906
+#define ETH_P_FIP	0x8914
+
+#define FIP_ALL_FCOE_MACS	{ 0x01, 0x10, 0x18, 0x01, 0x00, 0x00 }
+#define FIP_ALL_FCF_MACS	{ 0x01, 0x10, 0x18, 0x01, 0x00, 0x01 }
+#define FIP_ALL_ENODE_MACS	{ 0x01, 0x10, 0x18, 0x01, 0x00, 0x02 }
+
+struct fiphdr {
+	uint8_t		fip_version;	/* version, upper 4 bits only */
+	uint8_t		__resvd_0;
+	uint16_t	fip_proto;	/* protocol code */
+	uint8_t		__resvd_1;
+	uint8_t		fip_subcode;	/* subcode */
+	uint16_t	fip_desc_len;	/* descriptor list length */
+	uint16_t	fip_flags;
+};
+
+#define FIP_VERSION(n)	(n << 4)
+#define FIP_F_FP	(1 << 15)	/* FPMA supported/requested/granted */
+#define FIP_F_SP	(1 << 14)	/* SPMA supported/requested/granted */
+#define FIP_F_A		(1 << 2)	/* Available for Login */
+#define FIP_F_S		(1 << 1)	/* Solicited advertisement */
+#define FIP_F_F		(1 << 0)	/* FCF */
+
+/* FCF Discovery Protocol */
+#define FIP_PROTO_DISC	1
+#define FIP_DISC_SOL	1
+#define FIP_DISC_ADV	2
+
+/* Virtual Link Instantiation (encapsulated ELS) */
+#define FIP_PROTO_VLI	2
+#define FIP_VLI_REQ	1
+#define FIP_VLI_REPLY	2
+
+/* FIP Keep Alive */
+#define FIP_PROTO_FKA	3
+#define FIP_FKA		1
+#define FIP_FKA_CLEAR	2
+
+/* VLAN Discovery */
+#define FIP_PROTO_VLAN	4
+#define FIP_VLAN_REQ	1
+#define FIP_VLAN_NOTE	2
+
+struct fip_tlv_hdr {
+	uint8_t		tlv_type;
+	uint8_t		tlv_len;	/* length in quad-words of entire TLV */
+};
+
+#define FIP_TLV_PRIORITY		1
+#define FIP_TLV_MAC_ADDR		2
+#define FIP_TLV_FC_MAP			3
+#define FIP_TLV_NAME_IDENTIFIER		4
+#define FIP_TLV_FABRIC_NAME		5
+#define FIP_TLV_MAX_RECV_SIZE		6
+#define FIP_TLV_FLOGI			7
+#define FIP_TLV_FDISC			8
+#define FIP_TLV_LOGO			9
+#define FIP_TLV_ELP			10
+
+#define FIP_TLV_VLAN			14
+
+#define DEFAULT_FIP_PRIORITY		128
+
+/* Priority Descriptor */
+struct fip_tlv_priority {
+	struct fip_tlv_hdr hdr;
+	unsigned char __resvd;
+	uint8_t priority;
+};
+
+/* MAC Address Descriptor */
+struct fip_tlv_mac_addr {
+	struct fip_tlv_hdr hdr;
+	unsigned char mac_addr[ETHER_ADDR_LEN];
+};
+
+/* FC-MAP Descriptor */
+struct fip_tlv_fc_map {
+	struct fip_tlv_hdr hdr;
+	unsigned char __resvd[3];
+	uint8_t map[3];
+};
+
+/* Name Identifier Descriptor (also used for Fabric Name Descriptor) */
+struct fip_tlv_name_id {
+	struct fip_tlv_hdr hdr;
+	unsigned char __resvd[2];
+	unsigned char wwn[8];
+};
+
+/* Max Receive Size Descriptor */
+struct fip_tlv_max_recv_size {
+	struct fip_tlv_hdr hdr;
+	uint16_t mtu;
+};
+
+/* VLAN */
+struct fip_tlv_vlan {
+	struct fip_tlv_hdr hdr;
+	uint16_t vlan;	/* only lower 12 bits matter */
+};
+
+#endif /* FIP_H */
diff --git a/include/list.h b/include/list.h
new file mode 100644
index 0000000..35da9fd
--- /dev/null
+++ b/include/list.h
@@ -0,0 +1,444 @@
+/* Adapted from the Linux kernel, under the terms of the GPLv2 */
+
+#ifndef LIST_H
+#define LIST_H
+
+#include <stddef.h>
+
+#define container_of(ptr, type, member) ({ \
+	const typeof(((type *) 0)->member) * __mptr = (ptr); \
+	(type *)((char *) __mptr - offsetof(type, member)); \
+})
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->next = NULL;
+	entry->prev = NULL;
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+				struct list_head *new)
+{
+	new->next = old->next;
+	new->next->prev = new;
+	new->prev = old->prev;
+	new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+					struct list_head *new)
+{
+	list_replace(old, new);
+	INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+	__list_del(list->prev, list->next);
+	list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+				  struct list_head *head)
+{
+	__list_del(list->prev, list->next);
+	list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+				const struct list_head *head)
+{
+	return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+	struct list_head *next = head->next;
+	return (next == head) && (next == head->prev);
+}
+
+/**
+ * list_is_singular - tests whether a list has just one entry.
+ * @head: the list to test.
+ */
+static inline int list_is_singular(const struct list_head *head)
+{
+	return !list_empty(head) && (head->next == head->prev);
+}
+
+static inline void __list_splice(const struct list_head *list,
+				 struct list_head *head)
+{
+	struct list_head *first = list->next;
+	struct list_head *last = list->prev;
+	struct list_head *at = head->next;
+
+	first->prev = head;
+	head->next = first;
+
+	last->next = at;
+	at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(const struct list_head *list,
+				struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+				    struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr:	the list head to take the element from.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+	list_entry((ptr)->next, type, member)
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev	-	iterate over a list backwards
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; \
+	     pos != (head); \
+	     pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+	for (pos = (head)->prev, n = pos->prev; \
+	     pos != (head); \
+	     pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry	-	iterate over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member);	\
+	     &pos->member != (head); 	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)			\
+	for (pos = list_entry((head)->prev, typeof(*pos), member);	\
+	     &pos->member != (head); 	\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos:	the type * to use as a start point
+ * @head:	the head of the list
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Prepares a pos entry for use as a start point in
+ * list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+	((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member) 		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member)		\
+	for (pos = list_entry(pos->member.prev, typeof(*pos), member);	\
+	     &pos->member != (head);	\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member) 			\
+	for (; &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member)		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member), 	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member) 		\
+	for (n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member)		\
+	for (pos = list_entry((head)->prev, typeof(*pos), member),	\
+		n = list_entry(pos->member.prev, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+#endif /* LIST_H */
diff --git a/include/log.h b/include/log.h
new file mode 100644
index 0000000..7a2a138
--- /dev/null
+++ b/include/log.h
@@ -0,0 +1,15 @@
+#ifndef LOG_H
+#define LOG_H
+
+void log_start(char *program, int daemon, int level);
+void log_stop();
+void do_log(int priority, const char *fmt, va_list ap);
+void log_debug(int level, char *fmt, ...);
+void log_warn(char *fmt, ...);
+void log_err(char *fmt, ...);
+void _log_errno(const char *func, char *call, int errnum);
+
+#define log_errno(s) _log_errno(__func__, s, errno)
+
+#endif /* LOG_H */
+
diff --git a/log.c b/log.c
new file mode 100644
index 0000000..a99ab93
--- /dev/null
+++ b/log.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <string.h>
+
+static int log_syslog;
+static int log_debug_level;
+static char log_prefix[256];
+
+void log_start(char *program, int daemon, int level)
+{
+	log_syslog = daemon;
+	log_debug_level = level;
+	strncpy(log_prefix, program, 256);
+	log_prefix[255] = '\0';
+
+	if (log_syslog)
+		openlog(log_prefix, 0, LOG_DAEMON);
+}
+
+void log_stop()
+{
+	if (log_syslog)
+		closelog();
+}
+
+void do_log(int priority, const char *fmt, va_list ap)
+{
+	if (log_syslog)
+		vsyslog(priority, fmt, ap);
+	else {
+		printf("%s: ", log_prefix);
+		vprintf(fmt, ap);
+		printf("\n");
+	}
+}
+
+void log_debug(int level, char *fmt, ...)
+{
+	va_list ap;
+	if (log_debug_level >= level) {
+		va_start(ap, fmt);
+		do_log(LOG_DEBUG, fmt, ap);
+		va_end(ap);
+	}
+}
+
+void log_warn(char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	do_log(LOG_WARNING, fmt, ap);
+	va_end(ap);
+}
+
+void log_err(char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	do_log(LOG_ERR, fmt, ap);
+	va_end(ap);
+}
+
+void _log_errno(const char *func, char *call, int errnum)
+{
+	log_err("%s %s: %s", func, call, strerror(errnum));
+}
+
openSUSE Build Service is sponsored by